Another attempt to fix the http blog redirects.
[WebKit-https.git] / Websites / webkit.org / blog / wp-includes / js / tinymce / plugins / image / plugin.js
1 /**
2  * plugin.js
3  *
4  * Copyright, Moxiecode Systems AB
5  * Released under LGPL License.
6  *
7  * License: http://www.tinymce.com/license
8  * Contributing: http://www.tinymce.com/contributing
9  */
10
11 /*global tinymce:true */
12
13 tinymce.PluginManager.add('image', function(editor) {
14         function getImageSize(url, callback) {
15                 var img = document.createElement('img');
16
17                 function done(width, height) {
18                         if (img.parentNode) {
19                                 img.parentNode.removeChild(img);
20                         }
21
22                         callback({width: width, height: height});
23                 }
24
25                 img.onload = function() {
26                         done(img.clientWidth, img.clientHeight);
27                 };
28
29                 img.onerror = function() {
30                         done();
31                 };
32
33                 var style = img.style;
34                 style.visibility = 'hidden';
35                 style.position = 'fixed';
36                 style.bottom = style.left = 0;
37                 style.width = style.height = 'auto';
38
39                 document.body.appendChild(img);
40                 img.src = url;
41         }
42
43         function buildListItems(inputList, itemCallback, startItems) {
44                 function appendItems(values, output) {
45                         output = output || [];
46
47                         tinymce.each(values, function(item) {
48                                 var menuItem = {text: item.text || item.title};
49
50                                 if (item.menu) {
51                                         menuItem.menu = appendItems(item.menu);
52                                 } else {
53                                         menuItem.value = item.value;
54                                         itemCallback(menuItem);
55                                 }
56
57                                 output.push(menuItem);
58                         });
59
60                         return output;
61                 }
62
63                 return appendItems(inputList, startItems || []);
64         }
65
66         function createImageList(callback) {
67                 return function() {
68                         var imageList = editor.settings.image_list;
69
70                         if (typeof imageList == "string") {
71                                 tinymce.util.XHR.send({
72                                         url: imageList,
73                                         success: function(text) {
74                                                 callback(tinymce.util.JSON.parse(text));
75                                         }
76                                 });
77                         } else if (typeof imageList == "function") {
78                                 imageList(callback);
79                         } else {
80                                 callback(imageList);
81                         }
82                 };
83         }
84
85         function showDialog(imageList) {
86                 var win, data = {}, dom = editor.dom, imgElm = editor.selection.getNode();
87                 var width, height, imageListCtrl, classListCtrl, imageDimensions = editor.settings.image_dimensions !== false;
88
89                 function recalcSize() {
90                         var widthCtrl, heightCtrl, newWidth, newHeight;
91
92                         widthCtrl = win.find('#width')[0];
93                         heightCtrl = win.find('#height')[0];
94
95                         if (!widthCtrl || !heightCtrl) {
96                                 return;
97                         }
98
99                         newWidth = widthCtrl.value();
100                         newHeight = heightCtrl.value();
101
102                         if (win.find('#constrain')[0].checked() && width && height && newWidth && newHeight) {
103                                 if (width != newWidth) {
104                                         newHeight = Math.round((newWidth / width) * newHeight);
105
106                                         if (!isNaN(newHeight)) {
107                                                 heightCtrl.value(newHeight);
108                                         }
109                                 } else {
110                                         newWidth = Math.round((newHeight / height) * newWidth);
111
112                                         if (!isNaN(newWidth)) {
113                                                 widthCtrl.value(newWidth);
114                                         }
115                                 }
116                         }
117
118                         width = newWidth;
119                         height = newHeight;
120                 }
121
122                 function onSubmitForm() {
123                         function waitLoad(imgElm) {
124                                 function selectImage() {
125                                         imgElm.onload = imgElm.onerror = null;
126
127                                         if (editor.selection) {
128                                                 editor.selection.select(imgElm);
129                                                 editor.nodeChanged();
130                                         }
131                                 }
132
133                                 imgElm.onload = function() {
134                                         if (!data.width && !data.height && imageDimensions) {
135                                                 dom.setAttribs(imgElm, {
136                                                         width: imgElm.clientWidth,
137                                                         height: imgElm.clientHeight
138                                                 });
139                                                 //WP
140                                                 editor.fire( 'wpNewImageRefresh', { node: imgElm } );
141                                         }
142
143                                         selectImage();
144                                 };
145
146                                 imgElm.onerror = selectImage;
147                         }
148
149                         updateStyle();
150                         recalcSize();
151
152                         data = tinymce.extend(data, win.toJSON());
153                         var caption = data.caption; // WP
154
155                         if (!data.alt) {
156                                 data.alt = '';
157                         }
158
159                         if (!data.title) {
160                                 data.title = '';
161                         }
162
163                         if (data.width === '') {
164                                 data.width = null;
165                         }
166
167                         if (data.height === '') {
168                                 data.height = null;
169                         }
170
171                         if (!data.style) {
172                                 data.style = null;
173                         }
174
175                         // Setup new data excluding style properties
176                         /*eslint dot-notation: 0*/
177                         data = {
178                                 src: data.src,
179                                 alt: data.alt,
180                                 title: data.title,
181                                 width: data.width,
182                                 height: data.height,
183                                 style: data.style,
184                                 "class": data["class"]
185                         };
186
187                         editor.undoManager.transact(function() {
188                                 // WP
189                                 var eventData = { node: imgElm, data: data, caption: caption };
190
191                                 editor.fire( 'wpImageFormSubmit', { imgData: eventData } );
192
193                                 if ( eventData.cancel ) {
194                                         waitLoad( eventData.node );
195                                         return;
196                                 }
197                                 // WP end
198
199                                 if (!data.src) {
200                                         if (imgElm) {
201                                                 dom.remove(imgElm);
202                                                 editor.focus();
203                                                 editor.nodeChanged();
204                                         }
205
206                                         return;
207                                 }
208
209                                 if (data.title === "") {
210                                         data.title = null;
211                                 }
212
213                                 if (!imgElm) {
214                                         data.id = '__mcenew';
215                                         editor.focus();
216                                         editor.selection.setContent(dom.createHTML('img', data));
217                                         imgElm = dom.get('__mcenew');
218                                         dom.setAttrib(imgElm, 'id', null);
219                                 } else {
220                                         dom.setAttribs(imgElm, data);
221                                 }
222
223                                 waitLoad(imgElm);
224                         });
225                 }
226
227                 function removePixelSuffix(value) {
228                         if (value) {
229                                 value = value.replace(/px$/, '');
230                         }
231
232                         return value;
233                 }
234
235                 function srcChange(e) {
236                         var srcURL, prependURL, absoluteURLPattern, meta = e.meta || {};
237
238                         if (imageListCtrl) {
239                                 imageListCtrl.value(editor.convertURL(this.value(), 'src'));
240                         }
241
242                         tinymce.each(meta, function(value, key) {
243                                 win.find('#' + key).value(value);
244                         });
245
246                         if (!meta.width && !meta.height) {
247                                 srcURL = editor.convertURL(this.value(), 'src');
248
249                                 // Pattern test the src url and make sure we haven't already prepended the url
250                                 prependURL = editor.settings.image_prepend_url;
251                                 absoluteURLPattern = new RegExp('^(?:[a-z]+:)?//', 'i');
252                                 if (prependURL && !absoluteURLPattern.test(srcURL) && srcURL.substring(0, prependURL.length) !== prependURL) {
253                                         srcURL = prependURL + srcURL;
254                                 }
255
256                                 this.value(srcURL);
257
258                                 getImageSize(editor.documentBaseURI.toAbsolute(this.value()), function(data) {
259                                         if (data.width && data.height && imageDimensions) {
260                                                 width = data.width;
261                                                 height = data.height;
262
263                                                 win.find('#width').value(width);
264                                                 win.find('#height').value(height);
265                                         }
266                                 });
267                         }
268                 }
269
270                 width = dom.getAttrib(imgElm, 'width');
271                 height = dom.getAttrib(imgElm, 'height');
272
273                 if (imgElm.nodeName == 'IMG' && !imgElm.getAttribute('data-mce-object') && !imgElm.getAttribute('data-mce-placeholder')) {
274                         data = {
275                                 src: dom.getAttrib(imgElm, 'src'),
276                                 alt: dom.getAttrib(imgElm, 'alt'),
277                                 title: dom.getAttrib(imgElm, 'title'),
278                                 "class": dom.getAttrib(imgElm, 'class'),
279                                 width: width,
280                                 height: height
281                         };
282
283                         // WP
284                         editor.fire( 'wpLoadImageData', { imgData: { data: data, node: imgElm } } );
285                 } else {
286                         imgElm = null;
287                 }
288
289                 if (imageList) {
290                         imageListCtrl = {
291                                 type: 'listbox',
292                                 label: 'Image list',
293                                 values: buildListItems(
294                                         imageList,
295                                         function(item) {
296                                                 item.value = editor.convertURL(item.value || item.url, 'src');
297                                         },
298                                         [{text: 'None', value: ''}]
299                                 ),
300                                 value: data.src && editor.convertURL(data.src, 'src'),
301                                 onselect: function(e) {
302                                         var altCtrl = win.find('#alt');
303
304                                         if (!altCtrl.value() || (e.lastControl && altCtrl.value() == e.lastControl.text())) {
305                                                 altCtrl.value(e.control.text());
306                                         }
307
308                                         win.find('#src').value(e.control.value()).fire('change');
309                                 },
310                                 onPostRender: function() {
311                                         imageListCtrl = this;
312                                 }
313                         };
314                 }
315
316                 if (editor.settings.image_class_list) {
317                         classListCtrl = {
318                                 name: 'class',
319                                 type: 'listbox',
320                                 label: 'Class',
321                                 values: buildListItems(
322                                         editor.settings.image_class_list,
323                                         function(item) {
324                                                 if (item.value) {
325                                                         item.textStyle = function() {
326                                                                 return editor.formatter.getCssText({inline: 'img', classes: [item.value]});
327                                                         };
328                                                 }
329                                         }
330                                 )
331                         };
332                 }
333
334                 // General settings shared between simple and advanced dialogs
335                 var generalFormItems = [
336                         {
337                                 name: 'src',
338                                 type: 'filepicker',
339                                 filetype: 'image',
340                                 label: 'Source',
341                                 autofocus: true,
342                                 onchange: srcChange
343                         },
344                         imageListCtrl
345                 ];
346
347                 if (editor.settings.image_description !== false) {
348                         generalFormItems.push({name: 'alt', type: 'textbox', label: 'Image description'});
349                 }
350
351                 if (editor.settings.image_title) {
352                         generalFormItems.push({name: 'title', type: 'textbox', label: 'Image Title'});
353                 }
354
355                 if (imageDimensions) {
356                         generalFormItems.push({
357                                 type: 'container',
358                                 label: 'Dimensions',
359                                 layout: 'flex',
360                                 direction: 'row',
361                                 align: 'center',
362                                 spacing: 5,
363                                 items: [
364                                         {name: 'width', type: 'textbox', maxLength: 5, size: 3, onchange: recalcSize, ariaLabel: 'Width'},
365                                         {type: 'label', text: 'x'},
366                                         {name: 'height', type: 'textbox', maxLength: 5, size: 3, onchange: recalcSize, ariaLabel: 'Height'},
367                                         {name: 'constrain', type: 'checkbox', checked: true, text: 'Constrain proportions'}
368                                 ]
369                         });
370                 }
371
372                 generalFormItems.push(classListCtrl);
373
374                 // WP
375                 editor.fire( 'wpLoadImageForm', { data: generalFormItems } );
376
377                 function mergeMargins(css) {
378                         if (css.margin) {
379
380                                 var splitMargin = css.margin.split(" ");
381
382                                 switch (splitMargin.length) {
383                                         case 1: //margin: toprightbottomleft;
384                                                 css['margin-top'] = css['margin-top'] || splitMargin[0];
385                                                 css['margin-right'] = css['margin-right'] || splitMargin[0];
386                                                 css['margin-bottom'] = css['margin-bottom'] || splitMargin[0];
387                                                 css['margin-left'] = css['margin-left'] || splitMargin[0];
388                                                 break;
389                                         case 2: //margin: topbottom rightleft;
390                                                 css['margin-top'] = css['margin-top'] || splitMargin[0];
391                                                 css['margin-right'] = css['margin-right'] || splitMargin[1];
392                                                 css['margin-bottom'] = css['margin-bottom'] || splitMargin[0];
393                                                 css['margin-left'] = css['margin-left'] || splitMargin[1];
394                                                 break;
395                                         case 3: //margin: top rightleft bottom;
396                                                 css['margin-top'] = css['margin-top'] || splitMargin[0];
397                                                 css['margin-right'] = css['margin-right'] || splitMargin[1];
398                                                 css['margin-bottom'] = css['margin-bottom'] || splitMargin[2];
399                                                 css['margin-left'] = css['margin-left'] || splitMargin[1];
400                                                 break;
401                                         case 4: //margin: top right bottom left;
402                                                 css['margin-top'] = css['margin-top'] || splitMargin[0];
403                                                 css['margin-right'] = css['margin-right'] || splitMargin[1];
404                                                 css['margin-bottom'] = css['margin-bottom'] || splitMargin[2];
405                                                 css['margin-left'] = css['margin-left'] || splitMargin[3];
406                                 }
407                                 delete css.margin;
408                         }
409                         return css;
410                 }
411
412                 function updateStyle() {
413                         function addPixelSuffix(value) {
414                                 if (value.length > 0 && /^[0-9]+$/.test(value)) {
415                                         value += 'px';
416                                 }
417
418                                 return value;
419                         }
420
421                         if (!editor.settings.image_advtab) {
422                                 return;
423                         }
424
425                         var data = win.toJSON(),
426                                 css = dom.parseStyle(data.style);
427
428                         css = mergeMargins(css);
429
430                         if (data.vspace) {
431                                 css['margin-top'] = css['margin-bottom'] = addPixelSuffix(data.vspace);
432                         }
433                         if (data.hspace) {
434                                 css['margin-left'] = css['margin-right'] = addPixelSuffix(data.hspace);
435                         }
436                         if (data.border) {
437                                 css['border-width'] = addPixelSuffix(data.border);
438                         }
439
440                         win.find('#style').value(dom.serializeStyle(dom.parseStyle(dom.serializeStyle(css))));
441                 }
442
443                 function updateVSpaceHSpaceBorder() {
444                         if (!editor.settings.image_advtab) {
445                                 return;
446                         }
447
448                         var data = win.toJSON(),
449                                 css = dom.parseStyle(data.style);
450
451                         win.find('#vspace').value("");
452                         win.find('#hspace').value("");
453
454                         css = mergeMargins(css);
455
456                         //Move opposite equal margins to vspace/hspace field
457                         if ((css['margin-top'] && css['margin-bottom']) || (css['margin-right'] && css['margin-left'])) {
458                                 if (css['margin-top'] === css['margin-bottom']) {
459                                         win.find('#vspace').value(removePixelSuffix(css['margin-top']));
460                                 } else {
461                                         win.find('#vspace').value('');
462                                 }
463                                 if (css['margin-right'] === css['margin-left']) {
464                                         win.find('#hspace').value(removePixelSuffix(css['margin-right']));
465                                 } else {
466                                         win.find('#hspace').value('');
467                                 }
468                         }
469
470                         //Move border-width
471                         if (css['border-width']) {
472                                 win.find('#border').value(removePixelSuffix(css['border-width']));
473                         }
474
475                         win.find('#style').value(dom.serializeStyle(dom.parseStyle(dom.serializeStyle(css))));
476
477                 }
478
479                 if (editor.settings.image_advtab) {
480                         // Parse styles from img
481                         if (imgElm) {
482                                 if (imgElm.style.marginLeft && imgElm.style.marginRight && imgElm.style.marginLeft === imgElm.style.marginRight) {
483                                         data.hspace = removePixelSuffix(imgElm.style.marginLeft);
484                                 }
485                                 if (imgElm.style.marginTop && imgElm.style.marginBottom && imgElm.style.marginTop === imgElm.style.marginBottom) {
486                                         data.vspace = removePixelSuffix(imgElm.style.marginTop);
487                                 }
488                                 if (imgElm.style.borderWidth) {
489                                         data.border = removePixelSuffix(imgElm.style.borderWidth);
490                                 }
491
492                                 data.style = editor.dom.serializeStyle(editor.dom.parseStyle(editor.dom.getAttrib(imgElm, 'style')));
493                         }
494
495                         // Advanced dialog shows general+advanced tabs
496                         win = editor.windowManager.open({
497                                 title: 'Insert/edit image',
498                                 data: data,
499                                 bodyType: 'tabpanel',
500                                 body: [
501                                         {
502                                                 title: 'General',
503                                                 type: 'form',
504                                                 items: generalFormItems
505                                         },
506
507                                         {
508                                                 title: 'Advanced',
509                                                 type: 'form',
510                                                 pack: 'start',
511                                                 items: [
512                                                         {
513                                                                 label: 'Style',
514                                                                 name: 'style',
515                                                                 type: 'textbox',
516                                                                 onchange: updateVSpaceHSpaceBorder
517                                                         },
518                                                         {
519                                                                 type: 'form',
520                                                                 layout: 'grid',
521                                                                 packV: 'start',
522                                                                 columns: 2,
523                                                                 padding: 0,
524                                                                 alignH: ['left', 'right'],
525                                                                 defaults: {
526                                                                         type: 'textbox',
527                                                                         maxWidth: 50,
528                                                                         onchange: updateStyle
529                                                                 },
530                                                                 items: [
531                                                                         {label: 'Vertical space', name: 'vspace'},
532                                                                         {label: 'Horizontal space', name: 'hspace'},
533                                                                         {label: 'Border', name: 'border'}
534                                                                 ]
535                                                         }
536                                                 ]
537                                         }
538                                 ],
539                                 onSubmit: onSubmitForm
540                         });
541                 } else {
542                         // Simple default dialog
543                         win = editor.windowManager.open({
544                                 title: 'Insert/edit image',
545                                 data: data,
546                                 body: generalFormItems,
547                                 onSubmit: onSubmitForm
548                         });
549                 }
550         }
551
552         editor.addButton('image', {
553                 icon: 'image',
554                 tooltip: 'Insert/edit image',
555                 onclick: createImageList(showDialog),
556                 stateSelector: 'img:not([data-mce-object],[data-mce-placeholder])'
557         });
558
559         editor.addMenuItem('image', {
560                 icon: 'image',
561                 text: 'Insert/edit image',
562                 onclick: createImageList(showDialog),
563                 context: 'insert',
564                 prependToContext: true
565         });
566
567         editor.addCommand('mceImage', createImageList(showDialog));
568 });