Completed
Push — develop ( 48b424...bab30d )
by Daniel
15:53 queued 09:41
created

tinymce.PluginManager.add(ꞌimageꞌ)   F

Complexity

Conditions 23
Paths > 20000

Size

Total Lines 504

Duplication

Lines 16
Ratio 3.17 %

Importance

Changes 0
Metric Value
dl 16
loc 504
rs 2
c 0
b 0
f 0
cc 23
nc 33280
nop 1

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like tinymce.PluginManager.add(ꞌimageꞌ) often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

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) {
0 ignored issues
show
Bug introduced by
The variable tinymce seems to be never declared. If this is a global, consider adding a /** global: tinymce */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
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) {
0 ignored issues
show
Bug introduced by
The variable tinymce seems to be never declared. If this is a global, consider adding a /** global: tinymce */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
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({
0 ignored issues
show
Bug introduced by
The variable tinymce seems to be never declared. If this is a global, consider adding a /** global: tinymce */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
72
					url: imageList,
73
					success: function(text) {
74
						callback(tinymce.util.JSON.parse(text));
0 ignored issues
show
Bug introduced by
The variable tinymce seems to be never declared. If this is a global, consider adding a /** global: tinymce */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
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
					}
142
143
					selectImage();
144
				};
145
146
				imgElm.onerror = selectImage;
147
			}
148
149
			updateStyle();
150
			recalcSize();
151
152
			data = tinymce.extend(data, win.toJSON());
0 ignored issues
show
Bug introduced by
The variable tinymce seems to be never declared. If this is a global, consider adding a /** global: tinymce */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
153
154
			if (!data.alt) {
155
				data.alt = '';
156
			}
157
158
			if (!data.title) {
159
				data.title = '';
160
			}
161
162
			if (data.width === '') {
163
				data.width = null;
164
			}
165
166
			if (data.height === '') {
167
				data.height = null;
168
			}
169
170
			if (!data.style) {
171
				data.style = null;
172
			}
173
174
			// Setup new data excluding style properties
175
			/*eslint dot-notation: 0*/
176
			data = {
177
				src: data.src,
178
				alt: data.alt,
179
				title: data.title,
180
				width: data.width,
181
				height: data.height,
182
				style: data.style,
183
				caption: data.caption,
184
				"class": data["class"]
185
			};
186
187
			editor.undoManager.transact(function() {
188
				if (!data.src) {
189
					if (imgElm) {
190
						dom.remove(imgElm);
191
						editor.focus();
192
						editor.nodeChanged();
193
					}
194
195
					return;
196
				}
197
198
				if (data.title === "") {
199
					data.title = null;
200
				}
201
202
				if (!imgElm) {
203
					data.id = '__mcenew';
204
					editor.focus();
205
					editor.selection.setContent(dom.createHTML('img', data));
206
					imgElm = dom.get('__mcenew');
207
					dom.setAttrib(imgElm, 'id', null);
208
				} else {
209
					dom.setAttribs(imgElm, data);
210
				}
211
212
				editor.editorUpload.uploadImagesAuto();
213
214
				if (data.caption === false) {
215
					if (dom.is(imgElm.parentNode, 'figure.image')) {
216
						figureElm = imgElm.parentNode;
217
						dom.insertAfter(imgElm, figureElm);
218
						dom.remove(figureElm);
219
					}
220
				}
221
222
				function isTextBlock(node) {
223
					return editor.schema.getTextBlockElements()[node.nodeName];
224
				}
225
226
				if (data.caption === true) {
227
					if (!dom.is(imgElm.parentNode, 'figure.image')) {
228
						oldImg = imgElm;
229
						imgElm = imgElm.cloneNode(true);
230
						figureElm = dom.create('figure', {'class': 'image'});
231
						figureElm.appendChild(imgElm);
232
						figureElm.appendChild(dom.create('figcaption', {contentEditable: true}, 'Caption'));
233
						figureElm.contentEditable = false;
234
235
						var textBlock = dom.getParent(oldImg, isTextBlock);
236
						if (textBlock) {
237
							dom.split(textBlock, oldImg, figureElm);
238
						} else {
239
							dom.replace(figureElm, oldImg);
240
						}
241
242
						editor.selection.select(figureElm);
243
					}
244
245
					return;
246
				}
247
248
				waitLoad(imgElm);
249
			});
250
		}
251
252
		function removePixelSuffix(value) {
253
			if (value) {
254
				value = value.replace(/px$/, '');
255
			}
256
257
			return value;
258
		}
259
260
		function srcChange(e) {
261
			var srcURL, prependURL, absoluteURLPattern, meta = e.meta || {};
262
263
			if (imageListCtrl) {
264
				imageListCtrl.value(editor.convertURL(this.value(), 'src'));
265
			}
266
267
			tinymce.each(meta, function(value, key) {
0 ignored issues
show
Bug introduced by
The variable tinymce seems to be never declared. If this is a global, consider adding a /** global: tinymce */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
268
				win.find('#' + key).value(value);
269
			});
270
271
			if (!meta.width && !meta.height) {
272
				srcURL = editor.convertURL(this.value(), 'src');
273
274
				// Pattern test the src url and make sure we haven't already prepended the url
275
				prependURL = editor.settings.image_prepend_url;
276
				absoluteURLPattern = new RegExp('^(?:[a-z]+:)?//', 'i');
277
				if (prependURL && !absoluteURLPattern.test(srcURL) && srcURL.substring(0, prependURL.length) !== prependURL) {
278
					srcURL = prependURL + srcURL;
279
				}
280
281
				this.value(srcURL);
282
283
				getImageSize(editor.documentBaseURI.toAbsolute(this.value()), function(data) {
284
					if (data.width && data.height && imageDimensions) {
285
						width = data.width;
286
						height = data.height;
287
288
						win.find('#width').value(width);
289
						win.find('#height').value(height);
290
					}
291
				});
292
			}
293
		}
294
295
		function onBeforeCall(e) {
296
			e.meta = win.toJSON();
297
		}
298
299
		imgElm = editor.selection.getNode();
300
		figureElm = dom.getParent(imgElm, 'figure.image');
301
		if (figureElm) {
302
			imgElm = dom.select('img', figureElm)[0];
303
		}
304
305
		if (imgElm && (imgElm.nodeName != 'IMG' || imgElm.getAttribute('data-mce-object') || imgElm.getAttribute('data-mce-placeholder'))) {
306
			imgElm = null;
307
		}
308
309
		if (imgElm) {
310
			width = dom.getAttrib(imgElm, 'width');
311
			height = dom.getAttrib(imgElm, 'height');
312
313
			data = {
314
				src: dom.getAttrib(imgElm, 'src'),
315
				alt: dom.getAttrib(imgElm, 'alt'),
316
				title: dom.getAttrib(imgElm, 'title'),
317
				"class": dom.getAttrib(imgElm, 'class'),
318
				width: width,
319
				height: height,
320
				caption: !!figureElm
321
			};
322
		}
323
324
		if (imageList) {
325
			imageListCtrl = {
326
				type: 'listbox',
327
				label: 'Image list',
328
				values: buildListItems(
329
					imageList,
330
					function(item) {
331
						item.value = editor.convertURL(item.value || item.url, 'src');
332
					},
333
					[{text: 'None', value: ''}]
334
				),
335
				value: data.src && editor.convertURL(data.src, 'src'),
336
				onselect: function(e) {
337
					var altCtrl = win.find('#alt');
338
339
					if (!altCtrl.value() || (e.lastControl && altCtrl.value() == e.lastControl.text())) {
340
						altCtrl.value(e.control.text());
341
					}
342
343
					win.find('#src').value(e.control.value()).fire('change');
344
				},
345
				onPostRender: function() {
346
					/*eslint consistent-this: 0*/
347
					imageListCtrl = this;
348
				}
349
			};
350
		}
351
352
		if (editor.settings.image_class_list) {
353
			classListCtrl = {
354
				name: 'class',
355
				type: 'listbox',
356
				label: 'Class',
357
				values: buildListItems(
358
					editor.settings.image_class_list,
359
					function(item) {
360
						if (item.value) {
361
							item.textStyle = function() {
362
								return editor.formatter.getCssText({inline: 'img', classes: [item.value]});
363
							};
364
						}
365
					}
366
				)
367
			};
368
		}
369
370
		// General settings shared between simple and advanced dialogs
371
		var generalFormItems = [
372
			{
373
				name: 'src',
374
				type: 'filepicker',
375
				filetype: 'image',
376
				label: 'Source',
377
				autofocus: true,
378
				onchange: srcChange,
379
				onbeforecall: onBeforeCall
380
			},
381
			imageListCtrl
0 ignored issues
show
Bug introduced by
The variable imageListCtrl does not seem to be initialized in case imageList on line 324 is false. Are you sure this can never be the case?
Loading history...
382
		];
383
384
		if (editor.settings.image_description !== false) {
385
			generalFormItems.push({name: 'alt', type: 'textbox', label: 'Image description'});
386
		}
387
388
		if (editor.settings.image_title) {
389
			generalFormItems.push({name: 'title', type: 'textbox', label: 'Image Title'});
390
		}
391
392 View Code Duplication
		if (imageDimensions) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
393
			generalFormItems.push({
394
				type: 'container',
395
				label: 'Dimensions',
396
				layout: 'flex',
397
				direction: 'row',
398
				align: 'center',
399
				spacing: 5,
400
				items: [
401
					{name: 'width', type: 'textbox', maxLength: 5, size: 3, onchange: recalcSize, ariaLabel: 'Width'},
402
					{type: 'label', text: 'x'},
403
					{name: 'height', type: 'textbox', maxLength: 5, size: 3, onchange: recalcSize, ariaLabel: 'Height'},
404
					{name: 'constrain', type: 'checkbox', checked: true, text: 'Constrain proportions'}
405
				]
406
			});
407
		}
408
409
		generalFormItems.push(classListCtrl);
0 ignored issues
show
Bug introduced by
The variable classListCtrl does not seem to be initialized in case editor.settings.image_class_list on line 352 is false. Are you sure the function push handles undefined variables?
Loading history...
410
411
		if (editor.settings.image_caption && tinymce.Env.ceFalse) {
0 ignored issues
show
Bug introduced by
The variable tinymce seems to be never declared. If this is a global, consider adding a /** global: tinymce */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
412
			generalFormItems.push({name: 'caption', type: 'checkbox', label: 'Caption'});
413
		}
414
415
		function mergeMargins(css) {
416
			if (css.margin) {
417
418
				var splitMargin = css.margin.split(" ");
419
420
				switch (splitMargin.length) {
0 ignored issues
show
Coding Style introduced by
As per coding-style, switch statements should have a default case.
Loading history...
421
					case 1: //margin: toprightbottomleft;
422
						css['margin-top'] = css['margin-top'] || splitMargin[0];
423
						css['margin-right'] = css['margin-right'] || splitMargin[0];
424
						css['margin-bottom'] = css['margin-bottom'] || splitMargin[0];
425
						css['margin-left'] = css['margin-left'] || splitMargin[0];
426
						break;
427
					case 2: //margin: topbottom rightleft;
428
						css['margin-top'] = css['margin-top'] || splitMargin[0];
429
						css['margin-right'] = css['margin-right'] || splitMargin[1];
430
						css['margin-bottom'] = css['margin-bottom'] || splitMargin[0];
431
						css['margin-left'] = css['margin-left'] || splitMargin[1];
432
						break;
433
					case 3: //margin: top rightleft bottom;
434
						css['margin-top'] = css['margin-top'] || splitMargin[0];
435
						css['margin-right'] = css['margin-right'] || splitMargin[1];
436
						css['margin-bottom'] = css['margin-bottom'] || splitMargin[2];
437
						css['margin-left'] = css['margin-left'] || splitMargin[1];
438
						break;
439
					case 4: //margin: top right bottom left;
440
						css['margin-top'] = css['margin-top'] || splitMargin[0];
441
						css['margin-right'] = css['margin-right'] || splitMargin[1];
442
						css['margin-bottom'] = css['margin-bottom'] || splitMargin[2];
443
						css['margin-left'] = css['margin-left'] || splitMargin[3];
444
				}
445
				delete css.margin;
446
			}
447
			return css;
448
		}
449
450
		function updateStyle() {
451
			function addPixelSuffix(value) {
452
				if (value.length > 0 && /^[0-9]+$/.test(value)) {
453
					value += 'px';
454
				}
455
456
				return value;
457
			}
458
459
			if (!editor.settings.image_advtab) {
460
				return;
461
			}
462
463
			var data = win.toJSON(),
464
				css = dom.parseStyle(data.style);
465
466
			css = mergeMargins(css);
467
468
			if (data.vspace) {
469
				css['margin-top'] = css['margin-bottom'] = addPixelSuffix(data.vspace);
470
			}
471
			if (data.hspace) {
472
				css['margin-left'] = css['margin-right'] = addPixelSuffix(data.hspace);
473
			}
474
			if (data.border) {
475
				css['border-width'] = addPixelSuffix(data.border);
476
			}
477
478
			win.find('#style').value(dom.serializeStyle(dom.parseStyle(dom.serializeStyle(css))));
479
		}
480
481
		function updateVSpaceHSpaceBorder() {
482
			if (!editor.settings.image_advtab) {
483
				return;
484
			}
485
486
			var data = win.toJSON(),
487
				css = dom.parseStyle(data.style);
488
489
			win.find('#vspace').value("");
490
			win.find('#hspace').value("");
491
492
			css = mergeMargins(css);
493
494
			//Move opposite equal margins to vspace/hspace field
495
			if ((css['margin-top'] && css['margin-bottom']) || (css['margin-right'] && css['margin-left'])) {
496
				if (css['margin-top'] === css['margin-bottom']) {
497
					win.find('#vspace').value(removePixelSuffix(css['margin-top']));
498
				} else {
499
					win.find('#vspace').value('');
500
				}
501
				if (css['margin-right'] === css['margin-left']) {
502
					win.find('#hspace').value(removePixelSuffix(css['margin-right']));
503
				} else {
504
					win.find('#hspace').value('');
505
				}
506
			}
507
508
			//Move border-width
509
			if (css['border-width']) {
510
				win.find('#border').value(removePixelSuffix(css['border-width']));
511
			}
512
513
			win.find('#style').value(dom.serializeStyle(dom.parseStyle(dom.serializeStyle(css))));
514
515
		}
516
517
		if (editor.settings.image_advtab) {
518
			// Parse styles from img
519
			if (imgElm) {
520
				if (imgElm.style.marginLeft && imgElm.style.marginRight && imgElm.style.marginLeft === imgElm.style.marginRight) {
521
					data.hspace = removePixelSuffix(imgElm.style.marginLeft);
522
				}
523
				if (imgElm.style.marginTop && imgElm.style.marginBottom && imgElm.style.marginTop === imgElm.style.marginBottom) {
524
					data.vspace = removePixelSuffix(imgElm.style.marginTop);
525
				}
526
				if (imgElm.style.borderWidth) {
527
					data.border = removePixelSuffix(imgElm.style.borderWidth);
528
				}
529
530
				data.style = editor.dom.serializeStyle(editor.dom.parseStyle(editor.dom.getAttrib(imgElm, 'style')));
531
			}
532
533
			// Advanced dialog shows general+advanced tabs
534
			win = editor.windowManager.open({
535
				title: 'Insert/edit image',
536
				data: data,
537
				bodyType: 'tabpanel',
538
				body: [
539
					{
540
						title: 'General',
541
						type: 'form',
542
						items: generalFormItems
543
					},
544
545
					{
546
						title: 'Advanced',
547
						type: 'form',
548
						pack: 'start',
549
						items: [
550
							{
551
								label: 'Style',
552
								name: 'style',
553
								type: 'textbox',
554
								onchange: updateVSpaceHSpaceBorder
555
							},
556
							{
557
								type: 'form',
558
								layout: 'grid',
559
								packV: 'start',
560
								columns: 2,
561
								padding: 0,
562
								alignH: ['left', 'right'],
563
								defaults: {
564
									type: 'textbox',
565
									maxWidth: 50,
566
									onchange: updateStyle
567
								},
568
								items: [
569
									{label: 'Vertical space', name: 'vspace'},
570
									{label: 'Horizontal space', name: 'hspace'},
571
									{label: 'Border', name: 'border'}
572
								]
573
							}
574
						]
575
					}
576
				],
577
				onSubmit: onSubmitForm
578
			});
579
		} else {
580
			// Simple default dialog
581
			win = editor.windowManager.open({
582
				title: 'Insert/edit image',
583
				data: data,
584
				body: generalFormItems,
585
				onSubmit: onSubmitForm
586
			});
587
		}
588
	}
589
590
	editor.on('preInit', function() {
591
		function hasImageClass(node) {
592
			var className = node.attr('class');
593
			return className && /\bimage\b/.test(className);
594
		}
595
596
		function toggleContentEditableState(state) {
597
			return function(nodes) {
598
				var i = nodes.length, node;
599
600
				function toggleContentEditable(node) {
601
					node.attr('contenteditable', state ? 'true' : null);
602
				}
603
604
				while (i--) {
605
					node = nodes[i];
606
607
					if (hasImageClass(node)) {
608
						node.attr('contenteditable', state ? 'false' : null);
609
						tinymce.each(node.getAll('figcaption'), toggleContentEditable);
0 ignored issues
show
Bug introduced by
The variable tinymce seems to be never declared. If this is a global, consider adding a /** global: tinymce */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
610
					}
611
				}
612
			};
613
		}
614
615
		editor.parser.addNodeFilter('figure', toggleContentEditableState(true));
616
		editor.serializer.addNodeFilter('figure', toggleContentEditableState(false));
617
	});
618
619
	editor.addButton('image', {
620
		icon: 'image',
621
		tooltip: 'Insert/edit image',
622
		onclick: createImageList(showDialog),
623
		stateSelector: 'img:not([data-mce-object],[data-mce-placeholder]),figure.image'
624
	});
625
626
	editor.addMenuItem('image', {
627
		icon: 'image',
628
		text: 'Image',
629
		onclick: createImageList(showDialog),
630
		context: 'insert',
631
		prependToContext: true
632
	});
633
634
	editor.addCommand('mceImage', createImageList(showDialog));
635
});
636