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 });