Passed
Push — master ( 7e07d9...30706c )
by Ron
02:08 queued 12s
created

plugin.js ➔ dataToHtml   F

Complexity

Conditions 21
Paths 11520

Size

Total Lines 158
Code Lines 125

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 125
nc 11520
nop 3
dl 0
loc 158
c 0
b 0
f 0
cc 21
rs 0

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 plugin.js ➔ dataToHtml 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
(function () {
2
var fullpage = (function () {
0 ignored issues
show
Unused Code introduced by
The variable fullpage seems to be never used. Consider removing it.
Loading history...
3
    'use strict';
4
5
    var Cell = function (initial) {
6
      var value = initial;
7
      var get = function () {
8
        return value;
9
      };
10
      var set = function (v) {
11
        value = v;
12
      };
13
      var clone = function () {
14
        return Cell(get());
15
      };
16
      return {
17
        get: get,
18
        set: set,
19
        clone: clone
20
      };
21
    };
22
23
    var global = tinymce.util.Tools.resolve('tinymce.PluginManager');
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...
24
25
    var global$1 = tinymce.util.Tools.resolve('tinymce.util.Tools');
26
27
    var global$2 = tinymce.util.Tools.resolve('tinymce.html.DomParser');
28
29
    var global$3 = tinymce.util.Tools.resolve('tinymce.html.Node');
30
31
    var global$4 = tinymce.util.Tools.resolve('tinymce.html.Serializer');
32
33
    var shouldHideInSourceView = function (editor) {
34
      return editor.getParam('fullpage_hide_in_source_view');
35
    };
36
    var getDefaultXmlPi = function (editor) {
37
      return editor.getParam('fullpage_default_xml_pi');
38
    };
39
    var getDefaultEncoding = function (editor) {
40
      return editor.getParam('fullpage_default_encoding');
41
    };
42
    var getDefaultFontFamily = function (editor) {
43
      return editor.getParam('fullpage_default_font_family');
44
    };
45
    var getDefaultFontSize = function (editor) {
46
      return editor.getParam('fullpage_default_font_size');
47
    };
48
    var getDefaultTextColor = function (editor) {
49
      return editor.getParam('fullpage_default_text_color');
50
    };
51
    var getDefaultTitle = function (editor) {
52
      return editor.getParam('fullpage_default_title');
53
    };
54
    var getDefaultDocType = function (editor) {
55
      return editor.getParam('fullpage_default_doctype', '<!DOCTYPE html>');
56
    };
57
    var Settings = {
58
      shouldHideInSourceView: shouldHideInSourceView,
59
      getDefaultXmlPi: getDefaultXmlPi,
60
      getDefaultEncoding: getDefaultEncoding,
61
      getDefaultFontFamily: getDefaultFontFamily,
62
      getDefaultFontSize: getDefaultFontSize,
63
      getDefaultTextColor: getDefaultTextColor,
64
      getDefaultTitle: getDefaultTitle,
65
      getDefaultDocType: getDefaultDocType
66
    };
67
68
    var parseHeader = function (head) {
69
      return global$2({
70
        validate: false,
71
        root_name: '#document'
72
      }).parse(head);
73
    };
74
    var htmlToData = function (editor, head) {
75
      var headerFragment = parseHeader(head);
76
      var data = {};
77
      var elm, matches;
78
      function getAttr(elm, name) {
79
        var value = elm.attr(name);
80
        return value || '';
81
      }
82
      data.fontface = Settings.getDefaultFontFamily(editor);
83
      data.fontsize = Settings.getDefaultFontSize(editor);
84
      elm = headerFragment.firstChild;
85
      if (elm.type === 7) {
86
        data.xml_pi = true;
87
        matches = /encoding="([^"]+)"/.exec(elm.value);
88
        if (matches) {
89
          data.docencoding = matches[1];
90
        }
91
      }
92
      elm = headerFragment.getAll('#doctype')[0];
93
      if (elm) {
94
        data.doctype = '<!DOCTYPE' + elm.value + '>';
95
      }
96
      elm = headerFragment.getAll('title')[0];
97
      if (elm && elm.firstChild) {
98
        data.title = elm.firstChild.value;
99
      }
100
      global$1.each(headerFragment.getAll('meta'), function (meta) {
101
        var name = meta.attr('name');
102
        var httpEquiv = meta.attr('http-equiv');
103
        var matches;
104
        if (name) {
105
          data[name.toLowerCase()] = meta.attr('content');
106
        } else if (httpEquiv === 'Content-Type') {
107
          matches = /charset\s*=\s*(.*)\s*/gi.exec(meta.attr('content'));
108
          if (matches) {
109
            data.docencoding = matches[1];
110
          }
111
        }
112
      });
113
      elm = headerFragment.getAll('html')[0];
114
      if (elm) {
115
        data.langcode = getAttr(elm, 'lang') || getAttr(elm, 'xml:lang');
116
      }
117
      data.stylesheets = [];
118
      global$1.each(headerFragment.getAll('link'), function (link) {
119
        if (link.attr('rel') === 'stylesheet') {
120
          data.stylesheets.push(link.attr('href'));
121
        }
122
      });
123
      elm = headerFragment.getAll('body')[0];
124
      if (elm) {
125
        data.langdir = getAttr(elm, 'dir');
126
        data.style = getAttr(elm, 'style');
127
        data.visited_color = getAttr(elm, 'vlink');
128
        data.link_color = getAttr(elm, 'link');
129
        data.active_color = getAttr(elm, 'alink');
130
      }
131
      return data;
132
    };
133
    var dataToHtml = function (editor, data, head) {
134
      var headerFragment, headElement, html, elm, value;
135
      var dom = editor.dom;
136
      function setAttr(elm, name, value) {
137
        elm.attr(name, value ? value : undefined);
138
      }
139
      function addHeadNode(node) {
140
        if (headElement.firstChild) {
141
          headElement.insert(node, headElement.firstChild);
142
        } else {
143
          headElement.append(node);
144
        }
145
      }
146
      headerFragment = parseHeader(head);
147
      headElement = headerFragment.getAll('head')[0];
148
      if (!headElement) {
149
        elm = headerFragment.getAll('html')[0];
150
        headElement = new global$3('head', 1);
0 ignored issues
show
Coding Style Best Practice introduced by
By convention, constructors like global$3 should be capitalized.
Loading history...
151
        if (elm.firstChild) {
152
          elm.insert(headElement, elm.firstChild, true);
153
        } else {
154
          elm.append(headElement);
155
        }
156
      }
157
      elm = headerFragment.firstChild;
158
      if (data.xml_pi) {
159
        value = 'version="1.0"';
160
        if (data.docencoding) {
161
          value += ' encoding="' + data.docencoding + '"';
162
        }
163
        if (elm.type !== 7) {
164
          elm = new global$3('xml', 7);
0 ignored issues
show
Coding Style Best Practice introduced by
By convention, constructors like global$3 should be capitalized.
Loading history...
165
          headerFragment.insert(elm, headerFragment.firstChild, true);
166
        }
167
        elm.value = value;
168
      } else if (elm && elm.type === 7) {
169
        elm.remove();
170
      }
171
      elm = headerFragment.getAll('#doctype')[0];
172
      if (data.doctype) {
173
        if (!elm) {
174
          elm = new global$3('#doctype', 10);
0 ignored issues
show
Coding Style Best Practice introduced by
By convention, constructors like global$3 should be capitalized.
Loading history...
175
          if (data.xml_pi) {
176
            headerFragment.insert(elm, headerFragment.firstChild);
177
          } else {
178
            addHeadNode(elm);
179
          }
180
        }
181
        elm.value = data.doctype.substring(9, data.doctype.length - 1);
182
      } else if (elm) {
183
        elm.remove();
184
      }
185
      elm = null;
186
      global$1.each(headerFragment.getAll('meta'), function (meta) {
187
        if (meta.attr('http-equiv') === 'Content-Type') {
188
          elm = meta;
189
        }
190
      });
191
      if (data.docencoding) {
192
        if (!elm) {
193
          elm = new global$3('meta', 1);
0 ignored issues
show
Coding Style Best Practice introduced by
By convention, constructors like global$3 should be capitalized.
Loading history...
194
          elm.attr('http-equiv', 'Content-Type');
195
          elm.shortEnded = true;
196
          addHeadNode(elm);
197
        }
198
        elm.attr('content', 'text/html; charset=' + data.docencoding);
199
      } else if (elm) {
200
        elm.remove();
201
      }
202
      elm = headerFragment.getAll('title')[0];
203
      if (data.title) {
204
        if (!elm) {
205
          elm = new global$3('title', 1);
0 ignored issues
show
Coding Style Best Practice introduced by
By convention, constructors like global$3 should be capitalized.
Loading history...
206
          addHeadNode(elm);
207
        } else {
208
          elm.empty();
209
        }
210
        elm.append(new global$3('#text', 3)).value = data.title;
0 ignored issues
show
Coding Style Best Practice introduced by
By convention, constructors like global$3 should be capitalized.
Loading history...
211
      } else if (elm) {
212
        elm.remove();
213
      }
214
      global$1.each('keywords,description,author,copyright,robots'.split(','), function (name) {
215
        var nodes = headerFragment.getAll('meta');
216
        var i, meta;
217
        var value = data[name];
218
        for (i = 0; i < nodes.length; i++) {
219
          meta = nodes[i];
220
          if (meta.attr('name') === name) {
221
            if (value) {
222
              meta.attr('content', value);
223
            } else {
224
              meta.remove();
225
            }
226
            return;
227
          }
228
        }
229
        if (value) {
230
          elm = new global$3('meta', 1);
0 ignored issues
show
Coding Style Best Practice introduced by
By convention, constructors like global$3 should be capitalized.
Loading history...
231
          elm.attr('name', name);
232
          elm.attr('content', value);
233
          elm.shortEnded = true;
234
          addHeadNode(elm);
235
        }
236
      });
237
      var currentStyleSheetsMap = {};
238
      global$1.each(headerFragment.getAll('link'), function (stylesheet) {
239
        if (stylesheet.attr('rel') === 'stylesheet') {
240
          currentStyleSheetsMap[stylesheet.attr('href')] = stylesheet;
241
        }
242
      });
243
      global$1.each(data.stylesheets, function (stylesheet) {
244
        if (!currentStyleSheetsMap[stylesheet]) {
245
          elm = new global$3('link', 1);
0 ignored issues
show
Coding Style Best Practice introduced by
By convention, constructors like global$3 should be capitalized.
Loading history...
246
          elm.attr({
247
            rel: 'stylesheet',
248
            text: 'text/css',
249
            href: stylesheet
250
          });
251
          elm.shortEnded = true;
252
          addHeadNode(elm);
253
        }
254
        delete currentStyleSheetsMap[stylesheet];
255
      });
256
      global$1.each(currentStyleSheetsMap, function (stylesheet) {
257
        stylesheet.remove();
258
      });
259
      elm = headerFragment.getAll('body')[0];
260
      if (elm) {
261
        setAttr(elm, 'dir', data.langdir);
262
        setAttr(elm, 'style', data.style);
263
        setAttr(elm, 'vlink', data.visited_color);
264
        setAttr(elm, 'link', data.link_color);
265
        setAttr(elm, 'alink', data.active_color);
266
        dom.setAttribs(editor.getBody(), {
267
          style: data.style,
268
          dir: data.dir,
269
          vLink: data.visited_color,
270
          link: data.link_color,
271
          aLink: data.active_color
272
        });
273
      }
274
      elm = headerFragment.getAll('html')[0];
275
      if (elm) {
276
        setAttr(elm, 'lang', data.langcode);
277
        setAttr(elm, 'xml:lang', data.langcode);
278
      }
279
      if (!headElement.firstChild) {
280
        headElement.remove();
281
      }
282
      html = global$4({
283
        validate: false,
284
        indent: true,
285
        apply_source_formatting: true,
286
        indent_before: 'head,html,body,meta,title,script,link,style',
287
        indent_after: 'head,html,body,meta,title,script,link,style'
288
      }).serialize(headerFragment);
289
      return html.substring(0, html.indexOf('</body>'));
290
    };
291
    var Parser = {
292
      parseHeader: parseHeader,
293
      htmlToData: htmlToData,
294
      dataToHtml: dataToHtml
295
    };
296
297
    var open = function (editor, headState) {
298
      var data = Parser.htmlToData(editor, headState.get());
299
      editor.windowManager.open({
300
        title: 'Document properties',
301
        data: data,
302
        defaults: {
303
          type: 'textbox',
304
          size: 40
305
        },
306
        body: [
307
          {
308
            name: 'title',
309
            label: 'Title'
310
          },
311
          {
312
            name: 'keywords',
313
            label: 'Keywords'
314
          },
315
          {
316
            name: 'description',
317
            label: 'Description'
318
          },
319
          {
320
            name: 'robots',
321
            label: 'Robots'
322
          },
323
          {
324
            name: 'author',
325
            label: 'Author'
326
          },
327
          {
328
            name: 'docencoding',
329
            label: 'Encoding'
330
          }
331
        ],
332
        onSubmit: function (e) {
333
          var headHtml = Parser.dataToHtml(editor, global$1.extend(data, e.data), headState.get());
334
          headState.set(headHtml);
335
        }
336
      });
337
    };
338
    var Dialog = { open: open };
339
340
    var register = function (editor, headState) {
341
      editor.addCommand('mceFullPageProperties', function () {
342
        Dialog.open(editor, headState);
343
      });
344
    };
345
    var Commands = { register: register };
346
347
    var protectHtml = function (protect, html) {
348
      global$1.each(protect, function (pattern) {
349
        html = html.replace(pattern, function (str) {
350
          return '<!--mce:protected ' + escape(str) + '-->';
351
        });
352
      });
353
      return html;
354
    };
355
    var unprotectHtml = function (html) {
356
      return html.replace(/<!--mce:protected ([\s\S]*?)-->/g, function (a, m) {
357
        return unescape(m);
358
      });
359
    };
360
    var Protect = {
361
      protectHtml: protectHtml,
362
      unprotectHtml: unprotectHtml
363
    };
364
365
    var each = global$1.each;
366
    var low = function (s) {
367
      return s.replace(/<\/?[A-Z]+/g, function (a) {
368
        return a.toLowerCase();
369
      });
370
    };
371
    var handleSetContent = function (editor, headState, footState, evt) {
372
      var startPos, endPos, content, headerFragment, styles = '';
373
      var dom = editor.dom;
374
      var elm;
375
      if (evt.selection) {
376
        return;
377
      }
378
      content = Protect.protectHtml(editor.settings.protect, evt.content);
379
      if (evt.format === 'raw' && headState.get()) {
380
        return;
381
      }
382
      if (evt.source_view && Settings.shouldHideInSourceView(editor)) {
383
        return;
384
      }
385
      if (content.length === 0 && !evt.source_view) {
386
        content = global$1.trim(headState.get()) + '\n' + global$1.trim(content) + '\n' + global$1.trim(footState.get());
387
      }
388
      content = content.replace(/<(\/?)BODY/gi, '<$1body');
389
      startPos = content.indexOf('<body');
390
      if (startPos !== -1) {
391
        startPos = content.indexOf('>', startPos);
392
        headState.set(low(content.substring(0, startPos + 1)));
393
        endPos = content.indexOf('</body', startPos);
394
        if (endPos === -1) {
395
          endPos = content.length;
396
        }
397
        evt.content = global$1.trim(content.substring(startPos + 1, endPos));
398
        footState.set(low(content.substring(endPos)));
399
      } else {
400
        headState.set(getDefaultHeader(editor));
401
        footState.set('\n</body>\n</html>');
402
      }
403
      headerFragment = Parser.parseHeader(headState.get());
404
      each(headerFragment.getAll('style'), function (node) {
405
        if (node.firstChild) {
406
          styles += node.firstChild.value;
407
        }
408
      });
409
      elm = headerFragment.getAll('body')[0];
410
      if (elm) {
411
        dom.setAttribs(editor.getBody(), {
412
          style: elm.attr('style') || '',
413
          dir: elm.attr('dir') || '',
414
          vLink: elm.attr('vlink') || '',
415
          link: elm.attr('link') || '',
416
          aLink: elm.attr('alink') || ''
417
        });
418
      }
419
      dom.remove('fullpage_styles');
420
      var headElm = editor.getDoc().getElementsByTagName('head')[0];
421
      if (styles) {
422
        dom.add(headElm, 'style', { id: 'fullpage_styles' }, styles);
423
        elm = dom.get('fullpage_styles');
424
        if (elm.styleSheet) {
425
          elm.styleSheet.cssText = styles;
426
        }
427
      }
428
      var currentStyleSheetsMap = {};
429
      global$1.each(headElm.getElementsByTagName('link'), function (stylesheet) {
430
        if (stylesheet.rel === 'stylesheet' && stylesheet.getAttribute('data-mce-fullpage')) {
431
          currentStyleSheetsMap[stylesheet.href] = stylesheet;
432
        }
433
      });
434
      global$1.each(headerFragment.getAll('link'), function (stylesheet) {
435
        var href = stylesheet.attr('href');
436
        if (!href) {
437
          return true;
438
        }
439
        if (!currentStyleSheetsMap[href] && stylesheet.attr('rel') === 'stylesheet') {
440
          dom.add(headElm, 'link', {
441
            'rel': 'stylesheet',
442
            'text': 'text/css',
443
            'href': href,
444
            'data-mce-fullpage': '1'
445
          });
446
        }
447
        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...
448
      });
449
      global$1.each(currentStyleSheetsMap, function (stylesheet) {
450
        stylesheet.parentNode.removeChild(stylesheet);
451
      });
452
    };
453
    var getDefaultHeader = function (editor) {
454
      var header = '', value, styles = '';
455
      if (Settings.getDefaultXmlPi(editor)) {
456
        var piEncoding = Settings.getDefaultEncoding(editor);
457
        header += '<?xml version="1.0" encoding="' + (piEncoding ? piEncoding : 'ISO-8859-1') + '" ?>\n';
458
      }
459
      header += Settings.getDefaultDocType(editor);
460
      header += '\n<html>\n<head>\n';
461
      if (value = Settings.getDefaultTitle(editor)) {
462
        header += '<title>' + value + '</title>\n';
463
      }
464
      if (value = Settings.getDefaultEncoding(editor)) {
465
        header += '<meta http-equiv="Content-Type" content="text/html; charset=' + value + '" />\n';
466
      }
467
      if (value = Settings.getDefaultFontFamily(editor)) {
468
        styles += 'font-family: ' + value + ';';
469
      }
470
      if (value = Settings.getDefaultFontSize(editor)) {
471
        styles += 'font-size: ' + value + ';';
472
      }
473
      if (value = Settings.getDefaultTextColor(editor)) {
474
        styles += 'color: ' + value + ';';
475
      }
476
      header += '</head>\n<body' + (styles ? ' style="' + styles + '"' : '') + '>\n';
477
      return header;
478
    };
479
    var handleGetContent = function (editor, head, foot, evt) {
480
      if (!evt.selection && (!evt.source_view || !Settings.shouldHideInSourceView(editor))) {
481
        evt.content = Protect.unprotectHtml(global$1.trim(head) + '\n' + global$1.trim(evt.content) + '\n' + global$1.trim(foot));
482
      }
483
    };
484
    var setup = function (editor, headState, footState) {
485
      editor.on('BeforeSetContent', function (evt) {
486
        handleSetContent(editor, headState, footState, evt);
487
      });
488
      editor.on('GetContent', function (evt) {
489
        handleGetContent(editor, headState.get(), footState.get(), evt);
490
      });
491
    };
492
    var FilterContent = { setup: setup };
493
494
    var register$1 = function (editor) {
495
      editor.addButton('fullpage', {
496
        title: 'Document properties',
497
        cmd: 'mceFullPageProperties'
498
      });
499
      editor.addMenuItem('fullpage', {
500
        text: 'Document properties',
501
        cmd: 'mceFullPageProperties',
502
        context: 'file'
503
      });
504
    };
505
    var Buttons = { register: register$1 };
506
507
    global.add('fullpage', function (editor) {
508
      var headState = Cell(''), footState = Cell('');
509
      Commands.register(editor, headState);
510
      Buttons.register(editor);
511
      FilterContent.setup(editor, headState, footState);
512
    });
513
    function Plugin () {
514
    }
515
516
    return Plugin;
517
518
}());
519
})();
520