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

tinymce.PluginManager.add(ꞌfullpageꞌ)   F

Complexity

Conditions 13
Paths 2379

Size

Total Lines 113

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 113
rs 2
c 0
b 0
f 0
cc 13
nc 2379
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(ꞌfullpageꞌ) 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('fullpage', 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
	var each = tinymce.each, Node = tinymce.html.Node;
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...
15
	var head, foot;
16
17
	function showDialog() {
18
		var data = htmlToData();
19
20
		editor.windowManager.open({
21
			title: 'Document properties',
22
			data: data,
23
			defaults: {type: 'textbox', size: 40},
24
			body: [
25
				{name: 'title', label: 'Title'},
26
				{name: 'keywords', label: 'Keywords'},
27
				{name: 'description', label: 'Description'},
28
				{name: 'robots', label: 'Robots'},
29
				{name: 'author', label: 'Author'},
30
				{name: 'docencoding', label: 'Encoding'}
31
			],
32
			onSubmit: function(e) {
33
				dataToHtml(tinymce.extend(data, e.data));
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...
34
			}
35
		});
36
	}
37
38
	function htmlToData() {
39
		var headerFragment = parseHeader(), data = {}, elm, matches;
40
41
		function getAttr(elm, name) {
42
			var value = elm.attr(name);
43
44
			return value || '';
45
		}
46
47
		// Default some values
48
		data.fontface = editor.getParam("fullpage_default_fontface", "");
49
		data.fontsize = editor.getParam("fullpage_default_fontsize", "");
50
51
		// Parse XML PI
52
		elm = headerFragment.firstChild;
53
		if (elm.type == 7) {
54
			data.xml_pi = true;
55
			matches = /encoding="([^"]+)"/.exec(elm.value);
56
			if (matches) {
57
				data.docencoding = matches[1];
58
			}
59
		}
60
61
		// Parse doctype
62
		elm = headerFragment.getAll('#doctype')[0];
63
		if (elm) {
64
			data.doctype = '<!DOCTYPE' + elm.value + ">";
65
		}
66
67
		// Parse title element
68
		elm = headerFragment.getAll('title')[0];
69
		if (elm && elm.firstChild) {
70
			data.title = elm.firstChild.value;
71
		}
72
73
		// Parse meta elements
74
		each(headerFragment.getAll('meta'), function(meta) {
75
			var name = meta.attr('name'), httpEquiv = meta.attr('http-equiv'), matches;
76
77
			if (name) {
78
				data[name.toLowerCase()] = meta.attr('content');
79
			} else if (httpEquiv == "Content-Type") {
80
				matches = /charset\s*=\s*(.*)\s*/gi.exec(meta.attr('content'));
81
82
				if (matches) {
83
					data.docencoding = matches[1];
84
				}
85
			}
86
		});
87
88
		// Parse html attribs
89
		elm = headerFragment.getAll('html')[0];
90
		if (elm) {
91
			data.langcode = getAttr(elm, 'lang') || getAttr(elm, 'xml:lang');
92
		}
93
94
		// Parse stylesheets
95
		data.stylesheets = [];
96
		tinymce.each(headerFragment.getAll('link'), function(link) {
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...
97
			if (link.attr('rel') == 'stylesheet') {
98
				data.stylesheets.push(link.attr('href'));
99
			}
100
		});
101
102
		// Parse body parts
103
		elm = headerFragment.getAll('body')[0];
104
		if (elm) {
105
			data.langdir = getAttr(elm, 'dir');
106
			data.style = getAttr(elm, 'style');
107
			data.visited_color = getAttr(elm, 'vlink');
108
			data.link_color = getAttr(elm, 'link');
109
			data.active_color = getAttr(elm, 'alink');
110
		}
111
112
		return data;
113
	}
114
115
	function dataToHtml(data) {
116
		var headerFragment, headElement, html, elm, value, dom = editor.dom;
117
118
		function setAttr(elm, name, value) {
119
			elm.attr(name, value ? value : undefined);
120
		}
121
122
		function addHeadNode(node) {
123
			if (headElement.firstChild) {
124
				headElement.insert(node, headElement.firstChild);
125
			} else {
126
				headElement.append(node);
127
			}
128
		}
129
130
		headerFragment = parseHeader();
131
		headElement = headerFragment.getAll('head')[0];
132
		if (!headElement) {
133
			elm = headerFragment.getAll('html')[0];
134
			headElement = new Node('head', 1);
135
136
			if (elm.firstChild) {
137
				elm.insert(headElement, elm.firstChild, true);
138
			} else {
139
				elm.append(headElement);
140
			}
141
		}
142
143
		// Add/update/remove XML-PI
144
		elm = headerFragment.firstChild;
145
		if (data.xml_pi) {
146
			value = 'version="1.0"';
147
148
			if (data.docencoding) {
149
				value += ' encoding="' + data.docencoding + '"';
150
			}
151
152
			if (elm.type != 7) {
153
				elm = new Node('xml', 7);
154
				headerFragment.insert(elm, headerFragment.firstChild, true);
155
			}
156
157
			elm.value = value;
158
		} else if (elm && elm.type == 7) {
159
			elm.remove();
160
		}
161
162
		// Add/update/remove doctype
163
		elm = headerFragment.getAll('#doctype')[0];
164
		if (data.doctype) {
165
			if (!elm) {
166
				elm = new Node('#doctype', 10);
167
168
				if (data.xml_pi) {
169
					headerFragment.insert(elm, headerFragment.firstChild);
170
				} else {
171
					addHeadNode(elm);
172
				}
173
			}
174
175
			elm.value = data.doctype.substring(9, data.doctype.length - 1);
176
		} else if (elm) {
177
			elm.remove();
178
		}
179
180
		// Add meta encoding
181
		elm = null;
182
		each(headerFragment.getAll('meta'), function(meta) {
183
			if (meta.attr('http-equiv') == 'Content-Type') {
184
				elm = meta;
185
			}
186
		});
187
188
		if (data.docencoding) {
189
			if (!elm) {
190
				elm = new Node('meta', 1);
191
				elm.attr('http-equiv', 'Content-Type');
192
				elm.shortEnded = true;
193
				addHeadNode(elm);
194
			}
195
196
			elm.attr('content', 'text/html; charset=' + data.docencoding);
197
		} else if (elm) {
198
			elm.remove();
199
		}
200
201
		// Add/update/remove title
202
		elm = headerFragment.getAll('title')[0];
203
		if (data.title) {
204
			if (!elm) {
205
				elm = new Node('title', 1);
206
				addHeadNode(elm);
207
			} else {
208
				elm.empty();
209
			}
210
211
			elm.append(new Node('#text', 3)).value = data.title;
212
		} else if (elm) {
213
			elm.remove();
214
		}
215
216
		// Add/update/remove meta
217
		each('keywords,description,author,copyright,robots'.split(','), function(name) {
218
			var nodes = headerFragment.getAll('meta'), i, meta, value = data[name];
219
220
			for (i = 0; i < nodes.length; i++) {
221
				meta = nodes[i];
222
223
				if (meta.attr('name') == name) {
224
					if (value) {
225
						meta.attr('content', value);
226
					} else {
227
						meta.remove();
228
					}
229
230
					return;
231
				}
232
			}
233
234
			if (value) {
235
				elm = new Node('meta', 1);
236
				elm.attr('name', name);
237
				elm.attr('content', value);
238
				elm.shortEnded = true;
239
240
				addHeadNode(elm);
241
			}
242
		});
243
244
		var currentStyleSheetsMap = {};
245
		tinymce.each(headerFragment.getAll('link'), function(stylesheet) {
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...
246
			if (stylesheet.attr('rel') == 'stylesheet') {
247
				currentStyleSheetsMap[stylesheet.attr('href')] = stylesheet;
248
			}
249
		});
250
251
		// Add new
252
		tinymce.each(data.stylesheets, function(stylesheet) {
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...
253
			if (!currentStyleSheetsMap[stylesheet]) {
254
				elm = new Node('link', 1);
255
				elm.attr({
256
					rel: 'stylesheet',
257
					text: 'text/css',
258
					href: stylesheet
259
				});
260
				elm.shortEnded = true;
261
				addHeadNode(elm);
262
			}
263
264
			delete currentStyleSheetsMap[stylesheet];
265
		});
266
267
		// Delete old
268
		tinymce.each(currentStyleSheetsMap, function(stylesheet) {
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...
269
			stylesheet.remove();
270
		});
271
272
		// Update body attributes
273
		elm = headerFragment.getAll('body')[0];
274
		if (elm) {
275
			setAttr(elm, 'dir', data.langdir);
276
			setAttr(elm, 'style', data.style);
277
			setAttr(elm, 'vlink', data.visited_color);
278
			setAttr(elm, 'link', data.link_color);
279
			setAttr(elm, 'alink', data.active_color);
280
281
			// Update iframe body as well
282
			dom.setAttribs(editor.getBody(), {
283
				style: data.style,
284
				dir: data.dir,
285
				vLink: data.visited_color,
286
				link: data.link_color,
287
				aLink: data.active_color
288
			});
289
		}
290
291
		// Set html attributes
292
		elm = headerFragment.getAll('html')[0];
293
		if (elm) {
294
			setAttr(elm, 'lang', data.langcode);
295
			setAttr(elm, 'xml:lang', data.langcode);
296
		}
297
298
		// No need for a head element
299
		if (!headElement.firstChild) {
300
			headElement.remove();
301
		}
302
303
		// Serialize header fragment and crop away body part
304
		html = new tinymce.html.Serializer({
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...
305
			validate: false,
306
			indent: true,
307
			apply_source_formatting: true,
308
			indent_before: 'head,html,body,meta,title,script,link,style',
309
			indent_after: 'head,html,body,meta,title,script,link,style'
310
		}).serialize(headerFragment);
311
312
		head = html.substring(0, html.indexOf('</body>'));
313
	}
314
315
	function parseHeader() {
316
		// Parse the contents with a DOM parser
317
		return new tinymce.html.DomParser({
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...
318
			validate: false,
319
			root_name: '#document'
320
		}).parse(head);
321
	}
322
323
	function setContent(evt) {
324
		var startPos, endPos, content = evt.content, headerFragment, styles = '', dom = editor.dom, elm;
325
326
		if (evt.selection) {
327
			return;
328
		}
329
330
		function low(s) {
331
			return s.replace(/<\/?[A-Z]+/g, function(a) {
332
				return a.toLowerCase();
333
			});
334
		}
335
336
		// Ignore raw updated if we already have a head, this will fix issues with undo/redo keeping the head/foot separate
337
		if (evt.format == 'raw' && head) {
338
			return;
339
		}
340
341
		if (evt.source_view && editor.getParam('fullpage_hide_in_source_view')) {
342
			return;
343
		}
344
345
		// Fixed so new document/setContent('') doesn't remove existing header/footer except when it's in source code view
346
		if (content.length === 0 && !evt.source_view) {
347
			content = tinymce.trim(head) + '\n' + tinymce.trim(content) + '\n' + tinymce.trim(foot);
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...
348
		}
349
350
		// Parse out head, body and footer
351
		content = content.replace(/<(\/?)BODY/gi, '<$1body');
352
		startPos = content.indexOf('<body');
353
354
		if (startPos != -1) {
355
			startPos = content.indexOf('>', startPos);
356
			head = low(content.substring(0, startPos + 1));
357
358
			endPos = content.indexOf('</body', startPos);
359
			if (endPos == -1) {
360
				endPos = content.length;
361
			}
362
363
			evt.content = content.substring(startPos + 1, endPos);
364
			foot = low(content.substring(endPos));
365
		} else {
366
			head = getDefaultHeader();
367
			foot = '\n</body>\n</html>';
368
		}
369
370
		// Parse header and update iframe
371
		headerFragment = parseHeader();
372
		each(headerFragment.getAll('style'), function(node) {
373
			if (node.firstChild) {
374
				styles += node.firstChild.value;
375
			}
376
		});
377
378
		elm = headerFragment.getAll('body')[0];
379
		if (elm) {
380
			dom.setAttribs(editor.getBody(), {
381
				style: elm.attr('style') || '',
382
				dir: elm.attr('dir') || '',
383
				vLink: elm.attr('vlink') || '',
384
				link: elm.attr('link') || '',
385
				aLink: elm.attr('alink') || ''
386
			});
387
		}
388
389
		dom.remove('fullpage_styles');
390
391
		var headElm = editor.getDoc().getElementsByTagName('head')[0];
392
393
		if (styles) {
394
			dom.add(headElm, 'style', {
395
				id: 'fullpage_styles'
396
			}, styles);
397
398
			// Needed for IE 6/7
399
			elm = dom.get('fullpage_styles');
400
			if (elm.styleSheet) {
401
				elm.styleSheet.cssText = styles;
402
			}
403
		}
404
405
		var currentStyleSheetsMap = {};
406
		tinymce.each(headElm.getElementsByTagName('link'), function(stylesheet) {
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...
407
			if (stylesheet.rel == 'stylesheet' && stylesheet.getAttribute('data-mce-fullpage')) {
408
				currentStyleSheetsMap[stylesheet.href] = stylesheet;
409
			}
410
		});
411
412
		// Add new
413
		tinymce.each(headerFragment.getAll('link'), function(stylesheet) {
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...
414
			var href = stylesheet.attr('href');
415
			if (!href) {
416
				return true;
417
			}
418
419
			if (!currentStyleSheetsMap[href] && stylesheet.attr('rel') == 'stylesheet') {
420
				dom.add(headElm, 'link', {
421
					rel: 'stylesheet',
422
					text: 'text/css',
423
					href: href,
424
					'data-mce-fullpage': '1'
425
				});
426
			}
427
428
			delete currentStyleSheetsMap[href];
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
429
		});
430
431
		// Delete old
432
		tinymce.each(currentStyleSheetsMap, function(stylesheet) {
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...
433
			stylesheet.parentNode.removeChild(stylesheet);
434
		});
435
	}
436
437
	function getDefaultHeader() {
438
		var header = '', value, styles = '';
439
440
		if (editor.getParam('fullpage_default_xml_pi')) {
441
			header += '<?xml version="1.0" encoding="' + editor.getParam('fullpage_default_encoding', 'ISO-8859-1') + '" ?>\n';
442
		}
443
444
		header += editor.getParam('fullpage_default_doctype', '<!DOCTYPE html>');
445
		header += '\n<html>\n<head>\n';
446
447
		if ((value = editor.getParam('fullpage_default_title'))) {
448
			header += '<title>' + value + '</title>\n';
449
		}
450
451
		if ((value = editor.getParam('fullpage_default_encoding'))) {
452
			header += '<meta http-equiv="Content-Type" content="text/html; charset=' + value + '" />\n';
453
		}
454
455
		if ((value = editor.getParam('fullpage_default_font_family'))) {
456
			styles += 'font-family: ' + value + ';';
457
		}
458
459
		if ((value = editor.getParam('fullpage_default_font_size'))) {
460
			styles += 'font-size: ' + value + ';';
461
		}
462
463
		if ((value = editor.getParam('fullpage_default_text_color'))) {
464
			styles += 'color: ' + value + ';';
465
		}
466
467
		header += '</head>\n<body' + (styles ? ' style="' + styles + '"' : '') + '>\n';
468
469
		return header;
470
	}
471
472
	function getContent(evt) {
473
		if (!evt.selection && (!evt.source_view || !editor.getParam('fullpage_hide_in_source_view'))) {
474
			evt.content = tinymce.trim(head) + '\n' + tinymce.trim(evt.content) + '\n' + tinymce.trim(foot);
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...
475
		}
476
	}
477
478
	editor.addCommand('mceFullPageProperties', showDialog);
479
480
	editor.addButton('fullpage', {
481
		title: 'Document properties',
482
		cmd: 'mceFullPageProperties'
483
	});
484
485
	editor.addMenuItem('fullpage', {
486
		text: 'Document properties',
487
		cmd: 'mceFullPageProperties',
488
		context: 'file'
489
	});
490
491
	editor.on('BeforeSetContent', setContent);
492
	editor.on('GetContent', getContent);
493
});
494