]> scripts.mit.edu Git - autoinstalls/wordpress.git/blob - wp-includes/js/tinymce/plugins/image/plugin.js
WordPress 4.5
[autoinstalls/wordpress.git] / wp-includes / js / tinymce / plugins / image / plugin.js
1 /**
2  * plugin.js
3  *
4  * Released under LGPL License.
5  * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
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(Math.max(img.width, img.clientWidth), Math.max(img.height, 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, figureElm;
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                         var figureElm, oldImg;
124
125                         function waitLoad(imgElm) {
126                                 function selectImage() {
127                                         imgElm.onload = imgElm.onerror = null;
128
129                                         if (editor.selection) {
130                                                 editor.selection.select(imgElm);
131                                                 editor.nodeChanged();
132                                         }
133                                 }
134
135                                 imgElm.onload = function() {
136                                         if (!data.width && !data.height && imageDimensions) {
137                                                 dom.setAttribs(imgElm, {
138                                                         width: imgElm.clientWidth,
139                                                         height: imgElm.clientHeight
140                                                 });
141                                                 //WP
142                                                 editor.fire( 'wpNewImageRefresh', { node: imgElm } );
143                                         }
144
145                                         selectImage();
146                                 };
147
148                                 imgElm.onerror = selectImage;
149                         }
150
151                         updateStyle();
152                         recalcSize();
153
154                         data = tinymce.extend(data, win.toJSON());
155                         var wpcaption = data.wpcaption; // WP
156
157                         if (!data.alt) {
158                                 data.alt = '';
159                         }
160
161                         if (!data.title) {
162                                 data.title = '';
163                         }
164
165                         if (data.width === '') {
166                                 data.width = null;
167                         }
168
169                         if (data.height === '') {
170                                 data.height = null;
171                         }
172
173                         if (!data.style) {
174                                 data.style = null;
175                         }
176
177                         // Setup new data excluding style properties
178                         /*eslint dot-notation: 0*/
179                         data = {
180                                 src: data.src,
181                                 alt: data.alt,
182                                 title: data.title,
183                                 width: data.width,
184                                 height: data.height,
185                                 style: data.style,
186                                 caption: data.caption,
187                                 "class": data["class"]
188                         };
189
190                         editor.undoManager.transact(function() {
191                                 // WP
192                                 var eventData = { node: imgElm, data: data, wpcaption: wpcaption };
193
194                                 editor.fire( 'wpImageFormSubmit', { imgData: eventData } );
195
196                                 if ( eventData.cancel ) {
197                                         waitLoad( eventData.node );
198                                         return;
199                                 }
200                                 // WP end
201
202                                 if (!data.src) {
203                                         if (imgElm) {
204                                                 dom.remove(imgElm);
205                                                 editor.focus();
206                                                 editor.nodeChanged();
207                                         }
208
209                                         return;
210                                 }
211
212                                 if (data.title === "") {
213                                         data.title = null;
214                                 }
215
216                                 if (!imgElm) {
217                                         data.id = '__mcenew';
218                                         editor.focus();
219                                         editor.selection.setContent(dom.createHTML('img', data));
220                                         imgElm = dom.get('__mcenew');
221                                         dom.setAttrib(imgElm, 'id', null);
222                                 } else {
223                                         dom.setAttribs(imgElm, data);
224                                 }
225
226                                 editor.editorUpload.uploadImagesAuto();
227
228                                 if (data.caption === false) {
229                                         if (dom.is(imgElm.parentNode, 'figure.image')) {
230                                                 figureElm = imgElm.parentNode;
231                                                 dom.insertAfter(imgElm, figureElm);
232                                                 dom.remove(figureElm);
233                                         }
234                                 }
235
236                                 function isTextBlock(node) {
237                                         return editor.schema.getTextBlockElements()[node.nodeName];
238                                 }
239
240                                 if (data.caption === true) {
241                                         if (!dom.is(imgElm.parentNode, 'figure.image')) {
242                                                 oldImg = imgElm;
243                                                 imgElm = imgElm.cloneNode(true);
244                                                 figureElm = dom.create('figure', {'class': 'image'});
245                                                 figureElm.appendChild(imgElm);
246                                                 figureElm.appendChild(dom.create('figcaption', {contentEditable: true}, 'Caption'));
247                                                 figureElm.contentEditable = false;
248
249                                                 var textBlock = dom.getParent(oldImg, isTextBlock);
250                                                 if (textBlock) {
251                                                         dom.split(textBlock, oldImg, figureElm);
252                                                 } else {
253                                                         dom.replace(figureElm, oldImg);
254                                                 }
255
256                                                 editor.selection.select(figureElm);
257                                         }
258
259                                         return;
260                                 }
261
262                                 waitLoad(imgElm);
263                         });
264                 }
265
266                 function removePixelSuffix(value) {
267                         if (value) {
268                                 value = value.replace(/px$/, '');
269                         }
270
271                         return value;
272                 }
273
274                 function srcChange(e) {
275                         var srcURL, prependURL, absoluteURLPattern, meta = e.meta || {};
276
277                         if (imageListCtrl) {
278                                 imageListCtrl.value(editor.convertURL(this.value(), 'src'));
279                         }
280
281                         tinymce.each(meta, function(value, key) {
282                                 win.find('#' + key).value(value);
283                         });
284
285                         if (!meta.width && !meta.height) {
286                                 srcURL = editor.convertURL(this.value(), 'src');
287
288                                 // Pattern test the src url and make sure we haven't already prepended the url
289                                 prependURL = editor.settings.image_prepend_url;
290                                 absoluteURLPattern = new RegExp('^(?:[a-z]+:)?//', 'i');
291                                 if (prependURL && !absoluteURLPattern.test(srcURL) && srcURL.substring(0, prependURL.length) !== prependURL) {
292                                         srcURL = prependURL + srcURL;
293                                 }
294
295                                 this.value(srcURL);
296
297                                 getImageSize(editor.documentBaseURI.toAbsolute(this.value()), function(data) {
298                                         if (data.width && data.height && imageDimensions) {
299                                                 width = data.width;
300                                                 height = data.height;
301
302                                                 win.find('#width').value(width);
303                                                 win.find('#height').value(height);
304                                         }
305                                 });
306                         }
307                 }
308
309                 imgElm = editor.selection.getNode();
310                 figureElm = dom.getParent(imgElm, 'figure.image');
311                 if (figureElm) {
312                         imgElm = dom.select('img', figureElm)[0];
313                 }
314
315                 if (imgElm && (imgElm.nodeName != 'IMG' || imgElm.getAttribute('data-mce-object') || imgElm.getAttribute('data-mce-placeholder'))) {
316                         imgElm = null;
317                 }
318
319                 if (imgElm) {
320                         width = dom.getAttrib(imgElm, 'width');
321                         height = dom.getAttrib(imgElm, 'height');
322
323                         data = {
324                                 src: dom.getAttrib(imgElm, 'src'),
325                                 alt: dom.getAttrib(imgElm, 'alt'),
326                                 title: dom.getAttrib(imgElm, 'title'),
327                                 "class": dom.getAttrib(imgElm, 'class'),
328                                 width: width,
329                                 height: height,
330                                 caption: !!figureElm
331                         };
332
333                         // WP
334                         editor.fire( 'wpLoadImageData', { imgData: { data: data, node: imgElm } } );
335                 }
336
337                 if (imageList) {
338                         imageListCtrl = {
339                                 type: 'listbox',
340                                 label: 'Image list',
341                                 values: buildListItems(
342                                         imageList,
343                                         function(item) {
344                                                 item.value = editor.convertURL(item.value || item.url, 'src');
345                                         },
346                                         [{text: 'None', value: ''}]
347                                 ),
348                                 value: data.src && editor.convertURL(data.src, 'src'),
349                                 onselect: function(e) {
350                                         var altCtrl = win.find('#alt');
351
352                                         if (!altCtrl.value() || (e.lastControl && altCtrl.value() == e.lastControl.text())) {
353                                                 altCtrl.value(e.control.text());
354                                         }
355
356                                         win.find('#src').value(e.control.value()).fire('change');
357                                 },
358                                 onPostRender: function() {
359                                         /*eslint consistent-this: 0*/
360                                         imageListCtrl = this;
361                                 }
362                         };
363                 }
364
365                 if (editor.settings.image_class_list) {
366                         classListCtrl = {
367                                 name: 'class',
368                                 type: 'listbox',
369                                 label: 'Class',
370                                 values: buildListItems(
371                                         editor.settings.image_class_list,
372                                         function(item) {
373                                                 if (item.value) {
374                                                         item.textStyle = function() {
375                                                                 return editor.formatter.getCssText({inline: 'img', classes: [item.value]});
376                                                         };
377                                                 }
378                                         }
379                                 )
380                         };
381                 }
382
383                 // General settings shared between simple and advanced dialogs
384                 var generalFormItems = [
385                         {
386                                 name: 'src',
387                                 type: 'filepicker',
388                                 filetype: 'image',
389                                 label: 'Source',
390                                 autofocus: true,
391                                 onchange: srcChange
392                         },
393                         imageListCtrl
394                 ];
395
396                 if (editor.settings.image_description !== false) {
397                         generalFormItems.push({name: 'alt', type: 'textbox', label: 'Image description'});
398                 }
399
400                 if (editor.settings.image_title) {
401                         generalFormItems.push({name: 'title', type: 'textbox', label: 'Image Title'});
402                 }
403
404                 if (imageDimensions) {
405                         generalFormItems.push({
406                                 type: 'container',
407                                 label: 'Dimensions',
408                                 layout: 'flex',
409                                 direction: 'row',
410                                 align: 'center',
411                                 spacing: 5,
412                                 items: [
413                                         {name: 'width', type: 'textbox', maxLength: 5, size: 3, onchange: recalcSize, ariaLabel: 'Width'},
414                                         {type: 'label', text: 'x'},
415                                         {name: 'height', type: 'textbox', maxLength: 5, size: 3, onchange: recalcSize, ariaLabel: 'Height'},
416                                         {name: 'constrain', type: 'checkbox', checked: true, text: 'Constrain proportions'}
417                                 ]
418                         });
419                 }
420
421                 generalFormItems.push(classListCtrl);
422
423                 if (editor.settings.image_caption && tinymce.Env.ceFalse) {
424                         generalFormItems.push({name: 'caption', type: 'checkbox', label: 'Caption'});
425                 }
426
427                 // WP
428                 editor.fire( 'wpLoadImageForm', { data: generalFormItems } );
429
430                 function mergeMargins(css) {
431                         if (css.margin) {
432
433                                 var splitMargin = css.margin.split(" ");
434
435                                 switch (splitMargin.length) {
436                                         case 1: //margin: toprightbottomleft;
437                                                 css['margin-top'] = css['margin-top'] || splitMargin[0];
438                                                 css['margin-right'] = css['margin-right'] || splitMargin[0];
439                                                 css['margin-bottom'] = css['margin-bottom'] || splitMargin[0];
440                                                 css['margin-left'] = css['margin-left'] || splitMargin[0];
441                                                 break;
442                                         case 2: //margin: topbottom rightleft;
443                                                 css['margin-top'] = css['margin-top'] || splitMargin[0];
444                                                 css['margin-right'] = css['margin-right'] || splitMargin[1];
445                                                 css['margin-bottom'] = css['margin-bottom'] || splitMargin[0];
446                                                 css['margin-left'] = css['margin-left'] || splitMargin[1];
447                                                 break;
448                                         case 3: //margin: top rightleft bottom;
449                                                 css['margin-top'] = css['margin-top'] || splitMargin[0];
450                                                 css['margin-right'] = css['margin-right'] || splitMargin[1];
451                                                 css['margin-bottom'] = css['margin-bottom'] || splitMargin[2];
452                                                 css['margin-left'] = css['margin-left'] || splitMargin[1];
453                                                 break;
454                                         case 4: //margin: top right bottom left;
455                                                 css['margin-top'] = css['margin-top'] || splitMargin[0];
456                                                 css['margin-right'] = css['margin-right'] || splitMargin[1];
457                                                 css['margin-bottom'] = css['margin-bottom'] || splitMargin[2];
458                                                 css['margin-left'] = css['margin-left'] || splitMargin[3];
459                                 }
460                                 delete css.margin;
461                         }
462                         return css;
463                 }
464
465                 function updateStyle() {
466                         function addPixelSuffix(value) {
467                                 if (value.length > 0 && /^[0-9]+$/.test(value)) {
468                                         value += 'px';
469                                 }
470
471                                 return value;
472                         }
473
474                         if (!editor.settings.image_advtab) {
475                                 return;
476                         }
477
478                         var data = win.toJSON(),
479                                 css = dom.parseStyle(data.style);
480
481                         css = mergeMargins(css);
482
483                         if (data.vspace) {
484                                 css['margin-top'] = css['margin-bottom'] = addPixelSuffix(data.vspace);
485                         }
486                         if (data.hspace) {
487                                 css['margin-left'] = css['margin-right'] = addPixelSuffix(data.hspace);
488                         }
489                         if (data.border) {
490                                 css['border-width'] = addPixelSuffix(data.border);
491                         }
492
493                         win.find('#style').value(dom.serializeStyle(dom.parseStyle(dom.serializeStyle(css))));
494                 }
495
496                 function updateVSpaceHSpaceBorder() {
497                         if (!editor.settings.image_advtab) {
498                                 return;
499                         }
500
501                         var data = win.toJSON(),
502                                 css = dom.parseStyle(data.style);
503
504                         win.find('#vspace').value("");
505                         win.find('#hspace').value("");
506
507                         css = mergeMargins(css);
508
509                         //Move opposite equal margins to vspace/hspace field
510                         if ((css['margin-top'] && css['margin-bottom']) || (css['margin-right'] && css['margin-left'])) {
511                                 if (css['margin-top'] === css['margin-bottom']) {
512                                         win.find('#vspace').value(removePixelSuffix(css['margin-top']));
513                                 } else {
514                                         win.find('#vspace').value('');
515                                 }
516                                 if (css['margin-right'] === css['margin-left']) {
517                                         win.find('#hspace').value(removePixelSuffix(css['margin-right']));
518                                 } else {
519                                         win.find('#hspace').value('');
520                                 }
521                         }
522
523                         //Move border-width
524                         if (css['border-width']) {
525                                 win.find('#border').value(removePixelSuffix(css['border-width']));
526                         }
527
528                         win.find('#style').value(dom.serializeStyle(dom.parseStyle(dom.serializeStyle(css))));
529
530                 }
531
532                 if (editor.settings.image_advtab) {
533                         // Parse styles from img
534                         if (imgElm) {
535                                 if (imgElm.style.marginLeft && imgElm.style.marginRight && imgElm.style.marginLeft === imgElm.style.marginRight) {
536                                         data.hspace = removePixelSuffix(imgElm.style.marginLeft);
537                                 }
538                                 if (imgElm.style.marginTop && imgElm.style.marginBottom && imgElm.style.marginTop === imgElm.style.marginBottom) {
539                                         data.vspace = removePixelSuffix(imgElm.style.marginTop);
540                                 }
541                                 if (imgElm.style.borderWidth) {
542                                         data.border = removePixelSuffix(imgElm.style.borderWidth);
543                                 }
544
545                                 data.style = editor.dom.serializeStyle(editor.dom.parseStyle(editor.dom.getAttrib(imgElm, 'style')));
546                         }
547
548                         // Advanced dialog shows general+advanced tabs
549                         win = editor.windowManager.open({
550                                 title: 'Insert/edit image',
551                                 data: data,
552                                 bodyType: 'tabpanel',
553                                 body: [
554                                         {
555                                                 title: 'General',
556                                                 type: 'form',
557                                                 items: generalFormItems
558                                         },
559
560                                         {
561                                                 title: 'Advanced',
562                                                 type: 'form',
563                                                 pack: 'start',
564                                                 items: [
565                                                         {
566                                                                 label: 'Style',
567                                                                 name: 'style',
568                                                                 type: 'textbox',
569                                                                 onchange: updateVSpaceHSpaceBorder
570                                                         },
571                                                         {
572                                                                 type: 'form',
573                                                                 layout: 'grid',
574                                                                 packV: 'start',
575                                                                 columns: 2,
576                                                                 padding: 0,
577                                                                 alignH: ['left', 'right'],
578                                                                 defaults: {
579                                                                         type: 'textbox',
580                                                                         maxWidth: 50,
581                                                                         onchange: updateStyle
582                                                                 },
583                                                                 items: [
584                                                                         {label: 'Vertical space', name: 'vspace'},
585                                                                         {label: 'Horizontal space', name: 'hspace'},
586                                                                         {label: 'Border', name: 'border'}
587                                                                 ]
588                                                         }
589                                                 ]
590                                         }
591                                 ],
592                                 onSubmit: onSubmitForm
593                         });
594                 } else {
595                         // Simple default dialog
596                         win = editor.windowManager.open({
597                                 title: 'Insert/edit image',
598                                 data: data,
599                                 body: generalFormItems,
600                                 onSubmit: onSubmitForm
601                         });
602                 }
603         }
604
605         editor.on('preInit', function() {
606                 function hasImageClass(node) {
607                         var className = node.attr('class');
608                         return className && /\bimage\b/.test(className);
609                 }
610
611                 function toggleContentEditableState(state) {
612                         return function(nodes) {
613                                 var i = nodes.length, node;
614
615                                 function toggleContentEditable(node) {
616                                         node.attr('contenteditable', state ? 'true' : null);
617                                 }
618
619                                 while (i--) {
620                                         node = nodes[i];
621
622                                         if (hasImageClass(node)) {
623                                                 node.attr('contenteditable', state ? 'false' : null);
624                                                 tinymce.each(node.getAll('figcaption'), toggleContentEditable);
625                                         }
626                                 }
627                         };
628                 }
629
630                 editor.parser.addNodeFilter('figure', toggleContentEditableState(true));
631                 editor.serializer.addNodeFilter('figure', toggleContentEditableState(false));
632         });
633
634         editor.addButton('image', {
635                 icon: 'image',
636                 tooltip: 'Insert/edit image',
637                 onclick: createImageList(showDialog),
638                 stateSelector: 'img:not([data-mce-object],[data-mce-placeholder]),figure.image'
639         });
640
641         editor.addMenuItem('image', {
642                 icon: 'image',
643                 text: 'Insert/edit image',
644                 onclick: createImageList(showDialog),
645                 context: 'insert',
646                 prependToContext: true
647         });
648
649         editor.addCommand('mceImage', createImageList(showDialog));
650 });