Completed
Branch master (2f299a)
by Ron
09:20
created

public/js/tinymce/plugins/visualchars/plugin.js   F

Complexity

Total Complexity 108
Complexity/F 1.37

Size

Lines of Code 453
Function Count 79

Duplication

Duplicated Lines 115
Ratio 25.39 %

Importance

Changes 0
Metric Value
eloc 330
nc 98304
dl 115
loc 453
c 0
b 0
f 0
cc 0
rs 2
wmc 108
mnd 3
bc 93
fnc 79
bpm 1.1772
cpm 1.367
noi 24

48 Functions

Rating   Name   Duplication   Size   Complexity  
A plugin.js ➔ fromHtml 0 10 3
A plugin.js ➔ noop$$1 2 2 1
A plugin.js ➔ fireVisualChars 0 3 1
A me.getOrDie 3 3 1
A plugin.js ➔ charMapToRegExp 0 7 3
A plugin.js ➔ constant 0 5 1
A plugin.js ➔ fromText 0 5 1
A plugin.js ➔ isType 0 5 1
A plugin.js ➔ charMapToSelector 0 10 3
B plugin.js ➔ some 62 62 1
A plugin.js ➔ type 0 3 1
A plugin.js ➔ undef 3 3 1
B plugin.js ➔ typeOf 0 10 6
A plugin.js ➔ replaceWithSpans 0 3 1
A global.add(ꞌvisualcharsꞌ) 0 7 1
A plugin.js ➔ isMatch 0 3 1
A plugin.js ➔ fromPoint 0 4 1
A plugin.js ➔ each 0 6 2
A plugin.js ➔ nul 3 3 1
A plugin.js ➔ findParentElm 0 8 3
A plugin.js ➔ isType$1 0 5 1
B visualchars.constructor 115 451 2
A plugin.js ➔ setup 0 12 2
A plugin.js ➔ Plugin 0 2 1
A plugin.js ➔ wrapCharWithSpan 0 3 1
A plugin.js ➔ fromTag 0 5 1
A plugin.js ➔ toggleVisualChars 0 14 2
A plugin.js ➔ show 0 12 1
A plugin.js ➔ fromDom 0 5 3
B NONE.constructor 53 53 2
A plugin.js ➔ register$1 0 16 1
A plugin.js ➔ id 3 3 1
A plugin.js ➔ value 0 3 1
A me.toArray 3 3 1
A plugin.js ➔ toggle 0 9 2
A plugin.js ➔ map 0 9 2
A me.fold 3 3 1
A plugin.js ➔ toggleActiveState 0 8 1
A plugin.js ➔ filterDescendants 0 12 1
A plugin.js ➔ get 0 6 1
A plugin.js ➔ eq 3 3 1
A plugin.js ➔ from 0 3 3
A plugin.js ➔ ??? 0 3 1
A plugin.js ➔ hide 0 6 1
A plugin.js ➔ register 0 5 1
A plugin.js ➔ none 0 3 1
A plugin.js ➔ call$$1 3 3 1
A plugin.js ➔ Cell 0 17 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complexity

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like public/js/tinymce/plugins/visualchars/plugin.js 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 visualchars = (function () {
0 ignored issues
show
Unused Code introduced by
The variable visualchars 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 get = function (toggleState) {
26
      var isEnabled = function () {
27
        return toggleState.get();
28
      };
29
      return { isEnabled: isEnabled };
30
    };
31
    var Api = { get: get };
32
33
    var fireVisualChars = function (editor, state) {
34
      return editor.fire('VisualChars', { state: state });
35
    };
36
    var Events = { fireVisualChars: fireVisualChars };
37
38
    var charMap = {
39
      '\xA0': 'nbsp',
40
      '\xAD': 'shy'
41
    };
42
    var charMapToRegExp = function (charMap, global) {
43
      var key, regExp = '';
44
      for (key in charMap) {
45
        regExp += key;
46
      }
47
      return new RegExp('[' + regExp + ']', global ? 'g' : '');
48
    };
49
    var charMapToSelector = function (charMap) {
50
      var key, selector = '';
51
      for (key in charMap) {
52
        if (selector) {
53
          selector += ',';
54
        }
55
        selector += 'span.mce-' + charMap[key];
56
      }
57
      return selector;
58
    };
59
    var Data = {
60
      charMap: charMap,
61
      regExp: charMapToRegExp(charMap),
62
      regExpGlobal: charMapToRegExp(charMap, true),
63
      selector: charMapToSelector(charMap),
64
      charMapToRegExp: charMapToRegExp,
65
      charMapToSelector: charMapToSelector
66
    };
67
68
    var constant = function (value) {
69
      return function () {
70
        return value;
71
      };
72
    };
73
    var never = constant(false);
74
    var always = constant(true);
75
76
    var never$1 = never;
77
    var always$1 = always;
78
    var none = function () {
79
      return NONE;
80
    };
81 View Code Duplication
    var NONE = function () {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
82
      var eq = function (o) {
83
        return o.isNone();
84
      };
85
      var call$$1 = function (thunk) {
86
        return thunk();
87
      };
88
      var id = function (n) {
89
        return n;
90
      };
91
      var noop$$1 = function () {
92
      };
93
      var nul = function () {
94
        return null;
95
      };
96
      var undef = function () {
97
        return undefined;
98
      };
99
      var me = {
100
        fold: function (n, s) {
0 ignored issues
show
Unused Code introduced by
The parameter s is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
101
          return n();
102
        },
103
        is: never$1,
104
        isSome: never$1,
105
        isNone: always$1,
106
        getOr: id,
107
        getOrThunk: call$$1,
108
        getOrDie: function (msg) {
109
          throw new Error(msg || 'error: getOrDie called on none.');
110
        },
111
        getOrNull: nul,
112
        getOrUndefined: undef,
113
        or: id,
114
        orThunk: call$$1,
115
        map: none,
116
        ap: none,
117
        each: noop$$1,
118
        bind: none,
119
        flatten: none,
120
        exists: never$1,
121
        forall: always$1,
122
        filter: none,
123
        equals: eq,
124
        equals_: eq,
125
        toArray: function () {
126
          return [];
127
        },
128
        toString: constant('none()')
129
      };
130
      if (Object.freeze)
131
        Object.freeze(me);
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
132
      return me;
133
    }();
134 View Code Duplication
    var some = function (a) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
135
      var constant_a = function () {
136
        return a;
137
      };
138
      var self = function () {
139
        return me;
140
      };
141
      var map = function (f) {
142
        return some(f(a));
143
      };
144
      var bind = function (f) {
145
        return f(a);
146
      };
147
      var me = {
148
        fold: function (n, s) {
149
          return s(a);
150
        },
151
        is: function (v) {
152
          return a === v;
153
        },
154
        isSome: always$1,
155
        isNone: never$1,
156
        getOr: constant_a,
157
        getOrThunk: constant_a,
158
        getOrDie: constant_a,
159
        getOrNull: constant_a,
160
        getOrUndefined: constant_a,
161
        or: self,
162
        orThunk: self,
163
        map: map,
164
        ap: function (optfab) {
165
          return optfab.fold(none, function (fab) {
166
            return some(fab(a));
167
          });
168
        },
169
        each: function (f) {
170
          f(a);
171
        },
172
        bind: bind,
173
        flatten: constant_a,
174
        exists: bind,
175
        forall: bind,
176
        filter: function (f) {
177
          return f(a) ? me : NONE;
178
        },
179
        equals: function (o) {
180
          return o.is(a);
181
        },
182
        equals_: function (o, elementEq) {
183
          return o.fold(never$1, function (b) {
184
            return elementEq(a, b);
185
          });
186
        },
187
        toArray: function () {
188
          return [a];
189
        },
190
        toString: function () {
191
          return 'some(' + a + ')';
192
        }
193
      };
194
      return me;
195
    };
196
    var from = function (value) {
197
      return value === null || value === undefined ? NONE : some(value);
198
    };
199
    var Option = {
200
      some: some,
201
      none: none,
202
      from: from
203
    };
204
205
    var typeOf = function (x) {
206
      if (x === null)
207
        return 'null';
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
208
      var t = typeof x;
209
      if (t === 'object' && Array.prototype.isPrototypeOf(x))
210
        return 'array';
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
211
      if (t === 'object' && String.prototype.isPrototypeOf(x))
212
        return 'string';
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
213
      return t;
214
    };
215
    var isType = function (type) {
216
      return function (value) {
217
        return typeOf(value) === type;
218
      };
219
    };
220
    var isFunction = isType('function');
221
222
    var map = function (xs, f) {
223
      var len = xs.length;
224
      var r = new Array(len);
0 ignored issues
show
Coding Style Best Practice introduced by
Using the Array constructor is generally discouraged. Consider using an array literal instead.
Loading history...
225
      for (var i = 0; i < len; i++) {
226
        var x = xs[i];
227
        r[i] = f(x, i, xs);
228
      }
229
      return r;
230
    };
231
    var each = function (xs, f) {
232
      for (var i = 0, len = xs.length; i < len; i++) {
233
        var x = xs[i];
234
        f(x, i, xs);
235
      }
236
    };
237
    var slice = Array.prototype.slice;
238
    var from$1 = isFunction(Array.from) ? Array.from : function (x) {
0 ignored issues
show
Unused Code introduced by
The variable from$1 seems to be never used. Consider removing it.
Loading history...
239
      return slice.call(x);
240
    };
241
242
    var fromHtml = function (html, scope) {
243
      var doc = scope || document;
244
      var div = doc.createElement('div');
245
      div.innerHTML = html;
246
      if (!div.hasChildNodes() || div.childNodes.length > 1) {
247
        console.error('HTML does not have a single root node', html);
248
        throw 'HTML must have a single root node';
249
      }
250
      return fromDom(div.childNodes[0]);
251
    };
252
    var fromTag = function (tag, scope) {
253
      var doc = scope || document;
254
      var node = doc.createElement(tag);
255
      return fromDom(node);
256
    };
257
    var fromText = function (text, scope) {
258
      var doc = scope || document;
259
      var node = doc.createTextNode(text);
260
      return fromDom(node);
261
    };
262
    var fromDom = function (node) {
263
      if (node === null || node === undefined)
264
        throw new Error('Node cannot be null or undefined');
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
265
      return { dom: constant(node) };
266
    };
267
    var fromPoint = function (docElm, x, y) {
268
      var doc = docElm.dom();
269
      return Option.from(doc.elementFromPoint(x, y)).map(fromDom);
270
    };
271
    var Element$$1 = {
272
      fromHtml: fromHtml,
273
      fromTag: fromTag,
274
      fromText: fromText,
275
      fromDom: fromDom,
276
      fromPoint: fromPoint
277
    };
278
279
    var ATTRIBUTE = Node.ATTRIBUTE_NODE;
0 ignored issues
show
Unused Code introduced by
The variable ATTRIBUTE seems to be never used. Consider removing it.
Loading history...
Bug introduced by
The variable Node seems to be never declared. If this is a global, consider adding a /** global: Node */ 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...
280
    var CDATA_SECTION = Node.CDATA_SECTION_NODE;
0 ignored issues
show
Unused Code introduced by
The variable CDATA_SECTION seems to be never used. Consider removing it.
Loading history...
281
    var COMMENT = Node.COMMENT_NODE;
0 ignored issues
show
Unused Code introduced by
The variable COMMENT seems to be never used. Consider removing it.
Loading history...
282
    var DOCUMENT = Node.DOCUMENT_NODE;
0 ignored issues
show
Unused Code introduced by
The variable DOCUMENT seems to be never used. Consider removing it.
Loading history...
283
    var DOCUMENT_TYPE = Node.DOCUMENT_TYPE_NODE;
0 ignored issues
show
Unused Code introduced by
The variable DOCUMENT_TYPE seems to be never used. Consider removing it.
Loading history...
284
    var DOCUMENT_FRAGMENT = Node.DOCUMENT_FRAGMENT_NODE;
0 ignored issues
show
Unused Code introduced by
The variable DOCUMENT_FRAGMENT seems to be never used. Consider removing it.
Loading history...
285
    var ELEMENT = Node.ELEMENT_NODE;
0 ignored issues
show
Unused Code introduced by
The variable ELEMENT seems to be never used. Consider removing it.
Loading history...
286
    var TEXT = Node.TEXT_NODE;
287
    var PROCESSING_INSTRUCTION = Node.PROCESSING_INSTRUCTION_NODE;
0 ignored issues
show
Unused Code introduced by
The variable PROCESSING_INSTRUCTION seems to be never used. Consider removing it.
Loading history...
288
    var ENTITY_REFERENCE = Node.ENTITY_REFERENCE_NODE;
0 ignored issues
show
Unused Code introduced by
The variable ENTITY_REFERENCE seems to be never used. Consider removing it.
Loading history...
289
    var ENTITY = Node.ENTITY_NODE;
0 ignored issues
show
Unused Code introduced by
The variable ENTITY seems to be never used. Consider removing it.
Loading history...
290
    var NOTATION = Node.NOTATION_NODE;
0 ignored issues
show
Unused Code introduced by
The variable NOTATION seems to be never used. Consider removing it.
Loading history...
291
292
    var type = function (element) {
293
      return element.dom().nodeType;
294
    };
295
    var value = function (element) {
296
      return element.dom().nodeValue;
297
    };
298
    var isType$1 = function (t) {
299
      return function (element) {
300
        return type(element) === t;
301
      };
302
    };
303
    var isText = isType$1(TEXT);
304
305
    var wrapCharWithSpan = function (value) {
306
      return '<span data-mce-bogus="1" class="mce-' + Data.charMap[value] + '">' + value + '</span>';
307
    };
308
    var Html = { wrapCharWithSpan: wrapCharWithSpan };
309
310
    var isMatch = function (n) {
311
      return isText(n) && value(n) !== undefined && Data.regExp.test(value(n));
312
    };
313
    var filterDescendants = function (scope, predicate) {
314
      var result = [];
315
      var dom = scope.dom();
316
      var children = map(dom.childNodes, Element$$1.fromDom);
317
      each(children, function (x) {
318
        if (predicate(x)) {
319
          result = result.concat([x]);
320
        }
321
        result = result.concat(filterDescendants(x, predicate));
322
      });
323
      return result;
324
    };
325
    var findParentElm = function (elm, rootElm) {
326
      while (elm.parentNode) {
327
        if (elm.parentNode === rootElm) {
328
          return elm;
329
        }
330
        elm = elm.parentNode;
331
      }
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...
332
    };
333
    var replaceWithSpans = function (html) {
334
      return html.replace(Data.regExpGlobal, Html.wrapCharWithSpan);
335
    };
336
    var Nodes = {
337
      isMatch: isMatch,
338
      filterDescendants: filterDescendants,
339
      findParentElm: findParentElm,
340
      replaceWithSpans: replaceWithSpans
341
    };
342
343
    var show = function (editor, rootElm) {
344
      var node, div;
345
      var nodeList = Nodes.filterDescendants(Element$$1.fromDom(rootElm), Nodes.isMatch);
346
      each(nodeList, function (n) {
347
        var withSpans = Nodes.replaceWithSpans(value(n));
348
        div = editor.dom.create('div', null, withSpans);
349
        while (node = div.lastChild) {
350
          editor.dom.insertAfter(node, n.dom());
0 ignored issues
show
Bug introduced by
The variable node is changed as part of the while loop for example by div.lastChild on line 349. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
351
        }
352
        editor.dom.remove(n.dom());
353
      });
354
    };
355
    var hide = function (editor, body) {
356
      var nodeList = editor.dom.select(Data.selector, body);
357
      each(nodeList, function (node) {
358
        editor.dom.remove(node, 1);
359
      });
360
    };
361
    var toggle = function (editor) {
362
      var body = editor.getBody();
363
      var bookmark = editor.selection.getBookmark();
364
      var parentNode = Nodes.findParentElm(editor.selection.getNode(), body);
365
      parentNode = parentNode !== undefined ? parentNode : body;
366
      hide(editor, parentNode);
367
      show(editor, parentNode);
368
      editor.selection.moveToBookmark(bookmark);
369
    };
370
    var VisualChars = {
371
      show: show,
372
      hide: hide,
373
      toggle: toggle
374
    };
375
376
    var toggleVisualChars = function (editor, toggleState) {
377
      var body = editor.getBody();
378
      var selection = editor.selection;
379
      var bookmark;
380
      toggleState.set(!toggleState.get());
381
      Events.fireVisualChars(editor, toggleState.get());
382
      bookmark = selection.getBookmark();
383
      if (toggleState.get() === true) {
384
        VisualChars.show(editor, body);
385
      } else {
386
        VisualChars.hide(editor, body);
387
      }
388
      selection.moveToBookmark(bookmark);
389
    };
390
    var Actions = { toggleVisualChars: toggleVisualChars };
391
392
    var register = function (editor, toggleState) {
393
      editor.addCommand('mceVisualChars', function () {
394
        Actions.toggleVisualChars(editor, toggleState);
395
      });
396
    };
397
    var Commands = { register: register };
398
399
    var global$1 = tinymce.util.Tools.resolve('tinymce.util.Delay');
400
401
    var setup = function (editor, toggleState) {
402
      var debouncedToggle = global$1.debounce(function () {
403
        VisualChars.toggle(editor);
404
      }, 300);
405
      if (editor.settings.forced_root_block !== false) {
406
        editor.on('keydown', function (e) {
407
          if (toggleState.get() === true) {
408
            e.keyCode === 13 ? VisualChars.toggle(editor) : debouncedToggle();
409
          }
410
        });
411
      }
412
    };
413
    var Keyboard = { setup: setup };
414
415
    var toggleActiveState = function (editor) {
416
      return function (e) {
417
        var ctrl = e.control;
418
        editor.on('VisualChars', function (e) {
419
          ctrl.active(e.state);
420
        });
421
      };
422
    };
423
    var register$1 = function (editor) {
424
      editor.addButton('visualchars', {
425
        active: false,
426
        title: 'Show invisible characters',
427
        cmd: 'mceVisualChars',
428
        onPostRender: toggleActiveState(editor)
429
      });
430
      editor.addMenuItem('visualchars', {
431
        text: 'Show invisible characters',
432
        cmd: 'mceVisualChars',
433
        onPostRender: toggleActiveState(editor),
434
        selectable: true,
435
        context: 'view',
436
        prependToContext: true
437
      });
438
    };
439
440
    global.add('visualchars', function (editor) {
441
      var toggleState = Cell(false);
442
      Commands.register(editor, toggleState);
443
      register$1(editor);
444
      Keyboard.setup(editor, toggleState);
445
      return Api.get(toggleState);
446
    });
447
    function Plugin () {
448
    }
449
450
    return Plugin;
451
452
}());
453
})();
454