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

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

Complexity

Total Complexity 158
Complexity/F 1.61

Size

Lines of Code 616
Function Count 98

Duplication

Duplicated Lines 115
Ratio 18.67 %

Importance

Changes 0
Metric Value
eloc 440
nc 37748736
dl 115
loc 616
c 0
b 0
f 0
cc 0
rs 2
wmc 158
mnd 4
bc 135
fnc 98
bpm 1.3775
cpm 1.6122
noi 18

58 Functions

Rating   Name   Duplication   Size   Complexity  
B plugin.js ➔ some 62 62 1
A plugin.js ➔ constant 0 5 1
A plugin.js ➔ selectionInsertText 0 13 2
A plugin.js ➔ filter 0 10 3
A plugin.js ➔ hasContent 0 3 1
B textpattern.constructor 115 614 2
A plugin.js ➔ isBlockPattern 0 3 1
A plugin.js ➔ nul 3 3 1
A plugin.js ➔ get 0 3 2
A plugin.js ➔ createPatternSet 0 7 1
A plugin.js ➔ handleEnter 0 5 1
A plugin.js ➔ from 0 3 3
A plugin.js ➔ applyInlinePatternEnter 0 5 1
A plugin.js ➔ getPatternSet 0 4 1
A plugin.js ➔ splitAndApply 0 19 3
A plugin.js ➔ setup 0 28 1
A plugin.js ➔ has 0 3 1
A plugin.js ➔ sortPatterns 0 8 1
A plugin.js ➔ applyReplacementPatternEnter 0 3 1
A plugin.js ➔ sort 0 5 1
A plugin.js ➔ Cell 0 17 1
A plugin.js ➔ isText 0 3 1
A plugin.js ➔ checkKeyEvent 0 7 3
A plugin.js ➔ findPattern 0 12 5
A plugin.js ➔ ??? 0 3 1
A plugin.js ➔ applyReplacementPatternSpace 0 3 1
A plugin.js ➔ call$$1 3 3 1
A plugin.js ➔ undef 3 3 1
A me.toArray 3 3 1
A plugin.js ➔ isMatchingPattern 0 4 1
B NONE.constructor 53 53 2
A me.fold 3 3 1
A plugin.js ➔ id 3 3 1
A plugin.js ➔ isType 0 5 1
A plugin.js ➔ none 0 3 1
A plugin.js ➔ Plugin 0 2 1
A plugin.js ➔ applyReplacementPattern 0 13 3
A plugin.js ➔ applyInlinePatternSpace 0 11 1
A plugin.js ➔ checkCharCode 0 5 1
A me.getOrDie 3 3 1
A plugin.js ➔ findEndPattern 0 9 5
B plugin.js ➔ typeOf 0 10 6
A plugin.js ➔ splitContainer 0 7 2
A plugin.js ➔ get$1 0 12 1
A plugin.js ➔ applyReplacement 0 8 1
A plugin.js ➔ noop$$1 2 2 1
A plugin.js ➔ isReplacementPattern 0 3 1
C plugin.js ➔ findInlinePattern 0 33 9
A plugin.js ➔ isInlinePattern 0 3 1
A plugin.js ➔ applyInlinePattern 0 6 1
A plugin.js ➔ setSelection 0 6 1
A global.add(ꞌtextpatternꞌ) 0 5 1
A plugin.js ➔ extractChar 0 6 1
A plugin.js ➔ eq 3 3 1
D plugin.js ➔ applyBlockPattern 0 49 13
A plugin.js ➔ checkKeyCode 0 5 1
A plugin.js ➔ handleInlineKey 0 4 1
A plugin.js ➔ findReplacementPattern 0 12 3

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/textpattern/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 textpattern = (function () {
0 ignored issues
show
Unused Code introduced by
The variable textpattern 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 constant = function (value) {
26
      return function () {
27
        return value;
28
      };
29
    };
30
    var never = constant(false);
31
    var always = constant(true);
32
33
    var never$1 = never;
34
    var always$1 = always;
35
    var none = function () {
36
      return NONE;
37
    };
38 View Code Duplication
    var NONE = function () {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
39
      var eq = function (o) {
40
        return o.isNone();
41
      };
42
      var call$$1 = function (thunk) {
43
        return thunk();
44
      };
45
      var id = function (n) {
46
        return n;
47
      };
48
      var noop$$1 = function () {
49
      };
50
      var nul = function () {
51
        return null;
52
      };
53
      var undef = function () {
54
        return undefined;
55
      };
56
      var me = {
57
        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...
58
          return n();
59
        },
60
        is: never$1,
61
        isSome: never$1,
62
        isNone: always$1,
63
        getOr: id,
64
        getOrThunk: call$$1,
65
        getOrDie: function (msg) {
66
          throw new Error(msg || 'error: getOrDie called on none.');
67
        },
68
        getOrNull: nul,
69
        getOrUndefined: undef,
70
        or: id,
71
        orThunk: call$$1,
72
        map: none,
73
        ap: none,
74
        each: noop$$1,
75
        bind: none,
76
        flatten: none,
77
        exists: never$1,
78
        forall: always$1,
79
        filter: none,
80
        equals: eq,
81
        equals_: eq,
82
        toArray: function () {
83
          return [];
84
        },
85
        toString: constant('none()')
86
      };
87
      if (Object.freeze)
88
        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...
89
      return me;
90
    }();
91 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...
92
      var constant_a = function () {
93
        return a;
94
      };
95
      var self = function () {
96
        return me;
97
      };
98
      var map = function (f) {
99
        return some(f(a));
100
      };
101
      var bind = function (f) {
102
        return f(a);
103
      };
104
      var me = {
105
        fold: function (n, s) {
106
          return s(a);
107
        },
108
        is: function (v) {
109
          return a === v;
110
        },
111
        isSome: always$1,
112
        isNone: never$1,
113
        getOr: constant_a,
114
        getOrThunk: constant_a,
115
        getOrDie: constant_a,
116
        getOrNull: constant_a,
117
        getOrUndefined: constant_a,
118
        or: self,
119
        orThunk: self,
120
        map: map,
121
        ap: function (optfab) {
122
          return optfab.fold(none, function (fab) {
123
            return some(fab(a));
124
          });
125
        },
126
        each: function (f) {
127
          f(a);
128
        },
129
        bind: bind,
130
        flatten: constant_a,
131
        exists: bind,
132
        forall: bind,
133
        filter: function (f) {
134
          return f(a) ? me : NONE;
135
        },
136
        equals: function (o) {
137
          return o.is(a);
138
        },
139
        equals_: function (o, elementEq) {
140
          return o.fold(never$1, function (b) {
141
            return elementEq(a, b);
142
          });
143
        },
144
        toArray: function () {
145
          return [a];
146
        },
147
        toString: function () {
148
          return 'some(' + a + ')';
149
        }
150
      };
151
      return me;
152
    };
153
    var from = function (value) {
154
      return value === null || value === undefined ? NONE : some(value);
155
    };
156
    var Option = {
157
      some: some,
158
      none: none,
159
      from: from
160
    };
161
162
    var typeOf = function (x) {
163
      if (x === null)
164
        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...
165
      var t = typeof x;
166
      if (t === 'object' && Array.prototype.isPrototypeOf(x))
167
        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...
168
      if (t === 'object' && String.prototype.isPrototypeOf(x))
169
        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...
170
      return t;
171
    };
172
    var isType = function (type) {
173
      return function (value) {
174
        return typeOf(value) === type;
175
      };
176
    };
177
    var isFunction = isType('function');
178
179
    var filter = function (xs, pred) {
180
      var r = [];
181
      for (var i = 0, len = xs.length; i < len; i++) {
182
        var x = xs[i];
183
        if (pred(x, i, xs)) {
184
          r.push(x);
185
        }
186
      }
187
      return r;
188
    };
189
    var slice = Array.prototype.slice;
190
    var sort = function (xs, comparator) {
191
      var copy = slice.call(xs, 0);
192
      copy.sort(comparator);
193
      return copy;
194
    };
195
    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...
196
      return slice.call(x);
197
    };
198
199
    var hasOwnProperty = Object.hasOwnProperty;
200
    var get = function (obj, key) {
201
      return has(obj, key) ? Option.some(obj[key]) : Option.none();
202
    };
203
    var has = function (obj, key) {
204
      return hasOwnProperty.call(obj, key);
205
    };
206
207
    var isInlinePattern = function (pattern) {
208
      return has(pattern, 'start') && has(pattern, 'end');
209
    };
210
    var isBlockPattern = function (pattern) {
211
      return !has(pattern, 'end') && !has(pattern, 'replacement');
212
    };
213
    var isReplacementPattern = function (pattern) {
214
      return has(pattern, 'replacement');
215
    };
216
    var sortPatterns = function (patterns) {
217
      return sort(patterns, function (a, b) {
218
        if (a.start.length === b.start.length) {
219
          return 0;
220
        }
221
        return a.start.length > b.start.length ? -1 : 1;
222
      });
223
    };
224
    var createPatternSet = function (patterns) {
225
      return {
226
        inlinePatterns: sortPatterns(filter(patterns, isInlinePattern)),
227
        blockPatterns: sortPatterns(filter(patterns, isBlockPattern)),
228
        replacementPatterns: filter(patterns, isReplacementPattern)
229
      };
230
    };
231
232
    var get$1 = function (patternsState) {
233
      var setPatterns = function (newPatterns) {
234
        patternsState.set(createPatternSet(newPatterns));
235
      };
236
      var getPatterns = function () {
237
        return patternsState.get().inlinePatterns.concat(patternsState.get().blockPatterns, patternsState.get().replacementPatterns);
238
      };
239
      return {
240
        setPatterns: setPatterns,
241
        getPatterns: getPatterns
242
      };
243
    };
244
    var Api = { get: get$1 };
245
246
    var defaultPatterns = [
247
      {
248
        start: '*',
249
        end: '*',
250
        format: 'italic'
251
      },
252
      {
253
        start: '**',
254
        end: '**',
255
        format: 'bold'
256
      },
257
      {
258
        start: '***',
259
        end: '***',
260
        format: [
261
          'bold',
262
          'italic'
263
        ]
264
      },
265
      {
266
        start: '#',
267
        format: 'h1'
268
      },
269
      {
270
        start: '##',
271
        format: 'h2'
272
      },
273
      {
274
        start: '###',
275
        format: 'h3'
276
      },
277
      {
278
        start: '####',
279
        format: 'h4'
280
      },
281
      {
282
        start: '#####',
283
        format: 'h5'
284
      },
285
      {
286
        start: '######',
287
        format: 'h6'
288
      },
289
      {
290
        start: '1. ',
291
        cmd: 'InsertOrderedList'
292
      },
293
      {
294
        start: '* ',
295
        cmd: 'InsertUnorderedList'
296
      },
297
      {
298
        start: '- ',
299
        cmd: 'InsertUnorderedList'
300
      }
301
    ];
302
    var getPatternSet = function (editorSettings) {
303
      var patterns = get(editorSettings, 'textpattern_patterns').getOr(defaultPatterns);
304
      return createPatternSet(patterns);
305
    };
306
307
    var global$1 = tinymce.util.Tools.resolve('tinymce.util.Delay');
308
309
    var global$2 = tinymce.util.Tools.resolve('tinymce.util.VK');
310
311
    var global$3 = tinymce.util.Tools.resolve('tinymce.dom.TreeWalker');
312
313
    var global$4 = tinymce.util.Tools.resolve('tinymce.util.Tools');
314
315
    var findPattern = function (patterns, text) {
316
      for (var i = 0; i < patterns.length; i++) {
317
        var pattern = patterns[i];
318
        if (text.indexOf(pattern.start) !== 0) {
319
          continue;
320
        }
321
        if (pattern.end && text.lastIndexOf(pattern.end) !== text.length - pattern.end.length) {
322
          continue;
323
        }
324
        return pattern;
325
      }
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...
326
    };
327
    var isMatchingPattern = function (pattern, text, offset, delta) {
328
      var textEnd = text.substr(offset - pattern.end.length - delta, pattern.end.length);
329
      return textEnd === pattern.end;
330
    };
331
    var hasContent = function (offset, delta, pattern) {
332
      return offset - delta - pattern.end.length - pattern.start.length > 0;
333
    };
334
    var findEndPattern = function (patterns, text, offset, delta) {
335
      var pattern, i;
336
      for (i = 0; i < patterns.length; i++) {
337
        pattern = patterns[i];
338
        if (pattern.end !== undefined && isMatchingPattern(pattern, text, offset, delta) && hasContent(offset, delta, pattern)) {
339
          return pattern;
340
        }
341
      }
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...
342
    };
343
    var findInlinePattern = function (patterns, rng, space) {
344
      if (rng.collapsed === false) {
345
        return;
0 ignored issues
show
Comprehensibility Best Practice introduced by
Are you sure this return statement is not missing an argument? If this is intended, consider adding an explicit undefined like return undefined;.
Loading history...
346
      }
347
      var container = rng.startContainer;
348
      var text = container.data;
349
      var delta = space === true ? 1 : 0;
350
      if (container.nodeType !== 3) {
351
        return;
0 ignored issues
show
Comprehensibility Best Practice introduced by
Are you sure this return statement is not missing an argument? If this is intended, consider adding an explicit undefined like return undefined;.
Loading history...
352
      }
353
      var endPattern = findEndPattern(patterns, text, rng.startOffset, delta);
354
      if (endPattern === undefined) {
355
        return;
0 ignored issues
show
Comprehensibility Best Practice introduced by
Are you sure this return statement is not missing an argument? If this is intended, consider adding an explicit undefined like return undefined;.
Loading history...
356
      }
357
      var endOffset = text.lastIndexOf(endPattern.end, rng.startOffset - delta);
358
      var startOffset = text.lastIndexOf(endPattern.start, endOffset - endPattern.end.length);
359
      endOffset = text.indexOf(endPattern.end, startOffset + endPattern.start.length);
360
      if (startOffset === -1) {
361
        return;
0 ignored issues
show
Comprehensibility Best Practice introduced by
Are you sure this return statement is not missing an argument? If this is intended, consider adding an explicit undefined like return undefined;.
Loading history...
362
      }
363
      var patternRng = document.createRange();
364
      patternRng.setStart(container, startOffset);
365
      patternRng.setEnd(container, endOffset + endPattern.end.length);
366
      var startPattern = findPattern(patterns, patternRng.toString());
367
      if (endPattern === undefined || startPattern !== endPattern || container.data.length <= endPattern.start.length + endPattern.end.length) {
368
        return;
0 ignored issues
show
Comprehensibility Best Practice introduced by
Are you sure this return statement is not missing an argument? If this is intended, consider adding an explicit undefined like return undefined;.
Loading history...
369
      }
370
      return {
371
        pattern: endPattern,
372
        startOffset: startOffset,
373
        endOffset: endOffset
374
      };
375
    };
376
    var findReplacementPattern = function (patterns, startSearch, text) {
377
      for (var i = 0; i < patterns.length; i++) {
378
        var index = text.lastIndexOf(patterns[i].start, startSearch);
379
        if (index !== -1) {
380
          return Option.some({
381
            pattern: patterns[i],
382
            startOffset: index
383
          });
384
        }
385
      }
386
      return Option.none();
387
    };
388
389
    var isText = function (node) {
390
      return node && node.nodeType === 3;
391
    };
392
    var setSelection = function (editor, textNode, offset) {
393
      var newRng = editor.dom.createRng();
394
      newRng.setStart(textNode, offset);
395
      newRng.setEnd(textNode, offset);
396
      editor.selection.setRng(newRng);
397
    };
398
    var splitContainer = function (container, pattern, endOffset, startOffset) {
399
      container = startOffset > 0 ? container.splitText(startOffset) : container;
400
      container.splitText(endOffset - startOffset + pattern.end.length);
401
      container.deleteData(0, pattern.start.length);
402
      container.deleteData(container.data.length - pattern.end.length, pattern.end.length);
403
      return container;
404
    };
405
    var splitAndApply = function (editor, container, found, inline) {
406
      var formatArray = global$4.isArray(found.pattern.format) ? found.pattern.format : [found.pattern.format];
407
      var validFormats = global$4.grep(formatArray, function (formatName) {
408
        var format = editor.formatter.get(formatName);
409
        return format && format[0].inline;
410
      });
411
      if (validFormats.length !== 0) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if validFormats.length !== 0 is false. Are you sure this is correct? If so, consider adding return; explicitly.

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

Loading history...
412
        editor.undoManager.transact(function () {
413
          container = splitContainer(container, found.pattern, found.endOffset, found.startOffset);
414
          if (inline) {
415
            editor.selection.setCursorLocation(container.nextSibling, 1);
416
          }
417
          formatArray.forEach(function (format) {
418
            editor.formatter.apply(format, {}, container);
419
          });
420
        });
421
        return container;
422
      }
423
    };
424
    var applyInlinePattern = function (editor, patterns, inline) {
425
      var rng = editor.selection.getRng();
426
      return Option.from(findInlinePattern(patterns, rng, inline)).map(function (foundPattern) {
427
        return splitAndApply(editor, rng.startContainer, foundPattern, inline);
428
      });
429
    };
430
    var applyInlinePatternSpace = function (editor, patterns) {
431
      applyInlinePattern(editor, patterns, true).each(function (wrappedTextNode) {
432
        var lastChar = wrappedTextNode.data.slice(-1);
433
        if (/[\u00a0 ]/.test(lastChar)) {
434
          wrappedTextNode.deleteData(wrappedTextNode.data.length - 1, 1);
435
          var lastCharNode = editor.dom.doc.createTextNode(lastChar);
436
          editor.dom.insertAfter(lastCharNode, wrappedTextNode.parentNode);
437
          setSelection(editor, lastCharNode, 1);
438
        }
439
      });
440
    };
441
    var applyInlinePatternEnter = function (editor, patterns) {
442
      applyInlinePattern(editor, patterns, false).each(function (wrappedTextNode) {
443
        setSelection(editor, wrappedTextNode, wrappedTextNode.data.length);
444
      });
445
    };
446
    var applyBlockPattern = function (editor, patterns) {
447
      var selection, dom, container, firstTextNode, node, format, textBlockElm, pattern, walker, rng, offset;
448
      selection = editor.selection;
449
      dom = editor.dom;
450
      if (!selection.isCollapsed()) {
451
        return;
452
      }
453
      textBlockElm = dom.getParent(selection.getStart(), 'p');
454
      if (textBlockElm) {
455
        walker = new global$3(textBlockElm, textBlockElm);
0 ignored issues
show
Coding Style Best Practice introduced by
By convention, constructors like global$3 should be capitalized.
Loading history...
456
        while (node = walker.next()) {
457
          if (isText(node)) {
458
            firstTextNode = node;
459
            break;
460
          }
461
        }
462
        if (firstTextNode) {
463
          pattern = findPattern(patterns, firstTextNode.data);
464
          if (!pattern) {
465
            return;
466
          }
467
          rng = selection.getRng(true);
468
          container = rng.startContainer;
469
          offset = rng.startOffset;
470
          if (firstTextNode === container) {
471
            offset = Math.max(0, offset - pattern.start.length);
472
          }
473
          if (global$4.trim(firstTextNode.data).length === pattern.start.length) {
474
            return;
475
          }
476
          if (pattern.format) {
477
            format = editor.formatter.get(pattern.format);
478
            if (format && format[0].block) {
479
              firstTextNode.deleteData(0, pattern.start.length);
480
              editor.formatter.apply(pattern.format, {}, firstTextNode);
481
              rng.setStart(container, offset);
482
              rng.collapse(true);
483
              selection.setRng(rng);
484
            }
485
          }
486
          if (pattern.cmd) {
487
            editor.undoManager.transact(function () {
488
              firstTextNode.deleteData(0, pattern.start.length);
489
              editor.execCommand(pattern.cmd);
490
            });
491
          }
492
        }
493
      }
494
    };
495
    var selectionInsertText = function (editor, string) {
496
      var rng = editor.selection.getRng();
497
      var container = rng.startContainer;
498
      if (isText(container)) {
499
        var offset = rng.startOffset;
500
        container.insertData(offset, string);
501
        setSelection(editor, container, offset + string.length);
502
      } else {
503
        var newNode = editor.dom.doc.createTextNode(string);
504
        rng.insertNode(newNode);
505
        setSelection(editor, newNode, newNode.length);
506
      }
507
    };
508
    var applyReplacement = function (editor, target, match) {
509
      target.deleteData(match.startOffset, match.pattern.start.length);
510
      editor.insertContent(match.pattern.replacement);
511
      Option.from(target.nextSibling).filter(isText).each(function (nextSibling) {
512
        nextSibling.insertData(0, target.data);
513
        editor.dom.remove(target);
514
      });
515
    };
516
    var extractChar = function (node, match) {
517
      var offset = match.startOffset + match.pattern.start.length;
518
      var char = node.data.slice(offset, offset + 1);
519
      node.deleteData(offset, 1);
520
      return char;
521
    };
522
    var applyReplacementPattern = function (editor, patterns, inline) {
523
      var rng = editor.selection.getRng();
524
      var container = rng.startContainer;
525
      if (rng.collapsed && isText(container)) {
526
        findReplacementPattern(patterns, rng.startOffset, container.data).each(function (match) {
527
          var char = inline ? Option.some(extractChar(container, match)) : Option.none();
528
          applyReplacement(editor, container, match);
529
          char.each(function (ch) {
530
            return selectionInsertText(editor, ch);
531
          });
532
        });
533
      }
534
    };
535
    var applyReplacementPatternSpace = function (editor, patterns) {
536
      applyReplacementPattern(editor, patterns, true);
537
    };
538
    var applyReplacementPatternEnter = function (editor, patterns) {
539
      applyReplacementPattern(editor, patterns, false);
540
    };
541
542
    var handleEnter = function (editor, patternSet) {
543
      applyReplacementPatternEnter(editor, patternSet.replacementPatterns);
544
      applyInlinePatternEnter(editor, patternSet.inlinePatterns);
545
      applyBlockPattern(editor, patternSet.blockPatterns);
546
    };
547
    var handleInlineKey = function (editor, patternSet) {
548
      applyReplacementPatternSpace(editor, patternSet.replacementPatterns);
549
      applyInlinePatternSpace(editor, patternSet.inlinePatterns);
550
    };
551
    var checkKeyEvent = function (codes, event, predicate) {
552
      for (var i = 0; i < codes.length; i++) {
553
        if (predicate(codes[i], event)) {
554
          return true;
555
        }
556
      }
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...
557
    };
558
    var checkKeyCode = function (codes, event) {
559
      return checkKeyEvent(codes, event, function (code, event) {
560
        return code === event.keyCode && global$2.modifierPressed(event) === false;
561
      });
562
    };
563
    var checkCharCode = function (chars, event) {
564
      return checkKeyEvent(chars, event, function (chr, event) {
565
        return chr.charCodeAt(0) === event.charCode;
566
      });
567
    };
568
    var KeyHandler = {
569
      handleEnter: handleEnter,
570
      handleInlineKey: handleInlineKey,
571
      checkCharCode: checkCharCode,
572
      checkKeyCode: checkKeyCode
573
    };
574
575
    var setup = function (editor, patternsState) {
576
      var charCodes = [
577
        ',',
578
        '.',
579
        ';',
580
        ':',
581
        '!',
582
        '?'
583
      ];
584
      var keyCodes = [32];
585
      editor.on('keydown', function (e) {
586
        if (e.keyCode === 13 && !global$2.modifierPressed(e)) {
587
          KeyHandler.handleEnter(editor, patternsState.get());
588
        }
589
      }, true);
590
      editor.on('keyup', function (e) {
591
        if (KeyHandler.checkKeyCode(keyCodes, e)) {
592
          KeyHandler.handleInlineKey(editor, patternsState.get());
593
        }
594
      });
595
      editor.on('keypress', function (e) {
596
        if (KeyHandler.checkCharCode(charCodes, e)) {
597
          global$1.setEditorTimeout(editor, function () {
598
            KeyHandler.handleInlineKey(editor, patternsState.get());
599
          });
600
        }
601
      });
602
    };
603
    var Keyboard = { setup: setup };
604
605
    global.add('textpattern', function (editor) {
606
      var patternsState = Cell(getPatternSet(editor.settings));
607
      Keyboard.setup(editor, patternsState);
608
      return Api.get(patternsState);
609
    });
610
    function Plugin () {
611
    }
612
613
    return Plugin;
614
615
}());
616
})();
617