1 /** 2 * image dialog (support upload and remote) 3 * @author yiminghe@gmail.com 4 */ 5 KISSY.add("editor/plugin/image/dialog", function (S, IO, Editor, Overlay4E, Switchable, MenuButton) { 6 var dtd = Editor.XHTML_DTD, 7 UA = S.UA, 8 Node = S.Node, 9 HTTP_TIP = "http://", 10 AUTOMATIC_TIP = "自动", 11 MARGIN_DEFAULT = 10, 12 IMAGE_DIALOG_BODY_HTML = "<div class='ks-editor-image-wrap'>" + 13 "<ul class='ks-editor-tabs ks-clear ks-switchable-nav'>" + 14 "<li " + 15 "class='ks-active' " + 16 "" + 17 "hide" + 18 "focus" + 19 "='hide" + 20 "focus'>" + 21 "网络图片" + 22 "</li>" + 23 "<li " + 24 "hide" + 25 "focus" + 26 "='hide" + 27 "focus'>" + 28 "本地上传" + 29 "</li>" + 30 "</ul>" + 31 "<div style='" + 32 "padding:12px 20px 5px 20px;'>" + 33 "<div class='ks-editor-image-tabs-content-wrap ks-switchable-content' " + 34 ">" + 35 "<div>" + 36 "<label>" + 37 "<span " + 38 "class='ks-editor-image-title'" + 39 ">" + 40 "图片地址: " + 41 "</span>" + 42 "<input " + 43 " data-verify='^(https?:/)?/[^\\s]+$' " + 44 " data-warning='网址格式为:http:// 或 /' " + 45 "class='ks-editor-img-url ks-editor-input' " + 46 "style='width:390px;vertical-align:middle;' " + 47 "/>" + 48 "</label>" + 49 "</div>" + 50 "<div style='position:relative;display: none'>" + 51 "<form class='ks-editor-img-upload-form' enctype='multipart/form-data'>" + 52 "<p style='zoom:1;'>" + 53 "<input class='ks-editor-input ks-editor-img-local-url' " + 54 "readonly='readonly' " + 55 "style='margin-right: 15px; " + 56 "vertical-align: middle; " + 57 "width: 368px;" + 58 "color:#969696;'/>" + 59 "<a " + 60 "style='padding:3px 11px;" + 61 "position:absolute;" + 62 "left:390px;" + 63 "top:0px;" + 64 "z-index:1;' " + 65 "class='ks-editor-image-up ks-editor-button ks-inline-block'>浏览...</a>" + 66 "</p>" + 67 "<div class='ks-editor-img-up-extraHtml'>" + 68 "</div>" + 69 "</form>" + 70 "</div>" + 71 "</div>" + 72 "<table " + 73 "style='width:100%;margin-top:8px;' " + 74 "class='ks-editor-img-setting'>" + 75 "<tr>" + 76 "<td>" + 77 "<label>" + 78 "宽度: " + 79 "<input " + 80 " data-verify='^(" + AUTOMATIC_TIP + "|((?!0$)\\d+))?$' " + 81 " data-warning='宽度请输入正整数' " + 82 "class='ks-editor-img-width ks-editor-input' " + 83 "style='vertical-align:middle;width:60px' " + 84 "/> 像素 </label>" + 85 "</td>" + 86 "<td>" + 87 "<label>" + 88 "高度: " + 89 "<input " + 90 " data-verify='^(" + AUTOMATIC_TIP + "|((?!0$)\\d+))?$' " + 91 " data-warning='高度请输入正整数' " + 92 "class='ks-editor-img-height ks-editor-input' " + 93 "style='vertical-align:middle;width:60px' " + 94 "/> 像素 </label>" + 95 "<label>" + 96 "<input " + 97 "type='checkbox' " + 98 "class='ks-editor-img-ratio' " + 99 "style='vertical-align:middle;" + 100 "margin-left:5px;" + 101 "' " + 102 "checked='checked'/>" + 103 " 锁定高宽比" + 104 "</label>" + 105 "</td>" + 106 "</tr>" + 107 "<tr>" + 108 "<td>" + 109 "<label>" + 110 "对齐:" + 111 "<select class='ks-editor-img-align' title='对齐'>" + 112 "<option value='none'>无</option>" + 113 "<option value='left'>左对齐</option>" + 114 "<option value='right'>右对齐</option>" + 115 "</select>" + 116 "</label>" + 117 "</td>" + 118 "<td><label>" + 119 "间距: " + 120 "<input " + 121 "" + 122 " data-verify='^\\d+$' " + 123 " data-warning='间距请输入非负整数' " + 124 "class='ks-editor-img-margin ks-editor-input' style='width:60px'/> 像素" + 125 "</label>" + 126 "</td>" + 127 "</tr>" + 128 "<tr>" + 129 "<td colspan='2' style='padding-top: 6px'>" + 130 "<label>" + 131 "链接网址: " + 132 "<input " + 133 "class='ks-editor-img-link ks-editor-input' " + 134 "style='width:235px;vertical-align:middle;' " + 135 " data-verify='^(?:(?:\\s*)|(?:https?://[^\\s]+)|(?:#.+))$' " + 136 " data-warning='请输入合适的网址格式' " + 137 "/>" + 138 "</label>" + 139 "<label>" + 140 "<input " + 141 "class='ks-editor-img-link-blank' " + 142 "style='vertical-align:middle;" + 143 "margin-left:5px;" + 144 "' " + 145 "type='checkbox'/>" + 146 " 在新窗口打开链接" + 147 "</label>" + 148 "</td>" + 149 "</tr>" + 150 "</table>" + 151 "</div>" + 152 "</div>", 153 154 IMAGE_DIALOG_FOOT_HTML = "<div style='padding:5px 20px 20px;'>" + 155 "<a " + 156 "href='javascript:void(\'确定\')' " + 157 "class='ks-editor-img-insert ks-editor-button ks-inline-block' " + 158 "style='margin-right:30px;'>确定</a> " + 159 "<a " + 160 "href='javascript:void(\'取消\')' " + 161 "class='ks-editor-img-cancel ks-editor-button ks-inline-block'>取消</a></div>", 162 163 warning = "请点击浏览上传图片", 164 165 valInput = Editor.Utils.valInput; 166 167 function findAWithImg(img) { 168 var ret = img.parent(); 169 while (ret) { 170 var name = ret.nodeName(); 171 if (name == "a") { 172 return ret; 173 } 174 if (dtd.$block[name] || dtd.$blockLimit[name]) { 175 return null; 176 } 177 ret = ret.parent(); 178 } 179 return null; 180 } 181 182 183 function ImageDialog(editor, config) { 184 var self = this; 185 self.editor = editor; 186 self.imageCfg = config || {}; 187 self.cfg = self.imageCfg["upload"] || null; 188 self.suffix = self.cfg && self.cfg["suffix"] || "png,jpg,jpeg,gif"; 189 // 不要加g:http://yiminghe.javaeye.com/blog/581347 190 self.suffix_reg = new RegExp(self.suffix.split(/,/).join("|") + "$", "i"); 191 self.suffix_warning = "只允许后缀名为" + self.suffix + "的图片"; 192 } 193 194 S.augment(ImageDialog, { 195 _prepare:function () { 196 var self = this; 197 self.dialog = self.d = new Overlay4E.Dialog({ 198 autoRender:true, 199 width:500, 200 headerContent:"图片", 201 bodyContent:IMAGE_DIALOG_BODY_HTML, 202 footerContent:IMAGE_DIALOG_FOOT_HTML, 203 mask:true 204 }); 205 206 var content = self.d.get("el"), 207 cancel = content.one(".ks-editor-img-cancel"), 208 ok = content.one(".ks-editor-img-insert"), 209 verifyInputs = Editor.Utils.verifyInputs, 210 commonSettingTable = content.one(".ks-editor-img-setting"); 211 self.uploadForm = content.one(".ks-editor-img-upload-form"); 212 self.imgLocalUrl = content.one(".ks-editor-img-local-url"); 213 self.tab = new Switchable['Tabs'](self.d.get("body")[0], { 214 triggerType:"click" 215 }); 216 self.imgLocalUrl.val(warning); 217 self.imgUrl = content.one(".ks-editor-img-url"); 218 self.imgHeight = content.one(".ks-editor-img-height"); 219 self.imgWidth = content.one(".ks-editor-img-width"); 220 self.imgRatio = content.one(".ks-editor-img-ratio"); 221 self.imgAlign = MenuButton.Select.decorate(content.one(".ks-editor-img-align"), { 222 prefixCls:'ks-editor-big-', 223 elAttrs:{ 224 hideFocus:"hideFocus" 225 }, 226 width:80, 227 menuCfg:{ 228 prefixCls:'ks-editor-', 229 render:content 230 } 231 }); 232 self.imgMargin = content.one(".ks-editor-img-margin"); 233 self.imgLink = content.one(".ks-editor-img-link"); 234 self.imgLinkBlank = content.one(".ks-editor-img-link-blank"); 235 var placeholder = Editor.Utils.placeholder; 236 placeholder(self.imgUrl, HTTP_TIP); 237 placeholder(self.imgHeight, AUTOMATIC_TIP); 238 placeholder(self.imgWidth, AUTOMATIC_TIP); 239 placeholder(self.imgLink, "http://"); 240 241 self.imgHeight.on("keyup", function () { 242 var v = parseInt(valInput(self.imgHeight)); 243 if (!v || 244 !self.imgRatio[0].checked || 245 self.imgRatio[0].disabled || 246 !self.imgRatioValue) { 247 return; 248 } 249 valInput(self.imgWidth, Math.floor(v * self.imgRatioValue)); 250 }); 251 252 self.imgWidth.on("keyup", function () { 253 var v = parseInt(valInput(self.imgWidth)); 254 if (!v || 255 !self.imgRatio[0].checked || 256 self.imgRatio[0].disabled || 257 !self.imgRatioValue) { 258 return; 259 } 260 valInput(self.imgHeight, Math.floor(v / self.imgRatioValue)); 261 }); 262 263 cancel.on("click", function (ev) { 264 self.d.hide(); 265 ev.halt(); 266 }); 267 268 var loadingCancel = new Node("<a class='ks-editor-button ks-inline-block' " + 269 "style='position:absolute;" + 270 "z-index:" + 271 Editor.baseZIndex(Editor.zIndexManager.LOADING_CANCEL) + ";" + 272 "left:-9999px;" + 273 "top:-9999px;" + 274 "'>取消上传</a>").appendTo(document.body, undefined); 275 276 self.loadingCancel = loadingCancel; 277 278 function getFileSize(file) { 279 if (file['files']) { 280 return file['files'][0].size; 281 } else if (1 > 2) { 282 //ie 会安全警告 283 try { 284 var fso = new ActiveXObject("Scripting.FileSystemObject"), 285 file2 = fso['GetFile'](file.value); 286 return file2.size; 287 } catch (e) { 288 S.log(e.message); 289 } 290 } 291 return 0; 292 } 293 294 ok.on("click", function (ev) { 295 ev.halt(); 296 if (self.tab['activeIndex'] == 1 && self.cfg) { 297 298 if (!verifyInputs(commonSettingTable.all("input"))) { 299 return; 300 } 301 302 if (self.imgLocalUrl.val() == warning) { 303 alert("请先选择文件!"); 304 return; 305 } 306 307 if (!self.suffix_reg.test(self.imgLocalUrl.val())) { 308 alert(self.suffix_warning); 309 // 清除已选文件 ie 不能使用 val("") 310 self.uploadForm[0].reset(); 311 self.imgLocalUrl.val(warning); 312 return; 313 } 314 315 var size = (getFileSize(self.fileInput[0])); 316 317 if (sizeLimit && sizeLimit < (size / 1000)) { 318 alert("上传图片最大:" + sizeLimit / 1000 + "M"); 319 return; 320 } 321 322 self.d.loading(); 323 324 /** 325 * 取消当前iframe的上传 326 */ 327 loadingCancel.on("click", function (ev) { 328 ev.halt(); 329 uploadIO.abort(); 330 }); 331 332 var uploadIO = IO({ 333 data:Editor.Utils.normParams(self.cfg['serverParams']), 334 url:self.cfg['serverUrl'], 335 form:self.uploadForm[0], 336 dataType:'json', 337 type:'post', 338 complete:function (data, status) { 339 loadingCancel.css({ 340 left:-9999, 341 top:-9999 342 }); 343 self.d.unloading(); 344 if (status == "abort") { 345 return; 346 } 347 if (!data) { 348 data = {error:"服务器出错,请重试"}; 349 } 350 if (data.error) { 351 alert(data.error); 352 return; 353 } 354 valInput(self.imgUrl, data['imgUrl']); 355 // chrome 中空 iframe 的 img 请求 header 中没有 refer 356 // 在主页面先请求一次,带入 header 357 new Image().src = data['imgUrl']; 358 self._insert(); 359 } 360 }); 361 362 var loadingMaskEl = self.d.get("el"), 363 offset = loadingMaskEl.offset(), 364 width = loadingMaskEl[0].offsetWidth, 365 height = loadingMaskEl[0].offsetHeight; 366 367 loadingCancel.css({ 368 left:(offset.left + width / 2.5), 369 top:(offset.top + height / 1.5) 370 }); 371 372 } else { 373 if (!verifyInputs(content.all("input"))) 374 return; 375 self._insert(); 376 } 377 }); 378 379 if (self.cfg) { 380 if (self.cfg['extraHtml']) { 381 content.one(".ks-editor-img-up-extraHtml") 382 .html(self.cfg['extraHtml']); 383 } 384 var ke_image_up = content.one(".ks-editor-image-up"), 385 sizeLimit = self.cfg && self.cfg['sizeLimit']; 386 387 self.fileInput = new Node("<input " + 388 "type='file' " + 389 "style='position:absolute;" + 390 "cursor:pointer;" + 391 "left:" + 392 (UA['ie'] ? "360" : (UA["chrome"] ? "319" : "369")) + 393 "px;" + 394 "z-index:2;" + 395 "top:0px;" + 396 "height:26px;' " + 397 "size='1' " + 398 "name='" + (self.cfg['fileInput'] || "Filedata") + "'/>") 399 .insertAfter(self.imgLocalUrl); 400 if (sizeLimit) 401 warning = "单张图片容量不超过 " + (sizeLimit / 1000) + " M"; 402 self.imgLocalUrl.val(warning); 403 self.fileInput.css("opacity", 0); 404 self.fileInput.on("mouseenter", function () { 405 ke_image_up.addClass("ks-editor-button-hover"); 406 }); 407 self.fileInput.on("mouseleave", function () { 408 ke_image_up.removeClass("ks-editor-button-hover"); 409 }); 410 self.fileInput.on("change", function () { 411 var file = self.fileInput.val(); 412 //去除路径 413 self.imgLocalUrl.val(file.replace(/.+[\/\\]/, "")); 414 }); 415 416 if (self.imageCfg['remote'] === false) { 417 self.tab.remove(0); 418 } 419 } 420 else { 421 self.tab.remove(1); 422 } 423 424 self._prepare = S.noop; 425 }, 426 427 _insert:function () { 428 var self = this, 429 url = valInput(self.imgUrl), 430 img, 431 height = parseInt(valInput(self.imgHeight)), 432 width = parseInt(valInput(self.imgWidth)), 433 align = self.imgAlign.get("value"), 434 margin = parseInt(self.imgMargin.val()), 435 style = ''; 436 437 if (height) { 438 style += "height:" + height + "px;"; 439 } 440 if (width) { 441 style += "width:" + width + "px;"; 442 } 443 if (align != 'none') { 444 style += "float:" + align + ";"; 445 } 446 if (!isNaN(margin) && margin != 0) { 447 style += "margin:" + margin + "px;"; 448 } 449 450 self.d.hide(); 451 452 /** 453 * 2011-01-05 454 * <a><img></a> 这种结构,a不要设成 position:absolute 455 * 否则img select 不到?!!: editor.getSelection().selectElement(img) 选择不到 456 */ 457 if (self.selectedEl) { 458 img = self.selectedEl; 459 self.editor.execCommand("save"); 460 self.selectedEl.attr({ 461 src:url, 462 //注意设置,取的话要从 _ke_saved_src 里取 463 _ke_saved_src:url, 464 style:style 465 }); 466 } else { 467 img = new Node("<img " + 468 (style ? ("style='" + 469 style + 470 "'") : "") + 471 " src='" + 472 url + 473 "' " + 474 "_ke_saved_src='" + 475 url + 476 "' alt='' />", null, self.editor.get("document")[0]); 477 self.editor.insertElement(img); 478 } 479 480 // need a breath for firefox 481 // else insertElement(img); img[0].parentNode==null 482 setTimeout(function () { 483 var link = findAWithImg(img), 484 linkVal = S.trim(valInput(self.imgLink)), 485 sel = self.editor.getSelection(), 486 bs; 487 if (link) { 488 if (linkVal) { 489 link.attr("_ke_saved_href", linkVal) 490 .attr("href", linkVal) 491 .attr("target", self.imgLinkBlank.attr("checked") ? "_blank" : "_self"); 492 //editor.notifySelectionChange(); 493 } else { 494 // 删除 495 bs = sel.createBookmarks(); 496 link._4e_remove(true); 497 } 498 } else if (linkVal) { 499 // 新增需要 bookmark,标记 500 bs = sel.createBookmarks(); 501 link = new Node("<a></a>"); 502 link.attr("_ke_saved_href", linkVal) 503 .attr("href", linkVal) 504 .attr("target", self.imgLinkBlank.attr("checked") ? "_blank" : "_self"); 505 var t = img[0]; 506 t.parentNode.replaceChild(link[0], t); 507 link.append(t); 508 } 509 if (bs) { 510 sel.selectBookmarks(bs); 511 } 512 513 if (self.selectedEl) { 514 self.editor.execCommand("save"); 515 } 516 }, 100); 517 }, 518 519 _update:function (_selectedEl) { 520 var self = this, 521 active = 0, 522 resetInput = Editor.Utils.resetInput; 523 self.selectedEl = _selectedEl; 524 if (self.selectedEl && self.imageCfg['remote'] !== false) { 525 valInput(self.imgUrl, self.selectedEl.attr("src")); 526 var w = self.selectedEl.width(), 527 h = self.selectedEl.height(); 528 valInput(self.imgHeight, h); 529 valInput(self.imgWidth, w); 530 self.imgAlign.set("value", self.selectedEl.style("float") || "none"); 531 var margin = parseInt(self.selectedEl.style("margin")) 532 || 0; 533 self.imgMargin.val(margin); 534 self.imgRatio[0].disabled = false; 535 self.imgRatioValue = w / h; 536 var link = findAWithImg(self.selectedEl); 537 if (link) { 538 valInput(self.imgLink, link.attr("_ke_saved_href") || link.attr("href")); 539 self.imgLinkBlank.attr("checked", link.attr("target") == "_blank"); 540 } else { 541 resetInput(self.imgLink); 542 self.imgLinkBlank.attr("checked", true); 543 } 544 } else { 545 var defaultMargin = self.imageCfg['defaultMargin']; 546 if (defaultMargin == undefined) { 547 defaultMargin = MARGIN_DEFAULT; 548 } 549 if (self.tab['panels'].length == 2) { 550 active = 1; 551 } 552 self.imgLinkBlank.attr("checked", true); 553 resetInput(self.imgUrl); 554 resetInput(self.imgLink); 555 resetInput(self.imgHeight); 556 resetInput(self.imgWidth); 557 self.imgAlign.set("value", "none"); 558 self.imgMargin.val(defaultMargin); 559 self.imgRatio[0].disabled = true; 560 self.imgRatioValue = null; 561 } 562 self.uploadForm[0].reset(); 563 self.imgLocalUrl.val(warning); 564 self.tab['switchTo'](active); 565 }, 566 show:function (_selectedEl) { 567 var self = this; 568 self._prepare(); 569 self._update(_selectedEl); 570 self.d.show(); 571 }, 572 destroy:function () { 573 var self = this; 574 self.d.destroy(); 575 self.tab.destroy(); 576 if (self.loadingCancel) { 577 self.loadingCancel.remove(); 578 } 579 if (self.imgAlign) { 580 self.imgAlign.destroy(); 581 } 582 } 583 }); 584 585 return ImageDialog; 586 }, { 587 requires:['ajax', 'editor', '../overlay/', 'switchable', '../menubutton/'] 588 });