codemirror.js ➔ makeChangeSingleDocInEditor   F
last analyzed

Complexity

Conditions 14

Size

Total Lines 58
Code Lines 39

Duplication

Lines 58
Ratio 100 %

Importance

Changes 0
Metric Value
eloc 39
c 0
b 0
f 0
dl 58
loc 58
rs 3.6
cc 14

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 codemirror.js ➔ makeChangeSingleDocInEditor 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
// CodeMirror v5.0, copyright (c) by Marijn Haverbeke and others
2
// Distributed under an MIT license: http://codemirror.net/LICENSE
3
4
// This is CodeMirror (http://codemirror.net), a code editor
5
// implemented in JavaScript on top of the browser's DOM.
6
//
7
// You can find some technical background for some of the code below
8
// at http://marijnhaverbeke.nl/blog/#cm-internals .
9
10 View Code Duplication
(function(mod) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
11
  if (typeof exports == "object" && typeof module == "object") // CommonJS
12
    module.exports = mod();
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...
13
  else if (typeof define == "function" && define.amd) // AMD
0 ignored issues
show
Bug introduced by
The variable define seems to be never declared. If this is a global, consider adding a /** global: define */ 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
    return define([], mod);
15
  else // Plain browser env
16
    this.CodeMirror = mod();
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...
17
})(function() {
18
  "use strict";
19
20
  // BROWSER SNIFFING
21
22
  // Kludges for bugs and behavior differences that can't be feature
23
  // detected are enabled based on userAgent etc sniffing.
24
25
  var gecko = /gecko\/\d/i.test(navigator.userAgent);
0 ignored issues
show
Bug introduced by
The variable navigator seems to be never declared. If this is a global, consider adding a /** global: navigator */ 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...
26
  var ie_upto10 = /MSIE \d/.test(navigator.userAgent);
27
  var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(navigator.userAgent);
28
  var ie = ie_upto10 || ie_11up;
29
  var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : ie_11up[1]);
30
  var webkit = /WebKit\//.test(navigator.userAgent);
31
  var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(navigator.userAgent);
32
  var chrome = /Chrome\//.test(navigator.userAgent);
33
  var presto = /Opera\//.test(navigator.userAgent);
34
  var safari = /Apple Computer/.test(navigator.vendor);
35
  var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(navigator.userAgent);
36
  var phantom = /PhantomJS/.test(navigator.userAgent);
37
38
  var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent);
39
  // This is woefully incomplete. Suggestions for alternative methods welcome.
40
  var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(navigator.userAgent);
41
  var mac = ios || /Mac/.test(navigator.platform);
42
  var windows = /win/i.test(navigator.platform);
43
44
  var presto_version = presto && navigator.userAgent.match(/Version\/(\d*\.\d*)/);
45
  if (presto_version) presto_version = Number(presto_version[1]);
46
  if (presto_version && presto_version >= 15) { presto = false; webkit = true; }
47
  // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
48
  var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11));
0 ignored issues
show
Best Practice introduced by
Comparing presto_version to null using the == operator is not safe. Consider using === instead.
Loading history...
49
  var captureRightClick = gecko || (ie && ie_version >= 9);
50
51
  // Optimize some code when these features are not used.
52
  var sawReadOnlySpans = false, sawCollapsedSpans = false;
53
54
  // EDITOR CONSTRUCTOR
55
56
  // A CodeMirror instance represents an editor. This is the object
57
  // that user code is usually dealing with.
58
59
  function CodeMirror(place, options) {
60
    if (!(this instanceof CodeMirror)) return new CodeMirror(place, options);
61
62
    this.options = options = options ? copyObj(options) : {};
63
    // Determine effective options based on given values and defaults.
64
    copyObj(defaults, options, false);
65
    setGuttersForLineNumbers(options);
66
67
    var doc = options.value;
68
    if (typeof doc == "string") doc = new Doc(doc, options.mode);
69
    this.doc = doc;
70
71
    var input = new CodeMirror.inputStyles[options.inputStyle](this);
72
    var display = this.display = new Display(place, doc, input);
73
    display.wrapper.CodeMirror = this;
74
    updateGutters(this);
75
    themeChanged(this);
76
    if (options.lineWrapping)
77
      this.display.wrapper.className += " CodeMirror-wrap";
78
    if (options.autofocus && !mobile) display.input.focus();
79
    initScrollbars(this);
80
81
    this.state = {
82
      keyMaps: [],  // stores maps added by addKeyMap
83
      overlays: [], // highlighting overlays, as added by addOverlay
84
      modeGen: 0,   // bumped when mode/overlay changes, used to invalidate highlighting info
85
      overwrite: false, focused: false,
86
      suppressEdits: false, // used to disable editing during key handlers when in readOnly mode
87
      pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edits in input.poll
88
      draggingText: false,
89
      highlight: new Delayed(), // stores highlight worker timeout
90
      keySeq: null  // Unfinished key sequence
91
    };
92
93
    var cm = this;
94
95
    // Override magic textarea content restore that IE sometimes does
96
    // on our hidden textarea on reload
97
    if (ie && ie_version < 11) setTimeout(function() { cm.display.input.reset(true); }, 20);
98
99
    registerEventHandlers(this);
100
    ensureGlobalHandlers();
101
102
    startOperation(this);
103
    this.curOp.forceUpdate = true;
104
    attachDoc(this, doc);
105
106
    if ((options.autofocus && !mobile) || cm.hasFocus())
107
      setTimeout(bind(onFocus, this), 20);
108
    else
109
      onBlur(this);
110
111
    for (var opt in optionHandlers) if (optionHandlers.hasOwnProperty(opt))
112
      optionHandlers[opt](this, options[opt], Init);
113
    maybeUpdateLineNumberWidth(this);
114
    if (options.finishInit) options.finishInit(this);
115
    for (var i = 0; i < initHooks.length; ++i) initHooks[i](this);
116
    endOperation(this);
117
    // Suppress optimizelegibility in Webkit, since it breaks text
118
    // measuring on line wrapping boundaries.
119
    if (webkit && options.lineWrapping &&
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if webkit && options.lineWr...== "optimizelegibility" 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...
120
        getComputedStyle(display.lineDiv).textRendering == "optimizelegibility")
121
      display.lineDiv.style.textRendering = "auto";
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...
122
  }
123
124
  // DISPLAY CONSTRUCTOR
125
126
  // The display handles the DOM integration, both for input reading
127
  // and content drawing. It holds references to DOM nodes and
128
  // display-related state.
129
130
  function Display(place, doc, input) {
131
    var d = this;
132
    this.input = input;
133
134
    // Covers bottom-right square when both scrollbars are present.
135
    d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
136
    d.scrollbarFiller.setAttribute("cm-not-content", "true");
137
    // Covers bottom of gutter when coverGutterNextToScrollbar is on
138
    // and h scrollbar is present.
139
    d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler");
140
    d.gutterFiller.setAttribute("cm-not-content", "true");
141
    // Will contain the actual code, positioned to cover the viewport.
142
    d.lineDiv = elt("div", null, "CodeMirror-code");
143
    // Elements are added to these to represent selection and cursors.
144
    d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
145
    d.cursorDiv = elt("div", null, "CodeMirror-cursors");
146
    // A visibility: hidden element used to find the size of things.
147
    d.measure = elt("div", null, "CodeMirror-measure");
148
    // When lines outside of the viewport are measured, they are drawn in this.
149
    d.lineMeasure = elt("div", null, "CodeMirror-measure");
150
    // Wraps everything that needs to exist inside the vertically-padded coordinate system
151
    d.lineSpace = elt("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],
152
                      null, "position: relative; outline: none");
153
    // Moved around its parent to cover visible view.
154
    d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null, "position: relative");
155
    // Set to the height of the document, allowing scrolling.
156
    d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
157
    d.sizerWidth = null;
158
    // Behavior of elts with overflow: auto and padding is
159
    // inconsistent across browsers. This is used to ensure the
160
    // scrollable area is big enough.
161
    d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;");
162
    // Will contain the gutters, if any.
163
    d.gutters = elt("div", null, "CodeMirror-gutters");
164
    d.lineGutter = null;
165
    // Actual scrollable element.
166
    d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll");
167
    d.scroller.setAttribute("tabIndex", "-1");
168
    // The element in which the editor lives.
169
    d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror");
170
171
    // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)
172
    if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
173
    if (!webkit && !(gecko && mobile)) d.scroller.draggable = true;
174
175
    if (place) {
176
      if (place.appendChild) place.appendChild(d.wrapper);
177
      else place(d.wrapper);
178
    }
179
180
    // Current rendered range (may be bigger than the view window).
181
    d.viewFrom = d.viewTo = doc.first;
182
    d.reportedViewFrom = d.reportedViewTo = doc.first;
183
    // Information about the rendered lines.
184
    d.view = [];
185
    d.renderedView = null;
186
    // Holds info about a single rendered line when it was rendered
187
    // for measurement, while not in view.
188
    d.externalMeasured = null;
189
    // Empty space (in pixels) above the view
190
    d.viewOffset = 0;
191
    d.lastWrapHeight = d.lastWrapWidth = 0;
192
    d.updateLineNumbers = null;
193
194
    d.nativeBarWidth = d.barHeight = d.barWidth = 0;
195
    d.scrollbarsClipped = false;
196
197
    // Used to only resize the line number gutter when necessary (when
198
    // the amount of lines crosses a boundary that makes its width change)
199
    d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
200
    // Set to true when a non-horizontal-scrolling line widget is
201
    // added. As an optimization, line widget aligning is skipped when
202
    // this is false.
203
    d.alignWidgets = false;
204
205
    d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
206
207
    // Tracks the maximum line length so that the horizontal scrollbar
208
    // can be kept static when scrolling.
209
    d.maxLine = null;
210
    d.maxLineLength = 0;
211
    d.maxLineChanged = false;
212
213
    // Used for measuring wheel scrolling granularity
214
    d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
215
216
    // True when shift is held down.
217
    d.shift = false;
218
219
    // Used to track whether anything happened since the context menu
220
    // was opened.
221
    d.selForContextMenu = null;
222
223
    d.activeTouch = null;
224
225
    input.init(d);
226
  }
227
228
  // STATE UPDATES
229
230
  // Used to get the editor into a consistent state again when options change.
231
232
  function loadMode(cm) {
233
    cm.doc.mode = CodeMirror.getMode(cm.options, cm.doc.modeOption);
234
    resetModeState(cm);
235
  }
236
237
  function resetModeState(cm) {
238
    cm.doc.iter(function(line) {
239
      if (line.stateAfter) line.stateAfter = null;
240
      if (line.styles) line.styles = null;
241
    });
242
    cm.doc.frontier = cm.doc.first;
243
    startWorker(cm, 100);
244
    cm.state.modeGen++;
245
    if (cm.curOp) regChange(cm);
246
  }
247
248
  function wrappingChanged(cm) {
249
    if (cm.options.lineWrapping) {
250
      addClass(cm.display.wrapper, "CodeMirror-wrap");
251
      cm.display.sizer.style.minWidth = "";
252
      cm.display.sizerWidth = null;
253
    } else {
254
      rmClass(cm.display.wrapper, "CodeMirror-wrap");
255
      findMaxLine(cm);
256
    }
257
    estimateLineHeights(cm);
258
    regChange(cm);
259
    clearCaches(cm);
260
    setTimeout(function(){updateScrollbars(cm);}, 100);
261
  }
262
263
  // Returns a function that estimates the height of a line, to use as
264
  // first approximation until the line becomes visible (and is thus
265
  // properly measurable).
266
  function estimateHeight(cm) {
267
    var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
268
    var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
269
    return function(line) {
270
      if (lineIsHidden(cm.doc, line)) return 0;
271
272
      var widgetsHeight = 0;
273
      if (line.widgets) for (var i = 0; i < line.widgets.length; i++) {
274
        if (line.widgets[i].height) widgetsHeight += line.widgets[i].height;
275
      }
276
277
      if (wrapping)
278
        return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th;
279
      else
280
        return widgetsHeight + th;
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
281
    };
282
  }
283
284
  function estimateLineHeights(cm) {
285
    var doc = cm.doc, est = estimateHeight(cm);
286
    doc.iter(function(line) {
287
      var estHeight = est(line);
288
      if (estHeight != line.height) updateLineHeight(line, estHeight);
289
    });
290
  }
291
292
  function themeChanged(cm) {
293
    cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
294
      cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
295
    clearCaches(cm);
296
  }
297
298
  function guttersChanged(cm) {
299
    updateGutters(cm);
300
    regChange(cm);
301
    setTimeout(function(){alignHorizontally(cm);}, 20);
302
  }
303
304
  // Rebuild the gutter elements, ensure the margin to the left of the
305
  // code matches their width.
306
  function updateGutters(cm) {
307
    var gutters = cm.display.gutters, specs = cm.options.gutters;
308
    removeChildren(gutters);
309
    for (var i = 0; i < specs.length; ++i) {
310
      var gutterClass = specs[i];
311
      var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass));
312
      if (gutterClass == "CodeMirror-linenumbers") {
313
        cm.display.lineGutter = gElt;
314
        gElt.style.width = (cm.display.lineNumWidth || 1) + "px";
315
      }
316
    }
317
    gutters.style.display = i ? "" : "none";
318
    updateGutterSpace(cm);
319
  }
320
321
  function updateGutterSpace(cm) {
322
    var width = cm.display.gutters.offsetWidth;
323
    cm.display.sizer.style.marginLeft = width + "px";
324
  }
325
326
  // Compute the character length of a line, taking into account
327
  // collapsed ranges (see markText) that might hide parts, and join
328
  // other lines onto it.
329
  function lineLength(line) {
330
    if (line.height == 0) return 0;
0 ignored issues
show
Best Practice introduced by
Comparing line.height to 0 using the == operator is not safe. Consider using === instead.
Loading history...
331
    var len = line.text.length, merged, cur = line;
332
    while (merged = collapsedSpanAtStart(cur)) {
333
      var found = merged.find(0, true);
334
      cur = found.from.line;
335
      len += found.from.ch - found.to.ch;
336
    }
337
    cur = line;
338
    while (merged = collapsedSpanAtEnd(cur)) {
339
      var found = merged.find(0, true);
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable found already seems to be declared on line 333. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
340
      len -= cur.text.length - found.from.ch;
341
      cur = found.to.line;
342
      len += cur.text.length - found.to.ch;
343
    }
344
    return len;
345
  }
346
347
  // Find the longest line in the document.
348
  function findMaxLine(cm) {
349
    var d = cm.display, doc = cm.doc;
350
    d.maxLine = getLine(doc, doc.first);
351
    d.maxLineLength = lineLength(d.maxLine);
352
    d.maxLineChanged = true;
353
    doc.iter(function(line) {
354
      var len = lineLength(line);
355
      if (len > d.maxLineLength) {
356
        d.maxLineLength = len;
357
        d.maxLine = line;
358
      }
359
    });
360
  }
361
362
  // Make sure the gutters options contains the element
363
  // "CodeMirror-linenumbers" when the lineNumbers option is true.
364
  function setGuttersForLineNumbers(options) {
365
    var found = indexOf(options.gutters, "CodeMirror-linenumbers");
366
    if (found == -1 && options.lineNumbers) {
367
      options.gutters = options.gutters.concat(["CodeMirror-linenumbers"]);
368
    } else if (found > -1 && !options.lineNumbers) {
369
      options.gutters = options.gutters.slice(0);
370
      options.gutters.splice(found, 1);
371
    }
372
  }
373
374
  // SCROLLBARS
375
376
  // Prepare DOM reads needed to update the scrollbars. Done in one
377
  // shot to minimize update/measure roundtrips.
378
  function measureForScrollbars(cm) {
379
    var d = cm.display, gutterW = d.gutters.offsetWidth;
380
    var docH = Math.round(cm.doc.height + paddingVert(cm.display));
381
    return {
382
      clientHeight: d.scroller.clientHeight,
383
      viewHeight: d.wrapper.clientHeight,
384
      scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,
385
      viewWidth: d.wrapper.clientWidth,
386
      barLeft: cm.options.fixedGutter ? gutterW : 0,
387
      docHeight: docH,
388
      scrollHeight: docH + scrollGap(cm) + d.barHeight,
389
      nativeBarWidth: d.nativeBarWidth,
390
      gutterWidth: gutterW
391
    };
392
  }
393
394
  function NativeScrollbars(place, scroll, cm) {
395
    this.cm = cm;
396
    var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar");
397
    var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar");
398
    place(vert); place(horiz);
399
400
    on(vert, "scroll", function() {
401
      if (vert.clientHeight) scroll(vert.scrollTop, "vertical");
402
    });
403
    on(horiz, "scroll", function() {
404
      if (horiz.clientWidth) scroll(horiz.scrollLeft, "horizontal");
405
    });
406
407
    this.checkedOverlay = false;
408
    // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
409
    if (ie && ie_version < 8) this.horiz.style.minHeight = this.vert.style.minWidth = "18px";
410
  }
411
412
  NativeScrollbars.prototype = copyObj({
413
    update: function(measure) {
414
      var needsH = measure.scrollWidth > measure.clientWidth + 1;
415
      var needsV = measure.scrollHeight > measure.clientHeight + 1;
416
      var sWidth = measure.nativeBarWidth;
417
418
      if (needsV) {
419
        this.vert.style.display = "block";
420
        this.vert.style.bottom = needsH ? sWidth + "px" : "0";
421
        var totalHeight = measure.viewHeight - (needsH ? sWidth : 0);
422
        // A bug in IE8 can cause this value to be negative, so guard it.
423
        this.vert.firstChild.style.height =
424
          Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px";
425
      } else {
426
        this.vert.style.display = "";
427
        this.vert.firstChild.style.height = "0";
428
      }
429
430
      if (needsH) {
431
        this.horiz.style.display = "block";
432
        this.horiz.style.right = needsV ? sWidth + "px" : "0";
433
        this.horiz.style.left = measure.barLeft + "px";
434
        var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0);
435
        this.horiz.firstChild.style.width =
436
          (measure.scrollWidth - measure.clientWidth + totalWidth) + "px";
437
      } else {
438
        this.horiz.style.display = "";
439
        this.horiz.firstChild.style.width = "0";
440
      }
441
442
      if (!this.checkedOverlay && measure.clientHeight > 0) {
443
        if (sWidth == 0) this.overlayHack();
0 ignored issues
show
Best Practice introduced by
Comparing sWidth to 0 using the == operator is not safe. Consider using === instead.
Loading history...
444
        this.checkedOverlay = true;
445
      }
446
447
      return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0};
448
    },
449
    setScrollLeft: function(pos) {
450
      if (this.horiz.scrollLeft != pos) this.horiz.scrollLeft = pos;
451
    },
452
    setScrollTop: function(pos) {
453
      if (this.vert.scrollTop != pos) this.vert.scrollTop = pos;
454
    },
455
    overlayHack: function() {
456
      var w = mac && !mac_geMountainLion ? "12px" : "18px";
457
      this.horiz.style.minHeight = this.vert.style.minWidth = w;
458
      var self = this;
459
      var barMouseDown = function(e) {
460
        if (e_target(e) != self.vert && e_target(e) != self.horiz)
461
          operation(self.cm, onMouseDown)(e);
462
      };
463
      on(this.vert, "mousedown", barMouseDown);
464
      on(this.horiz, "mousedown", barMouseDown);
465
    },
466
    clear: function() {
467
      var parent = this.horiz.parentNode;
468
      parent.removeChild(this.horiz);
469
      parent.removeChild(this.vert);
470
    }
471
  }, NativeScrollbars.prototype);
472
473
  function NullScrollbars() {}
474
475
  NullScrollbars.prototype = copyObj({
476
    update: function() { return {bottom: 0, right: 0}; },
477
    setScrollLeft: function() {},
478
    setScrollTop: function() {},
479
    clear: function() {}
480
  }, NullScrollbars.prototype);
481
482
  CodeMirror.scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars};
483
484
  function initScrollbars(cm) {
485
    if (cm.display.scrollbars) {
486
      cm.display.scrollbars.clear();
487
      if (cm.display.scrollbars.addClass)
488
        rmClass(cm.display.wrapper, cm.display.scrollbars.addClass);
489
    }
490
491
    cm.display.scrollbars = new CodeMirror.scrollbarModel[cm.options.scrollbarStyle](function(node) {
492
      cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller);
493
      // Prevent clicks in the scrollbars from killing focus
494
      on(node, "mousedown", function() {
495
        if (cm.state.focused) setTimeout(function() { cm.display.input.focus(); }, 0);
496
      });
497
      node.setAttribute("cm-not-content", "true");
498
    }, function(pos, axis) {
499
      if (axis == "horizontal") setScrollLeft(cm, pos);
500
      else setScrollTop(cm, pos);
501
    }, cm);
502
    if (cm.display.scrollbars.addClass)
503
      addClass(cm.display.wrapper, cm.display.scrollbars.addClass);
504
  }
505
506
  function updateScrollbars(cm, measure) {
507
    if (!measure) measure = measureForScrollbars(cm);
508
    var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight;
509
    updateScrollbarsInner(cm, measure);
510
    for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) {
511
      if (startWidth != cm.display.barWidth && cm.options.lineWrapping)
512
        updateHeightsInViewport(cm);
513
      updateScrollbarsInner(cm, measureForScrollbars(cm));
514
      startWidth = cm.display.barWidth; startHeight = cm.display.barHeight;
515
    }
516
  }
517
518
  // Re-synchronize the fake scrollbars with the actual size of the
519
  // content.
520
  function updateScrollbarsInner(cm, measure) {
521
    var d = cm.display;
522
    var sizes = d.scrollbars.update(measure);
523
524
    d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px";
525
    d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px";
526
527
    if (sizes.right && sizes.bottom) {
528
      d.scrollbarFiller.style.display = "block";
529
      d.scrollbarFiller.style.height = sizes.bottom + "px";
530
      d.scrollbarFiller.style.width = sizes.right + "px";
531
    } else d.scrollbarFiller.style.display = "";
532
    if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
533
      d.gutterFiller.style.display = "block";
534
      d.gutterFiller.style.height = sizes.bottom + "px";
535
      d.gutterFiller.style.width = measure.gutterWidth + "px";
536
    } else d.gutterFiller.style.display = "";
537
  }
538
539
  // Compute the lines that are visible in a given viewport (defaults
540
  // the the current scroll position). viewport may contain top,
541
  // height, and ensure (see op.scrollToPos) properties.
542
  function visibleLines(display, doc, viewport) {
543
    var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop;
0 ignored issues
show
Best Practice introduced by
Comparing viewport.top to null using the != operator is not safe. Consider using !== instead.
Loading history...
544
    top = Math.floor(top - paddingTop(display));
545
    var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight;
0 ignored issues
show
Best Practice introduced by
Comparing viewport.bottom to null using the != operator is not safe. Consider using !== instead.
Loading history...
546
547
    var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom);
548
    // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
549
    // forces those lines into the viewport (if possible).
550
    if (viewport && viewport.ensure) {
551
      var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line;
552
      if (ensureFrom < from) {
553
        from = ensureFrom;
554
        to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight);
555
      } else if (Math.min(ensureTo, doc.lastLine()) >= to) {
556
        from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight);
557
        to = ensureTo;
558
      }
559
    }
560
    return {from: from, to: Math.max(to, from + 1)};
561
  }
562
563
  // LINE NUMBERS
564
565
  // Re-align line numbers and gutter marks to compensate for
566
  // horizontal scrolling.
567
  function alignHorizontally(cm) {
568
    var display = cm.display, view = display.view;
569
    if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) return;
570
    var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;
571
    var gutterW = display.gutters.offsetWidth, left = comp + "px";
572
    for (var i = 0; i < view.length; i++) if (!view[i].hidden) {
573
      if (cm.options.fixedGutter && view[i].gutter)
574
        view[i].gutter.style.left = left;
575
      var align = view[i].alignable;
576
      if (align) for (var j = 0; j < align.length; j++)
577
        align[j].style.left = left;
578
    }
579
    if (cm.options.fixedGutter)
580
      display.gutters.style.left = (comp + gutterW) + "px";
581
  }
582
583
  // Used to ensure that the line number gutter is still the right
584
  // size for the current document size. Returns true when an update
585
  // is needed.
586
  function maybeUpdateLineNumberWidth(cm) {
587
    if (!cm.options.lineNumbers) return false;
588
    var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;
589
    if (last.length != display.lineNumChars) {
590
      var test = display.measure.appendChild(elt("div", [elt("div", last)],
591
                                                 "CodeMirror-linenumber CodeMirror-gutter-elt"));
592
      var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
593
      display.lineGutter.style.width = "";
594
      display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding);
595
      display.lineNumWidth = display.lineNumInnerWidth + padding;
596
      display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
597
      display.lineGutter.style.width = display.lineNumWidth + "px";
598
      updateGutterSpace(cm);
599
      return true;
600
    }
601
    return false;
602
  }
603
604
  function lineNumberFor(options, i) {
605
    return String(options.lineNumberFormatter(i + options.firstLineNumber));
606
  }
607
608
  // Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
609
  // but using getBoundingClientRect to get a sub-pixel-accurate
610
  // result.
611
  function compensateForHScroll(display) {
612
    return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left;
613
  }
614
615
  // DISPLAY DRAWING
616
617
  function DisplayUpdate(cm, viewport, force) {
618
    var display = cm.display;
619
620
    this.viewport = viewport;
621
    // Store some values that we'll need later (but don't want to force a relayout for)
622
    this.visible = visibleLines(display, cm.doc, viewport);
623
    this.editorIsHidden = !display.wrapper.offsetWidth;
624
    this.wrapperHeight = display.wrapper.clientHeight;
625
    this.wrapperWidth = display.wrapper.clientWidth;
626
    this.oldDisplayWidth = displayWidth(cm);
627
    this.force = force;
628
    this.dims = getDimensions(cm);
629
    this.events = [];
630
  }
631
632
  DisplayUpdate.prototype.signal = function(emitter, type) {
633
    if (hasHandler(emitter, type))
634
      this.events.push(arguments);
635
  };
636
  DisplayUpdate.prototype.finish = function() {
637
    for (var i = 0; i < this.events.length; i++)
638
      signal.apply(null, this.events[i]);
639
  };
640
641
  function maybeClipScrollbars(cm) {
642
    var display = cm.display;
643
    if (!display.scrollbarsClipped && display.scroller.offsetWidth) {
644
      display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth;
645
      display.heightForcer.style.height = scrollGap(cm) + "px";
646
      display.sizer.style.marginBottom = -display.nativeBarWidth + "px";
647
      display.sizer.style.borderRightWidth = scrollGap(cm) + "px";
648
      display.scrollbarsClipped = true;
649
    }
650
  }
651
652
  // Does the actual updating of the line display. Bails out
653
  // (returning false) when there is nothing to be done and forced is
654
  // false.
655
  function updateDisplayIfNeeded(cm, update) {
656
    var display = cm.display, doc = cm.doc;
657
658
    if (update.editorIsHidden) {
659
      resetView(cm);
660
      return false;
661
    }
662
663
    // Bail out if the visible area is already rendered and nothing changed.
664
    if (!update.force &&
665
        update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&
666
        (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&
0 ignored issues
show
Best Practice introduced by
Comparing display.updateLineNumbers to null using the == operator is not safe. Consider using === instead.
Loading history...
667
        display.renderedView == display.view && countDirtyView(cm) == 0)
0 ignored issues
show
Best Practice introduced by
Comparing countDirtyView(cm) to 0 using the == operator is not safe. Consider using === instead.
Loading history...
668
      return false;
669
670
    if (maybeUpdateLineNumberWidth(cm)) {
671
      resetView(cm);
672
      update.dims = getDimensions(cm);
673
    }
674
675
    // Compute a suitable new viewport (from & to)
676
    var end = doc.first + doc.size;
677
    var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first);
678
    var to = Math.min(end, update.visible.to + cm.options.viewportMargin);
679
    if (display.viewFrom < from && from - display.viewFrom < 20) from = Math.max(doc.first, display.viewFrom);
680
    if (display.viewTo > to && display.viewTo - to < 20) to = Math.min(end, display.viewTo);
681
    if (sawCollapsedSpans) {
682
      from = visualLineNo(cm.doc, from);
683
      to = visualLineEndNo(cm.doc, to);
684
    }
685
686
    var different = from != display.viewFrom || to != display.viewTo ||
687
      display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth;
688
    adjustView(cm, from, to);
689
690
    display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom));
691
    // Position the mover div to align with the current scroll position
692
    cm.display.mover.style.top = display.viewOffset + "px";
693
694
    var toUpdate = countDirtyView(cm);
695
    if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view &&
0 ignored issues
show
Best Practice introduced by
Comparing toUpdate to 0 using the == operator is not safe. Consider using === instead.
Loading history...
696
        (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))
697
      return false;
698
699
    // For big changes, we hide the enclosing element during the
700
    // update, since that speeds up the operations on most browsers.
701
    var focused = activeElt();
702
    if (toUpdate > 4) display.lineDiv.style.display = "none";
703
    patchDisplay(cm, display.updateLineNumbers, update.dims);
704
    if (toUpdate > 4) display.lineDiv.style.display = "";
705
    display.renderedView = display.view;
706
    // There might have been a widget with a focused element that got
707
    // hidden or updated, if so re-focus it.
708
    if (focused && activeElt() != focused && focused.offsetHeight) focused.focus();
709
710
    // Prevent selection and cursors from interfering with the scroll
711
    // width and height.
712
    removeChildren(display.cursorDiv);
713
    removeChildren(display.selectionDiv);
714
    display.gutters.style.height = 0;
715
716
    if (different) {
717
      display.lastWrapHeight = update.wrapperHeight;
718
      display.lastWrapWidth = update.wrapperWidth;
719
      startWorker(cm, 400);
720
    }
721
722
    display.updateLineNumbers = null;
723
724
    return true;
725
  }
726
727
  function postUpdateDisplay(cm, update) {
728
    var force = update.force, viewport = update.viewport;
0 ignored issues
show
Unused Code introduced by
The assignment to variable force seems to be never used. Consider removing it.
Loading history...
729
    for (var first = true;; first = false) {
730
      if (first && cm.options.lineWrapping && update.oldDisplayWidth != displayWidth(cm)) {
731
        force = true;
732
      } else {
733
        force = false;
734
        // Clip forced viewport to actual scrollable area.
735
        if (viewport && viewport.top != null)
0 ignored issues
show
Best Practice introduced by
Comparing viewport.top to null using the != operator is not safe. Consider using !== instead.
Loading history...
736
          viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)};
737
        // Updated line heights might result in the drawn area not
738
        // actually covering the viewport. Keep looping until it does.
739
        update.visible = visibleLines(cm.display, cm.doc, viewport);
740
        if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)
741
          break;
742
      }
743
      if (!updateDisplayIfNeeded(cm, update)) break;
744
      updateHeightsInViewport(cm);
745
      var barMeasure = measureForScrollbars(cm);
746
      updateSelection(cm);
747
      setDocumentHeight(cm, barMeasure);
748
      updateScrollbars(cm, barMeasure);
749
    }
750
751
    update.signal(cm, "update", cm);
752
    if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {
753
      update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo);
754
      cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo;
755
    }
756
  }
757
758
  function updateDisplaySimple(cm, viewport) {
759
    var update = new DisplayUpdate(cm, viewport);
760
    if (updateDisplayIfNeeded(cm, update)) {
761
      updateHeightsInViewport(cm);
762
      postUpdateDisplay(cm, update);
763
      var barMeasure = measureForScrollbars(cm);
764
      updateSelection(cm);
765
      setDocumentHeight(cm, barMeasure);
766
      updateScrollbars(cm, barMeasure);
767
      update.finish();
768
    }
769
  }
770
771
  function setDocumentHeight(cm, measure) {
772
    cm.display.sizer.style.minHeight = measure.docHeight + "px";
773
    var total = measure.docHeight + cm.display.barHeight;
774
    cm.display.heightForcer.style.top = total + "px";
775
    cm.display.gutters.style.height = Math.max(total + scrollGap(cm), measure.clientHeight) + "px";
776
  }
777
778
  // Read the actual heights of the rendered lines, and update their
779
  // stored heights to match.
780
  function updateHeightsInViewport(cm) {
781
    var display = cm.display;
782
    var prevBottom = display.lineDiv.offsetTop;
783
    for (var i = 0; i < display.view.length; i++) {
784
      var cur = display.view[i], height;
785
      if (cur.hidden) continue;
786
      if (ie && ie_version < 8) {
787
        var bot = cur.node.offsetTop + cur.node.offsetHeight;
788
        height = bot - prevBottom;
789
        prevBottom = bot;
790
      } else {
791
        var box = cur.node.getBoundingClientRect();
792
        height = box.bottom - box.top;
793
      }
794
      var diff = cur.line.height - height;
795
      if (height < 2) height = textHeight(display);
796
      if (diff > .001 || diff < -.001) {
797
        updateLineHeight(cur.line, height);
798
        updateWidgetHeight(cur.line);
799
        if (cur.rest) for (var j = 0; j < cur.rest.length; j++)
800
          updateWidgetHeight(cur.rest[j]);
801
      }
802
    }
803
  }
804
805
  // Read and store the height of line widgets associated with the
806
  // given line.
807
  function updateWidgetHeight(line) {
808
    if (line.widgets) for (var i = 0; i < line.widgets.length; ++i)
809
      line.widgets[i].height = line.widgets[i].node.offsetHeight;
810
  }
811
812
  // Do a bulk-read of the DOM positions and sizes needed to draw the
813
  // view, so that we don't interleave reading and writing to the DOM.
814
  function getDimensions(cm) {
815
    var d = cm.display, left = {}, width = {};
816
    var gutterLeft = d.gutters.clientLeft;
817
    for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
818
      left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft;
819
      width[cm.options.gutters[i]] = n.clientWidth;
820
    }
821
    return {fixedPos: compensateForHScroll(d),
822
            gutterTotalWidth: d.gutters.offsetWidth,
823
            gutterLeft: left,
824
            gutterWidth: width,
825
            wrapperWidth: d.wrapper.clientWidth};
826
  }
827
828
  // Sync the actual display DOM structure with display.view, removing
829
  // nodes for lines that are no longer in view, and creating the ones
830
  // that are not there yet, and updating the ones that are out of
831
  // date.
832
  function patchDisplay(cm, updateNumbersFrom, dims) {
833
    var display = cm.display, lineNumbers = cm.options.lineNumbers;
834
    var container = display.lineDiv, cur = container.firstChild;
835
836
    function rm(node) {
837
      var next = node.nextSibling;
838
      // Works around a throw-scroll bug in OS X Webkit
839
      if (webkit && mac && cm.display.currentWheelTarget == node)
840
        node.style.display = "none";
841
      else
842
        node.parentNode.removeChild(node);
843
      return next;
844
    }
845
846
    var view = display.view, lineN = display.viewFrom;
847
    // Loop over the elements in the view, syncing cur (the DOM nodes
848
    // in display.lineDiv) with the view as we go.
849
    for (var i = 0; i < view.length; i++) {
850
      var lineView = view[i];
851
      if (lineView.hidden) {
0 ignored issues
show
Comprehensibility Documentation Best Practice introduced by
This code block is empty. Consider removing it or adding a comment to explain.
Loading history...
852
      } else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet
853
        var node = buildLineElement(cm, lineView, lineN, dims);
854
        container.insertBefore(node, cur);
855
      } else { // Already drawn
856
        while (cur != lineView.node) cur = rm(cur);
857
        var updateNumber = lineNumbers && updateNumbersFrom != null &&
0 ignored issues
show
Best Practice introduced by
Comparing updateNumbersFrom to null using the != operator is not safe. Consider using !== instead.
Loading history...
858
          updateNumbersFrom <= lineN && lineView.lineNumber;
859
        if (lineView.changes) {
860
          if (indexOf(lineView.changes, "gutter") > -1) updateNumber = false;
861
          updateLineForChanges(cm, lineView, lineN, dims);
862
        }
863
        if (updateNumber) {
864
          removeChildren(lineView.lineNumber);
865
          lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN)));
866
        }
867
        cur = lineView.node.nextSibling;
868
      }
869
      lineN += lineView.size;
870
    }
871
    while (cur) cur = rm(cur);
872
  }
873
874
  // When an aspect of a line changes, a string is added to
875
  // lineView.changes. This updates the relevant part of the line's
876
  // DOM structure.
877
  function updateLineForChanges(cm, lineView, lineN, dims) {
878
    for (var j = 0; j < lineView.changes.length; j++) {
879
      var type = lineView.changes[j];
880
      if (type == "text") updateLineText(cm, lineView);
881
      else if (type == "gutter") updateLineGutter(cm, lineView, lineN, dims);
882
      else if (type == "class") updateLineClasses(lineView);
883
      else if (type == "widget") updateLineWidgets(cm, lineView, dims);
884
    }
885
    lineView.changes = null;
886
  }
887
888
  // Lines with gutter elements, widgets or a background class need to
889
  // be wrapped, and have the extra elements added to the wrapper div
890
  function ensureLineWrapped(lineView) {
891
    if (lineView.node == lineView.text) {
892
      lineView.node = elt("div", null, null, "position: relative");
893
      if (lineView.text.parentNode)
894
        lineView.text.parentNode.replaceChild(lineView.node, lineView.text);
895
      lineView.node.appendChild(lineView.text);
896
      if (ie && ie_version < 8) lineView.node.style.zIndex = 2;
897
    }
898
    return lineView.node;
899
  }
900
901
  function updateLineBackground(lineView) {
902
    var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass;
903
    if (cls) cls += " CodeMirror-linebackground";
904
    if (lineView.background) {
905
      if (cls) lineView.background.className = cls;
906
      else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; }
907
    } else if (cls) {
908
      var wrap = ensureLineWrapped(lineView);
909
      lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild);
910
    }
911
  }
912
913
  // Wrapper around buildLineContent which will reuse the structure
914
  // in display.externalMeasured when possible.
915
  function getLineContent(cm, lineView) {
916
    var ext = cm.display.externalMeasured;
917
    if (ext && ext.line == lineView.line) {
918
      cm.display.externalMeasured = null;
919
      lineView.measure = ext.measure;
920
      return ext.built;
921
    }
922
    return buildLineContent(cm, lineView);
923
  }
924
925
  // Redraw the line's text. Interacts with the background and text
926
  // classes because the mode may output tokens that influence these
927
  // classes.
928
  function updateLineText(cm, lineView) {
929
    var cls = lineView.text.className;
930
    var built = getLineContent(cm, lineView);
931
    if (lineView.text == lineView.node) lineView.node = built.pre;
932
    lineView.text.parentNode.replaceChild(built.pre, lineView.text);
933
    lineView.text = built.pre;
934
    if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {
935
      lineView.bgClass = built.bgClass;
936
      lineView.textClass = built.textClass;
937
      updateLineClasses(lineView);
938
    } else if (cls) {
939
      lineView.text.className = cls;
940
    }
941
  }
942
943
  function updateLineClasses(lineView) {
944
    updateLineBackground(lineView);
945
    if (lineView.line.wrapClass)
946
      ensureLineWrapped(lineView).className = lineView.line.wrapClass;
947
    else if (lineView.node != lineView.text)
948
      lineView.node.className = "";
949
    var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass;
950
    lineView.text.className = textClass || "";
951
  }
952
953
  function updateLineGutter(cm, lineView, lineN, dims) {
954
    if (lineView.gutter) {
955
      lineView.node.removeChild(lineView.gutter);
956
      lineView.gutter = null;
957
    }
958
    var markers = lineView.line.gutterMarkers;
959
    if (cm.options.lineNumbers || markers) {
960
      var wrap = ensureLineWrapped(lineView);
961
      var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", "left: " +
962
                                             (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) +
963
                                             "px; width: " + dims.gutterTotalWidth + "px");
964
      cm.display.input.setUneditable(gutterWrap);
965
      wrap.insertBefore(gutterWrap, lineView.text);
966
      if (lineView.line.gutterClass)
967
        gutterWrap.className += " " + lineView.line.gutterClass;
968
      if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
969
        lineView.lineNumber = gutterWrap.appendChild(
970
          elt("div", lineNumberFor(cm.options, lineN),
971
              "CodeMirror-linenumber CodeMirror-gutter-elt",
972
              "left: " + dims.gutterLeft["CodeMirror-linenumbers"] + "px; width: "
973
              + cm.display.lineNumInnerWidth + "px"));
974
      if (markers) for (var k = 0; k < cm.options.gutters.length; ++k) {
975
        var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id];
976
        if (found)
977
          gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", "left: " +
978
                                     dims.gutterLeft[id] + "px; width: " + dims.gutterWidth[id] + "px"));
979
      }
980
    }
981
  }
982
983
  function updateLineWidgets(cm, lineView, dims) {
984
    if (lineView.alignable) lineView.alignable = null;
985
    for (var node = lineView.node.firstChild, next; node; node = next) {
986
      var next = node.nextSibling;
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable next already seems to be declared on line 985. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
987
      if (node.className == "CodeMirror-linewidget")
988
        lineView.node.removeChild(node);
989
    }
990
    insertLineWidgets(cm, lineView, dims);
991
  }
992
993
  // Build a line's DOM representation from scratch
994
  function buildLineElement(cm, lineView, lineN, dims) {
995
    var built = getLineContent(cm, lineView);
996
    lineView.text = lineView.node = built.pre;
997
    if (built.bgClass) lineView.bgClass = built.bgClass;
998
    if (built.textClass) lineView.textClass = built.textClass;
999
1000
    updateLineClasses(lineView);
1001
    updateLineGutter(cm, lineView, lineN, dims);
1002
    insertLineWidgets(cm, lineView, dims);
1003
    return lineView.node;
1004
  }
1005
1006
  // A lineView may contain multiple logical lines (when merged by
1007
  // collapsed spans). The widgets for all of them need to be drawn.
1008
  function insertLineWidgets(cm, lineView, dims) {
1009
    insertLineWidgetsFor(cm, lineView.line, lineView, dims, true);
1010
    if (lineView.rest) for (var i = 0; i < lineView.rest.length; i++)
1011
      insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false);
1012
  }
1013
1014
  function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {
1015
    if (!line.widgets) return;
1016
    var wrap = ensureLineWrapped(lineView);
1017
    for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
1018
      var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
1019
      if (!widget.handleMouseEvents) node.setAttribute("cm-ignore-events", "true");
1020
      positionLineWidget(widget, node, lineView, dims);
1021
      cm.display.input.setUneditable(node);
1022
      if (allowAbove && widget.above)
1023
        wrap.insertBefore(node, lineView.gutter || lineView.text);
1024
      else
1025
        wrap.appendChild(node);
1026
      signalLater(widget, "redraw");
1027
    }
1028
  }
1029
1030
  function positionLineWidget(widget, node, lineView, dims) {
1031
    if (widget.noHScroll) {
1032
      (lineView.alignable || (lineView.alignable = [])).push(node);
1033
      var width = dims.wrapperWidth;
1034
      node.style.left = dims.fixedPos + "px";
1035
      if (!widget.coverGutter) {
1036
        width -= dims.gutterTotalWidth;
1037
        node.style.paddingLeft = dims.gutterTotalWidth + "px";
1038
      }
1039
      node.style.width = width + "px";
1040
    }
1041
    if (widget.coverGutter) {
1042
      node.style.zIndex = 5;
1043
      node.style.position = "relative";
1044
      if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + "px";
1045
    }
1046
  }
1047
1048
  // POSITION OBJECT
1049
1050
  // A Pos instance represents a position within the text.
1051
  var Pos = CodeMirror.Pos = function(line, ch) {
1052
    if (!(this instanceof Pos)) return new Pos(line, ch);
1053
    this.line = line; this.ch = ch;
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...
1054
  };
1055
1056
  // Compare two positions, return 0 if they are the same, a negative
1057
  // number when a is less, and a positive number otherwise.
1058
  var cmp = CodeMirror.cmpPos = function(a, b) { return a.line - b.line || a.ch - b.ch; };
1059
1060
  function copyPos(x) {return Pos(x.line, x.ch);}
1061
  function maxPos(a, b) { return cmp(a, b) < 0 ? b : a; }
1062
  function minPos(a, b) { return cmp(a, b) < 0 ? a : b; }
1063
1064
  // INPUT HANDLING
1065
1066
  function ensureFocus(cm) {
1067
    if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); }
1068
  }
1069
1070
  function isReadOnly(cm) {
1071
    return cm.options.readOnly || cm.doc.cantEdit;
1072
  }
1073
1074
  // This will be set to an array of strings when copying, so that,
1075
  // when pasting, we know what kind of selections the copied text
1076
  // was made out of.
1077
  var lastCopied = null;
1078
1079
  function applyTextInput(cm, inserted, deleted, sel) {
1080
    var doc = cm.doc;
1081
    cm.display.shift = false;
1082
    if (!sel) sel = doc.sel;
1083
1084
    var textLines = splitLines(inserted), multiPaste = null;
1085
    // When pasing N lines into N selections, insert one line per selection
1086
    if (cm.state.pasteIncoming && sel.ranges.length > 1) {
1087
      if (lastCopied && lastCopied.join("\n") == inserted)
1088
        multiPaste = sel.ranges.length % lastCopied.length == 0 && map(lastCopied, splitLines);
0 ignored issues
show
Best Practice introduced by
Comparing sel.ranges.length % lastCopied.length to 0 using the == operator is not safe. Consider using === instead.
Loading history...
1089
      else if (textLines.length == sel.ranges.length)
1090
        multiPaste = map(textLines, function(l) { return [l]; });
1091
    }
1092
1093
    // Normal behavior is to insert the new text into every selection
1094
    for (var i = sel.ranges.length - 1; i >= 0; i--) {
1095
      var range = sel.ranges[i];
1096
      var from = range.from(), to = range.to();
1097
      if (range.empty()) {
1098
        if (deleted && deleted > 0) // Handle deletion
1099
          from = Pos(from.line, from.ch - deleted);
1100
        else if (cm.state.overwrite && !cm.state.pasteIncoming) // Handle overwrite
1101
          to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length));
1102
      }
1103
      var updateInput = cm.curOp.updateInput;
1104
      var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i % multiPaste.length] : textLines,
1105
                         origin: cm.state.pasteIncoming ? "paste" : cm.state.cutIncoming ? "cut" : "+input"};
1106
      makeChange(cm.doc, changeEvent);
1107
      signalLater(cm, "inputRead", cm, changeEvent);
1108
      // When an 'electric' character is inserted, immediately trigger a reindent
1109
      if (inserted && !cm.state.pasteIncoming && cm.options.electricChars &&
1110
          cm.options.smartIndent && range.head.ch < 100 &&
1111
          (!i || sel.ranges[i - 1].head.line != range.head.line)) {
1112
        var mode = cm.getModeAt(range.head);
1113
        var end = changeEnd(changeEvent);
1114
        if (mode.electricChars) {
1115
          for (var j = 0; j < mode.electricChars.length; j++)
1116
            if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
1117
              indentLine(cm, end.line, "smart");
1118
              break;
1119
            }
1120
        } else if (mode.electricInput) {
1121
          if (mode.electricInput.test(getLine(doc, end.line).text.slice(0, end.ch)))
1122
            indentLine(cm, end.line, "smart");
1123
        }
1124
      }
1125
    }
1126
    ensureCursorVisible(cm);
1127
    cm.curOp.updateInput = updateInput;
0 ignored issues
show
Bug introduced by
The variable updateInput seems to not be initialized for all possible execution paths.
Loading history...
1128
    cm.curOp.typing = true;
1129
    cm.state.pasteIncoming = cm.state.cutIncoming = false;
1130
  }
1131
1132
  function copyableRanges(cm) {
1133
    var text = [], ranges = [];
1134
    for (var i = 0; i < cm.doc.sel.ranges.length; i++) {
1135
      var line = cm.doc.sel.ranges[i].head.line;
1136
      var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)};
1137
      ranges.push(lineRange);
1138
      text.push(cm.getRange(lineRange.anchor, lineRange.head));
1139
    }
1140
    return {text: text, ranges: ranges};
1141
  }
1142
1143
  function disableBrowserMagic(field) {
1144
    field.setAttribute("autocorrect", "off");
1145
    field.setAttribute("autocapitalize", "off");
1146
    field.setAttribute("spellcheck", "false");
1147
  }
1148
1149
  // TEXTAREA INPUT STYLE
1150
1151
  function TextareaInput(cm) {
1152
    this.cm = cm;
1153
    // See input.poll and input.reset
1154
    this.prevInput = "";
1155
1156
    // Flag that indicates whether we expect input to appear real soon
1157
    // now (after some event like 'keypress' or 'input') and are
1158
    // polling intensively.
1159
    this.pollingFast = false;
1160
    // Self-resetting timeout for the poller
1161
    this.polling = new Delayed();
1162
    // Tracks when input.reset has punted to just putting a short
1163
    // string into the textarea instead of the full selection.
1164
    this.inaccurateSelection = false;
1165
    // Used to work around IE issue with selection being forgotten when focus moves away from textarea
1166
    this.hasSelection = false;
1167
  };
1168
1169
  function hiddenTextarea() {
1170
    var te = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none");
1171
    var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
1172
    // The textarea is kept positioned near the cursor to prevent the
1173
    // fact that it'll be scrolled into view on input from scrolling
1174
    // our fake cursor out of view. On webkit, when wrap=off, paste is
1175
    // very slow. So make the area wide instead.
1176
    if (webkit) te.style.width = "1000px";
1177
    else te.setAttribute("wrap", "off");
1178
    // If border: 0; -- iOS fails to open keyboard (issue #1287)
1179
    if (ios) te.style.border = "1px solid black";
1180
    disableBrowserMagic(te);
1181
    return div;
1182
  }
1183
1184
  TextareaInput.prototype = copyObj({
1185
    init: function(display) {
1186
      var input = this, cm = this.cm;
1187
1188
      // Wraps and hides input textarea
1189
      var div = this.wrapper = hiddenTextarea();
1190
      // The semihidden textarea that is focused when the editor is
1191
      // focused, and receives input.
1192
      var te = this.textarea = div.firstChild;
1193
      display.wrapper.insertBefore(div, display.wrapper.firstChild);
1194
1195
      // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)
1196
      if (ios) te.style.width = "0px";
1197
1198
      on(te, "input", function() {
1199
        if (ie && ie_version >= 9 && input.hasSelection) input.hasSelection = null;
1200
        input.poll();
1201
      });
1202
1203
      on(te, "paste", function() {
1204
        // Workaround for webkit bug https://bugs.webkit.org/show_bug.cgi?id=90206
1205
        // Add a char to the end of textarea before paste occur so that
1206
        // selection doesn't span to the end of textarea.
1207
        if (webkit && !cm.state.fakedLastChar && !(new Date - cm.state.lastMiddleDown < 200)) {
1208
          var start = te.selectionStart, end = te.selectionEnd;
1209
          te.value += "$";
1210
          // The selection end needs to be set before the start, otherwise there
1211
          // can be an intermediate non-empty selection between the two, which
1212
          // can override the middle-click paste buffer on linux and cause the
1213
          // wrong thing to get pasted.
1214
          te.selectionEnd = end;
1215
          te.selectionStart = start;
1216
          cm.state.fakedLastChar = true;
1217
        }
1218
        cm.state.pasteIncoming = true;
1219
        input.fastPoll();
1220
      });
1221
1222
      function prepareCopyCut(e) {
1223
        if (cm.somethingSelected()) {
1224
          lastCopied = cm.getSelections();
1225
          if (input.inaccurateSelection) {
1226
            input.prevInput = "";
1227
            input.inaccurateSelection = false;
1228
            te.value = lastCopied.join("\n");
1229
            selectInput(te);
1230
          }
1231
        } else {
1232
          var ranges = copyableRanges(cm);
1233
          lastCopied = ranges.text;
1234
          if (e.type == "cut") {
1235
            cm.setSelections(ranges.ranges, null, sel_dontScroll);
1236
          } else {
1237
            input.prevInput = "";
1238
            te.value = ranges.text.join("\n");
1239
            selectInput(te);
1240
          }
1241
        }
1242
        if (e.type == "cut") cm.state.cutIncoming = true;
1243
      }
1244
      on(te, "cut", prepareCopyCut);
1245
      on(te, "copy", prepareCopyCut);
1246
1247
      on(display.scroller, "paste", function(e) {
1248
        if (eventInWidget(display, e)) return;
1249
        cm.state.pasteIncoming = true;
1250
        input.focus();
1251
      });
1252
1253
      // Prevent normal selection in the editor (we handle our own)
1254
      on(display.lineSpace, "selectstart", function(e) {
1255
        if (!eventInWidget(display, e)) e_preventDefault(e);
1256
      });
1257
    },
1258
1259
    prepareSelection: function() {
1260
      // Redraw the selection and/or cursor
1261
      var cm = this.cm, display = cm.display, doc = cm.doc;
1262
      var result = prepareSelection(cm);
1263
1264
      // Move the hidden textarea near the cursor to prevent scrolling artifacts
1265
      if (cm.options.moveInputWithCursor) {
1266
        var headPos = cursorCoords(cm, doc.sel.primary().head, "div");
1267
        var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();
1268
        result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
1269
                                            headPos.top + lineOff.top - wrapOff.top));
1270
        result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
1271
                                             headPos.left + lineOff.left - wrapOff.left));
1272
      }
1273
1274
      return result;
1275
    },
1276
1277
    showSelection: function(drawn) {
1278
      var cm = this.cm, display = cm.display;
1279
      removeChildrenAndAdd(display.cursorDiv, drawn.cursors);
1280
      removeChildrenAndAdd(display.selectionDiv, drawn.selection);
1281
      if (drawn.teTop != null) {
0 ignored issues
show
Best Practice introduced by
Comparing drawn.teTop to null using the != operator is not safe. Consider using !== instead.
Loading history...
1282
        this.wrapper.style.top = drawn.teTop + "px";
1283
        this.wrapper.style.left = drawn.teLeft + "px";
1284
      }
1285
    },
1286
1287
    // Reset the input to correspond to the selection (or to be empty,
1288
    // when not typing and nothing is selected)
1289
    reset: function(typing) {
1290
      if (this.contextMenuPending) return;
1291
      var minimal, selected, cm = this.cm, doc = cm.doc;
1292
      if (cm.somethingSelected()) {
1293
        this.prevInput = "";
1294
        var range = doc.sel.primary();
1295
        minimal = hasCopyEvent &&
1296
          (range.to().line - range.from().line > 100 || (selected = cm.getSelection()).length > 1000);
1297
        var content = minimal ? "-" : selected || cm.getSelection();
1298
        this.textarea.value = content;
1299
        if (cm.state.focused) selectInput(this.textarea);
1300
        if (ie && ie_version >= 9) this.hasSelection = content;
1301
      } else if (!typing) {
1302
        this.prevInput = this.textarea.value = "";
1303
        if (ie && ie_version >= 9) this.hasSelection = null;
1304
      }
1305
      this.inaccurateSelection = minimal;
0 ignored issues
show
Bug introduced by
The variable minimal seems to not be initialized for all possible execution paths.
Loading history...
1306
    },
1307
1308
    getField: function() { return this.textarea; },
1309
1310
    supportsTouch: function() { return false; },
1311
1312
    focus: function() {
1313
      if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) {
1314
        try { this.textarea.focus(); }
1315
        catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM
0 ignored issues
show
Coding Style Comprehensibility Best Practice introduced by
Empty catch clauses should be used with caution; consider adding a comment why this is needed.
Loading history...
1316
      }
1317
    },
1318
1319
    blur: function() { this.textarea.blur(); },
1320
1321
    resetPosition: function() {
1322
      this.wrapper.style.top = this.wrapper.style.left = 0;
1323
    },
1324
1325
    receivedFocus: function() { this.slowPoll(); },
1326
1327
    // Poll for input changes, using the normal rate of polling. This
1328
    // runs as long as the editor is focused.
1329
    slowPoll: function() {
1330
      var input = this;
1331
      if (input.pollingFast) return;
1332
      input.polling.set(this.cm.options.pollInterval, function() {
1333
        input.poll();
1334
        if (input.cm.state.focused) input.slowPoll();
1335
      });
1336
    },
1337
1338
    // When an event has just come in that is likely to add or change
1339
    // something in the input textarea, we poll faster, to ensure that
1340
    // the change appears on the screen quickly.
1341
    fastPoll: function() {
1342
      var missed = false, input = this;
1343
      input.pollingFast = true;
1344
      function p() {
1345
        var changed = input.poll();
1346
        if (!changed && !missed) {missed = true; input.polling.set(60, p);}
1347
        else {input.pollingFast = false; input.slowPoll();}
1348
      }
1349
      input.polling.set(20, p);
1350
    },
1351
1352
    // Read input from the textarea, and update the document to match.
1353
    // When something is selected, it is present in the textarea, and
1354
    // selected (unless it is huge, in which case a placeholder is
1355
    // used). When nothing is selected, the cursor sits after previously
1356
    // seen text (can be empty), which is stored in prevInput (we must
1357
    // not reset the textarea when typing, because that breaks IME).
1358
    poll: function() {
1359
      var cm = this.cm, input = this.textarea, prevInput = this.prevInput;
1360
      // Since this is called a *lot*, try to bail out as cheaply as
1361
      // possible when it is clear that nothing happened. hasSelection
1362
      // will be the case when there is a lot of text in the textarea,
1363
      // in which case reading its value would be expensive.
1364
      if (!cm.state.focused || (hasSelection(input) && !prevInput) ||
1365
          isReadOnly(cm) || cm.options.disableInput || cm.state.keySeq)
1366
        return false;
1367
      // See paste handler for more on the fakedLastChar kludge
1368
      if (cm.state.pasteIncoming && cm.state.fakedLastChar) {
1369
        input.value = input.value.substring(0, input.value.length - 1);
1370
        cm.state.fakedLastChar = false;
1371
      }
1372
      var text = input.value;
1373
      // If nothing changed, bail.
1374
      if (text == prevInput && !cm.somethingSelected()) return false;
1375
      // Work around nonsensical selection resetting in IE9/10, and
1376
      // inexplicable appearance of private area unicode characters on
1377
      // some key combos in Mac (#2689).
1378
      if (ie && ie_version >= 9 && this.hasSelection === text ||
1379
          mac && /[\uf700-\uf7ff]/.test(text)) {
1380
        cm.display.input.reset();
1381
        return false;
1382
      }
1383
1384
      if (text.charCodeAt(0) == 0x200b && cm.doc.sel == cm.display.selForContextMenu && !prevInput)
1385
        prevInput = "\u200b";
1386
      // Find the part of the input that is actually new
1387
      var same = 0, l = Math.min(prevInput.length, text.length);
1388
      while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++same;
1389
1390
      var self = this;
1391
      runInOp(cm, function() {
1392
        applyTextInput(cm, text.slice(same), prevInput.length - same);
1393
1394
        // Don't leave long text in the textarea, since it makes further polling slow
1395
        if (text.length > 1000 || text.indexOf("\n") > -1) input.value = self.prevInput = "";
1396
        else self.prevInput = text;
1397
      });
1398
      return true;
1399
    },
1400
1401
    ensurePolled: function() {
1402
      if (this.pollingFast && this.poll()) this.pollingFast = false;
1403
    },
1404
1405
    onKeyPress: function() {
1406
      if (ie && ie_version >= 9) this.hasSelection = null;
1407
      this.fastPoll();
1408
    },
1409
1410
    onContextMenu: function(e) {
1411
      var input = this, cm = input.cm, display = cm.display, te = input.textarea;
1412
      var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
1413
      if (!pos || presto) return; // Opera is difficult.
1414
1415
      // Reset the current text selection only if the click is done outside of the selection
1416
      // and 'resetSelectionOnContextMenu' option is true.
1417
      var reset = cm.options.resetSelectionOnContextMenu;
1418
      if (reset && cm.doc.sel.contains(pos) == -1)
1419
        operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll);
1420
1421
      var oldCSS = te.style.cssText;
1422
      input.wrapper.style.position = "absolute";
1423
      te.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
1424
        "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: " +
1425
        (ie ? "rgba(255, 255, 255, .05)" : "transparent") +
1426
        "; outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
1427
      if (webkit) var oldScrollY = window.scrollY; // Work around Chrome issue (#2712)
1428
      display.input.focus();
1429
      if (webkit) window.scrollTo(null, oldScrollY);
0 ignored issues
show
Bug introduced by
The variable oldScrollY does not seem to be initialized in case webkit on line 1427 is false. Are you sure the function scrollTo handles undefined variables?
Loading history...
1430
      display.input.reset();
1431
      // Adds "Select all" to context menu in FF
1432
      if (!cm.somethingSelected()) te.value = input.prevInput = " ";
1433
      input.contextMenuPending = true;
1434
      display.selForContextMenu = cm.doc.sel;
1435
      clearTimeout(display.detectingSelectAll);
1436
1437
      // Select-all will be greyed out if there's nothing to select, so
1438
      // this adds a zero-width space so that we can later check whether
1439
      // it got selected.
1440
      function prepareSelectAllHack() {
1441
        if (te.selectionStart != null) {
0 ignored issues
show
Best Practice introduced by
Comparing te.selectionStart to null using the != operator is not safe. Consider using !== instead.
Loading history...
1442
          var selected = cm.somethingSelected();
1443
          var extval = te.value = "\u200b" + (selected ? te.value : "");
1444
          input.prevInput = selected ? "" : "\u200b";
1445
          te.selectionStart = 1; te.selectionEnd = extval.length;
1446
          // Re-set this, in case some other handler touched the
1447
          // selection in the meantime.
1448
          display.selForContextMenu = cm.doc.sel;
1449
        }
1450
      }
1451
      function rehide() {
1452
        input.contextMenuPending = false;
1453
        input.wrapper.style.position = "relative";
1454
        te.style.cssText = oldCSS;
1455
        if (ie && ie_version < 9) display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos);
1456
1457
        // Try to detect the user choosing select-all
1458
        if (te.selectionStart != null) {
0 ignored issues
show
Best Practice introduced by
Comparing te.selectionStart to null using the != operator is not safe. Consider using !== instead.
Loading history...
1459
          if (!ie || (ie && ie_version < 9)) prepareSelectAllHack();
1460
          var i = 0, poll = function() {
1461
            if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0)
0 ignored issues
show
Best Practice introduced by
Comparing te.selectionStart to 0 using the == operator is not safe. Consider using === instead.
Loading history...
1462
              operation(cm, commands.selectAll)(cm);
1463
            else if (i++ < 10) display.detectingSelectAll = setTimeout(poll, 500);
1464
            else display.input.reset();
1465
          };
1466
          display.detectingSelectAll = setTimeout(poll, 200);
1467
        }
1468
      }
1469
1470
      if (ie && ie_version >= 9) prepareSelectAllHack();
1471
      if (captureRightClick) {
1472
        e_stop(e);
1473
        var mouseup = function() {
1474
          off(window, "mouseup", mouseup);
1475
          setTimeout(rehide, 20);
1476
        };
1477
        on(window, "mouseup", mouseup);
1478
      } else {
1479
        setTimeout(rehide, 50);
1480
      }
1481
    },
1482
1483
    setUneditable: nothing,
1484
1485
    needsContentAttribute: false
1486
  }, TextareaInput.prototype);
1487
1488
  // CONTENTEDITABLE INPUT STYLE
1489
1490
  function ContentEditableInput(cm) {
1491
    this.cm = cm;
1492
    this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null;
1493
    this.polling = new Delayed();
1494
  }
1495
1496
  ContentEditableInput.prototype = copyObj({
1497
    init: function(display) {
1498
      var input = this, cm = input.cm;
1499
      var div = input.div = display.lineDiv;
1500
      div.contentEditable = "true";
1501
      disableBrowserMagic(div);
1502
1503
      on(div, "paste", function(e) {
1504
        var pasted = e.clipboardData && e.clipboardData.getData("text/plain");
1505
        if (pasted) {
1506
          e.preventDefault();
1507
          cm.replaceSelection(pasted, null, "paste");
1508
        }
1509
      });
1510
1511
      on(div, "compositionstart", function(e) {
1512
        var data = e.data;
1513
        input.composing = {sel: cm.doc.sel, data: data, startData: data};
1514
        if (!data) return;
1515
        var prim = cm.doc.sel.primary();
1516
        var line = cm.getLine(prim.head.line);
1517
        var found = line.indexOf(data, Math.max(0, prim.head.ch - data.length));
1518
        if (found > -1 && found <= prim.head.ch)
1519
          input.composing.sel = simpleSelection(Pos(prim.head.line, found),
1520
                                                Pos(prim.head.line, found + data.length));
1521
      });
1522
      on(div, "compositionupdate", function(e) {
1523
        input.composing.data = e.data;
1524
      });
1525
      on(div, "compositionend", function(e) {
1526
        var ours = input.composing;
1527
        if (!ours) return;
1528
        if (e.data != ours.startData && !/\u200b/.test(e.data))
1529
          ours.data = e.data;
1530
        // Need a small delay to prevent other code (input event,
1531
        // selection polling) from doing damage when fired right after
1532
        // compositionend.
1533
        setTimeout(function() {
1534
          if (!ours.handled)
1535
            input.applyComposition(ours);
1536
          if (input.composing == ours)
1537
            input.composing = null;
1538
        }, 50);
1539
      });
1540
1541
      on(div, "touchstart", function() {
1542
        input.forceCompositionEnd();
1543
      });
1544
1545
      on(div, "input", function() {
1546
        if (input.composing) return;
1547
        if (!input.pollContent())
1548
          runInOp(input.cm, function() {regChange(cm);});
1549
      });
1550
1551
      function onCopyCut(e) {
1552
        if (cm.somethingSelected()) {
1553
          lastCopied = cm.getSelections();
1554
          if (e.type == "cut") cm.replaceSelection("", null, "cut");
1555
        } else {
1556
          var ranges = copyableRanges(cm);
1557
          lastCopied = ranges.text;
1558
          if (e.type == "cut") {
1559
            cm.operation(function() {
1560
              cm.setSelections(ranges.ranges, 0, sel_dontScroll);
1561
              cm.replaceSelection("", null, "cut");
1562
            });
1563
          }
1564
        }
1565
        // iOS exposes the clipboard API, but seems to discard content inserted into it
1566
        if (e.clipboardData && !ios) {
1567
          e.preventDefault();
1568
          e.clipboardData.clearData();
1569
          e.clipboardData.setData("text/plain", lastCopied.join("\n"));
1570
        } else {
1571
          // Old-fashioned briefly-focus-a-textarea hack
1572
          var kludge = hiddenTextarea(), te = kludge.firstChild;
1573
          cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild);
1574
          te.value = lastCopied.join("\n");
1575
          var hadFocus = document.activeElement;
1576
          selectInput(te);
1577
          setTimeout(function() {
1578
            cm.display.lineSpace.removeChild(kludge);
1579
            hadFocus.focus();
1580
          }, 50);
1581
        }
1582
      }
1583
      on(div, "copy", onCopyCut);
1584
      on(div, "cut", onCopyCut);
1585
    },
1586
1587
    prepareSelection: function() {
1588
      var result = prepareSelection(this.cm, false);
1589
      result.focus = this.cm.state.focused;
1590
      return result;
1591
    },
1592
1593
    showSelection: function(info) {
1594
      if (!info || !this.cm.display.view.length) return;
1595
      if (info.focus) this.showPrimarySelection();
1596
      this.showMultipleSelections(info);
1597
    },
1598
1599
    showPrimarySelection: function() {
1600
      var sel = window.getSelection(), prim = this.cm.doc.sel.primary();
1601
      var curAnchor = domToPos(this.cm, sel.anchorNode, sel.anchorOffset);
1602
      var curFocus = domToPos(this.cm, sel.focusNode, sel.focusOffset);
1603
      if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad &&
1604
          cmp(minPos(curAnchor, curFocus), prim.from()) == 0 &&
0 ignored issues
show
Best Practice introduced by
Comparing cmp(minPos(curAnchor, curFocus), prim.from()) to 0 using the == operator is not safe. Consider using === instead.
Loading history...
1605
          cmp(maxPos(curAnchor, curFocus), prim.to()) == 0)
0 ignored issues
show
Best Practice introduced by
Comparing cmp(maxPos(curAnchor, curFocus), prim.to()) to 0 using the == operator is not safe. Consider using === instead.
Loading history...
1606
        return;
1607
1608
      var start = posToDOM(this.cm, prim.from());
1609
      var end = posToDOM(this.cm, prim.to());
1610
      if (!start && !end) return;
1611
1612
      var view = this.cm.display.view;
1613
      var old = sel.rangeCount && sel.getRangeAt(0);
1614
      if (!start) {
1615
        start = {node: view[0].measure.map[2], offset: 0};
1616
      } else if (!end) { // FIXME dangerously hacky
1617
        var measure = view[view.length - 1].measure;
1618
        var map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map;
1619
        end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map.length - 3]};
1620
      }
1621
1622
      try { var rng = range(start.node, start.offset, end.offset, end.node); }
1623
      catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible
0 ignored issues
show
Coding Style Comprehensibility Best Practice introduced by
Empty catch clauses should be used with caution; consider adding a comment why this is needed.
Loading history...
1624
      if (rng) {
1625
        sel.removeAllRanges();
1626
        sel.addRange(rng);
1627
        if (old && sel.anchorNode == null) sel.addRange(old);
0 ignored issues
show
Best Practice introduced by
Comparing sel.anchorNode to null using the == operator is not safe. Consider using === instead.
Loading history...
1628
      }
1629
      this.rememberSelection();
1630
    },
1631
1632
    showMultipleSelections: function(info) {
1633
      removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors);
1634
      removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection);
1635
    },
1636
1637
    rememberSelection: function() {
1638
      var sel = window.getSelection();
1639
      this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset;
1640
      this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset;
1641
    },
1642
1643
    selectionInEditor: function() {
1644
      var sel = window.getSelection();
1645
      if (!sel.rangeCount) return false;
1646
      var node = sel.getRangeAt(0).commonAncestorContainer;
1647
      return contains(this.div, node);
1648
    },
1649
1650
    focus: function() {
1651
      if (this.cm.options.readOnly != "nocursor") this.div.focus();
1652
    },
1653
    blur: function() { this.div.blur(); },
1654
    getField: function() { return this.div; },
1655
1656
    supportsTouch: function() { return true; },
1657
1658
    receivedFocus: function() {
1659
      var input = this;
1660
      if (this.selectionInEditor())
1661
        this.pollSelection();
1662
      else
1663
        runInOp(this.cm, function() { input.cm.curOp.selectionChanged = true; });
1664
1665
      function poll() {
1666
        if (input.cm.state.focused) {
1667
          input.pollSelection();
1668
          input.polling.set(input.cm.options.pollInterval, poll);
1669
        }
1670
      }
1671
      this.polling.set(this.cm.options.pollInterval, poll);
1672
    },
1673
1674
    pollSelection: function() {
1675
      if (this.composing) return;
1676
1677
      var sel = window.getSelection(), cm = this.cm;
1678
      if (sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||
1679
          sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset) {
1680
        this.rememberSelection();
1681
        var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
1682
        var head = domToPos(cm, sel.focusNode, sel.focusOffset);
1683
        if (anchor && head) runInOp(cm, function() {
1684
          setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll);
1685
          if (anchor.bad || head.bad) cm.curOp.selectionChanged = true;
1686
        });
1687
      }
1688
    },
1689
1690
    pollContent: function() {
1691
      var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary();
1692
      var from = sel.from(), to = sel.to();
1693
      if (from.line < display.viewFrom || to.line > display.viewTo - 1) return false;
1694
1695
      var fromIndex;
1696
      if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) {
0 ignored issues
show
Best Practice introduced by
Comparing fromIndex = findViewIndex(cm, from.line) to 0 using the == operator is not safe. Consider using === instead.
Loading history...
1697
        var fromLine = lineNo(display.view[0].line);
1698
        var fromNode = display.view[0].node;
1699
      } else {
1700
        var fromLine = lineNo(display.view[fromIndex].line);
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable fromLine already seems to be declared on line 1697. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
1701
        var fromNode = display.view[fromIndex - 1].node.nextSibling;
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable fromNode already seems to be declared on line 1698. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
1702
      }
1703
      var toIndex = findViewIndex(cm, to.line);
1704
      if (toIndex == display.view.length - 1) {
1705
        var toLine = display.viewTo - 1;
1706
        var toNode = display.view[toIndex].node;
1707
      } else {
1708
        var toLine = lineNo(display.view[toIndex + 1].line) - 1;
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable toLine already seems to be declared on line 1705. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
1709
        var toNode = display.view[toIndex + 1].node.previousSibling;
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable toNode already seems to be declared on line 1706. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
1710
      }
1711
1712
      var newText = splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine));
1713
      var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length));
1714
      while (newText.length > 1 && oldText.length > 1) {
1715
        if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; }
1716
        else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; }
1717
        else break;
1718
      }
1719
1720
      var cutFront = 0, cutEnd = 0;
1721
      var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length);
1722
      while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront))
1723
        ++cutFront;
1724
      var newBot = lst(newText), oldBot = lst(oldText);
1725
      var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0),
0 ignored issues
show
Best Practice introduced by
Comparing newText.length to 1 using the == operator is not safe. Consider using === instead.
Loading history...
1726
                               oldBot.length - (oldText.length == 1 ? cutFront : 0));
0 ignored issues
show
Best Practice introduced by
Comparing oldText.length to 1 using the == operator is not safe. Consider using === instead.
Loading history...
1727
      while (cutEnd < maxCutEnd &&
1728
             newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1))
1729
        ++cutEnd;
1730
1731
      newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd);
1732
      newText[0] = newText[0].slice(cutFront);
1733
1734
      var chFrom = Pos(fromLine, cutFront);
1735
      var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0);
1736
      if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if newText.length > 1 || ne....0 || cmp(chFrom, chTo) 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...
1737
        replaceRange(cm.doc, newText, chFrom, chTo, "+input");
1738
        return true;
1739
      }
1740
    },
1741
1742
    ensurePolled: function() {
1743
      this.forceCompositionEnd();
1744
    },
1745
    reset: function() {
1746
      this.forceCompositionEnd();
1747
    },
1748
    forceCompositionEnd: function() {
1749
      if (!this.composing || this.composing.handled) return;
1750
      this.applyComposition(this.composing);
1751
      this.composing.handled = true;
1752
      this.div.blur();
1753
      this.div.focus();
1754
    },
1755
    applyComposition: function(composing) {
1756
      if (composing.data && composing.data != composing.startData)
1757
        operation(this.cm, applyTextInput)(this.cm, composing.data, 0, composing.sel);
1758
    },
1759
1760
    setUneditable: function(node) {
1761
      node.setAttribute("contenteditable", "false");
1762
    },
1763
1764
    onKeyPress: function(e) {
1765
      e.preventDefault();
1766
      operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0);
0 ignored issues
show
Best Practice introduced by
Comparing e.charCode to null using the == operator is not safe. Consider using === instead.
Loading history...
1767
    },
1768
1769
    onContextMenu: nothing,
1770
    resetPosition: nothing,
1771
1772
    needsContentAttribute: true
1773
  }, ContentEditableInput.prototype);
1774
1775
  function posToDOM(cm, pos) {
1776
    var view = findViewForLine(cm, pos.line);
1777
    if (!view || view.hidden) return null;
1778
    var line = getLine(cm.doc, pos.line);
1779
    var info = mapFromLineView(view, line, pos.line);
1780
1781
    var order = getOrder(line), side = "left";
0 ignored issues
show
Unused Code introduced by
The variable side seems to be never used. Consider removing it.
Loading history...
1782
    if (order) {
1783
      var partPos = getBidiPartAt(order, pos.ch);
1784
      side = partPos % 2 ? "right" : "left";
1785
    }
1786
    var result = nodeAndOffsetInLineMap(info.map, pos.ch, "left");
1787
    result.offset = result.collapse == "right" ? result.end : result.start;
1788
    return result;
1789
  }
1790
1791
  function badPos(pos, bad) { if (bad) pos.bad = true; return pos; }
1792
1793
  function domToPos(cm, node, offset) {
1794
    var lineNode;
1795
    if (node == cm.display.lineDiv) {
1796
      lineNode = cm.display.lineDiv.childNodes[offset];
1797
      if (!lineNode) return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true);
1798
      node = null; offset = 0;
1799
    } else {
1800
      for (lineNode = node;; lineNode = lineNode.parentNode) {
1801
        if (!lineNode || lineNode == cm.display.lineDiv) return null;
1802
        if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) break;
1803
      }
1804
    }
1805
    for (var i = 0; i < cm.display.view.length; i++) {
1806
      var lineView = cm.display.view[i];
1807
      if (lineView.node == lineNode)
1808
        return locateNodeInLineView(lineView, node, offset);
1809
    }
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...
1810
  }
1811
1812
  function locateNodeInLineView(lineView, node, offset) {
1813
    var wrapper = lineView.text.firstChild, bad = false;
1814
    if (!node || !contains(wrapper, node)) return badPos(Pos(lineNo(lineView.line), 0), true);
1815
    if (node == wrapper) {
1816
      bad = true;
1817
      node = wrapper.childNodes[offset];
1818
      offset = 0;
1819
      if (!node) {
1820
        var line = lineView.rest ? lst(lineView.rest) : lineView.line;
1821
        return badPos(Pos(lineNo(line), line.text.length), bad);
1822
      }
1823
    }
1824
1825
    var textNode = node.nodeType == 3 ? node : null, topNode = node;
1826
    if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {
0 ignored issues
show
Best Practice introduced by
Comparing node.childNodes.length to 1 using the == operator is not safe. Consider using === instead.
Loading history...
1827
      textNode = node.firstChild;
1828
      if (offset) offset = textNode.nodeValue.length;
1829
    }
1830
    while (topNode.parentNode != wrapper) topNode = topNode.parentNode;
1831
    var measure = lineView.measure, maps = measure.maps;
1832
1833
    function find(textNode, topNode, offset) {
1834
      for (var i = -1; i < (maps ? maps.length : 0); i++) {
1835
        var map = i < 0 ? measure.map : maps[i];
1836
        for (var j = 0; j < map.length; j += 3) {
1837
          var curNode = map[j + 2];
1838
          if (curNode == textNode || curNode == topNode) {
1839
            var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]);
1840
            var ch = map[j] + offset;
1841
            if (offset < 0 || curNode != textNode) ch = map[j + (offset ? 1 : 0)];
1842
            return Pos(line, ch);
1843
          }
1844
        }
1845
      }
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...
1846
    }
1847
    var found = find(textNode, topNode, offset);
1848
    if (found) return badPos(found, bad);
1849
1850
    // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems
1851
    for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) {
1852
      found = find(after, after.firstChild, 0);
1853
      if (found)
1854
        return badPos(Pos(found.line, found.ch - dist), bad);
1855
      else
1856
        dist += after.textContent.length;
1857
    }
1858
    for (var before = topNode.previousSibling, dist = offset; before; before = before.previousSibling) {
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable dist already seems to be declared on line 1851. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
1859
      found = find(before, before.firstChild, -1);
1860
      if (found)
1861
        return badPos(Pos(found.line, found.ch + dist), bad);
1862
      else
1863
        dist += after.textContent.length;
1864
    }
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...
1865
  }
1866
1867
  function domTextBetween(cm, from, to, fromLine, toLine) {
1868
    var text = "", closing = false;
1869
    function recognizeMarker(id) { return function(marker) { return marker.id == id; }; }
1870
    function walk(node) {
1871
      if (node.nodeType == 1) {
0 ignored issues
show
Best Practice introduced by
Comparing node.nodeType to 1 using the == operator is not safe. Consider using === instead.
Loading history...
1872
        var cmText = node.getAttribute("cm-text");
1873
        if (cmText != null) {
0 ignored issues
show
Best Practice introduced by
Comparing cmText to null using the != operator is not safe. Consider using !== instead.
Loading history...
1874
          if (cmText == "") cmText = node.textContent.replace(/\u200b/g, "");
1875
          text += cmText;
1876
          return;
1877
        }
1878
        var markerID = node.getAttribute("cm-marker"), range;
1879
        if (markerID) {
1880
          var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID));
1881
          if (found.length && (range = found[0].find()))
1882
            text += getBetween(cm.doc, range.from, range.to).join("\n");
1883
          return;
1884
        }
1885
        if (node.getAttribute("contenteditable") == "false") return;
1886
        for (var i = 0; i < node.childNodes.length; i++)
1887
          walk(node.childNodes[i]);
1888
        if (/^(pre|div|p)$/i.test(node.nodeName))
1889
          closing = true;
1890
      } else if (node.nodeType == 3) {
1891
        var val = node.nodeValue;
1892
        if (!val) return;
1893
        if (closing) {
1894
          text += "\n";
1895
          closing = false;
1896
        }
1897
        text += val;
1898
      }
1899
    }
1900
    for (;;) {
1901
      walk(from);
1902
      if (from == to) break;
1903
      from = from.nextSibling;
1904
    }
1905
    return text;
1906
  }
1907
1908
  CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput};
1909
1910
  // SELECTION / CURSOR
1911
1912
  // Selection objects are immutable. A new one is created every time
1913
  // the selection changes. A selection is one or more non-overlapping
1914
  // (and non-touching) ranges, sorted, and an integer that indicates
1915
  // which one is the primary selection (the one that's scrolled into
1916
  // view, that getCursor returns, etc).
1917
  function Selection(ranges, primIndex) {
1918
    this.ranges = ranges;
1919
    this.primIndex = primIndex;
1920
  }
1921
1922
  Selection.prototype = {
1923
    primary: function() { return this.ranges[this.primIndex]; },
1924
    equals: function(other) {
1925
      if (other == this) return true;
1926
      if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) return false;
1927
      for (var i = 0; i < this.ranges.length; i++) {
1928
        var here = this.ranges[i], there = other.ranges[i];
1929
        if (cmp(here.anchor, there.anchor) != 0 || cmp(here.head, there.head) != 0) return false;
0 ignored issues
show
Best Practice introduced by
Comparing cmp(here.anchor, there.anchor) to 0 using the != operator is not safe. Consider using !== instead.
Loading history...
Best Practice introduced by
Comparing cmp(here.head, there.head) to 0 using the != operator is not safe. Consider using !== instead.
Loading history...
1930
      }
1931
      return true;
1932
    },
1933
    deepCopy: function() {
1934
      for (var out = [], i = 0; i < this.ranges.length; i++)
1935
        out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head));
1936
      return new Selection(out, this.primIndex);
1937
    },
1938
    somethingSelected: function() {
1939
      for (var i = 0; i < this.ranges.length; i++)
1940
        if (!this.ranges[i].empty()) return true;
1941
      return false;
1942
    },
1943
    contains: function(pos, end) {
1944
      if (!end) end = pos;
1945
      for (var i = 0; i < this.ranges.length; i++) {
1946
        var range = this.ranges[i];
1947
        if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)
1948
          return i;
1949
      }
1950
      return -1;
1951
    }
1952
  };
1953
1954
  function Range(anchor, head) {
1955
    this.anchor = anchor; this.head = head;
1956
  }
1957
1958
  Range.prototype = {
1959
    from: function() { return minPos(this.anchor, this.head); },
1960
    to: function() { return maxPos(this.anchor, this.head); },
1961
    empty: function() {
1962
      return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch;
1963
    }
1964
  };
1965
1966
  // Take an unsorted, potentially overlapping set of ranges, and
1967
  // build a selection out of it. 'Consumes' ranges array (modifying
1968
  // it).
1969
  function normalizeSelection(ranges, primIndex) {
1970
    var prim = ranges[primIndex];
1971
    ranges.sort(function(a, b) { return cmp(a.from(), b.from()); });
1972
    primIndex = indexOf(ranges, prim);
1973
    for (var i = 1; i < ranges.length; i++) {
1974
      var cur = ranges[i], prev = ranges[i - 1];
1975
      if (cmp(prev.to(), cur.from()) >= 0) {
1976
        var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to());
1977
        var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head;
1978
        if (i <= primIndex) --primIndex;
1979
        ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to));
0 ignored issues
show
Complexity Coding Style introduced by
You seem to be assigning a new value to the loop variable i here. Please check if this was indeed your intention. Even if it was, consider using another kind of loop instead.
Loading history...
1980
      }
1981
    }
1982
    return new Selection(ranges, primIndex);
1983
  }
1984
1985
  function simpleSelection(anchor, head) {
1986
    return new Selection([new Range(anchor, head || anchor)], 0);
1987
  }
1988
1989
  // Most of the external API clips given positions to make sure they
1990
  // actually exist within the document.
1991
  function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1));}
1992
  function clipPos(doc, pos) {
1993
    if (pos.line < doc.first) return Pos(doc.first, 0);
1994
    var last = doc.first + doc.size - 1;
1995
    if (pos.line > last) return Pos(last, getLine(doc, last).text.length);
1996
    return clipToLen(pos, getLine(doc, pos.line).text.length);
1997
  }
1998
  function clipToLen(pos, linelen) {
1999
    var ch = pos.ch;
2000
    if (ch == null || ch > linelen) return Pos(pos.line, linelen);
0 ignored issues
show
Best Practice introduced by
Comparing ch to null using the == operator is not safe. Consider using === instead.
Loading history...
2001
    else if (ch < 0) return Pos(pos.line, 0);
2002
    else return pos;
2003
  }
2004
  function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size;}
2005
  function clipPosArray(doc, array) {
2006
    for (var out = [], i = 0; i < array.length; i++) out[i] = clipPos(doc, array[i]);
2007
    return out;
2008
  }
2009
2010
  // SELECTION UPDATES
2011
2012
  // The 'scroll' parameter given to many of these indicated whether
2013
  // the new cursor position should be scrolled into view after
2014
  // modifying the selection.
2015
2016
  // If shift is held or the extend flag is set, extends a range to
2017
  // include a given position (and optionally a second position).
2018
  // Otherwise, simply returns the range between the given positions.
2019
  // Used for cursor motion and such.
2020
  function extendRange(doc, range, head, other) {
2021
    if (doc.cm && doc.cm.display.shift || doc.extend) {
2022
      var anchor = range.anchor;
2023
      if (other) {
2024
        var posBefore = cmp(head, anchor) < 0;
2025
        if (posBefore != (cmp(other, anchor) < 0)) {
2026
          anchor = head;
2027
          head = other;
2028
        } else if (posBefore != (cmp(head, other) < 0)) {
2029
          head = other;
2030
        }
2031
      }
2032
      return new Range(anchor, head);
2033
    } else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
2034
      return new Range(other || head, head);
2035
    }
2036
  }
2037
2038
  // Extend the primary selection range, discard the rest.
2039
  function extendSelection(doc, head, other, options) {
2040
    setSelection(doc, new Selection([extendRange(doc, doc.sel.primary(), head, other)], 0), options);
2041
  }
2042
2043
  // Extend all selections (pos is an array of selections with length
2044
  // equal the number of selections)
2045
  function extendSelections(doc, heads, options) {
2046
    for (var out = [], i = 0; i < doc.sel.ranges.length; i++)
2047
      out[i] = extendRange(doc, doc.sel.ranges[i], heads[i], null);
2048
    var newSel = normalizeSelection(out, doc.sel.primIndex);
2049
    setSelection(doc, newSel, options);
2050
  }
2051
2052
  // Updates a single range in the selection.
2053
  function replaceOneSelection(doc, i, range, options) {
2054
    var ranges = doc.sel.ranges.slice(0);
2055
    ranges[i] = range;
2056
    setSelection(doc, normalizeSelection(ranges, doc.sel.primIndex), options);
2057
  }
2058
2059
  // Reset the selection to a single range.
2060
  function setSimpleSelection(doc, anchor, head, options) {
2061
    setSelection(doc, simpleSelection(anchor, head), options);
2062
  }
2063
2064
  // Give beforeSelectionChange handlers a change to influence a
2065
  // selection update.
2066
  function filterSelectionChange(doc, sel) {
2067
    var obj = {
2068
      ranges: sel.ranges,
2069
      update: function(ranges) {
2070
        this.ranges = [];
2071
        for (var i = 0; i < ranges.length; i++)
2072
          this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),
2073
                                     clipPos(doc, ranges[i].head));
2074
      }
2075
    };
2076
    signal(doc, "beforeSelectionChange", doc, obj);
2077
    if (doc.cm) signal(doc.cm, "beforeSelectionChange", doc.cm, obj);
2078
    if (obj.ranges != sel.ranges) return normalizeSelection(obj.ranges, obj.ranges.length - 1);
2079
    else return sel;
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
2080
  }
2081
2082
  function setSelectionReplaceHistory(doc, sel, options) {
2083
    var done = doc.history.done, last = lst(done);
2084
    if (last && last.ranges) {
2085
      done[done.length - 1] = sel;
2086
      setSelectionNoUndo(doc, sel, options);
2087
    } else {
2088
      setSelection(doc, sel, options);
2089
    }
2090
  }
2091
2092
  // Set a new selection.
2093
  function setSelection(doc, sel, options) {
2094
    setSelectionNoUndo(doc, sel, options);
2095
    addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options);
2096
  }
2097
2098
  function setSelectionNoUndo(doc, sel, options) {
2099
    if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange"))
2100
      sel = filterSelectionChange(doc, sel);
2101
2102
    var bias = options && options.bias ||
2103
      (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1);
2104
    setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true));
2105
2106
    if (!(options && options.scroll === false) && doc.cm)
2107
      ensureCursorVisible(doc.cm);
2108
  }
2109
2110
  function setSelectionInner(doc, sel) {
2111
    if (sel.equals(doc.sel)) return;
2112
2113
    doc.sel = sel;
2114
2115
    if (doc.cm) {
2116
      doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true;
2117
      signalCursorActivity(doc.cm);
2118
    }
2119
    signalLater(doc, "cursorActivity", doc);
2120
  }
2121
2122
  // Verify that the selection does not partially select any atomic
2123
  // marked ranges.
2124
  function reCheckSelection(doc) {
2125
    setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false), sel_dontScroll);
0 ignored issues
show
Bug introduced by
The call to setSelectionInner seems to have too many arguments starting with sel_dontScroll.
Loading history...
2126
  }
2127
2128
  // Return a selection that does not partially select any atomic
2129
  // ranges.
2130
  function skipAtomicInSelection(doc, sel, bias, mayClear) {
2131
    var out;
2132
    for (var i = 0; i < sel.ranges.length; i++) {
2133
      var range = sel.ranges[i];
2134
      var newAnchor = skipAtomic(doc, range.anchor, bias, mayClear);
2135
      var newHead = skipAtomic(doc, range.head, bias, mayClear);
2136
      if (out || newAnchor != range.anchor || newHead != range.head) {
2137
        if (!out) out = sel.ranges.slice(0, i);
2138
        out[i] = new Range(newAnchor, newHead);
2139
      }
2140
    }
2141
    return out ? normalizeSelection(out, sel.primIndex) : sel;
2142
  }
2143
2144
  // Ensure a given position is not inside an atomic range.
2145
  function skipAtomic(doc, pos, bias, mayClear) {
2146
    var flipped = false, curPos = pos;
2147
    var dir = bias || 1;
2148
    doc.cantEdit = false;
2149
    search: for (;;) {
2150
      var line = getLine(doc, curPos.line);
2151
      if (line.markedSpans) {
2152
        for (var i = 0; i < line.markedSpans.length; ++i) {
2153
          var sp = line.markedSpans[i], m = sp.marker;
2154
          if ((sp.from == null || (m.inclusiveLeft ? sp.from <= curPos.ch : sp.from < curPos.ch)) &&
0 ignored issues
show
Best Practice introduced by
Comparing sp.from to null using the == operator is not safe. Consider using === instead.
Loading history...
2155
              (sp.to == null || (m.inclusiveRight ? sp.to >= curPos.ch : sp.to > curPos.ch))) {
0 ignored issues
show
Best Practice introduced by
Comparing sp.to to null using the == operator is not safe. Consider using === instead.
Loading history...
2156
            if (mayClear) {
2157
              signal(m, "beforeCursorEnter");
2158
              if (m.explicitlyCleared) {
2159
                if (!line.markedSpans) break;
2160
                else {--i; continue;}
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
Complexity Coding Style introduced by
You seem to be assigning a new value to the loop variable i here. Please check if this was indeed your intention. Even if it was, consider using another kind of loop instead.
Loading history...
2161
              }
2162
            }
2163
            if (!m.atomic) continue;
2164
            var newPos = m.find(dir < 0 ? -1 : 1);
2165
            if (cmp(newPos, curPos) == 0) {
0 ignored issues
show
Best Practice introduced by
Comparing cmp(newPos, curPos) to 0 using the == operator is not safe. Consider using === instead.
Loading history...
2166
              newPos.ch += dir;
2167
              if (newPos.ch < 0) {
2168
                if (newPos.line > doc.first) newPos = clipPos(doc, Pos(newPos.line - 1));
2169
                else newPos = null;
2170
              } else if (newPos.ch > line.text.length) {
2171
                if (newPos.line < doc.first + doc.size - 1) newPos = Pos(newPos.line + 1, 0);
2172
                else newPos = null;
2173
              }
2174
              if (!newPos) {
2175
                if (flipped) {
2176
                  // Driven in a corner -- no valid cursor position found at all
2177
                  // -- try again *with* clearing, if we didn't already
2178
                  if (!mayClear) return skipAtomic(doc, pos, bias, true);
2179
                  // Otherwise, turn off editing until further notice, and return the start of the doc
2180
                  doc.cantEdit = true;
2181
                  return Pos(doc.first, 0);
2182
                }
2183
                flipped = true; newPos = pos; dir = -dir;
2184
              }
2185
            }
2186
            curPos = newPos;
2187
            continue search;
2188
          }
2189
        }
2190
      }
2191
      return curPos;
2192
    }
2193
  }
2194
2195
  // SELECTION DRAWING
2196
2197
  function updateSelection(cm) {
2198
    cm.display.input.showSelection(cm.display.input.prepareSelection());
2199
  }
2200
2201
  function prepareSelection(cm, primary) {
2202
    var doc = cm.doc, result = {};
2203
    var curFragment = result.cursors = document.createDocumentFragment();
2204
    var selFragment = result.selection = document.createDocumentFragment();
2205
2206
    for (var i = 0; i < doc.sel.ranges.length; i++) {
2207
      if (primary === false && i == doc.sel.primIndex) continue;
2208
      var range = doc.sel.ranges[i];
2209
      var collapsed = range.empty();
2210
      if (collapsed || cm.options.showCursorWhenSelecting)
2211
        drawSelectionCursor(cm, range, curFragment);
2212
      if (!collapsed)
2213
        drawSelectionRange(cm, range, selFragment);
2214
    }
2215
    return result;
2216
  }
2217
2218
  // Draws a cursor for the given range
2219
  function drawSelectionCursor(cm, range, output) {
2220
    var pos = cursorCoords(cm, range.head, "div", null, null, !cm.options.singleCursorHeightPerLine);
2221
2222
    var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor"));
2223
    cursor.style.left = pos.left + "px";
2224
    cursor.style.top = pos.top + "px";
2225
    cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
2226
2227
    if (pos.other) {
2228
      // Secondary cursor, shown when on a 'jump' in bi-directional text
2229
      var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"));
2230
      otherCursor.style.display = "";
2231
      otherCursor.style.left = pos.other.left + "px";
2232
      otherCursor.style.top = pos.other.top + "px";
2233
      otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
2234
    }
2235
  }
2236
2237
  // Draws the given range as a highlighted selection
2238
  function drawSelectionRange(cm, range, output) {
2239
    var display = cm.display, doc = cm.doc;
2240
    var fragment = document.createDocumentFragment();
2241
    var padding = paddingH(cm.display), leftSide = padding.left;
2242
    var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right;
2243
2244
    function add(left, top, width, bottom) {
2245
      if (top < 0) top = 0;
2246
      top = Math.round(top);
2247
      bottom = Math.round(bottom);
2248
      fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left +
2249
                               "px; top: " + top + "px; width: " + (width == null ? rightSide - left : width) +
0 ignored issues
show
Best Practice introduced by
Comparing width to null using the == operator is not safe. Consider using === instead.
Loading history...
2250
                               "px; height: " + (bottom - top) + "px"));
2251
    }
2252
2253
    function drawForLine(line, fromArg, toArg) {
2254
      var lineObj = getLine(doc, line);
2255
      var lineLen = lineObj.text.length;
2256
      var start, end;
2257
      function coords(ch, bias) {
2258
        return charCoords(cm, Pos(line, ch), "div", lineObj, bias);
2259
      }
2260
2261
      iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) {
0 ignored issues
show
Best Practice introduced by
Comparing toArg to null using the == operator is not safe. Consider using === instead.
Loading history...
2262
        var leftPos = coords(from, "left"), rightPos, left, right;
2263
        if (from == to) {
2264
          rightPos = leftPos;
2265
          left = right = leftPos.left;
2266
        } else {
2267
          rightPos = coords(to - 1, "right");
2268
          if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp; }
2269
          left = leftPos.left;
2270
          right = rightPos.right;
2271
        }
2272
        if (fromArg == null && from == 0) left = leftSide;
0 ignored issues
show
Best Practice introduced by
Comparing fromArg to null using the == operator is not safe. Consider using === instead.
Loading history...
Best Practice introduced by
Comparing from to 0 using the == operator is not safe. Consider using === instead.
Loading history...
2273
        if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
2274
          add(left, leftPos.top, null, leftPos.bottom);
2275
          left = leftSide;
2276
          if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rightPos.top);
2277
        }
2278
        if (toArg == null && to == lineLen) right = rightSide;
0 ignored issues
show
Best Practice introduced by
Comparing toArg to null using the == operator is not safe. Consider using === instead.
Loading history...
2279
        if (!start || leftPos.top < start.top || leftPos.top == start.top && leftPos.left < start.left)
2280
          start = leftPos;
2281
        if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.bottom && rightPos.right > end.right)
2282
          end = rightPos;
2283
        if (left < leftSide + 1) left = leftSide;
2284
        add(left, rightPos.top, right - left, rightPos.bottom);
2285
      });
2286
      return {start: start, end: end};
2287
    }
2288
2289
    var sFrom = range.from(), sTo = range.to();
2290
    if (sFrom.line == sTo.line) {
2291
      drawForLine(sFrom.line, sFrom.ch, sTo.ch);
2292
    } else {
2293
      var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line);
2294
      var singleVLine = visualLine(fromLine) == visualLine(toLine);
2295
      var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end;
2296
      var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start;
2297
      if (singleVLine) {
2298
        if (leftEnd.top < rightStart.top - 2) {
2299
          add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);
2300
          add(leftSide, rightStart.top, rightStart.left, rightStart.bottom);
2301
        } else {
2302
          add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);
2303
        }
2304
      }
2305
      if (leftEnd.bottom < rightStart.top)
2306
        add(leftSide, leftEnd.bottom, null, rightStart.top);
2307
    }
2308
2309
    output.appendChild(fragment);
2310
  }
2311
2312
  // Cursor-blinking
2313
  function restartBlink(cm) {
2314
    if (!cm.state.focused) return;
2315
    var display = cm.display;
2316
    clearInterval(display.blinker);
2317
    var on = true;
2318
    display.cursorDiv.style.visibility = "";
2319
    if (cm.options.cursorBlinkRate > 0)
2320
      display.blinker = setInterval(function() {
2321
        display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden";
2322
      }, cm.options.cursorBlinkRate);
2323
    else if (cm.options.cursorBlinkRate < 0)
2324
      display.cursorDiv.style.visibility = "hidden";
2325
  }
2326
2327
  // HIGHLIGHT WORKER
2328
2329
  function startWorker(cm, time) {
2330
    if (cm.doc.mode.startState && cm.doc.frontier < cm.display.viewTo)
2331
      cm.state.highlight.set(time, bind(highlightWorker, cm));
2332
  }
2333
2334
  function highlightWorker(cm) {
2335
    var doc = cm.doc;
2336
    if (doc.frontier < doc.first) doc.frontier = doc.first;
2337
    if (doc.frontier >= cm.display.viewTo) return;
2338
    var end = +new Date + cm.options.workTime;
2339
    var state = copyState(doc.mode, getStateBefore(cm, doc.frontier));
2340
    var changedLines = [];
2341
2342
    doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function(line) {
2343
      if (doc.frontier >= cm.display.viewFrom) { // Visible
2344
        var oldStyles = line.styles;
2345
        var highlighted = highlightLine(cm, line, state, true);
2346
        line.styles = highlighted.styles;
2347
        var oldCls = line.styleClasses, newCls = highlighted.classes;
2348
        if (newCls) line.styleClasses = newCls;
2349
        else if (oldCls) line.styleClasses = null;
2350
        var ischange = !oldStyles || oldStyles.length != line.styles.length ||
2351
          oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass);
2352
        for (var i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldStyles[i] != line.styles[i];
2353
        if (ischange) changedLines.push(doc.frontier);
2354
        line.stateAfter = copyState(doc.mode, state);
2355
      } else {
2356
        processLine(cm, line.text, state);
2357
        line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null;
0 ignored issues
show
Best Practice introduced by
Comparing doc.frontier % 5 to 0 using the == operator is not safe. Consider using === instead.
Loading history...
2358
      }
2359
      ++doc.frontier;
2360
      if (+new Date > end) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if +(new Date()) > end 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...
2361
        startWorker(cm, cm.options.workDelay);
2362
        return true;
2363
      }
2364
    });
2365
    if (changedLines.length) runInOp(cm, function() {
2366
      for (var i = 0; i < changedLines.length; i++)
2367
        regLineChange(cm, changedLines[i], "text");
2368
    });
2369
  }
2370
2371
  // Finds the line to start with when starting a parse. Tries to
2372
  // find a line with a stateAfter, so that it can start with a
2373
  // valid state. If that fails, it returns the line with the
2374
  // smallest indentation, which tends to need the least context to
2375
  // parse correctly.
2376
  function findStartLine(cm, n, precise) {
2377
    var minindent, minline, doc = cm.doc;
2378
    var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100);
2379
    for (var search = n; search > lim; --search) {
2380
      if (search <= doc.first) return doc.first;
2381
      var line = getLine(doc, search - 1);
2382
      if (line.stateAfter && (!precise || search <= doc.frontier)) return search;
2383
      var indented = countColumn(line.text, null, cm.options.tabSize);
2384
      if (minline == null || minindent > indented) {
0 ignored issues
show
Best Practice introduced by
Comparing minline to null using the == operator is not safe. Consider using === instead.
Loading history...
Bug introduced by
The variable minline seems to not be initialized for all possible execution paths.
Loading history...
Bug introduced by
The variable minindent seems to not be initialized for all possible execution paths.
Loading history...
2385
        minline = search - 1;
2386
        minindent = indented;
2387
      }
2388
    }
2389
    return minline;
2390
  }
2391
2392
  function getStateBefore(cm, n, precise) {
2393
    var doc = cm.doc, display = cm.display;
2394
    if (!doc.mode.startState) return true;
2395
    var pos = findStartLine(cm, n, precise), state = pos > doc.first && getLine(doc, pos-1).stateAfter;
2396
    if (!state) state = startState(doc.mode);
2397
    else state = copyState(doc.mode, state);
2398
    doc.iter(pos, n, function(line) {
2399
      processLine(cm, line.text, state);
2400
      var save = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo;
0 ignored issues
show
Best Practice introduced by
Comparing pos % 5 to 0 using the == operator is not safe. Consider using === instead.
Loading history...
2401
      line.stateAfter = save ? copyState(doc.mode, state) : null;
2402
      ++pos;
2403
    });
2404
    if (precise) doc.frontier = pos;
2405
    return state;
2406
  }
2407
2408
  // POSITION MEASUREMENT
2409
2410
  function paddingTop(display) {return display.lineSpace.offsetTop;}
2411
  function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight;}
2412
  function paddingH(display) {
2413
    if (display.cachedPaddingH) return display.cachedPaddingH;
2414
    var e = removeChildrenAndAdd(display.measure, elt("pre", "x"));
2415
    var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle;
2416
    var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)};
2417
    if (!isNaN(data.left) && !isNaN(data.right)) display.cachedPaddingH = data;
2418
    return data;
2419
  }
2420
2421
  function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth; }
2422
  function displayWidth(cm) {
2423
    return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth;
2424
  }
2425
  function displayHeight(cm) {
2426
    return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight;
2427
  }
2428
2429
  // Ensure the lineView.wrapping.heights array is populated. This is
2430
  // an array of bottom offsets for the lines that make up a drawn
2431
  // line. When lineWrapping is on, there might be more than one
2432
  // height.
2433
  function ensureLineHeights(cm, lineView, rect) {
2434
    var wrapping = cm.options.lineWrapping;
2435
    var curWidth = wrapping && displayWidth(cm);
2436
    if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {
2437
      var heights = lineView.measure.heights = [];
2438
      if (wrapping) {
2439
        lineView.measure.width = curWidth;
2440
        var rects = lineView.text.firstChild.getClientRects();
2441
        for (var i = 0; i < rects.length - 1; i++) {
2442
          var cur = rects[i], next = rects[i + 1];
2443
          if (Math.abs(cur.bottom - next.bottom) > 2)
2444
            heights.push((cur.bottom + next.top) / 2 - rect.top);
2445
        }
2446
      }
2447
      heights.push(rect.bottom - rect.top);
2448
    }
2449
  }
2450
2451
  // Find a line map (mapping character offsets to text nodes) and a
2452
  // measurement cache for the given line number. (A line view might
2453
  // contain multiple lines when collapsed ranges are present.)
2454
  function mapFromLineView(lineView, line, lineN) {
2455
    if (lineView.line == line)
2456
      return {map: lineView.measure.map, cache: lineView.measure.cache};
2457
    for (var i = 0; i < lineView.rest.length; i++)
2458
      if (lineView.rest[i] == line)
2459
        return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]};
2460
    for (var i = 0; i < lineView.rest.length; i++)
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 2457. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
2461
      if (lineNo(lineView.rest[i]) > lineN)
2462
        return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i], before: true};
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...
2463
  }
2464
2465
  // Render a line into the hidden node display.externalMeasured. Used
2466
  // when measurement is needed for a line that's not in the viewport.
2467
  function updateExternalMeasurement(cm, line) {
2468
    line = visualLine(line);
2469
    var lineN = lineNo(line);
2470
    var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN);
2471
    view.lineN = lineN;
2472
    var built = view.built = buildLineContent(cm, view);
2473
    view.text = built.pre;
2474
    removeChildrenAndAdd(cm.display.lineMeasure, built.pre);
2475
    return view;
2476
  }
2477
2478
  // Get a {top, bottom, left, right} box (in line-local coordinates)
2479
  // for a given character.
2480
  function measureChar(cm, line, ch, bias) {
2481
    return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias);
2482
  }
2483
2484
  // Find a line view that corresponds to the given line number.
2485
  function findViewForLine(cm, lineN) {
2486
    if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo)
2487
      return cm.display.view[findViewIndex(cm, lineN)];
2488
    var ext = cm.display.externalMeasured;
2489
    if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size)
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if ext && lineN >= ext.line... < ext.lineN + ext.size 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...
2490
      return ext;
2491
  }
2492
2493
  // Measurement can be split in two steps, the set-up work that
2494
  // applies to the whole line, and the measurement of the actual
2495
  // character. Functions like coordsChar, that need to do a lot of
2496
  // measurements in a row, can thus ensure that the set-up work is
2497
  // only done once.
2498
  function prepareMeasureForLine(cm, line) {
2499
    var lineN = lineNo(line);
2500
    var view = findViewForLine(cm, lineN);
2501
    if (view && !view.text)
2502
      view = null;
2503
    else if (view && view.changes)
2504
      updateLineForChanges(cm, view, lineN, getDimensions(cm));
2505
    if (!view)
2506
      view = updateExternalMeasurement(cm, line);
2507
2508
    var info = mapFromLineView(view, line, lineN);
2509
    return {
2510
      line: line, view: view, rect: null,
2511
      map: info.map, cache: info.cache, before: info.before,
2512
      hasHeights: false
2513
    };
2514
  }
2515
2516
  // Given a prepared measurement object, measures the position of an
2517
  // actual character (or fetches it from the cache).
2518
  function measureCharPrepared(cm, prepared, ch, bias, varHeight) {
2519
    if (prepared.before) ch = -1;
2520
    var key = ch + (bias || ""), found;
2521
    if (prepared.cache.hasOwnProperty(key)) {
2522
      found = prepared.cache[key];
2523
    } else {
2524
      if (!prepared.rect)
2525
        prepared.rect = prepared.view.text.getBoundingClientRect();
2526
      if (!prepared.hasHeights) {
2527
        ensureLineHeights(cm, prepared.view, prepared.rect);
2528
        prepared.hasHeights = true;
2529
      }
2530
      found = measureCharInner(cm, prepared, ch, bias);
2531
      if (!found.bogus) prepared.cache[key] = found;
2532
    }
2533
    return {left: found.left, right: found.right,
2534
            top: varHeight ? found.rtop : found.top,
2535
            bottom: varHeight ? found.rbottom : found.bottom};
2536
  }
2537
2538
  var nullRect = {left: 0, right: 0, top: 0, bottom: 0};
2539
2540
  function nodeAndOffsetInLineMap(map, ch, bias) {
2541
    var node, start, end, collapse;
2542
    // First, search the line map for the text node corresponding to,
2543
    // or closest to, the target character.
2544
    for (var i = 0; i < map.length; i += 3) {
2545
      var mStart = map[i], mEnd = map[i + 1];
2546
      if (ch < mStart) {
2547
        start = 0; end = 1;
2548
        collapse = "left";
2549
      } else if (ch < mEnd) {
2550
        start = ch - mStart;
2551
        end = start + 1;
2552
      } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) {
2553
        end = mEnd - mStart;
2554
        start = end - 1;
2555
        if (ch >= mEnd) collapse = "right";
2556
      }
2557
      if (start != null) {
0 ignored issues
show
Best Practice introduced by
Comparing start to null using the != operator is not safe. Consider using !== instead.
Loading history...
Bug introduced by
The variable start seems to not be initialized for all possible execution paths.
Loading history...
2558
        node = map[i + 2];
2559
        if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right"))
2560
          collapse = bias;
2561
        if (bias == "left" && start == 0)
2562
          while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) {
2563
            node = map[(i -= 3) + 2];
2564
            collapse = "left";
2565
          }
2566
        if (bias == "right" && start == mEnd - mStart)
2567
          while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].insertLeft) {
2568
            node = map[(i += 3) + 2];
2569
            collapse = "right";
2570
          }
2571
        break;
2572
      }
2573
    }
2574
    return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd};
0 ignored issues
show
Bug introduced by
The variable mEnd seems to not be initialized for all possible execution paths.
Loading history...
Bug introduced by
The variable collapse seems to not be initialized for all possible execution paths.
Loading history...
Bug introduced by
The variable mStart seems to not be initialized for all possible execution paths.
Loading history...
Bug introduced by
The variable node seems to not be initialized for all possible execution paths.
Loading history...
Bug introduced by
The variable end seems to not be initialized for all possible execution paths.
Loading history...
2575
  }
2576
2577
  function measureCharInner(cm, prepared, ch, bias) {
2578
    var place = nodeAndOffsetInLineMap(prepared.map, ch, bias);
2579
    var node = place.node, start = place.start, end = place.end, collapse = place.collapse;
2580
2581
    var rect;
2582
    if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.
2583
      for (var i = 0; i < 4; i++) { // Retry a maximum of 4 times when nonsense rectangles are returned
2584
        while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) --start;
2585
        while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) ++end;
2586
        if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart) {
0 ignored issues
show
Best Practice introduced by
Comparing start to 0 using the == operator is not safe. Consider using === instead.
Loading history...
2587
          rect = node.parentNode.getBoundingClientRect();
2588
        } else if (ie && cm.options.lineWrapping) {
2589
          var rects = range(node, start, end).getClientRects();
2590
          if (rects.length)
2591
            rect = rects[bias == "right" ? rects.length - 1 : 0];
2592
          else
2593
            rect = nullRect;
2594
        } else {
2595
          rect = range(node, start, end).getBoundingClientRect() || nullRect;
2596
        }
2597
        if (rect.left || rect.right || start == 0) break;
2598
        end = start;
2599
        start = start - 1;
2600
        collapse = "right";
2601
      }
2602
      if (ie && ie_version < 11) rect = maybeUpdateRectForZooming(cm.display.measure, rect);
0 ignored issues
show
Bug introduced by
The variable rect seems to not be initialized for all possible execution paths. Are you sure maybeUpdateRectForZooming handles undefined variables?
Loading history...
2603
    } else { // If it is a widget, simply get the box for the whole widget.
2604
      if (start > 0) collapse = bias = "right";
2605
      var rects;
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable rects already seems to be declared on line 2589. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
2606
      if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)
2607
        rect = rects[bias == "right" ? rects.length - 1 : 0];
2608
      else
2609
        rect = node.getBoundingClientRect();
2610
    }
2611
    if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {
2612
      var rSpan = node.parentNode.getClientRects()[0];
2613
      if (rSpan)
2614
        rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom};
2615
      else
2616
        rect = nullRect;
2617
    }
2618
2619
    var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top;
2620
    var mid = (rtop + rbot) / 2;
2621
    var heights = prepared.view.measure.heights;
2622
    for (var i = 0; i < heights.length - 1; i++)
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 2583. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
2623
      if (mid < heights[i]) break;
2624
    var top = i ? heights[i - 1] : 0, bot = heights[i];
2625
    var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left,
2626
                  right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left,
2627
                  top: top, bottom: bot};
2628
    if (!rect.left && !rect.right) result.bogus = true;
2629
    if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; }
2630
2631
    return result;
2632
  }
2633
2634
  // Work around problem with bounding client rects on ranges being
2635
  // returned incorrectly when zoomed on IE10 and below.
2636
  function maybeUpdateRectForZooming(measure, rect) {
2637
    if (!window.screen || screen.logicalXDPI == null ||
0 ignored issues
show
Best Practice introduced by
Comparing screen.logicalXDPI to null using the == operator is not safe. Consider using === instead.
Loading history...
Bug introduced by
The variable screen seems to be never declared. If this is a global, consider adding a /** global: screen */ 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...
2638
        screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))
2639
      return rect;
2640
    var scaleX = screen.logicalXDPI / screen.deviceXDPI;
2641
    var scaleY = screen.logicalYDPI / screen.deviceYDPI;
2642
    return {left: rect.left * scaleX, right: rect.right * scaleX,
2643
            top: rect.top * scaleY, bottom: rect.bottom * scaleY};
2644
  }
2645
2646
  function clearLineMeasurementCacheFor(lineView) {
2647
    if (lineView.measure) {
2648
      lineView.measure.cache = {};
2649
      lineView.measure.heights = null;
2650
      if (lineView.rest) for (var i = 0; i < lineView.rest.length; i++)
2651
        lineView.measure.caches[i] = {};
2652
    }
2653
  }
2654
2655
  function clearLineMeasurementCache(cm) {
2656
    cm.display.externalMeasure = null;
2657
    removeChildren(cm.display.lineMeasure);
2658
    for (var i = 0; i < cm.display.view.length; i++)
2659
      clearLineMeasurementCacheFor(cm.display.view[i]);
2660
  }
2661
2662
  function clearCaches(cm) {
2663
    clearLineMeasurementCache(cm);
2664
    cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null;
2665
    if (!cm.options.lineWrapping) cm.display.maxLineChanged = true;
2666
    cm.display.lineNumChars = null;
2667
  }
2668
2669
  function pageScrollX() { return window.pageXOffset || (document.documentElement || document.body).scrollLeft; }
2670
  function pageScrollY() { return window.pageYOffset || (document.documentElement || document.body).scrollTop; }
2671
2672
  // Converts a {top, bottom, left, right} box from line-local
2673
  // coordinates into another coordinate system. Context may be one of
2674
  // "line", "div" (display.lineDiv), "local"/null (editor), "window",
2675
  // or "page".
2676
  function intoCoordSystem(cm, lineObj, rect, context) {
2677
    if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {
2678
      var size = widgetHeight(lineObj.widgets[i]);
2679
      rect.top += size; rect.bottom += size;
2680
    }
2681
    if (context == "line") return rect;
2682
    if (!context) context = "local";
2683
    var yOff = heightAtLine(lineObj);
2684
    if (context == "local") yOff += paddingTop(cm.display);
2685
    else yOff -= cm.display.viewOffset;
2686
    if (context == "page" || context == "window") {
2687
      var lOff = cm.display.lineSpace.getBoundingClientRect();
2688
      yOff += lOff.top + (context == "window" ? 0 : pageScrollY());
2689
      var xOff = lOff.left + (context == "window" ? 0 : pageScrollX());
2690
      rect.left += xOff; rect.right += xOff;
2691
    }
2692
    rect.top += yOff; rect.bottom += yOff;
2693
    return rect;
2694
  }
2695
2696
  // Coverts a box from "div" coords to another coordinate system.
2697
  // Context may be "window", "page", "div", or "local"/null.
2698
  function fromCoordSystem(cm, coords, context) {
2699
    if (context == "div") return coords;
2700
    var left = coords.left, top = coords.top;
2701
    // First move into "page" coordinate system
2702
    if (context == "page") {
2703
      left -= pageScrollX();
2704
      top -= pageScrollY();
2705
    } else if (context == "local" || !context) {
2706
      var localBox = cm.display.sizer.getBoundingClientRect();
2707
      left += localBox.left;
2708
      top += localBox.top;
2709
    }
2710
2711
    var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect();
2712
    return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top};
2713
  }
2714
2715
  function charCoords(cm, pos, context, lineObj, bias) {
2716
    if (!lineObj) lineObj = getLine(cm.doc, pos.line);
2717
    return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context);
2718
  }
2719
2720
  // Returns a box for a given cursor position, which may have an
2721
  // 'other' property containing the position of the secondary cursor
2722
  // on a bidi boundary.
2723
  function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
2724
    lineObj = lineObj || getLine(cm.doc, pos.line);
2725
    if (!preparedMeasure) preparedMeasure = prepareMeasureForLine(cm, lineObj);
2726
    function get(ch, right) {
2727
      var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight);
2728
      if (right) m.left = m.right; else m.right = m.left;
2729
      return intoCoordSystem(cm, lineObj, m, context);
2730
    }
2731
    function getBidi(ch, partPos) {
2732
      var part = order[partPos], right = part.level % 2;
2733
      if (ch == bidiLeft(part) && partPos && part.level < order[partPos - 1].level) {
2734
        part = order[--partPos];
2735
        ch = bidiRight(part) - (part.level % 2 ? 0 : 1);
2736
        right = true;
2737
      } else if (ch == bidiRight(part) && partPos < order.length - 1 && part.level < order[partPos + 1].level) {
2738
        part = order[++partPos];
2739
        ch = bidiLeft(part) - part.level % 2;
2740
        right = false;
2741
      }
2742
      if (right && ch == part.to && ch > part.from) return get(ch - 1);
2743
      return get(ch, right);
2744
    }
2745
    var order = getOrder(lineObj), ch = pos.ch;
2746
    if (!order) return get(ch);
2747
    var partPos = getBidiPartAt(order, ch);
2748
    var val = getBidi(ch, partPos);
2749
    if (bidiOther != null) val.other = getBidi(ch, bidiOther);
0 ignored issues
show
Best Practice introduced by
Comparing bidiOther to null using the != operator is not safe. Consider using !== instead.
Loading history...
2750
    return val;
2751
  }
2752
2753
  // Used to cheaply estimate the coordinates for a position. Used for
2754
  // intermediate scroll updates.
2755
  function estimateCoords(cm, pos) {
2756
    var left = 0, pos = clipPos(cm.doc, pos);
2757
    if (!cm.options.lineWrapping) left = charWidth(cm.display) * pos.ch;
2758
    var lineObj = getLine(cm.doc, pos.line);
2759
    var top = heightAtLine(lineObj) + paddingTop(cm.display);
2760
    return {left: left, right: left, top: top, bottom: top + lineObj.height};
2761
  }
2762
2763
  // Positions returned by coordsChar contain some extra information.
2764
  // xRel is the relative x position of the input coordinates compared
2765
  // to the found position (so xRel > 0 means the coordinates are to
2766
  // the right of the character position, for example). When outside
2767
  // is true, that means the coordinates lie outside the line's
2768
  // vertical range.
2769
  function PosWithInfo(line, ch, outside, xRel) {
2770
    var pos = Pos(line, ch);
2771
    pos.xRel = xRel;
2772
    if (outside) pos.outside = true;
2773
    return pos;
2774
  }
2775
2776
  // Compute the character position closest to the given coordinates.
2777
  // Input must be lineSpace-local ("div" coordinate system).
2778
  function coordsChar(cm, x, y) {
2779
    var doc = cm.doc;
2780
    y += cm.display.viewOffset;
2781
    if (y < 0) return PosWithInfo(doc.first, 0, true, -1);
2782
    var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
2783
    if (lineN > last)
2784
      return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, true, 1);
2785
    if (x < 0) x = 0;
2786
2787
    var lineObj = getLine(doc, lineN);
2788
    for (;;) {
2789
      var found = coordsCharInner(cm, lineObj, lineN, x, y);
2790
      var merged = collapsedSpanAtEnd(lineObj);
2791
      var mergedPos = merged && merged.find(0, true);
2792
      if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0))
2793
        lineN = lineNo(lineObj = mergedPos.to.line);
2794
      else
2795
        return found;
2796
    }
2797
  }
2798
2799
  function coordsCharInner(cm, lineObj, lineNo, x, y) {
2800
    var innerOff = y - heightAtLine(lineObj);
2801
    var wrongLine = false, adjust = 2 * cm.display.wrapper.clientWidth;
2802
    var preparedMeasure = prepareMeasureForLine(cm, lineObj);
2803
2804
    function getX(ch) {
2805
      var sp = cursorCoords(cm, Pos(lineNo, ch), "line", lineObj, preparedMeasure);
2806
      wrongLine = true;
2807
      if (innerOff > sp.bottom) return sp.left - adjust;
2808
      else if (innerOff < sp.top) return sp.left + adjust;
2809
      else wrongLine = false;
2810
      return sp.left;
2811
    }
2812
2813
    var bidi = getOrder(lineObj), dist = lineObj.text.length;
2814
    var from = lineLeft(lineObj), to = lineRight(lineObj);
2815
    var fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine;
2816
2817
    if (x > toX) return PosWithInfo(lineNo, to, toOutside, 1);
2818
    // Do a binary search between these bounds.
2819
    for (;;) {
2820
      if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
2821
        var ch = x < fromX || x - fromX <= toX - x ? from : to;
2822
        var xDiff = x - (ch == from ? fromX : toX);
2823
        while (isExtendingChar(lineObj.text.charAt(ch))) ++ch;
2824
        var pos = PosWithInfo(lineNo, ch, ch == from ? fromOutside : toOutside,
2825
                              xDiff < -1 ? -1 : xDiff > 1 ? 1 : 0);
2826
        return pos;
2827
      }
2828
      var step = Math.ceil(dist / 2), middle = from + step;
2829
      if (bidi) {
2830
        middle = from;
2831
        for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1);
2832
      }
2833
      var middleX = getX(middle);
2834
      if (middleX > x) {to = middle; toX = middleX; if (toOutside = wrongLine) toX += 1000; dist = step;}
2835
      else {from = middle; fromX = middleX; fromOutside = wrongLine; dist -= step;}
2836
    }
2837
  }
2838
2839
  var measureText;
2840
  // Compute the default text height.
2841
  function textHeight(display) {
2842
    if (display.cachedTextHeight != null) return display.cachedTextHeight;
0 ignored issues
show
Best Practice introduced by
Comparing display.cachedTextHeight to null using the != operator is not safe. Consider using !== instead.
Loading history...
2843
    if (measureText == null) {
0 ignored issues
show
Best Practice introduced by
Comparing measureText to null using the == operator is not safe. Consider using === instead.
Loading history...
2844
      measureText = elt("pre");
2845
      // Measure a bunch of lines, for browsers that compute
2846
      // fractional heights.
2847
      for (var i = 0; i < 49; ++i) {
2848
        measureText.appendChild(document.createTextNode("x"));
2849
        measureText.appendChild(elt("br"));
2850
      }
2851
      measureText.appendChild(document.createTextNode("x"));
2852
    }
2853
    removeChildrenAndAdd(display.measure, measureText);
0 ignored issues
show
Bug introduced by
The variable measureText does not seem to be initialized in case measureText == null on line 2843 is false. Are you sure the function removeChildrenAndAdd handles undefined variables?
Loading history...
2854
    var height = measureText.offsetHeight / 50;
2855
    if (height > 3) display.cachedTextHeight = height;
2856
    removeChildren(display.measure);
2857
    return height || 1;
2858
  }
2859
2860
  // Compute the default character width.
2861
  function charWidth(display) {
2862
    if (display.cachedCharWidth != null) return display.cachedCharWidth;
0 ignored issues
show
Best Practice introduced by
Comparing display.cachedCharWidth to null using the != operator is not safe. Consider using !== instead.
Loading history...
2863
    var anchor = elt("span", "xxxxxxxxxx");
2864
    var pre = elt("pre", [anchor]);
2865
    removeChildrenAndAdd(display.measure, pre);
2866
    var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10;
2867
    if (width > 2) display.cachedCharWidth = width;
2868
    return width || 10;
2869
  }
2870
2871
  // OPERATIONS
2872
2873
  // Operations are used to wrap a series of changes to the editor
2874
  // state in such a way that each change won't have to update the
2875
  // cursor and display (which would be awkward, slow, and
2876
  // error-prone). Instead, display updates are batched and then all
2877
  // combined and executed at once.
2878
2879
  var operationGroup = null;
2880
2881
  var nextOpId = 0;
2882
  // Start a new operation.
2883
  function startOperation(cm) {
2884
    cm.curOp = {
2885
      cm: cm,
2886
      viewChanged: false,      // Flag that indicates that lines might need to be redrawn
2887
      startHeight: cm.doc.height, // Used to detect need to update scrollbar
2888
      forceUpdate: false,      // Used to force a redraw
2889
      updateInput: null,       // Whether to reset the input textarea
2890
      typing: false,           // Whether this reset should be careful to leave existing text (for compositing)
2891
      changeObjs: null,        // Accumulated changes, for firing change events
2892
      cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on
2893
      cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
2894
      selectionChanged: false, // Whether the selection needs to be redrawn
2895
      updateMaxLine: false,    // Set when the widest line needs to be determined anew
2896
      scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
2897
      scrollToPos: null,       // Used to scroll to a specific position
2898
      id: ++nextOpId           // Unique ID
2899
    };
2900
    if (operationGroup) {
2901
      operationGroup.ops.push(cm.curOp);
2902
    } else {
2903
      cm.curOp.ownsGroup = operationGroup = {
2904
        ops: [cm.curOp],
2905
        delayedCallbacks: []
2906
      };
2907
    }
2908
  }
2909
2910
  function fireCallbacksForOps(group) {
2911
    // Calls delayed callbacks and cursorActivity handlers until no
2912
    // new ones appear
2913
    var callbacks = group.delayedCallbacks, i = 0;
2914
    do {
2915
      for (; i < callbacks.length; i++)
2916
        callbacks[i]();
2917
      for (var j = 0; j < group.ops.length; j++) {
2918
        var op = group.ops[j];
2919
        if (op.cursorActivityHandlers)
2920
          while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
2921
            op.cursorActivityHandlers[op.cursorActivityCalled++](op.cm);
2922
      }
2923
    } while (i < callbacks.length);
2924
  }
2925
2926
  // Finish an operation, updating the display and signalling delayed events
2927
  function endOperation(cm) {
2928
    var op = cm.curOp, group = op.ownsGroup;
2929
    if (!group) return;
2930
2931
    try { fireCallbacksForOps(group); }
2932
    finally {
2933
      operationGroup = null;
2934
      for (var i = 0; i < group.ops.length; i++)
2935
        group.ops[i].cm.curOp = null;
2936
      endOperations(group);
2937
    }
2938
  }
2939
2940
  // The DOM updates done when an operation finishes are batched so
2941
  // that the minimum number of relayouts are required.
2942
  function endOperations(group) {
2943
    var ops = group.ops;
2944
    for (var i = 0; i < ops.length; i++) // Read DOM
2945
      endOperation_R1(ops[i]);
2946
    for (var i = 0; i < ops.length; i++) // Write DOM (maybe)
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 2944. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
2947
      endOperation_W1(ops[i]);
2948
    for (var i = 0; i < ops.length; i++) // Read DOM
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 2944. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
2949
      endOperation_R2(ops[i]);
2950
    for (var i = 0; i < ops.length; i++) // Write DOM (maybe)
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 2944. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
2951
      endOperation_W2(ops[i]);
2952
    for (var i = 0; i < ops.length; i++) // Read DOM
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 2944. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
2953
      endOperation_finish(ops[i]);
2954
  }
2955
2956
  function endOperation_R1(op) {
2957
    var cm = op.cm, display = cm.display;
2958
    maybeClipScrollbars(cm);
2959
    if (op.updateMaxLine) findMaxLine(cm);
2960
2961
    op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||
0 ignored issues
show
Best Practice introduced by
Comparing op.scrollTop to null using the != operator is not safe. Consider using !== instead.
Loading history...
2962
      op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
2963
                         op.scrollToPos.to.line >= display.viewTo) ||
2964
      display.maxLineChanged && cm.options.lineWrapping;
2965
    op.update = op.mustUpdate &&
2966
      new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate);
2967
  }
2968
2969
  function endOperation_W1(op) {
2970
    op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update);
2971
  }
2972
2973
  function endOperation_R2(op) {
2974
    var cm = op.cm, display = cm.display;
2975
    if (op.updatedDisplay) updateHeightsInViewport(cm);
2976
2977
    op.barMeasure = measureForScrollbars(cm);
2978
2979
    // If the max line changed since it was last measured, measure it,
2980
    // and ensure the document's width matches it.
2981
    // updateDisplay_W2 will use these properties to do the actual resizing
2982
    if (display.maxLineChanged && !cm.options.lineWrapping) {
2983
      op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3;
2984
      cm.display.sizerWidth = op.adjustWidthTo;
2985
      op.barMeasure.scrollWidth =
2986
        Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth);
2987
      op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm));
2988
    }
2989
2990
    if (op.updatedDisplay || op.selectionChanged)
2991
      op.preparedSelection = display.input.prepareSelection();
2992
  }
2993
2994
  function endOperation_W2(op) {
2995
    var cm = op.cm;
2996
2997
    if (op.adjustWidthTo != null) {
0 ignored issues
show
Best Practice introduced by
Comparing op.adjustWidthTo to null using the != operator is not safe. Consider using !== instead.
Loading history...
2998
      cm.display.sizer.style.minWidth = op.adjustWidthTo + "px";
2999
      if (op.maxScrollLeft < cm.doc.scrollLeft)
3000
        setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true);
3001
      cm.display.maxLineChanged = false;
3002
    }
3003
3004
    if (op.preparedSelection)
3005
      cm.display.input.showSelection(op.preparedSelection);
3006
    if (op.updatedDisplay)
3007
      setDocumentHeight(cm, op.barMeasure);
3008
    if (op.updatedDisplay || op.startHeight != cm.doc.height)
3009
      updateScrollbars(cm, op.barMeasure);
3010
3011
    if (op.selectionChanged) restartBlink(cm);
3012
3013
    if (cm.state.focused && op.updateInput)
3014
      cm.display.input.reset(op.typing);
3015
  }
3016
3017
  function endOperation_finish(op) {
3018
    var cm = op.cm, display = cm.display, doc = cm.doc;
3019
3020
    if (op.updatedDisplay) postUpdateDisplay(cm, op.update);
3021
3022
    // Abort mouse wheel delta measurement, when scrolling explicitly
3023
    if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))
0 ignored issues
show
Best Practice introduced by
Comparing op.scrollTop to null using the != operator is not safe. Consider using !== instead.
Loading history...
Best Practice introduced by
Comparing op.scrollLeft to null using the != operator is not safe. Consider using !== instead.
Loading history...
Best Practice introduced by
Comparing display.wheelStartX to null using the != operator is not safe. Consider using !== instead.
Loading history...
3024
      display.wheelStartX = display.wheelStartY = null;
3025
3026
    // Propagate the scroll position to the actual DOM scroller
3027
    if (op.scrollTop != null && (display.scroller.scrollTop != op.scrollTop || op.forceScroll)) {
3028
      doc.scrollTop = Math.max(0, Math.min(display.scroller.scrollHeight - display.scroller.clientHeight, op.scrollTop));
3029
      display.scrollbars.setScrollTop(doc.scrollTop);
3030
      display.scroller.scrollTop = doc.scrollTop;
3031
    }
3032
    if (op.scrollLeft != null && (display.scroller.scrollLeft != op.scrollLeft || op.forceScroll)) {
3033
      doc.scrollLeft = Math.max(0, Math.min(display.scroller.scrollWidth - displayWidth(cm), op.scrollLeft));
3034
      display.scrollbars.setScrollLeft(doc.scrollLeft);
3035
      display.scroller.scrollLeft = doc.scrollLeft;
3036
      alignHorizontally(cm);
3037
    }
3038
    // If we need to scroll a specific position into view, do so.
3039
    if (op.scrollToPos) {
3040
      var coords = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
3041
                                     clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin);
3042
      if (op.scrollToPos.isCursor && cm.state.focused) maybeScrollWindow(cm, coords);
3043
    }
3044
3045
    // Fire events for markers that are hidden/unidden by editing or
3046
    // undoing
3047
    var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
3048
    if (hidden) for (var i = 0; i < hidden.length; ++i)
3049
      if (!hidden[i].lines.length) signal(hidden[i], "hide");
3050
    if (unhidden) for (var i = 0; i < unhidden.length; ++i)
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 3048. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
3051
      if (unhidden[i].lines.length) signal(unhidden[i], "unhide");
3052
3053
    if (display.wrapper.offsetHeight)
3054
      doc.scrollTop = cm.display.scroller.scrollTop;
3055
3056
    // Fire change events, and delayed event handlers
3057
    if (op.changeObjs)
3058
      signal(cm, "changes", cm, op.changeObjs);
3059
    if (op.update)
3060
      op.update.finish();
3061
  }
3062
3063
  // Run the given function in an operation
3064
  function runInOp(cm, f) {
3065
    if (cm.curOp) return f();
3066
    startOperation(cm);
3067
    try { return f(); }
3068
    finally { endOperation(cm); }
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...
3069
  }
3070
  // Wraps a function in an operation. Returns the wrapped function.
3071
  function operation(cm, f) {
3072
    return function() {
3073
      if (cm.curOp) return f.apply(cm, arguments);
3074
      startOperation(cm);
3075
      try { return f.apply(cm, arguments); }
3076
      finally { endOperation(cm); }
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...
3077
    };
3078
  }
3079
  // Used to add methods to editor and doc instances, wrapping them in
3080
  // operations.
3081
  function methodOp(f) {
3082
    return function() {
3083
      if (this.curOp) return f.apply(this, arguments);
3084
      startOperation(this);
3085
      try { return f.apply(this, arguments); }
3086
      finally { endOperation(this); }
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...
3087
    };
3088
  }
3089
  function docMethodOp(f) {
3090
    return function() {
3091
      var cm = this.cm;
3092
      if (!cm || cm.curOp) return f.apply(this, arguments);
3093
      startOperation(cm);
3094
      try { return f.apply(this, arguments); }
3095
      finally { endOperation(cm); }
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...
3096
    };
3097
  }
3098
3099
  // VIEW TRACKING
3100
3101
  // These objects are used to represent the visible (currently drawn)
3102
  // part of the document. A LineView may correspond to multiple
3103
  // logical lines, if those are connected by collapsed ranges.
3104
  function LineView(doc, line, lineN) {
3105
    // The starting line
3106
    this.line = line;
3107
    // Continuing lines, if any
3108
    this.rest = visualLineContinued(line);
3109
    // Number of logical lines in this visual line
3110
    this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1;
3111
    this.node = this.text = null;
3112
    this.hidden = lineIsHidden(doc, line);
3113
  }
3114
3115
  // Create a range of LineView objects for the given lines.
3116
  function buildViewArray(cm, from, to) {
3117
    var array = [], nextPos;
3118
    for (var pos = from; pos < to; pos = nextPos) {
3119
      var view = new LineView(cm.doc, getLine(cm.doc, pos), pos);
3120
      nextPos = pos + view.size;
3121
      array.push(view);
3122
    }
3123
    return array;
3124
  }
3125
3126
  // Updates the display.view data structure for a given change to the
3127
  // document. From and to are in pre-change coordinates. Lendiff is
3128
  // the amount of lines added or subtracted by the change. This is
3129
  // used for changes that span multiple lines, or change the way
3130
  // lines are divided into visual lines. regLineChange (below)
3131
  // registers single-line changes.
3132
  function regChange(cm, from, to, lendiff) {
3133
    if (from == null) from = cm.doc.first;
0 ignored issues
show
Best Practice introduced by
Comparing from to null using the == operator is not safe. Consider using === instead.
Loading history...
3134
    if (to == null) to = cm.doc.first + cm.doc.size;
0 ignored issues
show
Best Practice introduced by
Comparing to to null using the == operator is not safe. Consider using === instead.
Loading history...
3135
    if (!lendiff) lendiff = 0;
3136
3137
    var display = cm.display;
3138
    if (lendiff && to < display.viewTo &&
3139
        (display.updateLineNumbers == null || display.updateLineNumbers > from))
0 ignored issues
show
Best Practice introduced by
Comparing display.updateLineNumbers to null using the == operator is not safe. Consider using === instead.
Loading history...
3140
      display.updateLineNumbers = from;
3141
3142
    cm.curOp.viewChanged = true;
3143
3144
    if (from >= display.viewTo) { // Change after
3145
      if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)
3146
        resetView(cm);
3147
    } else if (to <= display.viewFrom) { // Change before
3148
      if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {
3149
        resetView(cm);
3150
      } else {
3151
        display.viewFrom += lendiff;
3152
        display.viewTo += lendiff;
3153
      }
3154
    } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap
3155
      resetView(cm);
3156
    } else if (from <= display.viewFrom) { // Top overlap
3157
      var cut = viewCuttingPoint(cm, to, to + lendiff, 1);
3158
      if (cut) {
3159
        display.view = display.view.slice(cut.index);
3160
        display.viewFrom = cut.lineN;
3161
        display.viewTo += lendiff;
3162
      } else {
3163
        resetView(cm);
3164
      }
3165
    } else if (to >= display.viewTo) { // Bottom overlap
3166
      var cut = viewCuttingPoint(cm, from, from, -1);
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable cut already seems to be declared on line 3157. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
3167
      if (cut) {
3168
        display.view = display.view.slice(0, cut.index);
3169
        display.viewTo = cut.lineN;
3170
      } else {
3171
        resetView(cm);
3172
      }
3173
    } else { // Gap in the middle
3174
      var cutTop = viewCuttingPoint(cm, from, from, -1);
3175
      var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1);
3176
      if (cutTop && cutBot) {
3177
        display.view = display.view.slice(0, cutTop.index)
3178
          .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))
3179
          .concat(display.view.slice(cutBot.index));
3180
        display.viewTo += lendiff;
3181
      } else {
3182
        resetView(cm);
3183
      }
3184
    }
3185
3186
    var ext = display.externalMeasured;
3187
    if (ext) {
3188
      if (to < ext.lineN)
3189
        ext.lineN += lendiff;
3190
      else if (from < ext.lineN + ext.size)
3191
        display.externalMeasured = null;
3192
    }
3193
  }
3194
3195
  // Register a change to a single line. Type must be one of "text",
3196
  // "gutter", "class", "widget"
3197
  function regLineChange(cm, line, type) {
3198
    cm.curOp.viewChanged = true;
3199
    var display = cm.display, ext = cm.display.externalMeasured;
3200
    if (ext && line >= ext.lineN && line < ext.lineN + ext.size)
3201
      display.externalMeasured = null;
3202
3203
    if (line < display.viewFrom || line >= display.viewTo) return;
3204
    var lineView = display.view[findViewIndex(cm, line)];
3205
    if (lineView.node == null) return;
0 ignored issues
show
Best Practice introduced by
Comparing lineView.node to null using the == operator is not safe. Consider using === instead.
Loading history...
3206
    var arr = lineView.changes || (lineView.changes = []);
3207
    if (indexOf(arr, type) == -1) arr.push(type);
3208
  }
3209
3210
  // Clear the view.
3211
  function resetView(cm) {
3212
    cm.display.viewFrom = cm.display.viewTo = cm.doc.first;
3213
    cm.display.view = [];
3214
    cm.display.viewOffset = 0;
3215
  }
3216
3217
  // Find the view element corresponding to a given line. Return null
3218
  // when the line isn't visible.
3219
  function findViewIndex(cm, n) {
3220
    if (n >= cm.display.viewTo) return null;
3221
    n -= cm.display.viewFrom;
3222
    if (n < 0) return null;
3223
    var view = cm.display.view;
3224
    for (var i = 0; i < view.length; i++) {
3225
      n -= view[i].size;
3226
      if (n < 0) return i;
3227
    }
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...
3228
  }
3229
3230
  function viewCuttingPoint(cm, oldN, newN, dir) {
3231
    var index = findViewIndex(cm, oldN), diff, view = cm.display.view;
3232
    if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)
3233
      return {index: index, lineN: newN};
3234
    for (var i = 0, n = cm.display.viewFrom; i < index; i++)
3235
      n += view[i].size;
3236
    if (n != oldN) {
3237
      if (dir > 0) {
3238
        if (index == view.length - 1) return null;
3239
        diff = (n + view[index].size) - oldN;
3240
        index++;
3241
      } else {
3242
        diff = n - oldN;
3243
      }
3244
      oldN += diff; newN += diff;
0 ignored issues
show
Unused Code introduced by
The assignment to variable oldN seems to be never used. Consider removing it.
Loading history...
3245
    }
3246
    while (visualLineNo(cm.doc, newN) != newN) {
3247
      if (index == (dir < 0 ? 0 : view.length - 1)) return null;
3248
      newN += dir * view[index - (dir < 0 ? 1 : 0)].size;
3249
      index += dir;
3250
    }
3251
    return {index: index, lineN: newN};
3252
  }
3253
3254
  // Force the view to cover a given range, adding empty view element
3255
  // or clipping off existing ones as needed.
3256
  function adjustView(cm, from, to) {
3257
    var display = cm.display, view = display.view;
3258
    if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {
0 ignored issues
show
Best Practice introduced by
Comparing view.length to 0 using the == operator is not safe. Consider using === instead.
Loading history...
3259
      display.view = buildViewArray(cm, from, to);
3260
      display.viewFrom = from;
3261
    } else {
3262
      if (display.viewFrom > from)
3263
        display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view);
3264
      else if (display.viewFrom < from)
3265
        display.view = display.view.slice(findViewIndex(cm, from));
3266
      display.viewFrom = from;
3267
      if (display.viewTo < to)
3268
        display.view = display.view.concat(buildViewArray(cm, display.viewTo, to));
3269
      else if (display.viewTo > to)
3270
        display.view = display.view.slice(0, findViewIndex(cm, to));
3271
    }
3272
    display.viewTo = to;
3273
  }
3274
3275
  // Count the number of lines in the view whose DOM representation is
3276
  // out of date (or nonexistent).
3277
  function countDirtyView(cm) {
3278
    var view = cm.display.view, dirty = 0;
3279
    for (var i = 0; i < view.length; i++) {
3280
      var lineView = view[i];
3281
      if (!lineView.hidden && (!lineView.node || lineView.changes)) ++dirty;
3282
    }
3283
    return dirty;
3284
  }
3285
3286
  // EVENT HANDLERS
3287
3288
  // Attach the necessary event handlers when initializing the editor
3289
  function registerEventHandlers(cm) {
3290
    var d = cm.display;
3291
    on(d.scroller, "mousedown", operation(cm, onMouseDown));
3292
    // Older IE's will not fire a second mousedown for a double click
3293
    if (ie && ie_version < 11)
3294
      on(d.scroller, "dblclick", operation(cm, function(e) {
3295
        if (signalDOMEvent(cm, e)) return;
3296
        var pos = posFromMouse(cm, e);
3297
        if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) return;
3298
        e_preventDefault(e);
3299
        var word = cm.findWordAt(pos);
3300
        extendSelection(cm.doc, word.anchor, word.head);
3301
      }));
3302
    else
3303
      on(d.scroller, "dblclick", function(e) { signalDOMEvent(cm, e) || e_preventDefault(e); });
3304
    // Some browsers fire contextmenu *after* opening the menu, at
3305
    // which point we can't mess with it anymore. Context menu is
3306
    // handled in onMouseDown for these browsers.
3307
    if (!captureRightClick) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);});
3308
3309
    // Used to suppress mouse event handling when a touch happens
3310
    var touchFinished, prevTouch = {end: 0};
3311
    function finishTouch() {
3312
      if (d.activeTouch) {
3313
        touchFinished = setTimeout(function() {d.activeTouch = null;}, 1000);
3314
        prevTouch = d.activeTouch;
3315
        prevTouch.end = +new Date;
3316
      }
3317
    };
3318
    function isMouseLikeTouchEvent(e) {
3319
      if (e.touches.length != 1) return false;
0 ignored issues
show
Best Practice introduced by
Comparing e.touches.length to 1 using the != operator is not safe. Consider using !== instead.
Loading history...
3320
      var touch = e.touches[0];
3321
      return touch.radiusX <= 1 && touch.radiusY <= 1;
3322
    }
3323
    function farAway(touch, other) {
3324
      if (other.left == null) return true;
0 ignored issues
show
Best Practice introduced by
Comparing other.left to null using the == operator is not safe. Consider using === instead.
Loading history...
3325
      var dx = other.left - touch.left, dy = other.top - touch.top;
3326
      return dx * dx + dy * dy > 20 * 20;
3327
    }
3328
    on(d.scroller, "touchstart", function(e) {
3329
      if (!isMouseLikeTouchEvent(e)) {
3330
        clearTimeout(touchFinished);
3331
        var now = +new Date;
3332
        d.activeTouch = {start: now, moved: false,
3333
                         prev: now - prevTouch.end <= 300 ? prevTouch : null};
3334
        if (e.touches.length == 1) {
0 ignored issues
show
Best Practice introduced by
Comparing e.touches.length to 1 using the == operator is not safe. Consider using === instead.
Loading history...
3335
          d.activeTouch.left = e.touches[0].pageX;
3336
          d.activeTouch.top = e.touches[0].pageY;
3337
        }
3338
      }
3339
    });
3340
    on(d.scroller, "touchmove", function() {
3341
      if (d.activeTouch) d.activeTouch.moved = true;
3342
    });
3343
    on(d.scroller, "touchend", function(e) {
3344
      var touch = d.activeTouch;
3345
      if (touch && !eventInWidget(d, e) && touch.left != null &&
0 ignored issues
show
Best Practice introduced by
Comparing touch.left to null using the != operator is not safe. Consider using !== instead.
Loading history...
3346
          !touch.moved && new Date - touch.start < 300) {
3347
        var pos = cm.coordsChar(d.activeTouch, "page"), range;
3348
        if (!touch.prev || farAway(touch, touch.prev)) // Single tap
3349
          range = new Range(pos, pos);
3350
        else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap
3351
          range = cm.findWordAt(pos);
3352
        else // Triple tap
3353
          range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0)));
3354
        cm.setSelection(range.anchor, range.head);
3355
        cm.focus();
3356
        e_preventDefault(e);
3357
      }
3358
      finishTouch();
3359
    });
3360
    on(d.scroller, "touchcancel", finishTouch);
3361
3362
    // Sync scrolling between fake scrollbars and real scrollable
3363
    // area, ensure viewport is updated when scrolling.
3364
    on(d.scroller, "scroll", function() {
3365
      if (d.scroller.clientHeight) {
3366
        setScrollTop(cm, d.scroller.scrollTop);
3367
        setScrollLeft(cm, d.scroller.scrollLeft, true);
3368
        signal(cm, "scroll", cm);
3369
      }
3370
    });
3371
3372
    // Listen to wheel events in order to try and update the viewport on time.
3373
    on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);});
3374
    on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);});
3375
3376
    // Prevent wrapper from ever scrolling
3377
    on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
3378
3379
    function drag_(e) {
3380
      if (!signalDOMEvent(cm, e)) e_stop(e);
3381
    }
3382
    if (cm.options.dragDrop) {
3383
      on(d.scroller, "dragstart", function(e){onDragStart(cm, e);});
3384
      on(d.scroller, "dragenter", drag_);
3385
      on(d.scroller, "dragover", drag_);
3386
      on(d.scroller, "drop", operation(cm, onDrop));
3387
    }
3388
3389
    var inp = d.input.getField();
3390
    on(inp, "keyup", function(e) { onKeyUp.call(cm, e); });
3391
    on(inp, "keydown", operation(cm, onKeyDown));
3392
    on(inp, "keypress", operation(cm, onKeyPress));
3393
    on(inp, "focus", bind(onFocus, cm));
3394
    on(inp, "blur", bind(onBlur, cm));
3395
  }
3396
3397
  // Called when the window resizes
3398
  function onResize(cm) {
3399
    var d = cm.display;
3400
    if (d.lastWrapHeight == d.wrapper.clientHeight && d.lastWrapWidth == d.wrapper.clientWidth)
3401
      return;
3402
    // Might be a text scaling operation, clear size caches.
3403
    d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
3404
    d.scrollbarsClipped = false;
3405
    cm.setSize();
3406
  }
3407
3408
  // MOUSE EVENTS
3409
3410
  // Return true when the given mouse event happened in a widget
3411
  function eventInWidget(display, e) {
3412
    for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
3413
      if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") ||
0 ignored issues
show
Best Practice introduced by
Comparing n.nodeType to 1 using the == operator is not safe. Consider using === instead.
Loading history...
3414
          (n.parentNode == display.sizer && n != display.mover))
3415
        return true;
3416
    }
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...
3417
  }
3418
3419
  // Given a mouse event, find the corresponding position. If liberal
3420
  // is false, it checks whether a gutter or scrollbar was clicked,
3421
  // and returns null if it was. forRect is used by rectangular
3422
  // selections, and tries to estimate a character position even for
3423
  // coordinates beyond the right of the text.
3424
  function posFromMouse(cm, e, liberal, forRect) {
3425
    var display = cm.display;
3426
    if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") return null;
3427
3428
    var x, y, space = display.lineSpace.getBoundingClientRect();
3429
    // Fails unpredictably on IE[67] when mouse is dragged around quickly.
3430
    try { x = e.clientX - space.left; y = e.clientY - space.top; }
3431
    catch (e) { return null; }
3432
    var coords = coordsChar(cm, x, y), line;
3433
    if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) {
0 ignored issues
show
Best Practice introduced by
Comparing coords.xRel to 1 using the == operator is not safe. Consider using === instead.
Loading history...
3434
      var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length;
3435
      coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff));
3436
    }
3437
    return coords;
3438
  }
3439
3440
  // A mouse down can be a single click, double click, triple click,
3441
  // start of selection drag, start of text drag, new cursor
3442
  // (ctrl-click), rectangle drag (alt-drag), or xwin
3443
  // middle-click-paste. Or it might be a click on something we should
3444
  // not interfere with, such as a scrollbar or widget.
3445
  function onMouseDown(e) {
3446
    var cm = this, display = cm.display;
3447
    if (display.activeTouch && display.input.supportsTouch() || signalDOMEvent(cm, e)) return;
3448
    display.shift = e.shiftKey;
3449
3450
    if (eventInWidget(display, e)) {
3451
      if (!webkit) {
3452
        // Briefly turn off draggability, to allow widgets to do
3453
        // normal dragging things.
3454
        display.scroller.draggable = false;
3455
        setTimeout(function(){display.scroller.draggable = true;}, 100);
3456
      }
3457
      return;
3458
    }
3459
    if (clickInGutter(cm, e)) return;
3460
    var start = posFromMouse(cm, e);
3461
    window.focus();
3462
3463
    switch (e_button(e)) {
3464
    case 1:
3465
      if (start)
3466
        leftButtonDown(cm, e, start);
3467
      else if (e_target(e) == display.scroller)
3468
        e_preventDefault(e);
3469
      break;
3470
    case 2:
3471
      if (webkit) cm.state.lastMiddleDown = +new Date;
3472
      if (start) extendSelection(cm.doc, start);
3473
      setTimeout(function() {display.input.focus();}, 20);
3474
      e_preventDefault(e);
3475
      break;
3476
    case 3:
3477
      if (captureRightClick) onContextMenu(cm, e);
3478
      break;
3479
    }
3480
  }
3481
3482
  var lastClick, lastDoubleClick;
3483
  function leftButtonDown(cm, e, start) {
3484
    if (ie) setTimeout(bind(ensureFocus, cm), 0);
3485
    else ensureFocus(cm);
3486
3487
    var now = +new Date, type;
3488
    if (lastDoubleClick && lastDoubleClick.time > now - 400 && cmp(lastDoubleClick.pos, start) == 0) {
0 ignored issues
show
Best Practice introduced by
Comparing cmp(lastDoubleClick.pos, start) to 0 using the == operator is not safe. Consider using === instead.
Loading history...
3489
      type = "triple";
3490
    } else if (lastClick && lastClick.time > now - 400 && cmp(lastClick.pos, start) == 0) {
0 ignored issues
show
Best Practice introduced by
Comparing cmp(lastClick.pos, start) to 0 using the == operator is not safe. Consider using === instead.
Loading history...
3491
      type = "double";
3492
      lastDoubleClick = {time: now, pos: start};
3493
    } else {
3494
      type = "single";
3495
      lastClick = {time: now, pos: start};
3496
    }
3497
3498
    var sel = cm.doc.sel, modifier = mac ? e.metaKey : e.ctrlKey, contained;
3499
    if (cm.options.dragDrop && dragAndDrop && !isReadOnly(cm) &&
3500
        type == "single" && (contained = sel.contains(start)) > -1 &&
3501
        !sel.ranges[contained].empty())
3502
      leftButtonStartDrag(cm, e, start, modifier);
3503
    else
3504
      leftButtonSelect(cm, e, start, type, modifier);
3505
  }
3506
3507
  // Start a text drag. When it ends, see if any dragging actually
3508
  // happen, and treat as a click if it didn't.
3509
  function leftButtonStartDrag(cm, e, start, modifier) {
3510
    var display = cm.display;
3511
    var dragEnd = operation(cm, function(e2) {
3512
      if (webkit) display.scroller.draggable = false;
3513
      cm.state.draggingText = false;
3514
      off(document, "mouseup", dragEnd);
3515
      off(display.scroller, "drop", dragEnd);
3516
      if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
3517
        e_preventDefault(e2);
3518
        if (!modifier)
3519
          extendSelection(cm.doc, start);
3520
        display.input.focus();
3521
        // Work around unexplainable focus problem in IE9 (#2127)
3522
        if (ie && ie_version == 9)
3523
          setTimeout(function() {document.body.focus(); display.input.focus();}, 20);
3524
      }
3525
    });
3526
    // Let the drag handler handle this.
3527
    if (webkit) display.scroller.draggable = true;
3528
    cm.state.draggingText = dragEnd;
3529
    // IE's approach to draggable
3530
    if (display.scroller.dragDrop) display.scroller.dragDrop();
3531
    on(document, "mouseup", dragEnd);
3532
    on(display.scroller, "drop", dragEnd);
3533
  }
3534
3535
  // Normal selection, as opposed to text dragging.
3536
  function leftButtonSelect(cm, e, start, type, addNew) {
3537
    var display = cm.display, doc = cm.doc;
3538
    e_preventDefault(e);
3539
3540
    var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges;
3541
    if (addNew && !e.shiftKey) {
3542
      ourIndex = doc.sel.contains(start);
3543
      if (ourIndex > -1)
3544
        ourRange = ranges[ourIndex];
3545
      else
3546
        ourRange = new Range(start, start);
3547
    } else {
3548
      ourRange = doc.sel.primary();
3549
    }
3550
3551
    if (e.altKey) {
3552
      type = "rect";
3553
      if (!addNew) ourRange = new Range(start, start);
3554
      start = posFromMouse(cm, e, true, true);
3555
      ourIndex = -1;
3556
    } else if (type == "double") {
3557
      var word = cm.findWordAt(start);
3558
      if (cm.display.shift || doc.extend)
3559
        ourRange = extendRange(doc, ourRange, word.anchor, word.head);
3560
      else
3561
        ourRange = word;
3562
    } else if (type == "triple") {
3563
      var line = new Range(Pos(start.line, 0), clipPos(doc, Pos(start.line + 1, 0)));
3564
      if (cm.display.shift || doc.extend)
3565
        ourRange = extendRange(doc, ourRange, line.anchor, line.head);
3566
      else
3567
        ourRange = line;
3568
    } else {
3569
      ourRange = extendRange(doc, ourRange, start);
3570
    }
3571
3572
    if (!addNew) {
3573
      ourIndex = 0;
3574
      setSelection(doc, new Selection([ourRange], 0), sel_mouse);
3575
      startSel = doc.sel;
3576
    } else if (ourIndex == -1) {
0 ignored issues
show
Bug introduced by
The variable ourIndex seems to not be initialized for all possible execution paths.
Loading history...
3577
      ourIndex = ranges.length;
3578
      setSelection(doc, normalizeSelection(ranges.concat([ourRange]), ourIndex),
3579
                   {scroll: false, origin: "*mouse"});
3580
    } else if (ranges.length > 1 && ranges[ourIndex].empty() && type == "single") {
3581
      setSelection(doc, normalizeSelection(ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0));
3582
      startSel = doc.sel;
3583
    } else {
3584
      replaceOneSelection(doc, ourIndex, ourRange, sel_mouse);
3585
    }
3586
3587
    var lastPos = start;
3588
    function extendTo(pos) {
3589
      if (cmp(lastPos, pos) == 0) return;
0 ignored issues
show
Best Practice introduced by
Comparing cmp(lastPos, pos) to 0 using the == operator is not safe. Consider using === instead.
Loading history...
3590
      lastPos = pos;
3591
3592
      if (type == "rect") {
3593
        var ranges = [], tabSize = cm.options.tabSize;
3594
        var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize);
3595
        var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize);
3596
        var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol);
3597
        for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line));
3598
             line <= end; line++) {
3599
          var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize);
3600
          if (left == right)
3601
            ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos)));
3602
          else if (text.length > leftPos)
3603
            ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize))));
3604
        }
3605
        if (!ranges.length) ranges.push(new Range(start, start));
3606
        setSelection(doc, normalizeSelection(startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex),
0 ignored issues
show
Bug introduced by
The variable ourIndex seems to not be initialized for all possible execution paths. Are you sure slice handles undefined variables?
Loading history...
3607
                     {origin: "*mouse", scroll: false});
3608
        cm.scrollIntoView(pos);
3609
      } else {
3610
        var oldRange = ourRange;
3611
        var anchor = oldRange.anchor, head = pos;
3612
        if (type != "single") {
3613
          if (type == "double")
3614
            var range = cm.findWordAt(pos);
3615
          else
3616
            var range = new Range(Pos(pos.line, 0), clipPos(doc, Pos(pos.line + 1, 0)));
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable range already seems to be declared on line 3614. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
3617
          if (cmp(range.anchor, anchor) > 0) {
3618
            head = range.head;
3619
            anchor = minPos(oldRange.from(), range.anchor);
3620
          } else {
3621
            head = range.anchor;
3622
            anchor = maxPos(oldRange.to(), range.head);
3623
          }
3624
        }
3625
        var ranges = startSel.ranges.slice(0);
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable ranges already seems to be declared on line 3593. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
3626
        ranges[ourIndex] = new Range(clipPos(doc, anchor), head);
3627
        setSelection(doc, normalizeSelection(ranges, ourIndex), sel_mouse);
3628
      }
3629
    }
3630
3631
    var editorSize = display.wrapper.getBoundingClientRect();
3632
    // Used to ensure timeout re-tries don't fire when another extend
3633
    // happened in the meantime (clearTimeout isn't reliable -- at
3634
    // least on Chrome, the timeouts still happen even when cleared,
3635
    // if the clear happens after their scheduled firing time).
3636
    var counter = 0;
3637
3638
    function extend(e) {
3639
      var curCount = ++counter;
3640
      var cur = posFromMouse(cm, e, true, type == "rect");
3641
      if (!cur) return;
3642
      if (cmp(cur, lastPos) != 0) {
0 ignored issues
show
Best Practice introduced by
Comparing cmp(cur, lastPos) to 0 using the != operator is not safe. Consider using !== instead.
Loading history...
3643
        ensureFocus(cm);
3644
        extendTo(cur);
3645
        var visible = visibleLines(display, doc);
3646
        if (cur.line >= visible.to || cur.line < visible.from)
3647
          setTimeout(operation(cm, function(){if (counter == curCount) extend(e);}), 150);
3648
      } else {
3649
        var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;
3650
        if (outside) setTimeout(operation(cm, function() {
3651
          if (counter != curCount) return;
3652
          display.scroller.scrollTop += outside;
3653
          extend(e);
3654
        }), 50);
3655
      }
3656
    }
3657
3658
    function done(e) {
3659
      counter = Infinity;
0 ignored issues
show
Comprehensibility Best Practice introduced by
You seem to be aliasing the built-in name Infinity as counter. This makes your code very difficult to follow, consider using the built-in name directly.
Loading history...
3660
      e_preventDefault(e);
3661
      display.input.focus();
3662
      off(document, "mousemove", move);
3663
      off(document, "mouseup", up);
3664
      doc.history.lastSelOrigin = null;
3665
    }
3666
3667
    var move = operation(cm, function(e) {
3668
      if (!e_button(e)) done(e);
3669
      else extend(e);
3670
    });
3671
    var up = operation(cm, done);
3672
    on(document, "mousemove", move);
3673
    on(document, "mouseup", up);
3674
  }
3675
3676
  // Determines whether an event happened in the gutter, and fires the
3677
  // handlers for the corresponding event.
3678
  function gutterEvent(cm, e, type, prevent, signalfn) {
3679
    try { var mX = e.clientX, mY = e.clientY; }
3680
    catch(e) { return false; }
3681
    if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) return false;
3682
    if (prevent) e_preventDefault(e);
3683
3684
    var display = cm.display;
3685
    var lineBox = display.lineDiv.getBoundingClientRect();
3686
3687
    if (mY > lineBox.bottom || !hasHandler(cm, type)) return e_defaultPrevented(e);
3688
    mY -= lineBox.top - display.viewOffset;
3689
3690
    for (var i = 0; i < cm.options.gutters.length; ++i) {
3691
      var g = display.gutters.childNodes[i];
3692
      if (g && g.getBoundingClientRect().right >= mX) {
3693
        var line = lineAtHeight(cm.doc, mY);
3694
        var gutter = cm.options.gutters[i];
3695
        signalfn(cm, type, cm, line, gutter, e);
3696
        return e_defaultPrevented(e);
3697
      }
3698
    }
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...
3699
  }
3700
3701
  function clickInGutter(cm, e) {
3702
    return gutterEvent(cm, e, "gutterClick", true, signalLater);
3703
  }
3704
3705
  // Kludge to work around strange IE behavior where it'll sometimes
3706
  // re-fire a series of drag-related events right after the drop (#1551)
3707
  var lastDrop = 0;
3708
3709
  function onDrop(e) {
3710
    var cm = this;
3711
    if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))
3712
      return;
3713
    e_preventDefault(e);
3714
    if (ie) lastDrop = +new Date;
3715
    var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
3716
    if (!pos || isReadOnly(cm)) return;
3717
    // Might be a file drop, in which case we simply extract the text
3718
    // and insert it.
3719
    if (files && files.length && window.FileReader && window.File) {
3720
      var n = files.length, text = Array(n), read = 0;
3721
      var loadFile = function(file, i) {
3722
        var reader = new FileReader;
0 ignored issues
show
Bug introduced by
The variable FileReader seems to be never declared. If this is a global, consider adding a /** global: FileReader */ 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...
3723
        reader.onload = operation(cm, function() {
3724
          text[i] = reader.result;
3725
          if (++read == n) {
3726
            pos = clipPos(cm.doc, pos);
3727
            var change = {from: pos, to: pos, text: splitLines(text.join("\n")), origin: "paste"};
3728
            makeChange(cm.doc, change);
3729
            setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change)));
3730
          }
3731
        });
3732
        reader.readAsText(file);
3733
      };
3734
      for (var i = 0; i < n; ++i) loadFile(files[i], i);
3735
    } else { // Normal drop
3736
      // Don't do a replace if the drop happened inside of the selected text.
3737
      if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {
3738
        cm.state.draggingText(e);
3739
        // Ensure the editor is re-focused
3740
        setTimeout(function() {cm.display.input.focus();}, 20);
3741
        return;
3742
      }
3743
      try {
3744
        var text = e.dataTransfer.getData("Text");
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable text already seems to be declared on line 3720. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
3745
        if (text) {
3746
          if (cm.state.draggingText && !(mac ? e.metaKey : e.ctrlKey))
3747
            var selected = cm.listSelections();
3748
          setSelectionNoUndo(cm.doc, simpleSelection(pos, pos));
3749
          if (selected) for (var i = 0; i < selected.length; ++i)
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 3734. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
3750
            replaceRange(cm.doc, "", selected[i].anchor, selected[i].head, "drag");
3751
          cm.replaceSelection(text, "around", "paste");
3752
          cm.display.input.focus();
3753
        }
3754
      }
3755
      catch(e){}
0 ignored issues
show
Coding Style Comprehensibility Best Practice introduced by
Empty catch clauses should be used with caution; consider adding a comment why this is needed.
Loading history...
3756
    }
3757
  }
3758
3759
  function onDragStart(cm, e) {
3760
    if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return; }
3761
    if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) return;
3762
3763
    e.dataTransfer.setData("Text", cm.getSelection());
3764
3765
    // Use dummy image instead of default browsers image.
3766
    // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
3767
    if (e.dataTransfer.setDragImage && !safari) {
3768
      var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
3769
      img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
3770
      if (presto) {
3771
        img.width = img.height = 1;
3772
        cm.display.wrapper.appendChild(img);
3773
        // Force a relayout, or Opera won't use our image for some obscure reason
3774
        img._top = img.offsetTop;
3775
      }
3776
      e.dataTransfer.setDragImage(img, 0, 0);
3777
      if (presto) img.parentNode.removeChild(img);
3778
    }
3779
  }
3780
3781
  // SCROLL EVENTS
3782
3783
  // Sync the scrollable area and scrollbars, ensure the viewport
3784
  // covers the visible area.
3785
  function setScrollTop(cm, val) {
3786
    if (Math.abs(cm.doc.scrollTop - val) < 2) return;
3787
    cm.doc.scrollTop = val;
3788
    if (!gecko) updateDisplaySimple(cm, {top: val});
3789
    if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val;
3790
    cm.display.scrollbars.setScrollTop(val);
3791
    if (gecko) updateDisplaySimple(cm);
3792
    startWorker(cm, 100);
3793
  }
3794
  // Sync scroller and scrollbar, ensure the gutter elements are
3795
  // aligned.
3796
  function setScrollLeft(cm, val, isScroller) {
3797
    if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) return;
3798
    val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth);
3799
    cm.doc.scrollLeft = val;
3800
    alignHorizontally(cm);
3801
    if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val;
3802
    cm.display.scrollbars.setScrollLeft(val);
3803
  }
3804
3805
  // Since the delta values reported on mouse wheel events are
3806
  // unstandardized between browsers and even browser versions, and
3807
  // generally horribly unpredictable, this code starts by measuring
3808
  // the scroll effect that the first few mouse wheel events have,
3809
  // and, from that, detects the way it can convert deltas to pixel
3810
  // offsets afterwards.
3811
  //
3812
  // The reason we want to know the amount a wheel event will scroll
3813
  // is that it gives us a chance to update the display before the
3814
  // actual scrolling happens, reducing flickering.
3815
3816
  var wheelSamples = 0, wheelPixelsPerUnit = null;
3817
  // Fill in a browser-detected starting value on browsers where we
3818
  // know one. These don't have to be accurate -- the result of them
3819
  // being wrong would just be a slight flicker on the first wheel
3820
  // scroll (if it is large enough).
3821
  if (ie) wheelPixelsPerUnit = -.53;
3822
  else if (gecko) wheelPixelsPerUnit = 15;
3823
  else if (chrome) wheelPixelsPerUnit = -.7;
3824
  else if (safari) wheelPixelsPerUnit = -1/3;
3825
3826
  var wheelEventDelta = function(e) {
3827
    var dx = e.wheelDeltaX, dy = e.wheelDeltaY;
3828
    if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) dx = e.detail;
0 ignored issues
show
Best Practice introduced by
Comparing dx to null using the == operator is not safe. Consider using === instead.
Loading history...
3829
    if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) dy = e.detail;
0 ignored issues
show
Best Practice introduced by
Comparing dy to null using the == operator is not safe. Consider using === instead.
Loading history...
3830
    else if (dy == null) dy = e.wheelDelta;
3831
    return {x: dx, y: dy};
3832
  };
3833
  CodeMirror.wheelEventPixels = function(e) {
3834
    var delta = wheelEventDelta(e);
3835
    delta.x *= wheelPixelsPerUnit;
3836
    delta.y *= wheelPixelsPerUnit;
3837
    return delta;
3838
  };
3839
3840
  function onScrollWheel(cm, e) {
3841
    var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y;
3842
3843
    var display = cm.display, scroll = display.scroller;
3844
    // Quit if there's nothing to scroll here
3845
    if (!(dx && scroll.scrollWidth > scroll.clientWidth ||
3846
          dy && scroll.scrollHeight > scroll.clientHeight)) return;
3847
3848
    // Webkit browsers on OS X abort momentum scrolls when the target
3849
    // of the scroll event is removed from the scrollable element.
3850
    // This hack (see related code in patchDisplay) makes sure the
3851
    // element is kept around.
3852
    if (dy && mac && webkit) {
3853
      outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {
3854
        for (var i = 0; i < view.length; i++) {
3855
          if (view[i].node == cur) {
3856
            cm.display.currentWheelTarget = cur;
3857
            break outer;
3858
          }
3859
        }
3860
      }
3861
    }
3862
3863
    // On some browsers, horizontal scrolling will cause redraws to
3864
    // happen before the gutter has been realigned, causing it to
3865
    // wriggle around in a most unseemly way. When we have an
3866
    // estimated pixels/delta value, we just handle horizontal
3867
    // scrolling entirely here. It'll be slightly off from native, but
3868
    // better than glitching out.
3869
    if (dx && !gecko && !presto && wheelPixelsPerUnit != null) {
0 ignored issues
show
Best Practice introduced by
Comparing wheelPixelsPerUnit to null using the != operator is not safe. Consider using !== instead.
Loading history...
3870
      if (dy)
3871
        setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight)));
3872
      setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth)));
3873
      e_preventDefault(e);
3874
      display.wheelStartX = null; // Abort measurement, if in progress
3875
      return;
3876
    }
3877
3878
    // 'Project' the visible viewport to cover the area that is being
3879
    // scrolled into view (if we know enough to estimate it).
3880
    if (dy && wheelPixelsPerUnit != null) {
3881
      var pixels = dy * wheelPixelsPerUnit;
3882
      var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
3883
      if (pixels < 0) top = Math.max(0, top + pixels - 50);
3884
      else bot = Math.min(cm.doc.height, bot + pixels + 50);
3885
      updateDisplaySimple(cm, {top: top, bottom: bot});
3886
    }
3887
3888
    if (wheelSamples < 20) {
3889
      if (display.wheelStartX == null) {
0 ignored issues
show
Best Practice introduced by
Comparing display.wheelStartX to null using the == operator is not safe. Consider using === instead.
Loading history...
3890
        display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;
3891
        display.wheelDX = dx; display.wheelDY = dy;
3892
        setTimeout(function() {
3893
          if (display.wheelStartX == null) return;
0 ignored issues
show
Best Practice introduced by
Comparing display.wheelStartX to null using the == operator is not safe. Consider using === instead.
Loading history...
3894
          var movedX = scroll.scrollLeft - display.wheelStartX;
3895
          var movedY = scroll.scrollTop - display.wheelStartY;
3896
          var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
3897
            (movedX && display.wheelDX && movedX / display.wheelDX);
3898
          display.wheelStartX = display.wheelStartY = null;
3899
          if (!sample) return;
3900
          wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
3901
          ++wheelSamples;
3902
        }, 200);
3903
      } else {
3904
        display.wheelDX += dx; display.wheelDY += dy;
3905
      }
3906
    }
3907
  }
3908
3909
  // KEY EVENTS
3910
3911
  // Run a handler that was bound to a key.
3912
  function doHandleBinding(cm, bound, dropShift) {
3913
    if (typeof bound == "string") {
3914
      bound = commands[bound];
3915
      if (!bound) return false;
3916
    }
3917
    // Ensure previous input has been read, so that the handler sees a
3918
    // consistent view of the document
3919
    cm.display.input.ensurePolled();
3920
    var prevShift = cm.display.shift, done = false;
3921
    try {
3922
      if (isReadOnly(cm)) cm.state.suppressEdits = true;
3923
      if (dropShift) cm.display.shift = false;
3924
      done = bound(cm) != Pass;
3925
    } finally {
3926
      cm.display.shift = prevShift;
3927
      cm.state.suppressEdits = false;
3928
    }
3929
    return done;
3930
  }
3931
3932
  function lookupKeyForEditor(cm, name, handle) {
3933
    for (var i = 0; i < cm.state.keyMaps.length; i++) {
3934
      var result = lookupKey(name, cm.state.keyMaps[i], handle, cm);
3935
      if (result) return result;
3936
    }
3937
    return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm))
3938
      || lookupKey(name, cm.options.keyMap, handle, cm);
3939
  }
3940
3941
  var stopSeq = new Delayed;
3942
  function dispatchKey(cm, name, e, handle) {
3943
    var seq = cm.state.keySeq;
3944
    if (seq) {
3945
      if (isModifierKey(name)) return "handled";
3946
      stopSeq.set(50, function() {
3947
        if (cm.state.keySeq == seq) {
3948
          cm.state.keySeq = null;
3949
          cm.display.input.reset();
3950
        }
3951
      });
3952
      name = seq + " " + name;
3953
    }
3954
    var result = lookupKeyForEditor(cm, name, handle);
3955
3956
    if (result == "multi")
3957
      cm.state.keySeq = name;
3958
    if (result == "handled")
3959
      signalLater(cm, "keyHandled", cm, name, e);
3960
3961
    if (result == "handled" || result == "multi") {
3962
      e_preventDefault(e);
3963
      restartBlink(cm);
3964
    }
3965
3966
    if (seq && !result && /\'$/.test(name)) {
3967
      e_preventDefault(e);
3968
      return true;
3969
    }
3970
    return !!result;
3971
  }
3972
3973
  // Handle a key from the keydown event.
3974
  function handleKeyBinding(cm, e) {
3975
    var name = keyName(e, true);
3976
    if (!name) return false;
3977
3978
    if (e.shiftKey && !cm.state.keySeq) {
3979
      // First try to resolve full name (including 'Shift-'). Failing
3980
      // that, see if there is a cursor-motion command (starting with
3981
      // 'go') bound to the keyname without 'Shift-'.
3982
      return dispatchKey(cm, "Shift-" + name, e, function(b) {return doHandleBinding(cm, b, true);})
3983
          || dispatchKey(cm, name, e, function(b) {
3984
               if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if typeof b == "string" ? ...[A-Z].test(b): b.motion 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...
3985
                 return doHandleBinding(cm, b);
3986
             });
3987
    } else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
3988
      return dispatchKey(cm, name, e, function(b) { return doHandleBinding(cm, b); });
3989
    }
3990
  }
3991
3992
  // Handle a key from the keypress event
3993
  function handleCharBinding(cm, e, ch) {
3994
    return dispatchKey(cm, "'" + ch + "'", e,
3995
                       function(b) { return doHandleBinding(cm, b, true); });
3996
  }
3997
3998
  var lastStoppedKey = null;
3999
  function onKeyDown(e) {
4000
    var cm = this;
4001
    ensureFocus(cm);
4002
    if (signalDOMEvent(cm, e)) return;
4003
    // IE does strange things with escape.
4004
    if (ie && ie_version < 11 && e.keyCode == 27) e.returnValue = false;
4005
    var code = e.keyCode;
4006
    cm.display.shift = code == 16 || e.shiftKey;
4007
    var handled = handleKeyBinding(cm, e);
4008
    if (presto) {
4009
      lastStoppedKey = handled ? code : null;
4010
      // Opera has no cut event... we try to at least catch the key combo
4011
      if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
4012
        cm.replaceSelection("", null, "cut");
4013
    }
4014
4015
    // Turn mouse into crosshair when Alt is held on Mac.
4016
    if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className))
4017
      showCrossHair(cm);
4018
  }
4019
4020
  function showCrossHair(cm) {
4021
    var lineDiv = cm.display.lineDiv;
4022
    addClass(lineDiv, "CodeMirror-crosshair");
4023
4024
    function up(e) {
4025
      if (e.keyCode == 18 || !e.altKey) {
4026
        rmClass(lineDiv, "CodeMirror-crosshair");
4027
        off(document, "keyup", up);
4028
        off(document, "mouseover", up);
4029
      }
4030
    }
4031
    on(document, "keyup", up);
4032
    on(document, "mouseover", up);
4033
  }
4034
4035
  function onKeyUp(e) {
4036
    if (e.keyCode == 16) this.doc.sel.shift = false;
4037
    signalDOMEvent(this, e);
4038
  }
4039
4040
  function onKeyPress(e) {
4041
    var cm = this;
4042
    if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) return;
4043
    var keyCode = e.keyCode, charCode = e.charCode;
4044
    if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
4045
    if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) return;
4046
    var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
0 ignored issues
show
Best Practice introduced by
Comparing charCode to null using the == operator is not safe. Consider using === instead.
Loading history...
4047
    if (handleCharBinding(cm, e, ch)) return;
4048
    cm.display.input.onKeyPress(e);
4049
  }
4050
4051
  // FOCUS/BLUR EVENTS
4052
4053
  function onFocus(cm) {
4054
    if (cm.options.readOnly == "nocursor") return;
4055
    if (!cm.state.focused) {
4056
      signal(cm, "focus", cm);
4057
      cm.state.focused = true;
4058
      addClass(cm.display.wrapper, "CodeMirror-focused");
4059
      // This test prevents this from firing when a context
4060
      // menu is closed (since the input reset would kill the
4061
      // select-all detection hack)
4062
      if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {
4063
        cm.display.input.reset();
4064
        if (webkit) setTimeout(function() { cm.display.input.reset(true); }, 20); // Issue #1730
4065
      }
4066
      cm.display.input.receivedFocus();
4067
    }
4068
    restartBlink(cm);
4069
  }
4070
  function onBlur(cm) {
4071
    if (cm.state.focused) {
4072
      signal(cm, "blur", cm);
4073
      cm.state.focused = false;
4074
      rmClass(cm.display.wrapper, "CodeMirror-focused");
4075
    }
4076
    clearInterval(cm.display.blinker);
4077
    setTimeout(function() {if (!cm.state.focused) cm.display.shift = false;}, 150);
4078
  }
4079
4080
  // CONTEXT MENU HANDLING
4081
4082
  // To make the context menu work, we need to briefly unhide the
4083
  // textarea (making it as unobtrusive as possible) to let the
4084
  // right-click take effect on it.
4085
  function onContextMenu(cm, e) {
4086
    if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) return;
4087
    cm.display.input.onContextMenu(e);
4088
  }
4089
4090
  function contextMenuInGutter(cm, e) {
4091
    if (!hasHandler(cm, "gutterContextMenu")) return false;
4092
    return gutterEvent(cm, e, "gutterContextMenu", false, signal);
4093
  }
4094
4095
  // UPDATING
4096
4097
  // Compute the position of the end of a change (its 'to' property
4098
  // refers to the pre-change end).
4099
  var changeEnd = CodeMirror.changeEnd = function(change) {
4100
    if (!change.text) return change.to;
4101
    return Pos(change.from.line + change.text.length - 1,
4102
               lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0));
0 ignored issues
show
Best Practice introduced by
Comparing change.text.length to 1 using the == operator is not safe. Consider using === instead.
Loading history...
4103
  };
4104
4105
  // Adjust a position to refer to the post-change position of the
4106
  // same text, or the end of the change if the change covers it.
4107
  function adjustForChange(pos, change) {
4108
    if (cmp(pos, change.from) < 0) return pos;
4109
    if (cmp(pos, change.to) <= 0) return changeEnd(change);
4110
4111
    var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;
4112
    if (pos.line == change.to.line) ch += changeEnd(change).ch - change.to.ch;
4113
    return Pos(line, ch);
4114
  }
4115
4116
  function computeSelAfterChange(doc, change) {
4117
    var out = [];
4118
    for (var i = 0; i < doc.sel.ranges.length; i++) {
4119
      var range = doc.sel.ranges[i];
4120
      out.push(new Range(adjustForChange(range.anchor, change),
4121
                         adjustForChange(range.head, change)));
4122
    }
4123
    return normalizeSelection(out, doc.sel.primIndex);
4124
  }
4125
4126
  function offsetPos(pos, old, nw) {
4127
    if (pos.line == old.line)
4128
      return Pos(nw.line, pos.ch - old.ch + nw.ch);
4129
    else
4130
      return Pos(nw.line + (pos.line - old.line), pos.ch);
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
4131
  }
4132
4133
  // Used by replaceSelections to allow moving the selection to the
4134
  // start or around the replaced test. Hint may be "start" or "around".
4135
  function computeReplacedSel(doc, changes, hint) {
4136
    var out = [];
4137
    var oldPrev = Pos(doc.first, 0), newPrev = oldPrev;
4138
    for (var i = 0; i < changes.length; i++) {
4139
      var change = changes[i];
4140
      var from = offsetPos(change.from, oldPrev, newPrev);
4141
      var to = offsetPos(changeEnd(change), oldPrev, newPrev);
4142
      oldPrev = change.to;
4143
      newPrev = to;
4144
      if (hint == "around") {
4145
        var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0;
4146
        out[i] = new Range(inv ? to : from, inv ? from : to);
4147
      } else {
4148
        out[i] = new Range(from, from);
4149
      }
4150
    }
4151
    return new Selection(out, doc.sel.primIndex);
4152
  }
4153
4154
  // Allow "beforeChange" event handlers to influence a change
4155
  function filterChange(doc, change, update) {
4156
    var obj = {
4157
      canceled: false,
4158
      from: change.from,
4159
      to: change.to,
4160
      text: change.text,
4161
      origin: change.origin,
4162
      cancel: function() { this.canceled = true; }
4163
    };
4164
    if (update) obj.update = function(from, to, text, origin) {
4165
      if (from) this.from = clipPos(doc, from);
4166
      if (to) this.to = clipPos(doc, to);
4167
      if (text) this.text = text;
4168
      if (origin !== undefined) this.origin = origin;
4169
    };
4170
    signal(doc, "beforeChange", doc, obj);
4171
    if (doc.cm) signal(doc.cm, "beforeChange", doc.cm, obj);
4172
4173
    if (obj.canceled) return null;
4174
    return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin};
4175
  }
4176
4177
  // Apply a change to a document, and add it to the document's
4178
  // history, and propagating it to all linked documents.
4179
  function makeChange(doc, change, ignoreReadOnly) {
4180
    if (doc.cm) {
4181
      if (!doc.cm.curOp) return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly);
4182
      if (doc.cm.state.suppressEdits) 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...
4183
    }
4184
4185
    if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
4186
      change = filterChange(doc, change, true);
4187
      if (!change) 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...
4188
    }
4189
4190
    // Possibly split or suppress the update based on the presence
4191
    // of read-only spans in its range.
4192
    var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);
4193
    if (split) {
4194
      for (var i = split.length - 1; i >= 0; --i)
4195
        makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text});
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...
4196
    } else {
4197
      makeChangeInner(doc, change);
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...
4198
    }
4199
  }
4200
4201
  function makeChangeInner(doc, change) {
4202
    if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) return;
0 ignored issues
show
Best Practice introduced by
Comparing cmp(change.from, change.to) to 0 using the == operator is not safe. Consider using === instead.
Loading history...
Best Practice introduced by
Comparing change.text.length to 1 using the == operator is not safe. Consider using === instead.
Loading history...
4203
    var selAfter = computeSelAfterChange(doc, change);
4204
    addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);
4205
4206
    makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));
4207
    var rebased = [];
4208
4209
    linkedDocs(doc, function(doc, sharedHist) {
4210
      if (!sharedHist && indexOf(rebased, doc.history) == -1) {
4211
        rebaseHist(doc.history, change);
4212
        rebased.push(doc.history);
4213
      }
4214
      makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change));
4215
    });
4216
  }
4217
4218
  // Revert a change stored in a document's history.
4219
  function makeChangeFromHistory(doc, type, allowSelectionOnly) {
4220
    if (doc.cm && doc.cm.state.suppressEdits) return;
4221
4222
    var hist = doc.history, event, selAfter = doc.sel;
4223
    var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done;
4224
4225
    // Verify that there is a useable event (so that ctrl-z won't
4226
    // needlessly clear selection events)
4227
    for (var i = 0; i < source.length; i++) {
4228
      event = source[i];
4229
      if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges)
4230
        break;
4231
    }
4232
    if (i == source.length) return;
4233
    hist.lastOrigin = hist.lastSelOrigin = null;
4234
4235
    for (;;) {
4236
      event = source.pop();
4237
      if (event.ranges) {
4238
        pushSelectionToHistory(event, dest);
4239
        if (allowSelectionOnly && !event.equals(doc.sel)) {
4240
          setSelection(doc, event, {clearRedo: false});
4241
          return;
4242
        }
4243
        selAfter = event;
4244
      }
4245
      else break;
4246
    }
4247
4248
    // Build up a reverse change object to add to the opposite history
4249
    // stack (redo when undoing, and vice versa).
4250
    var antiChanges = [];
4251
    pushSelectionToHistory(selAfter, dest);
4252
    dest.push({changes: antiChanges, generation: hist.generation});
4253
    hist.generation = event.generation || ++hist.maxGeneration;
4254
4255
    var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange");
4256
4257
    for (var i = event.changes.length - 1; i >= 0; --i) {
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 4227. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
4258
      var change = event.changes[i];
4259
      change.origin = type;
4260
      if (filter && !filterChange(doc, change, false)) {
4261
        source.length = 0;
4262
        return;
4263
      }
4264
4265
      antiChanges.push(historyChangeFromChange(doc, change));
4266
4267
      var after = i ? computeSelAfterChange(doc, change) : lst(source);
4268
      makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
4269
      if (!i && doc.cm) doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)});
4270
      var rebased = [];
4271
4272
      // Propagate to the linked documents
4273
      linkedDocs(doc, function(doc, sharedHist) {
4274
        if (!sharedHist && indexOf(rebased, doc.history) == -1) {
0 ignored issues
show
Bug introduced by
The variable rebased is changed as part of the for loop for example by [] on line 4270. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
4275
          rebaseHist(doc.history, change);
0 ignored issues
show
Bug introduced by
The variable change is changed as part of the for loop for example by event.changes.i on line 4258. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
4276
          rebased.push(doc.history);
4277
        }
4278
        makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change));
4279
      });
4280
    }
4281
  }
4282
4283
  // Sub-views need their line numbers shifted when text is added
4284
  // above or below them in the parent document.
4285
  function shiftDoc(doc, distance) {
4286
    if (distance == 0) return;
0 ignored issues
show
Best Practice introduced by
Comparing distance to 0 using the == operator is not safe. Consider using === instead.
Loading history...
4287
    doc.first += distance;
4288
    doc.sel = new Selection(map(doc.sel.ranges, function(range) {
4289
      return new Range(Pos(range.anchor.line + distance, range.anchor.ch),
4290
                       Pos(range.head.line + distance, range.head.ch));
4291
    }), doc.sel.primIndex);
4292
    if (doc.cm) {
4293
      regChange(doc.cm, doc.first, doc.first - distance, distance);
4294
      for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++)
4295
        regLineChange(doc.cm, l, "gutter");
4296
    }
4297
  }
4298
4299
  // More lower-level change function, handling only a single document
4300
  // (not linked ones).
4301
  function makeChangeSingleDoc(doc, change, selAfter, spans) {
4302
    if (doc.cm && !doc.cm.curOp)
4303
      return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans);
4304
4305
    if (change.to.line < doc.first) {
4306
      shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line));
4307
      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...
4308
    }
4309
    if (change.from.line > doc.lastLine()) 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...
4310
4311
    // Clip the change to the size of this doc
4312
    if (change.from.line < doc.first) {
4313
      var shift = change.text.length - 1 - (doc.first - change.from.line);
4314
      shiftDoc(doc, shift);
4315
      change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
4316
                text: [lst(change.text)], origin: change.origin};
4317
    }
4318
    var last = doc.lastLine();
4319
    if (change.to.line > last) {
4320
      change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
4321
                text: [change.text[0]], origin: change.origin};
4322
    }
4323
4324
    change.removed = getBetween(doc, change.from, change.to);
4325
4326
    if (!selAfter) selAfter = computeSelAfterChange(doc, change);
4327
    if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans);
4328
    else updateDoc(doc, change, spans);
4329
    setSelectionNoUndo(doc, selAfter, sel_dontScroll);
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...
4330
  }
4331
4332
  // Handle the interaction of a change to a document with the editor
4333
  // that this document is part of.
4334
  function makeChangeSingleDocInEditor(cm, change, spans) {
4335
    var doc = cm.doc, display = cm.display, from = change.from, to = change.to;
4336
4337
    var recomputeMaxLength = false, checkWidthStart = from.line;
4338
    if (!cm.options.lineWrapping) {
4339
      checkWidthStart = lineNo(visualLine(getLine(doc, from.line)));
4340
      doc.iter(checkWidthStart, to.line + 1, function(line) {
4341
        if (line == display.maxLine) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if line == display.maxLine 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...
4342
          recomputeMaxLength = true;
4343
          return true;
4344
        }
4345
      });
4346
    }
4347
4348
    if (doc.sel.contains(change.from, change.to) > -1)
4349
      signalCursorActivity(cm);
4350
4351
    updateDoc(doc, change, spans, estimateHeight(cm));
4352
4353
    if (!cm.options.lineWrapping) {
4354
      doc.iter(checkWidthStart, from.line + change.text.length, function(line) {
4355
        var len = lineLength(line);
4356
        if (len > display.maxLineLength) {
4357
          display.maxLine = line;
4358
          display.maxLineLength = len;
4359
          display.maxLineChanged = true;
4360
          recomputeMaxLength = false;
4361
        }
4362
      });
4363
      if (recomputeMaxLength) cm.curOp.updateMaxLine = true;
4364
    }
4365
4366
    // Adjust frontier, schedule worker
4367
    doc.frontier = Math.min(doc.frontier, from.line);
4368
    startWorker(cm, 400);
4369
4370
    var lendiff = change.text.length - (to.line - from.line) - 1;
4371
    // Remember that these lines changed, for updating the display
4372
    if (change.full)
4373
      regChange(cm);
4374
    else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change))
0 ignored issues
show
Best Practice introduced by
Comparing change.text.length to 1 using the == operator is not safe. Consider using === instead.
Loading history...
4375
      regLineChange(cm, from.line, "text");
4376
    else
4377
      regChange(cm, from.line, to.line + 1, lendiff);
4378
4379
    var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change");
4380
    if (changeHandler || changesHandler) {
4381
      var obj = {
4382
        from: from, to: to,
4383
        text: change.text,
4384
        removed: change.removed,
4385
        origin: change.origin
4386
      };
4387
      if (changeHandler) signalLater(cm, "change", cm, obj);
4388
      if (changesHandler) (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj);
4389
    }
4390
    cm.display.selForContextMenu = null;
4391
  }
4392
4393
  function replaceRange(doc, code, from, to, origin) {
4394
    if (!to) to = from;
4395
    if (cmp(to, from) < 0) { var tmp = to; to = from; from = tmp; }
4396
    if (typeof code == "string") code = splitLines(code);
4397
    makeChange(doc, {from: from, to: to, text: code, origin: origin});
4398
  }
4399
4400
  // SCROLLING THINGS INTO VIEW
4401
4402
  // If an editor sits on the top or bottom of the window, partially
4403
  // scrolled out of view, this ensures that the cursor is visible.
4404
  function maybeScrollWindow(cm, coords) {
4405
    if (signalDOMEvent(cm, "scrollCursorIntoView")) return;
4406
4407
    var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;
4408
    if (coords.top + box.top < 0) doScroll = true;
4409
    else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
4410
    if (doScroll != null && !phantom) {
0 ignored issues
show
Best Practice introduced by
Comparing doScroll to null using the != operator is not safe. Consider using !== instead.
Loading history...
4411
      var scrollNode = elt("div", "\u200b", null, "position: absolute; top: " +
4412
                           (coords.top - display.viewOffset - paddingTop(cm.display)) + "px; height: " +
4413
                           (coords.bottom - coords.top + scrollGap(cm) + display.barHeight) + "px; left: " +
4414
                           coords.left + "px; width: 2px;");
4415
      cm.display.lineSpace.appendChild(scrollNode);
4416
      scrollNode.scrollIntoView(doScroll);
4417
      cm.display.lineSpace.removeChild(scrollNode);
4418
    }
4419
  }
4420
4421
  // Scroll a given position into view (immediately), verifying that
4422
  // it actually became visible (as line heights are accurately
4423
  // measured, the position of something may 'drift' during drawing).
4424
  function scrollPosIntoView(cm, pos, end, margin) {
4425
    if (margin == null) margin = 0;
0 ignored issues
show
Best Practice introduced by
Comparing margin to null using the == operator is not safe. Consider using === instead.
Loading history...
4426
    for (var limit = 0; limit < 5; limit++) {
4427
      var changed = false, coords = cursorCoords(cm, pos);
4428
      var endCoords = !end || end == pos ? coords : cursorCoords(cm, end);
4429
      var scrollPos = calculateScrollPos(cm, Math.min(coords.left, endCoords.left),
4430
                                         Math.min(coords.top, endCoords.top) - margin,
4431
                                         Math.max(coords.left, endCoords.left),
4432
                                         Math.max(coords.bottom, endCoords.bottom) + margin);
4433
      var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
4434
      if (scrollPos.scrollTop != null) {
0 ignored issues
show
Best Practice introduced by
Comparing scrollPos.scrollTop to null using the != operator is not safe. Consider using !== instead.
Loading history...
4435
        setScrollTop(cm, scrollPos.scrollTop);
4436
        if (Math.abs(cm.doc.scrollTop - startTop) > 1) changed = true;
4437
      }
4438
      if (scrollPos.scrollLeft != null) {
0 ignored issues
show
Best Practice introduced by
Comparing scrollPos.scrollLeft to null using the != operator is not safe. Consider using !== instead.
Loading history...
4439
        setScrollLeft(cm, scrollPos.scrollLeft);
4440
        if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) changed = true;
4441
      }
4442
      if (!changed) break;
4443
    }
4444
    return coords;
0 ignored issues
show
Bug introduced by
The variable coords seems to not be initialized for all possible execution paths.
Loading history...
4445
  }
4446
4447
  // Scroll a given set of coordinates into view (immediately).
4448
  function scrollIntoView(cm, x1, y1, x2, y2) {
4449
    var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2);
4450
    if (scrollPos.scrollTop != null) setScrollTop(cm, scrollPos.scrollTop);
0 ignored issues
show
Best Practice introduced by
Comparing scrollPos.scrollTop to null using the != operator is not safe. Consider using !== instead.
Loading history...
4451
    if (scrollPos.scrollLeft != null) setScrollLeft(cm, scrollPos.scrollLeft);
0 ignored issues
show
Best Practice introduced by
Comparing scrollPos.scrollLeft to null using the != operator is not safe. Consider using !== instead.
Loading history...
4452
  }
4453
4454
  // Calculate a new scroll position needed to scroll the given
4455
  // rectangle into view. Returns an object with scrollTop and
4456
  // scrollLeft properties. When these are undefined, the
4457
  // vertical/horizontal position does not need to be adjusted.
4458
  function calculateScrollPos(cm, x1, y1, x2, y2) {
4459
    var display = cm.display, snapMargin = textHeight(cm.display);
4460
    if (y1 < 0) y1 = 0;
4461
    var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop;
0 ignored issues
show
Best Practice introduced by
Comparing cm.curOp.scrollTop to null using the != operator is not safe. Consider using !== instead.
Loading history...
4462
    var screen = displayHeight(cm), result = {};
4463
    if (y2 - y1 > screen) y2 = y1 + screen;
4464
    var docBottom = cm.doc.height + paddingVert(display);
4465
    var atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin;
4466
    if (y1 < screentop) {
4467
      result.scrollTop = atTop ? 0 : y1;
4468
    } else if (y2 > screentop + screen) {
4469
      var newTop = Math.min(y1, (atBottom ? docBottom : y2) - screen);
4470
      if (newTop != screentop) result.scrollTop = newTop;
4471
    }
4472
4473
    var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft;
0 ignored issues
show
Best Practice introduced by
Comparing cm.curOp.scrollLeft to null using the != operator is not safe. Consider using !== instead.
Loading history...
4474
    var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0);
4475
    var tooWide = x2 - x1 > screenw;
4476
    if (tooWide) x2 = x1 + screenw;
4477
    if (x1 < 10)
4478
      result.scrollLeft = 0;
4479
    else if (x1 < screenleft)
4480
      result.scrollLeft = Math.max(0, x1 - (tooWide ? 0 : 10));
4481
    else if (x2 > screenw + screenleft - 3)
4482
      result.scrollLeft = x2 + (tooWide ? 0 : 10) - screenw;
4483
    return result;
4484
  }
4485
4486
  // Store a relative adjustment to the scroll position in the current
4487
  // operation (to be applied when the operation finishes).
4488
  function addToScrollPos(cm, left, top) {
4489
    if (left != null || top != null) resolveScrollToPos(cm);
0 ignored issues
show
Best Practice introduced by
Comparing top to null using the != operator is not safe. Consider using !== instead.
Loading history...
Best Practice introduced by
Comparing left to null using the != operator is not safe. Consider using !== instead.
Loading history...
4490
    if (left != null)
4491
      cm.curOp.scrollLeft = (cm.curOp.scrollLeft == null ? cm.doc.scrollLeft : cm.curOp.scrollLeft) + left;
0 ignored issues
show
Best Practice introduced by
Comparing cm.curOp.scrollLeft to null using the == operator is not safe. Consider using === instead.
Loading history...
4492
    if (top != null)
4493
      cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top;
0 ignored issues
show
Best Practice introduced by
Comparing cm.curOp.scrollTop to null using the == operator is not safe. Consider using === instead.
Loading history...
4494
  }
4495
4496
  // Make sure that at the end of the operation the current cursor is
4497
  // shown.
4498
  function ensureCursorVisible(cm) {
4499
    resolveScrollToPos(cm);
4500
    var cur = cm.getCursor(), from = cur, to = cur;
4501
    if (!cm.options.lineWrapping) {
4502
      from = cur.ch ? Pos(cur.line, cur.ch - 1) : cur;
4503
      to = Pos(cur.line, cur.ch + 1);
4504
    }
4505
    cm.curOp.scrollToPos = {from: from, to: to, margin: cm.options.cursorScrollMargin, isCursor: true};
4506
  }
4507
4508
  // When an operation has its scrollToPos property set, and another
4509
  // scroll action is applied before the end of the operation, this
4510
  // 'simulates' scrolling that position into view in a cheap way, so
4511
  // that the effect of intermediate scroll commands is not ignored.
4512
  function resolveScrollToPos(cm) {
4513
    var range = cm.curOp.scrollToPos;
4514
    if (range) {
4515
      cm.curOp.scrollToPos = null;
4516
      var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to);
4517
      var sPos = calculateScrollPos(cm, Math.min(from.left, to.left),
4518
                                    Math.min(from.top, to.top) - range.margin,
4519
                                    Math.max(from.right, to.right),
4520
                                    Math.max(from.bottom, to.bottom) + range.margin);
4521
      cm.scrollTo(sPos.scrollLeft, sPos.scrollTop);
4522
    }
4523
  }
4524
4525
  // API UTILITIES
4526
4527
  // Indent the given line. The how parameter can be "smart",
4528
  // "add"/null, "subtract", or "prev". When aggressive is false
4529
  // (typically set to true for forced single-line indents), empty
4530
  // lines are not indented, and places where the mode returns Pass
4531
  // are left alone.
4532
  function indentLine(cm, n, how, aggressive) {
4533
    var doc = cm.doc, state;
4534
    if (how == null) how = "add";
0 ignored issues
show
Best Practice introduced by
Comparing how to null using the == operator is not safe. Consider using === instead.
Loading history...
4535
    if (how == "smart") {
4536
      // Fall back to "prev" when the mode doesn't have an indentation
4537
      // method.
4538
      if (!doc.mode.indent) how = "prev";
4539
      else state = getStateBefore(cm, n);
4540
    }
4541
4542
    var tabSize = cm.options.tabSize;
4543
    var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
4544
    if (line.stateAfter) line.stateAfter = null;
4545
    var curSpaceString = line.text.match(/^\s*/)[0], indentation;
4546
    if (!aggressive && !/\S/.test(line.text)) {
4547
      indentation = 0;
4548
      how = "not";
4549
    } else if (how == "smart") {
4550
      indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
0 ignored issues
show
Bug introduced by
The variable state seems to not be initialized for all possible execution paths. Are you sure indent handles undefined variables?
Loading history...
4551
      if (indentation == Pass || indentation > 150) {
4552
        if (!aggressive) return;
4553
        how = "prev";
4554
      }
4555
    }
4556
    if (how == "prev") {
4557
      if (n > doc.first) indentation = countColumn(getLine(doc, n-1).text, null, tabSize);
4558
      else indentation = 0;
4559
    } else if (how == "add") {
4560
      indentation = curSpace + cm.options.indentUnit;
4561
    } else if (how == "subtract") {
4562
      indentation = curSpace - cm.options.indentUnit;
4563
    } else if (typeof how == "number") {
4564
      indentation = curSpace + how;
4565
    }
4566
    indentation = Math.max(0, indentation);
0 ignored issues
show
Bug introduced by
The variable indentation does not seem to be initialized in case how == "smart" on line 4549 is false. Are you sure the function max handles undefined variables?
Loading history...
4567
4568
    var indentString = "", pos = 0;
4569
    if (cm.options.indentWithTabs)
4570
      for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";}
4571
    if (pos < indentation) indentString += spaceStr(indentation - pos);
4572
4573
    if (indentString != curSpaceString) {
4574
      replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
4575
    } else {
4576
      // Ensure that, if the cursor was in the whitespace at the start
4577
      // of the line, it is moved to the end of that space.
4578
      for (var i = 0; i < doc.sel.ranges.length; i++) {
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 4570. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
4579
        var range = doc.sel.ranges[i];
4580
        if (range.head.line == n && range.head.ch < curSpaceString.length) {
4581
          var pos = Pos(n, curSpaceString.length);
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable pos already seems to be declared on line 4568. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
4582
          replaceOneSelection(doc, i, new Range(pos, pos));
4583
          break;
4584
        }
4585
      }
4586
    }
4587
    line.stateAfter = null;
4588
  }
4589
4590
  // Utility for applying a change to a line by handle or number,
4591
  // returning the number and optionally registering the line as
4592
  // changed.
4593
  function changeLine(doc, handle, changeType, op) {
4594
    var no = handle, line = handle;
4595
    if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle));
4596
    else no = lineNo(handle);
4597
    if (no == null) return null;
0 ignored issues
show
Best Practice introduced by
Comparing no to null using the == operator is not safe. Consider using === instead.
Loading history...
4598
    if (op(line, no) && doc.cm) regLineChange(doc.cm, no, changeType);
4599
    return line;
4600
  }
4601
4602
  // Helper for deleting text near the selection(s), used to implement
4603
  // backspace, delete, and similar functionality.
4604
  function deleteNearSelection(cm, compute) {
4605
    var ranges = cm.doc.sel.ranges, kill = [];
4606
    // Build up a set of ranges to kill first, merging overlapping
4607
    // ranges.
4608
    for (var i = 0; i < ranges.length; i++) {
4609
      var toKill = compute(ranges[i]);
4610
      while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {
4611
        var replaced = kill.pop();
4612
        if (cmp(replaced.from, toKill.from) < 0) {
4613
          toKill.from = replaced.from;
4614
          break;
4615
        }
4616
      }
4617
      kill.push(toKill);
4618
    }
4619
    // Next, remove those actual ranges.
4620
    runInOp(cm, function() {
4621
      for (var i = kill.length - 1; i >= 0; i--)
4622
        replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete");
4623
      ensureCursorVisible(cm);
4624
    });
4625
  }
4626
4627
  // Used for horizontal relative motion. Dir is -1 or 1 (left or
4628
  // right), unit can be "char", "column" (like char, but doesn't
4629
  // cross line boundaries), "word" (across next word), or "group" (to
4630
  // the start of next group of word or non-word-non-whitespace
4631
  // chars). The visually param controls whether, in right-to-left
4632
  // text, direction 1 means to move towards the next index in the
4633
  // string, or towards the character to the right of the current
4634
  // position. The resulting position will have a hitSide=true
4635
  // property if it reached the end of the document.
4636
  function findPosH(doc, pos, dir, unit, visually) {
4637
    var line = pos.line, ch = pos.ch, origDir = dir;
4638
    var lineObj = getLine(doc, line);
4639
    var possible = true;
4640
    function findNextLine() {
4641
      var l = line + dir;
4642
      if (l < doc.first || l >= doc.first + doc.size) return (possible = false);
4643
      line = l;
4644
      return lineObj = getLine(doc, l);
4645
    }
4646
    function moveOnce(boundToLine) {
4647
      var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, true);
4648
      if (next == null) {
0 ignored issues
show
Best Practice introduced by
Comparing next to null using the == operator is not safe. Consider using === instead.
Loading history...
4649
        if (!boundToLine && findNextLine()) {
4650
          if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj);
4651
          else ch = dir < 0 ? lineObj.text.length : 0;
4652
        } else return (possible = false);
4653
      } else ch = next;
4654
      return true;
4655
    }
4656
4657
    if (unit == "char") moveOnce();
4658
    else if (unit == "column") moveOnce(true);
4659
    else if (unit == "word" || unit == "group") {
4660
      var sawType = null, group = unit == "group";
4661
      var helper = doc.cm && doc.cm.getHelper(pos, "wordChars");
4662
      for (var first = true;; first = false) {
4663
        if (dir < 0 && !moveOnce(!first)) break;
4664
        var cur = lineObj.text.charAt(ch) || "\n";
4665
        var type = isWordChar(cur, helper) ? "w"
4666
          : group && cur == "\n" ? "n"
4667
          : !group || /\s/.test(cur) ? null
4668
          : "p";
4669
        if (group && !first && !type) type = "s";
4670
        if (sawType && sawType != type) {
4671
          if (dir < 0) {dir = 1; moveOnce();}
4672
          break;
4673
        }
4674
4675
        if (type) sawType = type;
4676
        if (dir > 0 && !moveOnce(!first)) break;
4677
      }
4678
    }
4679
    var result = skipAtomic(doc, Pos(line, ch), origDir, true);
4680
    if (!possible) result.hitSide = true;
4681
    return result;
4682
  }
4683
4684
  // For relative vertical movement. Dir may be -1 or 1. Unit can be
4685
  // "page" or "line". The resulting position will have a hitSide=true
4686
  // property if it reached the end of the document.
4687
  function findPosV(cm, pos, dir, unit) {
4688
    var doc = cm.doc, x = pos.left, y;
4689
    if (unit == "page") {
4690
      var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
4691
      y = pos.top + dir * (pageSize - (dir < 0 ? 1.5 : .5) * textHeight(cm.display));
4692
    } else if (unit == "line") {
4693
      y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
4694
    }
4695
    for (;;) {
4696
      var target = coordsChar(cm, x, y);
0 ignored issues
show
Bug introduced by
The variable y does not seem to be initialized in case unit == "line" on line 4692 is false. Are you sure the function coordsChar handles undefined variables?
Loading history...
4697
      if (!target.outside) break;
4698
      if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break; }
4699
      y += dir * 5;
4700
    }
4701
    return target;
4702
  }
4703
4704
  // EDITOR METHODS
4705
4706
  // The publicly visible API. Note that methodOp(f) means
4707
  // 'wrap f in an operation, performed on its `this` parameter'.
4708
4709
  // This is not the complete set of editor methods. Most of the
4710
  // methods defined on the Doc type are also injected into
4711
  // CodeMirror.prototype, for backwards compatibility and
4712
  // convenience.
4713
4714
  CodeMirror.prototype = {
4715
    constructor: CodeMirror,
4716
    focus: function(){window.focus(); this.display.input.focus();},
4717
4718
    setOption: function(option, value) {
4719
      var options = this.options, old = options[option];
4720
      if (options[option] == value && option != "mode") return;
4721
      options[option] = value;
4722
      if (optionHandlers.hasOwnProperty(option))
4723
        operation(this, optionHandlers[option])(this, value, old);
4724
    },
4725
4726
    getOption: function(option) {return this.options[option];},
4727
    getDoc: function() {return this.doc;},
4728
4729
    addKeyMap: function(map, bottom) {
4730
      this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map));
4731
    },
4732
    removeKeyMap: function(map) {
4733
      var maps = this.state.keyMaps;
4734
      for (var i = 0; i < maps.length; ++i)
4735
        if (maps[i] == map || maps[i].name == map) {
4736
          maps.splice(i, 1);
4737
          return true;
4738
        }
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...
4739
    },
4740
4741
    addOverlay: methodOp(function(spec, options) {
4742
      var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
4743
      if (mode.startState) throw new Error("Overlays may not be stateful.");
4744
      this.state.overlays.push({mode: mode, modeSpec: spec, opaque: options && options.opaque});
4745
      this.state.modeGen++;
4746
      regChange(this);
4747
    }),
4748
    removeOverlay: methodOp(function(spec) {
4749
      var overlays = this.state.overlays;
4750
      for (var i = 0; i < overlays.length; ++i) {
4751
        var cur = overlays[i].modeSpec;
4752
        if (cur == spec || typeof spec == "string" && cur.name == spec) {
4753
          overlays.splice(i, 1);
4754
          this.state.modeGen++;
4755
          regChange(this);
4756
          return;
4757
        }
4758
      }
4759
    }),
4760
4761
    indentLine: methodOp(function(n, dir, aggressive) {
4762
      if (typeof dir != "string" && typeof dir != "number") {
4763
        if (dir == null) dir = this.options.smartIndent ? "smart" : "prev";
0 ignored issues
show
Best Practice introduced by
Comparing dir to null using the == operator is not safe. Consider using === instead.
Loading history...
4764
        else dir = dir ? "add" : "subtract";
4765
      }
4766
      if (isLine(this.doc, n)) indentLine(this, n, dir, aggressive);
4767
    }),
4768
    indentSelection: methodOp(function(how) {
4769
      var ranges = this.doc.sel.ranges, end = -1;
4770
      for (var i = 0; i < ranges.length; i++) {
4771
        var range = ranges[i];
4772
        if (!range.empty()) {
4773
          var from = range.from(), to = range.to();
4774
          var start = Math.max(end, from.line);
4775
          end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1;
4776
          for (var j = start; j < end; ++j)
4777
            indentLine(this, j, how);
4778
          var newRanges = this.doc.sel.ranges;
4779
          if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0)
0 ignored issues
show
Best Practice introduced by
Comparing from.ch to 0 using the == operator is not safe. Consider using === instead.
Loading history...
4780
            replaceOneSelection(this.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll);
4781
        } else if (range.head.line > end) {
4782
          indentLine(this, range.head.line, how, true);
4783
          end = range.head.line;
4784
          if (i == this.doc.sel.primIndex) ensureCursorVisible(this);
4785
        }
4786
      }
4787
    }),
4788
4789
    // Fetch the parser token for a given character. Useful for hacks
4790
    // that want to inspect the mode state (say, for completion).
4791
    getTokenAt: function(pos, precise) {
4792
      return takeToken(this, pos, precise);
4793
    },
4794
4795
    getLineTokens: function(line, precise) {
4796
      return takeToken(this, Pos(line), precise, true);
4797
    },
4798
4799
    getTokenTypeAt: function(pos) {
4800
      pos = clipPos(this.doc, pos);
4801
      var styles = getLineStyles(this, getLine(this.doc, pos.line));
4802
      var before = 0, after = (styles.length - 1) / 2, ch = pos.ch;
4803
      var type;
4804
      if (ch == 0) type = styles[2];
0 ignored issues
show
Best Practice introduced by
Comparing ch to 0 using the == operator is not safe. Consider using === instead.
Loading history...
4805
      else for (;;) {
4806
        var mid = (before + after) >> 1;
4807
        if ((mid ? styles[mid * 2 - 1] : 0) >= ch) after = mid;
4808
        else if (styles[mid * 2 + 1] < ch) before = mid + 1;
4809
        else { type = styles[mid * 2 + 2]; break; }
4810
      }
4811
      var cut = type ? type.indexOf("cm-overlay ") : -1;
4812
      return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1);
0 ignored issues
show
Best Practice introduced by
Comparing cut to 0 using the == operator is not safe. Consider using === instead.
Loading history...
4813
    },
4814
4815
    getModeAt: function(pos) {
4816
      var mode = this.doc.mode;
4817
      if (!mode.innerMode) return mode;
4818
      return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode;
4819
    },
4820
4821
    getHelper: function(pos, type) {
4822
      return this.getHelpers(pos, type)[0];
4823
    },
4824
4825
    getHelpers: function(pos, type) {
4826
      var found = [];
4827
      if (!helpers.hasOwnProperty(type)) return helpers;
4828
      var help = helpers[type], mode = this.getModeAt(pos);
4829
      if (typeof mode[type] == "string") {
4830
        if (help[mode[type]]) found.push(help[mode[type]]);
4831
      } else if (mode[type]) {
4832
        for (var i = 0; i < mode[type].length; i++) {
4833
          var val = help[mode[type][i]];
4834
          if (val) found.push(val);
4835
        }
4836
      } else if (mode.helperType && help[mode.helperType]) {
4837
        found.push(help[mode.helperType]);
4838
      } else if (help[mode.name]) {
4839
        found.push(help[mode.name]);
4840
      }
4841
      for (var i = 0; i < help._global.length; i++) {
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 4832. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
4842
        var cur = help._global[i];
4843
        if (cur.pred(mode, this) && indexOf(found, cur.val) == -1)
4844
          found.push(cur.val);
4845
      }
4846
      return found;
4847
    },
4848
4849
    getStateAfter: function(line, precise) {
4850
      var doc = this.doc;
4851
      line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);
0 ignored issues
show
Best Practice introduced by
Comparing line to null using the == operator is not safe. Consider using === instead.
Loading history...
4852
      return getStateBefore(this, line + 1, precise);
4853
    },
4854
4855
    cursorCoords: function(start, mode) {
4856
      var pos, range = this.doc.sel.primary();
4857
      if (start == null) pos = range.head;
0 ignored issues
show
Best Practice introduced by
Comparing start to null using the == operator is not safe. Consider using === instead.
Loading history...
4858
      else if (typeof start == "object") pos = clipPos(this.doc, start);
4859
      else pos = start ? range.from() : range.to();
4860
      return cursorCoords(this, pos, mode || "page");
4861
    },
4862
4863
    charCoords: function(pos, mode) {
4864
      return charCoords(this, clipPos(this.doc, pos), mode || "page");
4865
    },
4866
4867
    coordsChar: function(coords, mode) {
4868
      coords = fromCoordSystem(this, coords, mode || "page");
4869
      return coordsChar(this, coords.left, coords.top);
4870
    },
4871
4872
    lineAtHeight: function(height, mode) {
4873
      height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top;
4874
      return lineAtHeight(this.doc, height + this.display.viewOffset);
4875
    },
4876
    heightAtLine: function(line, mode) {
4877
      var end = false, last = this.doc.first + this.doc.size - 1;
4878
      if (line < this.doc.first) line = this.doc.first;
4879
      else if (line > last) { line = last; end = true; }
4880
      var lineObj = getLine(this.doc, line);
4881
      return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page").top +
4882
        (end ? this.doc.height - heightAtLine(lineObj) : 0);
4883
    },
4884
4885
    defaultTextHeight: function() { return textHeight(this.display); },
4886
    defaultCharWidth: function() { return charWidth(this.display); },
4887
4888
    setGutterMarker: methodOp(function(line, gutterID, value) {
4889
      return changeLine(this.doc, line, "gutter", function(line) {
4890
        var markers = line.gutterMarkers || (line.gutterMarkers = {});
4891
        markers[gutterID] = value;
4892
        if (!value && isEmpty(markers)) line.gutterMarkers = null;
4893
        return true;
4894
      });
4895
    }),
4896
4897
    clearGutter: methodOp(function(gutterID) {
4898
      var cm = this, doc = cm.doc, i = doc.first;
4899
      doc.iter(function(line) {
4900
        if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
4901
          line.gutterMarkers[gutterID] = null;
4902
          regLineChange(cm, i, "gutter");
4903
          if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null;
4904
        }
4905
        ++i;
4906
      });
4907
    }),
4908
4909
    addLineWidget: methodOp(function(handle, node, options) {
4910
      return addLineWidget(this, handle, node, options);
4911
    }),
4912
4913
    removeLineWidget: function(widget) { widget.clear(); },
4914
4915
    lineInfo: function(line) {
4916
      if (typeof line == "number") {
4917
        if (!isLine(this.doc, line)) return null;
4918
        var n = line;
4919
        line = getLine(this.doc, line);
4920
        if (!line) return null;
4921
      } else {
4922
        var n = lineNo(line);
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable n already seems to be declared on line 4918. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
4923
        if (n == null) return null;
0 ignored issues
show
Best Practice introduced by
Comparing n to null using the == operator is not safe. Consider using === instead.
Loading history...
4924
      }
4925
      return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
4926
              textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
4927
              widgets: line.widgets};
4928
    },
4929
4930
    getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo};},
4931
4932
    addWidget: function(pos, node, scroll, vert, horiz) {
4933
      var display = this.display;
4934
      pos = cursorCoords(this, clipPos(this.doc, pos));
4935
      var top = pos.bottom, left = pos.left;
4936
      node.style.position = "absolute";
4937
      node.setAttribute("cm-ignore-events", "true");
4938
      this.display.input.setUneditable(node);
4939
      display.sizer.appendChild(node);
4940
      if (vert == "over") {
4941
        top = pos.top;
4942
      } else if (vert == "above" || vert == "near") {
4943
        var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
4944
        hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
4945
        // Default to positioning above (if specified and possible); otherwise default to positioning below
4946
        if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
4947
          top = pos.top - node.offsetHeight;
4948
        else if (pos.bottom + node.offsetHeight <= vspace)
4949
          top = pos.bottom;
4950
        if (left + node.offsetWidth > hspace)
4951
          left = hspace - node.offsetWidth;
4952
      }
4953
      node.style.top = top + "px";
4954
      node.style.left = node.style.right = "";
4955
      if (horiz == "right") {
4956
        left = display.sizer.clientWidth - node.offsetWidth;
4957
        node.style.right = "0px";
4958
      } else {
4959
        if (horiz == "left") left = 0;
4960
        else if (horiz == "middle") left = (display.sizer.clientWidth - node.offsetWidth) / 2;
4961
        node.style.left = left + "px";
4962
      }
4963
      if (scroll)
4964
        scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight);
4965
    },
4966
4967
    triggerOnKeyDown: methodOp(onKeyDown),
4968
    triggerOnKeyPress: methodOp(onKeyPress),
4969
    triggerOnKeyUp: onKeyUp,
4970
4971
    execCommand: function(cmd) {
4972
      if (commands.hasOwnProperty(cmd))
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if commands.hasOwnProperty(cmd) 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...
4973
        return commands[cmd](this);
4974
    },
4975
4976
    findPosH: function(from, amount, unit, visually) {
4977
      var dir = 1;
4978
      if (amount < 0) { dir = -1; amount = -amount; }
4979
      for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
4980
        cur = findPosH(this.doc, cur, dir, unit, visually);
4981
        if (cur.hitSide) break;
4982
      }
4983
      return cur;
4984
    },
4985
4986
    moveH: methodOp(function(dir, unit) {
4987
      var cm = this;
4988
      cm.extendSelectionsBy(function(range) {
4989
        if (cm.display.shift || cm.doc.extend || range.empty())
4990
          return findPosH(cm.doc, range.head, dir, unit, cm.options.rtlMoveVisually);
4991
        else
4992
          return dir < 0 ? range.from() : range.to();
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
4993
      }, sel_move);
4994
    }),
4995
4996
    deleteH: methodOp(function(dir, unit) {
4997
      var sel = this.doc.sel, doc = this.doc;
4998
      if (sel.somethingSelected())
4999
        doc.replaceSelection("", null, "+delete");
5000
      else
5001
        deleteNearSelection(this, function(range) {
5002
          var other = findPosH(doc, range.head, dir, unit, false);
5003
          return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other};
5004
        });
5005
    }),
5006
5007
    findPosV: function(from, amount, unit, goalColumn) {
5008
      var dir = 1, x = goalColumn;
5009
      if (amount < 0) { dir = -1; amount = -amount; }
5010
      for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
5011
        var coords = cursorCoords(this, cur, "div");
5012
        if (x == null) x = coords.left;
0 ignored issues
show
Best Practice introduced by
Comparing x to null using the == operator is not safe. Consider using === instead.
Loading history...
5013
        else coords.left = x;
5014
        cur = findPosV(this, coords, dir, unit);
5015
        if (cur.hitSide) break;
5016
      }
5017
      return cur;
5018
    },
5019
5020
    moveV: methodOp(function(dir, unit) {
5021
      var cm = this, doc = this.doc, goals = [];
5022
      var collapse = !cm.display.shift && !doc.extend && doc.sel.somethingSelected();
5023
      doc.extendSelectionsBy(function(range) {
5024
        if (collapse)
5025
          return dir < 0 ? range.from() : range.to();
5026
        var headPos = cursorCoords(cm, range.head, "div");
5027
        if (range.goalColumn != null) headPos.left = range.goalColumn;
0 ignored issues
show
Best Practice introduced by
Comparing range.goalColumn to null using the != operator is not safe. Consider using !== instead.
Loading history...
5028
        goals.push(headPos.left);
5029
        var pos = findPosV(cm, headPos, dir, unit);
5030
        if (unit == "page" && range == doc.sel.primary())
5031
          addToScrollPos(cm, null, charCoords(cm, pos, "div").top - headPos.top);
5032
        return pos;
5033
      }, sel_move);
5034
      if (goals.length) for (var i = 0; i < doc.sel.ranges.length; i++)
5035
        doc.sel.ranges[i].goalColumn = goals[i];
5036
    }),
5037
5038
    // Find the word at the given position (as returned by coordsChar).
5039
    findWordAt: function(pos) {
5040
      var doc = this.doc, line = getLine(doc, pos.line).text;
5041
      var start = pos.ch, end = pos.ch;
5042
      if (line) {
5043
        var helper = this.getHelper(pos, "wordChars");
5044
        if ((pos.xRel < 0 || end == line.length) && start) --start; else ++end;
5045
        var startChar = line.charAt(start);
5046
        var check = isWordChar(startChar, helper)
5047
          ? function(ch) { return isWordChar(ch, helper); }
5048
          : /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);}
5049
          : function(ch) {return !/\s/.test(ch) && !isWordChar(ch);};
5050
        while (start > 0 && check(line.charAt(start - 1))) --start;
5051
        while (end < line.length && check(line.charAt(end))) ++end;
5052
      }
5053
      return new Range(Pos(pos.line, start), Pos(pos.line, end));
5054
    },
5055
5056
    toggleOverwrite: function(value) {
5057
      if (value != null && value == this.state.overwrite) return;
0 ignored issues
show
Best Practice introduced by
Comparing value to null using the != operator is not safe. Consider using !== instead.
Loading history...
5058
      if (this.state.overwrite = !this.state.overwrite)
5059
        addClass(this.display.cursorDiv, "CodeMirror-overwrite");
5060
      else
5061
        rmClass(this.display.cursorDiv, "CodeMirror-overwrite");
5062
5063
      signal(this, "overwriteToggle", this, this.state.overwrite);
5064
    },
5065
    hasFocus: function() { return this.display.input.getField() == activeElt(); },
5066
5067
    scrollTo: methodOp(function(x, y) {
5068
      if (x != null || y != null) resolveScrollToPos(this);
0 ignored issues
show
Best Practice introduced by
Comparing x to null using the != operator is not safe. Consider using !== instead.
Loading history...
Best Practice introduced by
Comparing y to null using the != operator is not safe. Consider using !== instead.
Loading history...
5069
      if (x != null) this.curOp.scrollLeft = x;
5070
      if (y != null) this.curOp.scrollTop = y;
5071
    }),
5072
    getScrollInfo: function() {
5073
      var scroller = this.display.scroller;
5074
      return {left: scroller.scrollLeft, top: scroller.scrollTop,
5075
              height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight,
5076
              width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth,
5077
              clientHeight: displayHeight(this), clientWidth: displayWidth(this)};
5078
    },
5079
5080
    scrollIntoView: methodOp(function(range, margin) {
5081
      if (range == null) {
0 ignored issues
show
Best Practice introduced by
Comparing range to null using the == operator is not safe. Consider using === instead.
Loading history...
5082
        range = {from: this.doc.sel.primary().head, to: null};
5083
        if (margin == null) margin = this.options.cursorScrollMargin;
0 ignored issues
show
Best Practice introduced by
Comparing margin to null using the == operator is not safe. Consider using === instead.
Loading history...
5084
      } else if (typeof range == "number") {
5085
        range = {from: Pos(range, 0), to: null};
5086
      } else if (range.from == null) {
0 ignored issues
show
Best Practice introduced by
Comparing range.from to null using the == operator is not safe. Consider using === instead.
Loading history...
5087
        range = {from: range, to: null};
5088
      }
5089
      if (!range.to) range.to = range.from;
5090
      range.margin = margin || 0;
5091
5092
      if (range.from.line != null) {
0 ignored issues
show
Best Practice introduced by
Comparing range.from.line to null using the != operator is not safe. Consider using !== instead.
Loading history...
5093
        resolveScrollToPos(this);
5094
        this.curOp.scrollToPos = range;
5095
      } else {
5096
        var sPos = calculateScrollPos(this, Math.min(range.from.left, range.to.left),
5097
                                      Math.min(range.from.top, range.to.top) - range.margin,
5098
                                      Math.max(range.from.right, range.to.right),
5099
                                      Math.max(range.from.bottom, range.to.bottom) + range.margin);
5100
        this.scrollTo(sPos.scrollLeft, sPos.scrollTop);
5101
      }
5102
    }),
5103
5104
    setSize: methodOp(function(width, height) {
5105
      var cm = this;
5106
      function interpret(val) {
5107
        return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val;
5108
      }
5109
      if (width != null) cm.display.wrapper.style.width = interpret(width);
0 ignored issues
show
Best Practice introduced by
Comparing width to null using the != operator is not safe. Consider using !== instead.
Loading history...
5110
      if (height != null) cm.display.wrapper.style.height = interpret(height);
0 ignored issues
show
Best Practice introduced by
Comparing height to null using the != operator is not safe. Consider using !== instead.
Loading history...
5111
      if (cm.options.lineWrapping) clearLineMeasurementCache(this);
5112
      var lineNo = cm.display.viewFrom;
5113
      cm.doc.iter(lineNo, cm.display.viewTo, function(line) {
5114
        if (line.widgets) for (var i = 0; i < line.widgets.length; i++)
5115
          if (line.widgets[i].noHScroll) { regLineChange(cm, lineNo, "widget"); break; }
5116
        ++lineNo;
5117
      });
5118
      cm.curOp.forceUpdate = true;
5119
      signal(cm, "refresh", this);
5120
    }),
5121
5122
    operation: function(f){return runInOp(this, f);},
5123
5124
    refresh: methodOp(function() {
5125
      var oldHeight = this.display.cachedTextHeight;
5126
      regChange(this);
5127
      this.curOp.forceUpdate = true;
5128
      clearCaches(this);
5129
      this.scrollTo(this.doc.scrollLeft, this.doc.scrollTop);
5130
      updateGutterSpace(this);
5131
      if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5)
0 ignored issues
show
Best Practice introduced by
Comparing oldHeight to null using the == operator is not safe. Consider using === instead.
Loading history...
5132
        estimateLineHeights(this);
5133
      signal(this, "refresh", this);
5134
    }),
5135
5136
    swapDoc: methodOp(function(doc) {
5137
      var old = this.doc;
5138
      old.cm = null;
5139
      attachDoc(this, doc);
5140
      clearCaches(this);
5141
      this.display.input.reset();
5142
      this.scrollTo(doc.scrollLeft, doc.scrollTop);
5143
      this.curOp.forceScroll = true;
5144
      signalLater(this, "swapDoc", this, old);
5145
      return old;
5146
    }),
5147
5148
    getInputField: function(){return this.display.input.getField();},
5149
    getWrapperElement: function(){return this.display.wrapper;},
5150
    getScrollerElement: function(){return this.display.scroller;},
5151
    getGutterElement: function(){return this.display.gutters;}
5152
  };
5153
  eventMixin(CodeMirror);
5154
5155
  // OPTION DEFAULTS
5156
5157
  // The default configuration options.
5158
  var defaults = CodeMirror.defaults = {};
5159
  // Functions to run when options are changed.
5160
  var optionHandlers = CodeMirror.optionHandlers = {};
5161
5162
  function option(name, deflt, handle, notOnInit) {
5163
    CodeMirror.defaults[name] = deflt;
5164
    if (handle) optionHandlers[name] =
5165
      notOnInit ? function(cm, val, old) {if (old != Init) handle(cm, val, old);} : handle;
5166
  }
5167
5168
  // Passed to option handlers when there is no old value.
5169
  var Init = CodeMirror.Init = {toString: function(){return "CodeMirror.Init";}};
5170
5171
  // These two are, on init, called from the constructor because they
5172
  // have to be initialized before the editor can start at all.
5173
  option("value", "", function(cm, val) {
5174
    cm.setValue(val);
5175
  }, true);
5176
  option("mode", null, function(cm, val) {
5177
    cm.doc.modeOption = val;
5178
    loadMode(cm);
5179
  }, true);
5180
5181
  option("indentUnit", 2, loadMode, true);
5182
  option("indentWithTabs", false);
5183
  option("smartIndent", true);
5184
  option("tabSize", 4, function(cm) {
5185
    resetModeState(cm);
5186
    clearCaches(cm);
5187
    regChange(cm);
5188
  }, true);
5189
  option("specialChars", /[\t\u0000-\u0019\u00ad\u200b-\u200f\u2028\u2029\ufeff]/g, function(cm, val) {
5190
    cm.options.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g");
5191
    cm.refresh();
5192
  }, true);
5193
  option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function(cm) {cm.refresh();}, true);
5194
  option("electricChars", true);
5195
  option("inputStyle", mobile ? "contenteditable" : "textarea", function() {
5196
    throw new Error("inputStyle can not (yet) be changed in a running editor"); // FIXME
5197
  }, true);
5198
  option("rtlMoveVisually", !windows);
5199
  option("wholeLineUpdateBefore", true);
5200
5201
  option("theme", "default", function(cm) {
5202
    themeChanged(cm);
5203
    guttersChanged(cm);
5204
  }, true);
5205
  option("keyMap", "default", function(cm, val, old) {
5206
    var next = getKeyMap(val);
5207
    var prev = old != CodeMirror.Init && getKeyMap(old);
5208
    if (prev && prev.detach) prev.detach(cm, next);
5209
    if (next.attach) next.attach(cm, prev || null);
5210
  });
5211
  option("extraKeys", null);
5212
5213
  option("lineWrapping", false, wrappingChanged, true);
5214
  option("gutters", [], function(cm) {
5215
    setGuttersForLineNumbers(cm.options);
5216
    guttersChanged(cm);
5217
  }, true);
5218
  option("fixedGutter", true, function(cm, val) {
5219
    cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
5220
    cm.refresh();
5221
  }, true);
5222
  option("coverGutterNextToScrollbar", false, function(cm) {updateScrollbars(cm);}, true);
5223
  option("scrollbarStyle", "native", function(cm) {
5224
    initScrollbars(cm);
5225
    updateScrollbars(cm);
5226
    cm.display.scrollbars.setScrollTop(cm.doc.scrollTop);
5227
    cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft);
5228
  }, true);
5229
  option("lineNumbers", false, function(cm) {
5230
    setGuttersForLineNumbers(cm.options);
5231
    guttersChanged(cm);
5232
  }, true);
5233
  option("firstLineNumber", 1, guttersChanged, true);
5234
  option("lineNumberFormatter", function(integer) {return integer;}, guttersChanged, true);
5235
  option("showCursorWhenSelecting", false, updateSelection, true);
5236
5237
  option("resetSelectionOnContextMenu", true);
5238
5239
  option("readOnly", false, function(cm, val) {
5240
    if (val == "nocursor") {
5241
      onBlur(cm);
5242
      cm.display.input.blur();
5243
      cm.display.disabled = true;
5244
    } else {
5245
      cm.display.disabled = false;
5246
      if (!val) cm.display.input.reset();
5247
    }
5248
  });
5249
  option("disableInput", false, function(cm, val) {if (!val) cm.display.input.reset();}, true);
5250
  option("dragDrop", true);
5251
5252
  option("cursorBlinkRate", 530);
5253
  option("cursorScrollMargin", 0);
5254
  option("cursorHeight", 1, updateSelection, true);
5255
  option("singleCursorHeightPerLine", true, updateSelection, true);
5256
  option("workTime", 100);
5257
  option("workDelay", 100);
5258
  option("flattenSpans", true, resetModeState, true);
5259
  option("addModeClass", false, resetModeState, true);
5260
  option("pollInterval", 100);
5261
  option("undoDepth", 200, function(cm, val){cm.doc.history.undoDepth = val;});
5262
  option("historyEventDelay", 1250);
5263
  option("viewportMargin", 10, function(cm){cm.refresh();}, true);
5264
  option("maxHighlightLength", 10000, resetModeState, true);
5265
  option("moveInputWithCursor", true, function(cm, val) {
5266
    if (!val) cm.display.input.resetPosition();
5267
  });
5268
5269
  option("tabindex", null, function(cm, val) {
5270
    cm.display.input.getField().tabIndex = val || "";
5271
  });
5272
  option("autofocus", null);
5273
5274
  // MODE DEFINITION AND QUERYING
5275
5276
  // Known modes, by name and by MIME
5277
  var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {};
5278
5279
  // Extra arguments are stored as the mode's dependencies, which is
5280
  // used by (legacy) mechanisms like loadmode.js to automatically
5281
  // load a mode. (Preferred mechanism is the require/define calls.)
5282
  CodeMirror.defineMode = function(name, mode) {
5283
    if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
5284
    if (arguments.length > 2)
5285
      mode.dependencies = Array.prototype.slice.call(arguments, 2);
5286
    modes[name] = mode;
5287
  };
5288
5289
  CodeMirror.defineMIME = function(mime, spec) {
5290
    mimeModes[mime] = spec;
5291
  };
5292
5293
  // Given a MIME type, a {name, ...options} config object, or a name
5294
  // string, return a mode config object.
5295
  CodeMirror.resolveMode = function(spec) {
5296
    if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
5297
      spec = mimeModes[spec];
5298
    } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
5299
      var found = mimeModes[spec.name];
5300
      if (typeof found == "string") found = {name: found};
5301
      spec = createObj(found, spec);
5302
      spec.name = found.name;
5303
    } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
5304
      return CodeMirror.resolveMode("application/xml");
5305
    }
5306
    if (typeof spec == "string") return {name: spec};
5307
    else return spec || {name: "null"};
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
5308
  };
5309
5310
  // Given a mode spec (anything that resolveMode accepts), find and
5311
  // initialize an actual mode object.
5312
  CodeMirror.getMode = function(options, spec) {
5313
    var spec = CodeMirror.resolveMode(spec);
5314
    var mfactory = modes[spec.name];
5315
    if (!mfactory) return CodeMirror.getMode(options, "text/plain");
5316
    var modeObj = mfactory(options, spec);
5317
    if (modeExtensions.hasOwnProperty(spec.name)) {
5318
      var exts = modeExtensions[spec.name];
5319
      for (var prop in exts) {
5320
        if (!exts.hasOwnProperty(prop)) continue;
5321
        if (modeObj.hasOwnProperty(prop)) modeObj["_" + prop] = modeObj[prop];
5322
        modeObj[prop] = exts[prop];
5323
      }
5324
    }
5325
    modeObj.name = spec.name;
5326
    if (spec.helperType) modeObj.helperType = spec.helperType;
5327
    if (spec.modeProps) for (var prop in spec.modeProps)
0 ignored issues
show
Complexity introduced by
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
Comprehensibility Naming Best Practice introduced by
The variable prop already seems to be declared on line 5319. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
5328
      modeObj[prop] = spec.modeProps[prop];
5329
5330
    return modeObj;
5331
  };
5332
5333
  // Minimal default mode.
5334
  CodeMirror.defineMode("null", function() {
5335
    return {token: function(stream) {stream.skipToEnd();}};
5336
  });
5337
  CodeMirror.defineMIME("text/plain", "null");
5338
5339
  // This can be used to attach properties to mode objects from
5340
  // outside the actual mode definition.
5341
  var modeExtensions = CodeMirror.modeExtensions = {};
5342
  CodeMirror.extendMode = function(mode, properties) {
5343
    var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
5344
    copyObj(properties, exts);
5345
  };
5346
5347
  // EXTENSIONS
5348
5349
  CodeMirror.defineExtension = function(name, func) {
5350
    CodeMirror.prototype[name] = func;
5351
  };
5352
  CodeMirror.defineDocExtension = function(name, func) {
5353
    Doc.prototype[name] = func;
5354
  };
5355
  CodeMirror.defineOption = option;
5356
5357
  var initHooks = [];
5358
  CodeMirror.defineInitHook = function(f) {initHooks.push(f);};
5359
5360
  var helpers = CodeMirror.helpers = {};
5361
  CodeMirror.registerHelper = function(type, name, value) {
5362
    if (!helpers.hasOwnProperty(type)) helpers[type] = CodeMirror[type] = {_global: []};
5363
    helpers[type][name] = value;
5364
  };
5365
  CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {
5366
    CodeMirror.registerHelper(type, name, value);
5367
    helpers[type]._global.push({pred: predicate, val: value});
5368
  };
5369
5370
  // MODE STATE HANDLING
5371
5372
  // Utility functions for working with state. Exported because nested
5373
  // modes need to do this for their inner modes.
5374
5375
  var copyState = CodeMirror.copyState = function(mode, state) {
5376
    if (state === true) return state;
5377
    if (mode.copyState) return mode.copyState(state);
5378
    var nstate = {};
5379
    for (var n in state) {
0 ignored issues
show
Complexity introduced by
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
5380
      var val = state[n];
5381
      if (val instanceof Array) val = val.concat([]);
5382
      nstate[n] = val;
5383
    }
5384
    return nstate;
5385
  };
5386
5387
  var startState = CodeMirror.startState = function(mode, a1, a2) {
5388
    return mode.startState ? mode.startState(a1, a2) : true;
5389
  };
5390
5391
  // Given a mode and a state (for that mode), find the inner mode and
5392
  // state at the position that the state refers to.
5393
  CodeMirror.innerMode = function(mode, state) {
5394
    while (mode.innerMode) {
5395
      var info = mode.innerMode(state);
5396
      if (!info || info.mode == mode) break;
5397
      state = info.state;
5398
      mode = info.mode;
5399
    }
5400
    return info || {mode: mode, state: state};
5401
  };
5402
5403
  // STANDARD COMMANDS
5404
5405
  // Commands are parameter-less actions that can be performed on an
5406
  // editor, mostly used for keybindings.
5407
  var commands = CodeMirror.commands = {
5408
    selectAll: function(cm) {cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll);},
5409
    singleSelection: function(cm) {
5410
      cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll);
5411
    },
5412
    killLine: function(cm) {
5413
      deleteNearSelection(cm, function(range) {
5414
        if (range.empty()) {
5415
          var len = getLine(cm.doc, range.head.line).text.length;
5416
          if (range.head.ch == len && range.head.line < cm.lastLine())
5417
            return {from: range.head, to: Pos(range.head.line + 1, 0)};
5418
          else
5419
            return {from: range.head, to: Pos(range.head.line, len)};
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
5420
        } else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
5421
          return {from: range.from(), to: range.to()};
5422
        }
5423
      });
5424
    },
5425
    deleteLine: function(cm) {
5426
      deleteNearSelection(cm, function(range) {
5427
        return {from: Pos(range.from().line, 0),
5428
                to: clipPos(cm.doc, Pos(range.to().line + 1, 0))};
5429
      });
5430
    },
5431
    delLineLeft: function(cm) {
5432
      deleteNearSelection(cm, function(range) {
5433
        return {from: Pos(range.from().line, 0), to: range.from()};
5434
      });
5435
    },
5436
    delWrappedLineLeft: function(cm) {
5437
      deleteNearSelection(cm, function(range) {
5438
        var top = cm.charCoords(range.head, "div").top + 5;
5439
        var leftPos = cm.coordsChar({left: 0, top: top}, "div");
5440
        return {from: leftPos, to: range.from()};
5441
      });
5442
    },
5443
    delWrappedLineRight: function(cm) {
5444
      deleteNearSelection(cm, function(range) {
5445
        var top = cm.charCoords(range.head, "div").top + 5;
5446
        var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div");
5447
        return {from: range.from(), to: rightPos };
5448
      });
5449
    },
5450
    undo: function(cm) {cm.undo();},
5451
    redo: function(cm) {cm.redo();},
5452
    undoSelection: function(cm) {cm.undoSelection();},
5453
    redoSelection: function(cm) {cm.redoSelection();},
5454
    goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));},
5455
    goDocEnd: function(cm) {cm.extendSelection(Pos(cm.lastLine()));},
5456
    goLineStart: function(cm) {
5457
      cm.extendSelectionsBy(function(range) { return lineStart(cm, range.head.line); },
5458
                            {origin: "+move", bias: 1});
5459
    },
5460
    goLineStartSmart: function(cm) {
5461
      cm.extendSelectionsBy(function(range) {
5462
        return lineStartSmart(cm, range.head);
5463
      }, {origin: "+move", bias: 1});
5464
    },
5465
    goLineEnd: function(cm) {
5466
      cm.extendSelectionsBy(function(range) { return lineEnd(cm, range.head.line); },
5467
                            {origin: "+move", bias: -1});
5468
    },
5469
    goLineRight: function(cm) {
5470
      cm.extendSelectionsBy(function(range) {
5471
        var top = cm.charCoords(range.head, "div").top + 5;
5472
        return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div");
5473
      }, sel_move);
5474
    },
5475
    goLineLeft: function(cm) {
5476
      cm.extendSelectionsBy(function(range) {
5477
        var top = cm.charCoords(range.head, "div").top + 5;
5478
        return cm.coordsChar({left: 0, top: top}, "div");
5479
      }, sel_move);
5480
    },
5481
    goLineLeftSmart: function(cm) {
5482
      cm.extendSelectionsBy(function(range) {
5483
        var top = cm.charCoords(range.head, "div").top + 5;
5484
        var pos = cm.coordsChar({left: 0, top: top}, "div");
5485
        if (pos.ch < cm.getLine(pos.line).search(/\S/)) return lineStartSmart(cm, range.head);
5486
        return pos;
5487
      }, sel_move);
5488
    },
5489
    goLineUp: function(cm) {cm.moveV(-1, "line");},
5490
    goLineDown: function(cm) {cm.moveV(1, "line");},
5491
    goPageUp: function(cm) {cm.moveV(-1, "page");},
5492
    goPageDown: function(cm) {cm.moveV(1, "page");},
5493
    goCharLeft: function(cm) {cm.moveH(-1, "char");},
5494
    goCharRight: function(cm) {cm.moveH(1, "char");},
5495
    goColumnLeft: function(cm) {cm.moveH(-1, "column");},
5496
    goColumnRight: function(cm) {cm.moveH(1, "column");},
5497
    goWordLeft: function(cm) {cm.moveH(-1, "word");},
5498
    goGroupRight: function(cm) {cm.moveH(1, "group");},
5499
    goGroupLeft: function(cm) {cm.moveH(-1, "group");},
5500
    goWordRight: function(cm) {cm.moveH(1, "word");},
5501
    delCharBefore: function(cm) {cm.deleteH(-1, "char");},
5502
    delCharAfter: function(cm) {cm.deleteH(1, "char");},
5503
    delWordBefore: function(cm) {cm.deleteH(-1, "word");},
5504
    delWordAfter: function(cm) {cm.deleteH(1, "word");},
5505
    delGroupBefore: function(cm) {cm.deleteH(-1, "group");},
5506
    delGroupAfter: function(cm) {cm.deleteH(1, "group");},
5507
    indentAuto: function(cm) {cm.indentSelection("smart");},
5508
    indentMore: function(cm) {cm.indentSelection("add");},
5509
    indentLess: function(cm) {cm.indentSelection("subtract");},
5510
    insertTab: function(cm) {cm.replaceSelection("\t");},
5511
    insertSoftTab: function(cm) {
5512
      var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize;
5513
      for (var i = 0; i < ranges.length; i++) {
5514
        var pos = ranges[i].from();
5515
        var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize);
5516
        spaces.push(new Array(tabSize - col % tabSize + 1).join(" "));
5517
      }
5518
      cm.replaceSelections(spaces);
5519
    },
5520
    defaultTab: function(cm) {
5521
      if (cm.somethingSelected()) cm.indentSelection("add");
5522
      else cm.execCommand("insertTab");
5523
    },
5524
    transposeChars: function(cm) {
5525
      runInOp(cm, function() {
5526
        var ranges = cm.listSelections(), newSel = [];
5527
        for (var i = 0; i < ranges.length; i++) {
5528
          var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text;
5529
          if (line) {
5530
            if (cur.ch == line.length) cur = new Pos(cur.line, cur.ch - 1);
5531
            if (cur.ch > 0) {
5532
              cur = new Pos(cur.line, cur.ch + 1);
5533
              cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),
5534
                              Pos(cur.line, cur.ch - 2), cur, "+transpose");
5535
            } else if (cur.line > cm.doc.first) {
5536
              var prev = getLine(cm.doc, cur.line - 1).text;
5537
              if (prev)
5538
                cm.replaceRange(line.charAt(0) + "\n" + prev.charAt(prev.length - 1),
5539
                                Pos(cur.line - 1, prev.length - 1), Pos(cur.line, 1), "+transpose");
5540
            }
5541
          }
5542
          newSel.push(new Range(cur, cur));
5543
        }
5544
        cm.setSelections(newSel);
5545
      });
5546
    },
5547
    newlineAndIndent: function(cm) {
5548
      runInOp(cm, function() {
5549
        var len = cm.listSelections().length;
5550
        for (var i = 0; i < len; i++) {
5551
          var range = cm.listSelections()[i];
5552
          cm.replaceRange("\n", range.anchor, range.head, "+input");
5553
          cm.indentLine(range.from().line + 1, null, true);
5554
          ensureCursorVisible(cm);
5555
        }
5556
      });
5557
    },
5558
    toggleOverwrite: function(cm) {cm.toggleOverwrite();}
5559
  };
5560
5561
5562
  // STANDARD KEYMAPS
5563
5564
  var keyMap = CodeMirror.keyMap = {};
5565
5566
  keyMap.basic = {
5567
    "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
5568
    "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
5569
    "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore",
5570
    "Tab": "defaultTab", "Shift-Tab": "indentAuto",
5571
    "Enter": "newlineAndIndent", "Insert": "toggleOverwrite",
5572
    "Esc": "singleSelection"
5573
  };
5574
  // Note that the save and find-related commands aren't defined by
5575
  // default. User code or addons can define them. Unknown commands
5576
  // are simply ignored.
5577
  keyMap.pcDefault = {
5578
    "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
5579
    "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown",
5580
    "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
5581
    "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
5582
    "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
5583
    "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
5584
    "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection",
5585
    fallthrough: "basic"
5586
  };
5587
  // Very basic readline/emacs-style bindings, which are standard on Mac.
5588
  keyMap.emacsy = {
5589
    "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
5590
    "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
5591
    "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
5592
    "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars"
5593
  };
5594
  keyMap.macDefault = {
5595
    "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
5596
    "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
5597
    "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore",
5598
    "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
5599
    "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
5600
    "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight",
5601
    "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd",
5602
    fallthrough: ["basic", "emacsy"]
5603
  };
5604
  keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
5605
5606
  // KEYMAP DISPATCH
5607
5608
  function normalizeKeyName(name) {
5609
    var parts = name.split(/-(?!$)/), name = parts[parts.length - 1];
5610
    var alt, ctrl, shift, cmd;
5611
    for (var i = 0; i < parts.length - 1; i++) {
5612
      var mod = parts[i];
5613
      if (/^(cmd|meta|m)$/i.test(mod)) cmd = true;
5614
      else if (/^a(lt)?$/i.test(mod)) alt = true;
5615
      else if (/^(c|ctrl|control)$/i.test(mod)) ctrl = true;
5616
      else if (/^s(hift)$/i.test(mod)) shift = true;
5617
      else throw new Error("Unrecognized modifier name: " + mod);
5618
    }
5619
    if (alt) name = "Alt-" + name;
5620
    if (ctrl) name = "Ctrl-" + name;
5621
    if (cmd) name = "Cmd-" + name;
5622
    if (shift) name = "Shift-" + name;
5623
    return name;
5624
  }
5625
5626
  // This is a kludge to keep keymaps mostly working as raw objects
5627
  // (backwards compatibility) while at the same time support features
5628
  // like normalization and multi-stroke key bindings. It compiles a
5629
  // new normalized keymap, and then updates the old object to reflect
5630
  // this.
5631
  CodeMirror.normalizeKeyMap = function(keymap) {
5632
    var copy = {};
5633
    for (var keyname in keymap) if (keymap.hasOwnProperty(keyname)) {
5634
      var value = keymap[keyname];
5635
      if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) continue;
5636
      if (value == "...") { delete keymap[keyname]; continue; }
5637
5638
      var keys = map(keyname.split(" "), normalizeKeyName);
5639
      for (var i = 0; i < keys.length; i++) {
5640
        var val, name;
5641
        if (i == keys.length - 1) {
5642
          name = keyname;
5643
          val = value;
5644
        } else {
5645
          name = keys.slice(0, i + 1).join(" ");
5646
          val = "...";
5647
        }
5648
        var prev = copy[name];
5649
        if (!prev) copy[name] = val;
5650
        else if (prev != val) throw new Error("Inconsistent bindings for " + name);
5651
      }
5652
      delete keymap[keyname];
5653
    }
5654
    for (var prop in copy) keymap[prop] = copy[prop];
0 ignored issues
show
Complexity introduced by
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
5655
    return keymap;
5656
  };
5657
5658
  var lookupKey = CodeMirror.lookupKey = function(key, map, handle, context) {
5659
    map = getKeyMap(map);
5660
    var found = map.call ? map.call(key, context) : map[key];
5661
    if (found === false) return "nothing";
5662
    if (found === "...") return "multi";
5663
    if (found != null && handle(found)) return "handled";
0 ignored issues
show
Best Practice introduced by
Comparing found to null using the != operator is not safe. Consider using !== instead.
Loading history...
5664
5665
    if (map.fallthrough) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if map.fallthrough 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...
5666
      if (Object.prototype.toString.call(map.fallthrough) != "[object Array]")
5667
        return lookupKey(key, map.fallthrough, handle, context);
5668
      for (var i = 0; i < map.fallthrough.length; i++) {
5669
        var result = lookupKey(key, map.fallthrough[i], handle, context);
5670
        if (result) return result;
5671
      }
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...
5672
    }
5673
  };
5674
5675
  // Modifier key presses don't count as 'real' key presses for the
5676
  // purpose of keymap fallthrough.
5677
  var isModifierKey = CodeMirror.isModifierKey = function(value) {
5678
    var name = typeof value == "string" ? value : keyNames[value.keyCode];
5679
    return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
5680
  };
5681
5682
  // Look up the name of a key as indicated by an event object.
5683
  var keyName = CodeMirror.keyName = function(event, noShift) {
5684
    if (presto && event.keyCode == 34 && event["char"]) return false;
5685
    var base = keyNames[event.keyCode], name = base;
5686
    if (name == null || event.altGraphKey) return false;
0 ignored issues
show
Best Practice introduced by
Comparing name to null using the == operator is not safe. Consider using === instead.
Loading history...
5687
    if (event.altKey && base != "Alt") name = "Alt-" + name;
5688
    if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") name = "Ctrl-" + name;
5689
    if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") name = "Cmd-" + name;
5690
    if (!noShift && event.shiftKey && base != "Shift") name = "Shift-" + name;
5691
    return name;
5692
  };
5693
5694
  function getKeyMap(val) {
5695
    return typeof val == "string" ? keyMap[val] : val;
5696
  }
5697
5698
  // FROMTEXTAREA
5699
5700
  CodeMirror.fromTextArea = function(textarea, options) {
5701
    options = options ? copyObj(options) : {};
5702
    options.value = textarea.value;
5703
    if (!options.tabindex && textarea.tabIndex)
5704
      options.tabindex = textarea.tabIndex;
5705
    if (!options.placeholder && textarea.placeholder)
5706
      options.placeholder = textarea.placeholder;
5707
    // Set autofocus to true if this textarea is focused, or if it has
5708
    // autofocus and no other element is focused.
5709
    if (options.autofocus == null) {
0 ignored issues
show
Best Practice introduced by
Comparing options.autofocus to null using the == operator is not safe. Consider using === instead.
Loading history...
5710
      var hasFocus = activeElt();
5711
      options.autofocus = hasFocus == textarea ||
5712
        textarea.getAttribute("autofocus") != null && hasFocus == document.body;
0 ignored issues
show
Best Practice introduced by
Comparing textarea.getAttribute("autofocus") to null using the != operator is not safe. Consider using !== instead.
Loading history...
5713
    }
5714
5715
    function save() {textarea.value = cm.getValue();}
5716
    if (textarea.form) {
5717
      on(textarea.form, "submit", save);
5718
      // Deplorable hack to make the submit method do the right thing.
5719
      if (!options.leaveSubmitMethodAlone) {
5720
        var form = textarea.form, realSubmit = form.submit;
5721
        try {
5722
          var wrappedSubmit = form.submit = function() {
5723
            save();
5724
            form.submit = realSubmit;
5725
            form.submit();
5726
            form.submit = wrappedSubmit;
5727
          };
5728
        } catch(e) {}
0 ignored issues
show
Coding Style Comprehensibility Best Practice introduced by
Empty catch clauses should be used with caution; consider adding a comment why this is needed.
Loading history...
5729
      }
5730
    }
5731
5732
    options.finishInit = function(cm) {
5733
      cm.save = save;
5734
      cm.getTextArea = function() { return textarea; };
5735
      cm.toTextArea = function() {
5736
        cm.toTextArea = isNaN; // Prevent this from being ran twice
0 ignored issues
show
Bug introduced by
The variable isNaN seems to be never declared. If this is a global, consider adding a /** global: isNaN */ 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...
5737
        save();
5738
        textarea.parentNode.removeChild(cm.getWrapperElement());
5739
        textarea.style.display = "";
5740
        if (textarea.form) {
5741
          off(textarea.form, "submit", save);
5742
          if (typeof textarea.form.submit == "function")
5743
            textarea.form.submit = realSubmit;
0 ignored issues
show
Bug introduced by
The variable realSubmit seems to not be initialized for all possible execution paths.
Loading history...
5744
        }
5745
      };
5746
    };
5747
5748
    textarea.style.display = "none";
5749
    var cm = CodeMirror(function(node) {
5750
      textarea.parentNode.insertBefore(node, textarea.nextSibling);
5751
    }, options);
5752
    return cm;
5753
  };
5754
5755
  // STRING STREAM
5756
5757
  // Fed to the mode parsers, provides helper functions to make
5758
  // parsers more succinct.
5759
5760
  var StringStream = CodeMirror.StringStream = function(string, tabSize) {
5761
    this.pos = this.start = 0;
5762
    this.string = string;
5763
    this.tabSize = tabSize || 8;
5764
    this.lastColumnPos = this.lastColumnValue = 0;
5765
    this.lineStart = 0;
5766
  };
5767
5768
  StringStream.prototype = {
5769
    eol: function() {return this.pos >= this.string.length;},
5770
    sol: function() {return this.pos == this.lineStart;},
5771
    peek: function() {return this.string.charAt(this.pos) || undefined;},
5772
    next: function() {
5773
      if (this.pos < this.string.length)
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if this.pos < this.string.length 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...
5774
        return this.string.charAt(this.pos++);
5775
    },
5776
    eat: function(match) {
5777
      var ch = this.string.charAt(this.pos);
5778
      if (typeof match == "string") var ok = ch == match;
5779
      else var ok = ch && (match.test ? match.test(ch) : match(ch));
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable ok already seems to be declared on line 5778. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
5780
      if (ok) {++this.pos; return ch;}
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if ok 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...
5781
    },
5782
    eatWhile: function(match) {
5783
      var start = this.pos;
5784
      while (this.eat(match)){}
0 ignored issues
show
Comprehensibility Documentation Best Practice introduced by
This code block is empty. Consider removing it or adding a comment to explain.
Loading history...
5785
      return this.pos > start;
5786
    },
5787
    eatSpace: function() {
5788
      var start = this.pos;
5789
      while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
5790
      return this.pos > start;
5791
    },
5792
    skipToEnd: function() {this.pos = this.string.length;},
5793
    skipTo: function(ch) {
5794
      var found = this.string.indexOf(ch, this.pos);
5795
      if (found > -1) {this.pos = found; return true;}
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if found > -1 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...
5796
    },
5797
    backUp: function(n) {this.pos -= n;},
5798
    column: function() {
5799
      if (this.lastColumnPos < this.start) {
5800
        this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
5801
        this.lastColumnPos = this.start;
5802
      }
5803
      return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0);
5804
    },
5805
    indentation: function() {
5806
      return countColumn(this.string, null, this.tabSize) -
5807
        (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0);
5808
    },
5809
    match: function(pattern, consume, caseInsensitive) {
5810
      if (typeof pattern == "string") {
5811
        var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
5812
        var substr = this.string.substr(this.pos, pattern.length);
5813
        if (cased(substr) == cased(pattern)) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if cased(substr) == cased(pattern) 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...
5814
          if (consume !== false) this.pos += pattern.length;
5815
          return true;
5816
        }
5817
      } else {
5818
        var match = this.string.slice(this.pos).match(pattern);
5819
        if (match && match.index > 0) return null;
5820
        if (match && consume !== false) this.pos += match[0].length;
5821
        return match;
5822
      }
5823
    },
5824
    current: function(){return this.string.slice(this.start, this.pos);},
5825
    hideFirstChars: function(n, inner) {
5826
      this.lineStart += n;
5827
      try { return inner(); }
5828
      finally { this.lineStart -= n; }
5829
    }
5830
  };
5831
5832
  // TEXTMARKERS
5833
5834
  // Created with markText and setBookmark methods. A TextMarker is a
5835
  // handle that can be used to clear or find a marked position in the
5836
  // document. Line objects hold arrays (markedSpans) containing
5837
  // {from, to, marker} object pointing to such marker objects, and
5838
  // indicating that such a marker is present on that line. Multiple
5839
  // lines may point to the same marker when it spans across lines.
5840
  // The spans will have null for their from/to properties when the
5841
  // marker continues beyond the start/end of the line. Markers have
5842
  // links back to the lines they currently touch.
5843
5844
  var nextMarkerId = 0;
5845
5846
  var TextMarker = CodeMirror.TextMarker = function(doc, type) {
5847
    this.lines = [];
5848
    this.type = type;
5849
    this.doc = doc;
5850
    this.id = ++nextMarkerId;
5851
  };
5852
  eventMixin(TextMarker);
5853
5854
  // Clear the marker.
5855
  TextMarker.prototype.clear = function() {
5856
    if (this.explicitlyCleared) return;
5857
    var cm = this.doc.cm, withOp = cm && !cm.curOp;
5858
    if (withOp) startOperation(cm);
5859
    if (hasHandler(this, "clear")) {
5860
      var found = this.find();
5861
      if (found) signalLater(this, "clear", found.from, found.to);
5862
    }
5863
    var min = null, max = null;
5864
    for (var i = 0; i < this.lines.length; ++i) {
5865
      var line = this.lines[i];
5866
      var span = getMarkedSpanFor(line.markedSpans, this);
5867
      if (cm && !this.collapsed) regLineChange(cm, lineNo(line), "text");
5868
      else if (cm) {
5869
        if (span.to != null) max = lineNo(line);
0 ignored issues
show
Best Practice introduced by
Comparing span.to to null using the != operator is not safe. Consider using !== instead.
Loading history...
5870
        if (span.from != null) min = lineNo(line);
0 ignored issues
show
Best Practice introduced by
Comparing span.from to null using the != operator is not safe. Consider using !== instead.
Loading history...
5871
      }
5872
      line.markedSpans = removeMarkedSpan(line.markedSpans, span);
5873
      if (span.from == null && this.collapsed && !lineIsHidden(this.doc, line) && cm)
5874
        updateLineHeight(line, textHeight(cm.display));
5875
    }
5876
    if (cm && this.collapsed && !cm.options.lineWrapping) for (var i = 0; i < this.lines.length; ++i) {
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 5864. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
5877
      var visual = visualLine(this.lines[i]), len = lineLength(visual);
5878
      if (len > cm.display.maxLineLength) {
5879
        cm.display.maxLine = visual;
5880
        cm.display.maxLineLength = len;
5881
        cm.display.maxLineChanged = true;
5882
      }
5883
    }
5884
5885
    if (min != null && cm && this.collapsed) regChange(cm, min, max + 1);
0 ignored issues
show
Best Practice introduced by
Comparing min to null using the != operator is not safe. Consider using !== instead.
Loading history...
5886
    this.lines.length = 0;
5887
    this.explicitlyCleared = true;
5888
    if (this.atomic && this.doc.cantEdit) {
5889
      this.doc.cantEdit = false;
5890
      if (cm) reCheckSelection(cm.doc);
5891
    }
5892
    if (cm) signalLater(cm, "markerCleared", cm, this);
5893
    if (withOp) endOperation(cm);
5894
    if (this.parent) this.parent.clear();
5895
  };
5896
5897
  // Find the position of the marker in the document. Returns a {from,
5898
  // to} object by default. Side can be passed to get a specific side
5899
  // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the
5900
  // Pos objects returned contain a line object, rather than a line
5901
  // number (used to prevent looking up the same line twice).
5902
  TextMarker.prototype.find = function(side, lineObj) {
5903
    if (side == null && this.type == "bookmark") side = 1;
0 ignored issues
show
Best Practice introduced by
Comparing side to null using the == operator is not safe. Consider using === instead.
Loading history...
5904
    var from, to;
5905
    for (var i = 0; i < this.lines.length; ++i) {
5906
      var line = this.lines[i];
5907
      var span = getMarkedSpanFor(line.markedSpans, this);
5908
      if (span.from != null) {
0 ignored issues
show
Best Practice introduced by
Comparing span.from to null using the != operator is not safe. Consider using !== instead.
Loading history...
5909
        from = Pos(lineObj ? line : lineNo(line), span.from);
5910
        if (side == -1) return from;
5911
      }
5912
      if (span.to != null) {
0 ignored issues
show
Best Practice introduced by
Comparing span.to to null using the != operator is not safe. Consider using !== instead.
Loading history...
5913
        to = Pos(lineObj ? line : lineNo(line), span.to);
5914
        if (side == 1) return to;
5915
      }
5916
    }
5917
    return from && {from: from, to: to};
0 ignored issues
show
Bug introduced by
The variable to seems to not be initialized for all possible execution paths.
Loading history...
5918
  };
5919
5920
  // Signals that the marker's widget changed, and surrounding layout
5921
  // should be recomputed.
5922
  TextMarker.prototype.changed = function() {
5923
    var pos = this.find(-1, true), widget = this, cm = this.doc.cm;
5924
    if (!pos || !cm) return;
5925
    runInOp(cm, function() {
5926
      var line = pos.line, lineN = lineNo(pos.line);
5927
      var view = findViewForLine(cm, lineN);
5928
      if (view) {
5929
        clearLineMeasurementCacheFor(view);
5930
        cm.curOp.selectionChanged = cm.curOp.forceUpdate = true;
5931
      }
5932
      cm.curOp.updateMaxLine = true;
5933
      if (!lineIsHidden(widget.doc, line) && widget.height != null) {
0 ignored issues
show
Best Practice introduced by
Comparing widget.height to null using the != operator is not safe. Consider using !== instead.
Loading history...
5934
        var oldHeight = widget.height;
5935
        widget.height = null;
5936
        var dHeight = widgetHeight(widget) - oldHeight;
5937
        if (dHeight)
5938
          updateLineHeight(line, line.height + dHeight);
5939
      }
5940
    });
5941
  };
5942
5943
  TextMarker.prototype.attachLine = function(line) {
5944
    if (!this.lines.length && this.doc.cm) {
5945
      var op = this.doc.cm.curOp;
5946
      if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
5947
        (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this);
5948
    }
5949
    this.lines.push(line);
5950
  };
5951
  TextMarker.prototype.detachLine = function(line) {
5952
    this.lines.splice(indexOf(this.lines, line), 1);
5953
    if (!this.lines.length && this.doc.cm) {
5954
      var op = this.doc.cm.curOp;
5955
      (op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this);
5956
    }
5957
  };
5958
5959
  // Collapsed markers have unique ids, in order to be able to order
5960
  // them, which is needed for uniquely determining an outer marker
5961
  // when they overlap (they may nest, but not partially overlap).
5962
  var nextMarkerId = 0;
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable nextMarkerId already seems to be declared on line 5844. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
5963
5964
  // Create a marker, wire it up to the right lines, and
5965
  function markText(doc, from, to, options, type) {
5966
    // Shared markers (across linked documents) are handled separately
5967
    // (markTextShared will call out to this again, once per
5968
    // document).
5969
    if (options && options.shared) return markTextShared(doc, from, to, options, type);
5970
    // Ensure we are in an operation.
5971
    if (doc.cm && !doc.cm.curOp) return operation(doc.cm, markText)(doc, from, to, options, type);
5972
5973
    var marker = new TextMarker(doc, type), diff = cmp(from, to);
5974
    if (options) copyObj(options, marker, false);
5975
    // Don't connect empty markers unless clearWhenEmpty is false
5976
    if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false)
0 ignored issues
show
Best Practice introduced by
Comparing diff to 0 using the == operator is not safe. Consider using === instead.
Loading history...
5977
      return marker;
5978
    if (marker.replacedWith) {
5979
      // Showing up as a widget implies collapsed (widget replaces text)
5980
      marker.collapsed = true;
5981
      marker.widgetNode = elt("span", [marker.replacedWith], "CodeMirror-widget");
5982
      if (!options.handleMouseEvents) marker.widgetNode.setAttribute("cm-ignore-events", "true");
5983
      if (options.insertLeft) marker.widgetNode.insertLeft = true;
5984
    }
5985
    if (marker.collapsed) {
5986
      if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||
5987
          from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker))
5988
        throw new Error("Inserting collapsed marker partially overlapping an existing one");
5989
      sawCollapsedSpans = true;
5990
    }
5991
5992
    if (marker.addToHistory)
5993
      addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN);
5994
5995
    var curLine = from.line, cm = doc.cm, updateMaxLine;
5996
    doc.iter(curLine, to.line + 1, function(line) {
5997
      if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine)
5998
        updateMaxLine = true;
5999
      if (marker.collapsed && curLine != from.line) updateLineHeight(line, 0);
6000
      addMarkedSpan(line, new MarkedSpan(marker,
6001
                                         curLine == from.line ? from.ch : null,
6002
                                         curLine == to.line ? to.ch : null));
6003
      ++curLine;
6004
    });
6005
    // lineIsHidden depends on the presence of the spans, so needs a second pass
6006
    if (marker.collapsed) doc.iter(from.line, to.line + 1, function(line) {
6007
      if (lineIsHidden(doc, line)) updateLineHeight(line, 0);
6008
    });
6009
6010
    if (marker.clearOnEnter) on(marker, "beforeCursorEnter", function() { marker.clear(); });
6011
6012
    if (marker.readOnly) {
6013
      sawReadOnlySpans = true;
6014
      if (doc.history.done.length || doc.history.undone.length)
6015
        doc.clearHistory();
6016
    }
6017
    if (marker.collapsed) {
6018
      marker.id = ++nextMarkerId;
6019
      marker.atomic = true;
6020
    }
6021
    if (cm) {
6022
      // Sync editor state
6023
      if (updateMaxLine) cm.curOp.updateMaxLine = true;
6024
      if (marker.collapsed)
6025
        regChange(cm, from.line, to.line + 1);
6026
      else if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.css)
6027
        for (var i = from.line; i <= to.line; i++) regLineChange(cm, i, "text");
6028
      if (marker.atomic) reCheckSelection(cm.doc);
6029
      signalLater(cm, "markerAdded", cm, marker);
6030
    }
6031
    return marker;
6032
  }
6033
6034
  // SHARED TEXTMARKERS
6035
6036
  // A shared marker spans multiple linked documents. It is
6037
  // implemented as a meta-marker-object controlling multiple normal
6038
  // markers.
6039
  var SharedTextMarker = CodeMirror.SharedTextMarker = function(markers, primary) {
6040
    this.markers = markers;
6041
    this.primary = primary;
6042
    for (var i = 0; i < markers.length; ++i)
6043
      markers[i].parent = this;
6044
  };
6045
  eventMixin(SharedTextMarker);
6046
6047
  SharedTextMarker.prototype.clear = function() {
6048
    if (this.explicitlyCleared) return;
6049
    this.explicitlyCleared = true;
6050
    for (var i = 0; i < this.markers.length; ++i)
6051
      this.markers[i].clear();
6052
    signalLater(this, "clear");
6053
  };
6054
  SharedTextMarker.prototype.find = function(side, lineObj) {
6055
    return this.primary.find(side, lineObj);
6056
  };
6057
6058
  function markTextShared(doc, from, to, options, type) {
6059
    options = copyObj(options);
6060
    options.shared = false;
6061
    var markers = [markText(doc, from, to, options, type)], primary = markers[0];
6062
    var widget = options.widgetNode;
6063
    linkedDocs(doc, function(doc) {
6064
      if (widget) options.widgetNode = widget.cloneNode(true);
6065
      markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));
6066
      for (var i = 0; i < doc.linked.length; ++i)
6067
        if (doc.linked[i].isParent) return;
6068
      primary = lst(markers);
6069
    });
6070
    return new SharedTextMarker(markers, primary);
6071
  }
6072
6073
  function findSharedMarkers(doc) {
6074
    return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())),
6075
                         function(m) { return m.parent; });
6076
  }
6077
6078
  function copySharedMarkers(doc, markers) {
6079
    for (var i = 0; i < markers.length; i++) {
6080
      var marker = markers[i], pos = marker.find();
6081
      var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to);
6082
      if (cmp(mFrom, mTo)) {
6083
        var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type);
6084
        marker.markers.push(subMark);
6085
        subMark.parent = marker;
6086
      }
6087
    }
6088
  }
6089
6090
  function detachSharedMarkers(markers) {
6091
    for (var i = 0; i < markers.length; i++) {
6092
      var marker = markers[i], linked = [marker.primary.doc];;
6093
      linkedDocs(marker.primary.doc, function(d) { linked.push(d); });
0 ignored issues
show
Bug introduced by
The variable linked is changed as part of the for loop for example by [marker.primary.doc] on line 6092. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
6094
      for (var j = 0; j < marker.markers.length; j++) {
6095
        var subMarker = marker.markers[j];
6096
        if (indexOf(linked, subMarker.doc) == -1) {
6097
          subMarker.parent = null;
6098
          marker.markers.splice(j--, 1);
0 ignored issues
show
Complexity Coding Style introduced by
You seem to be assigning a new value to the loop variable j here. Please check if this was indeed your intention. Even if it was, consider using another kind of loop instead.
Loading history...
6099
        }
6100
      }
6101
    }
6102
  }
6103
6104
  // TEXTMARKER SPANS
6105
6106
  function MarkedSpan(marker, from, to) {
6107
    this.marker = marker;
6108
    this.from = from; this.to = to;
6109
  }
6110
6111
  // Search an array of spans for a span matching the given marker.
6112
  function getMarkedSpanFor(spans, marker) {
6113
    if (spans) for (var i = 0; i < spans.length; ++i) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if spans 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...
6114
      var span = spans[i];
6115
      if (span.marker == marker) return span;
6116
    }
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...
6117
  }
6118
  // Remove a span from an array, returning undefined if no spans are
6119
  // left (we don't store arrays for lines without spans).
6120
  function removeMarkedSpan(spans, span) {
6121
    for (var r, i = 0; i < spans.length; ++i)
6122
      if (spans[i] != span) (r || (r = [])).push(spans[i]);
6123
    return r;
0 ignored issues
show
Bug introduced by
The variable r seems to not be initialized for all possible execution paths.
Loading history...
6124
  }
6125
  // Add a span to a line.
6126
  function addMarkedSpan(line, span) {
6127
    line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
6128
    span.marker.attachLine(line);
6129
  }
6130
6131
  // Used for the algorithm that adjusts markers for a change in the
6132
  // document. These functions cut an array of spans at a given
6133
  // character position, returning an array of remaining chunks (or
6134
  // undefined if nothing remains).
6135
  function markedSpansBefore(old, startCh, isInsert) {
6136
    if (old) for (var i = 0, nw; i < old.length; ++i) {
6137
      var span = old[i], marker = span.marker;
6138
      var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
0 ignored issues
show
Best Practice introduced by
Comparing span.from to null using the == operator is not safe. Consider using === instead.
Loading history...
6139
      if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) {
6140
        var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh);
0 ignored issues
show
Best Practice introduced by
Comparing span.to to null using the == operator is not safe. Consider using === instead.
Loading history...
6141
        (nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to));
6142
      }
6143
    }
6144
    return nw;
0 ignored issues
show
Bug introduced by
The variable nw seems to not be initialized for all possible execution paths.
Loading history...
6145
  }
6146
  function markedSpansAfter(old, endCh, isInsert) {
6147
    if (old) for (var i = 0, nw; i < old.length; ++i) {
6148
      var span = old[i], marker = span.marker;
6149
      var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
0 ignored issues
show
Best Practice introduced by
Comparing span.to to null using the == operator is not safe. Consider using === instead.
Loading history...
6150
      if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) {
6151
        var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh);
0 ignored issues
show
Best Practice introduced by
Comparing span.from to null using the == operator is not safe. Consider using === instead.
Loading history...
6152
        (nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh,
6153
                                              span.to == null ? null : span.to - endCh));
6154
      }
6155
    }
6156
    return nw;
0 ignored issues
show
Bug introduced by
The variable nw seems to not be initialized for all possible execution paths.
Loading history...
6157
  }
6158
6159
  // Given a change object, compute the new set of marker spans that
6160
  // cover the line in which the change took place. Removes spans
6161
  // entirely within the change, reconnects spans belonging to the
6162
  // same marker that appear on both sides of the change, and cuts off
6163
  // spans partially within the change. Returns an array of span
6164
  // arrays with one element for each line in (after) the change.
6165
  function stretchSpansOverChange(doc, change) {
6166
    if (change.full) return null;
6167
    var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans;
6168
    var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans;
6169
    if (!oldFirst && !oldLast) return null;
6170
6171
    var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0;
0 ignored issues
show
Best Practice introduced by
Comparing cmp(change.from, change.to) to 0 using the == operator is not safe. Consider using === instead.
Loading history...
6172
    // Get the spans that 'stick out' on both sides
6173
    var first = markedSpansBefore(oldFirst, startCh, isInsert);
6174
    var last = markedSpansAfter(oldLast, endCh, isInsert);
6175
6176
    // Next, merge those two ends
6177
    var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0);
0 ignored issues
show
Best Practice introduced by
Comparing change.text.length to 1 using the == operator is not safe. Consider using === instead.
Loading history...
6178
    if (first) {
6179
      // Fix up .to properties of first
6180
      for (var i = 0; i < first.length; ++i) {
6181
        var span = first[i];
6182
        if (span.to == null) {
0 ignored issues
show
Best Practice introduced by
Comparing span.to to null using the == operator is not safe. Consider using === instead.
Loading history...
6183
          var found = getMarkedSpanFor(last, span.marker);
6184
          if (!found) span.to = startCh;
6185
          else if (sameLine) span.to = found.to == null ? null : found.to + offset;
0 ignored issues
show
Best Practice introduced by
Comparing found.to to null using the == operator is not safe. Consider using === instead.
Loading history...
6186
        }
6187
      }
6188
    }
6189
    if (last) {
6190
      // Fix up .from in last (or move them into first in case of sameLine)
6191
      for (var i = 0; i < last.length; ++i) {
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 6180. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
6192
        var span = last[i];
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable span already seems to be declared on line 6181. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
6193
        if (span.to != null) span.to += offset;
6194
        if (span.from == null) {
0 ignored issues
show
Best Practice introduced by
Comparing span.from to null using the == operator is not safe. Consider using === instead.
Loading history...
6195
          var found = getMarkedSpanFor(first, span.marker);
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable found already seems to be declared on line 6183. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
6196
          if (!found) {
6197
            span.from = offset;
6198
            if (sameLine) (first || (first = [])).push(span);
6199
          }
6200
        } else {
6201
          span.from += offset;
6202
          if (sameLine) (first || (first = [])).push(span);
6203
        }
6204
      }
6205
    }
6206
    // Make sure we didn't create any zero-length spans
6207
    if (first) first = clearEmptySpans(first);
6208
    if (last && last != first) last = clearEmptySpans(last);
6209
6210
    var newMarkers = [first];
6211
    if (!sameLine) {
6212
      // Fill gap with whole-line-spans
6213
      var gap = change.text.length - 2, gapMarkers;
6214
      if (gap > 0 && first)
6215
        for (var i = 0; i < first.length; ++i)
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 6180. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
6216
          if (first[i].to == null)
0 ignored issues
show
Best Practice introduced by
Comparing first.i.to to null using the == operator is not safe. Consider using === instead.
Loading history...
6217
            (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i].marker, null, null));
6218
      for (var i = 0; i < gap; ++i)
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 6180. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
6219
        newMarkers.push(gapMarkers);
0 ignored issues
show
Bug introduced by
The variable gapMarkers seems to not be initialized for all possible execution paths. Are you sure push handles undefined variables?
Loading history...
6220
      newMarkers.push(last);
6221
    }
6222
    return newMarkers;
6223
  }
6224
6225
  // Remove spans that are empty and don't have a clearWhenEmpty
6226
  // option of false.
6227
  function clearEmptySpans(spans) {
6228
    for (var i = 0; i < spans.length; ++i) {
6229
      var span = spans[i];
6230
      if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)
0 ignored issues
show
Best Practice introduced by
Comparing span.from to null using the != operator is not safe. Consider using !== instead.
Loading history...
6231
        spans.splice(i--, 1);
0 ignored issues
show
Complexity Coding Style introduced by
You seem to be assigning a new value to the loop variable i here. Please check if this was indeed your intention. Even if it was, consider using another kind of loop instead.
Loading history...
6232
    }
6233
    if (!spans.length) return null;
6234
    return spans;
6235
  }
6236
6237
  // Used for un/re-doing changes from the history. Combines the
6238
  // result of computing the existing spans with the set of spans that
6239
  // existed in the history (so that deleting around a span and then
6240
  // undoing brings back the span).
6241
  function mergeOldSpans(doc, change) {
6242
    var old = getOldSpans(doc, change);
6243
    var stretched = stretchSpansOverChange(doc, change);
6244
    if (!old) return stretched;
6245
    if (!stretched) return old;
6246
6247
    for (var i = 0; i < old.length; ++i) {
6248
      var oldCur = old[i], stretchCur = stretched[i];
6249
      if (oldCur && stretchCur) {
6250
        spans: for (var j = 0; j < stretchCur.length; ++j) {
6251
          var span = stretchCur[j];
6252
          for (var k = 0; k < oldCur.length; ++k)
6253
            if (oldCur[k].marker == span.marker) continue spans;
6254
          oldCur.push(span);
6255
        }
6256
      } else if (stretchCur) {
6257
        old[i] = stretchCur;
6258
      }
6259
    }
6260
    return old;
6261
  }
6262
6263
  // Used to 'clip' out readOnly ranges when making a change.
6264
  function removeReadOnlyRanges(doc, from, to) {
6265
    var markers = null;
6266
    doc.iter(from.line, to.line + 1, function(line) {
6267
      if (line.markedSpans) for (var i = 0; i < line.markedSpans.length; ++i) {
6268
        var mark = line.markedSpans[i].marker;
6269
        if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
0 ignored issues
show
Bug introduced by
The variable markers is changed as part of the for loop for example by [] on line 6270. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
6270
          (markers || (markers = [])).push(mark);
6271
      }
6272
    });
6273
    if (!markers) return null;
6274
    var parts = [{from: from, to: to}];
6275
    for (var i = 0; i < markers.length; ++i) {
6276
      var mk = markers[i], m = mk.find(0);
6277
      for (var j = 0; j < parts.length; ++j) {
6278
        var p = parts[j];
6279
        if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) continue;
6280
        var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to);
6281
        if (dfrom < 0 || !mk.inclusiveLeft && !dfrom)
6282
          newParts.push({from: p.from, to: m.from});
6283
        if (dto > 0 || !mk.inclusiveRight && !dto)
6284
          newParts.push({from: m.to, to: p.to});
6285
        parts.splice.apply(parts, newParts);
6286
        j += newParts.length - 1;
0 ignored issues
show
Complexity Coding Style introduced by
You seem to be assigning a new value to the loop variable j here. Please check if this was indeed your intention. Even if it was, consider using another kind of loop instead.
Loading history...
6287
      }
6288
    }
6289
    return parts;
6290
  }
6291
6292
  // Connect or disconnect spans from a line.
6293
  function detachMarkedSpans(line) {
6294
    var spans = line.markedSpans;
6295
    if (!spans) return;
6296
    for (var i = 0; i < spans.length; ++i)
6297
      spans[i].marker.detachLine(line);
6298
    line.markedSpans = null;
6299
  }
6300
  function attachMarkedSpans(line, spans) {
6301
    if (!spans) return;
6302
    for (var i = 0; i < spans.length; ++i)
6303
      spans[i].marker.attachLine(line);
6304
    line.markedSpans = spans;
6305
  }
6306
6307
  // Helpers used when computing which overlapping collapsed span
6308
  // counts as the larger one.
6309
  function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0; }
6310
  function extraRight(marker) { return marker.inclusiveRight ? 1 : 0; }
6311
6312
  // Returns a number indicating which of two overlapping collapsed
6313
  // spans is larger (and thus includes the other). Falls back to
6314
  // comparing ids when the spans cover exactly the same range.
6315
  function compareCollapsedMarkers(a, b) {
6316
    var lenDiff = a.lines.length - b.lines.length;
6317
    if (lenDiff != 0) return lenDiff;
0 ignored issues
show
Best Practice introduced by
Comparing lenDiff to 0 using the != operator is not safe. Consider using !== instead.
Loading history...
6318
    var aPos = a.find(), bPos = b.find();
6319
    var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b);
6320
    if (fromCmp) return -fromCmp;
6321
    var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b);
6322
    if (toCmp) return toCmp;
6323
    return b.id - a.id;
6324
  }
6325
6326
  // Find out whether a line ends or starts in a collapsed span. If
6327
  // so, return the marker for that span.
6328
  function collapsedSpanAtSide(line, start) {
6329
    var sps = sawCollapsedSpans && line.markedSpans, found;
6330
    if (sps) for (var sp, i = 0; i < sps.length; ++i) {
6331
      sp = sps[i];
6332
      if (sp.marker.collapsed && (start ? sp.from : sp.to) == null &&
0 ignored issues
show
Best Practice introduced by
Comparing start ? sp.from: sp.to to null using the == operator is not safe. Consider using === instead.
Loading history...
6333
          (!found || compareCollapsedMarkers(found, sp.marker) < 0))
6334
        found = sp.marker;
6335
    }
6336
    return found;
0 ignored issues
show
Bug introduced by
The variable found seems to not be initialized for all possible execution paths.
Loading history...
6337
  }
6338
  function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true); }
6339
  function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false); }
6340
6341
  // Test whether there exists a collapsed span that partially
6342
  // overlaps (covers the start or end, but not both) of a new span.
6343
  // Such overlap is not allowed.
6344
  function conflictingCollapsedRange(doc, lineNo, from, to, marker) {
6345
    var line = getLine(doc, lineNo);
6346
    var sps = sawCollapsedSpans && line.markedSpans;
6347
    if (sps) for (var i = 0; i < sps.length; ++i) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if sps 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...
6348
      var sp = sps[i];
6349
      if (!sp.marker.collapsed) continue;
6350
      var found = sp.marker.find(0);
6351
      var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker);
6352
      var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker);
6353
      if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) continue;
6354
      if (fromCmp <= 0 && (cmp(found.to, from) > 0 || (sp.marker.inclusiveRight && marker.inclusiveLeft)) ||
6355
          fromCmp >= 0 && (cmp(found.from, to) < 0 || (sp.marker.inclusiveLeft && marker.inclusiveRight)))
6356
        return true;
6357
    }
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...
6358
  }
6359
6360
  // A visual line is a line as drawn on the screen. Folding, for
6361
  // example, can cause multiple logical lines to appear on the same
6362
  // visual line. This finds the start of the visual line that the
6363
  // given line is part of (usually that is the line itself).
6364
  function visualLine(line) {
6365
    var merged;
6366
    while (merged = collapsedSpanAtStart(line))
6367
      line = merged.find(-1, true).line;
6368
    return line;
6369
  }
6370
6371
  // Returns an array of logical lines that continue the visual line
6372
  // started by the argument, or undefined if there are no such lines.
6373
  function visualLineContinued(line) {
6374
    var merged, lines;
6375
    while (merged = collapsedSpanAtEnd(line)) {
6376
      line = merged.find(1, true).line;
6377
      (lines || (lines = [])).push(line);
6378
    }
6379
    return lines;
0 ignored issues
show
Comprehensibility Bug introduced by
The variable lines does not seem to be initialized in case the while loop on line 6375 is not entered. Are you sure this can never be the case?
Loading history...
6380
  }
6381
6382
  // Get the line number of the start of the visual line that the
6383
  // given line number is part of.
6384
  function visualLineNo(doc, lineN) {
6385
    var line = getLine(doc, lineN), vis = visualLine(line);
6386
    if (line == vis) return lineN;
6387
    return lineNo(vis);
6388
  }
6389
  // Get the line number of the start of the next visual line after
6390
  // the given line.
6391
  function visualLineEndNo(doc, lineN) {
6392
    if (lineN > doc.lastLine()) return lineN;
6393
    var line = getLine(doc, lineN), merged;
6394
    if (!lineIsHidden(doc, line)) return lineN;
6395
    while (merged = collapsedSpanAtEnd(line))
6396
      line = merged.find(1, true).line;
6397
    return lineNo(line) + 1;
6398
  }
6399
6400
  // Compute whether a line is hidden. Lines count as hidden when they
6401
  // are part of a visual line that starts with another line, or when
6402
  // they are entirely covered by collapsed, non-widget span.
6403
  function lineIsHidden(doc, line) {
6404
    var sps = sawCollapsedSpans && line.markedSpans;
6405
    if (sps) for (var sp, i = 0; i < sps.length; ++i) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if sps 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...
6406
      sp = sps[i];
6407
      if (!sp.marker.collapsed) continue;
6408
      if (sp.from == null) return true;
0 ignored issues
show
Best Practice introduced by
Comparing sp.from to null using the == operator is not safe. Consider using === instead.
Loading history...
6409
      if (sp.marker.widgetNode) continue;
6410
      if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
0 ignored issues
show
Best Practice introduced by
Comparing sp.from to 0 using the == operator is not safe. Consider using === instead.
Loading history...
6411
        return true;
6412
    }
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...
6413
  }
6414
  function lineIsHiddenInner(doc, line, span) {
6415
    if (span.to == null) {
0 ignored issues
show
Best Practice introduced by
Comparing span.to to null using the == operator is not safe. Consider using === instead.
Loading history...
6416
      var end = span.marker.find(1, true);
6417
      return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker));
6418
    }
6419
    if (span.marker.inclusiveRight && span.to == line.text.length)
6420
      return true;
6421
    for (var sp, i = 0; i < line.markedSpans.length; ++i) {
6422
      sp = line.markedSpans[i];
6423
      if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to &&
6424
          (sp.to == null || sp.to != span.from) &&
0 ignored issues
show
Best Practice introduced by
Comparing sp.to to null using the == operator is not safe. Consider using === instead.
Loading history...
6425
          (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
6426
          lineIsHiddenInner(doc, line, sp)) return true;
6427
    }
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...
6428
  }
6429
6430
  // LINE WIDGETS
6431
6432
  // Line widgets are block elements displayed above or below a line.
6433
6434
  var LineWidget = CodeMirror.LineWidget = function(cm, node, options) {
6435
    if (options) for (var opt in options) if (options.hasOwnProperty(opt))
6436
      this[opt] = options[opt];
6437
    this.cm = cm;
6438
    this.node = node;
6439
  };
6440
  eventMixin(LineWidget);
6441
6442
  function adjustScrollWhenAboveVisible(cm, line, diff) {
6443
    if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))
6444
      addToScrollPos(cm, null, diff);
6445
  }
6446
6447
  LineWidget.prototype.clear = function() {
6448
    var cm = this.cm, ws = this.line.widgets, line = this.line, no = lineNo(line);
6449
    if (no == null || !ws) return;
0 ignored issues
show
Best Practice introduced by
Comparing no to null using the == operator is not safe. Consider using === instead.
Loading history...
6450
    for (var i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1);
0 ignored issues
show
Complexity Coding Style introduced by
You seem to be assigning a new value to the loop variable i here. Please check if this was indeed your intention. Even if it was, consider using another kind of loop instead.
Loading history...
6451
    if (!ws.length) line.widgets = null;
6452
    var height = widgetHeight(this);
6453
    runInOp(cm, function() {
6454
      adjustScrollWhenAboveVisible(cm, line, -height);
6455
      regLineChange(cm, no, "widget");
6456
      updateLineHeight(line, Math.max(0, line.height - height));
6457
    });
6458
  };
6459
  LineWidget.prototype.changed = function() {
6460
    var oldH = this.height, cm = this.cm, line = this.line;
6461
    this.height = null;
6462
    var diff = widgetHeight(this) - oldH;
6463
    if (!diff) return;
6464
    runInOp(cm, function() {
6465
      cm.curOp.forceUpdate = true;
6466
      adjustScrollWhenAboveVisible(cm, line, diff);
6467
      updateLineHeight(line, line.height + diff);
6468
    });
6469
  };
6470
6471
  function widgetHeight(widget) {
6472
    if (widget.height != null) return widget.height;
0 ignored issues
show
Best Practice introduced by
Comparing widget.height to null using the != operator is not safe. Consider using !== instead.
Loading history...
6473
    if (!contains(document.body, widget.node)) {
6474
      var parentStyle = "position: relative;";
6475
      if (widget.coverGutter)
6476
        parentStyle += "margin-left: -" + widget.cm.display.gutters.offsetWidth + "px;";
6477
      if (widget.noHScroll)
6478
        parentStyle += "width: " + widget.cm.display.wrapper.clientWidth + "px;";
6479
      removeChildrenAndAdd(widget.cm.display.measure, elt("div", [widget.node], null, parentStyle));
6480
    }
6481
    return widget.height = widget.node.offsetHeight;
6482
  }
6483
6484
  function addLineWidget(cm, handle, node, options) {
6485
    var widget = new LineWidget(cm, node, options);
6486
    if (widget.noHScroll) cm.display.alignWidgets = true;
6487
    changeLine(cm.doc, handle, "widget", function(line) {
6488
      var widgets = line.widgets || (line.widgets = []);
6489
      if (widget.insertAt == null) widgets.push(widget);
0 ignored issues
show
Best Practice introduced by
Comparing widget.insertAt to null using the == operator is not safe. Consider using === instead.
Loading history...
6490
      else widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget);
6491
      widget.line = line;
6492
      if (!lineIsHidden(cm.doc, line)) {
6493
        var aboveVisible = heightAtLine(line) < cm.doc.scrollTop;
6494
        updateLineHeight(line, line.height + widgetHeight(widget));
6495
        if (aboveVisible) addToScrollPos(cm, null, widget.height);
6496
        cm.curOp.forceUpdate = true;
6497
      }
6498
      return true;
6499
    });
6500
    return widget;
6501
  }
6502
6503
  // LINE DATA STRUCTURE
6504
6505
  // Line objects. These hold state related to a line, including
6506
  // highlighting info (the styles array).
6507
  var Line = CodeMirror.Line = function(text, markedSpans, estimateHeight) {
6508
    this.text = text;
6509
    attachMarkedSpans(this, markedSpans);
6510
    this.height = estimateHeight ? estimateHeight(this) : 1;
6511
  };
6512
  eventMixin(Line);
6513
  Line.prototype.lineNo = function() { return lineNo(this); };
6514
6515
  // Change the content (text, markers) of a line. Automatically
6516
  // invalidates cached information and tries to re-estimate the
6517
  // line's height.
6518
  function updateLine(line, text, markedSpans, estimateHeight) {
6519
    line.text = text;
6520
    if (line.stateAfter) line.stateAfter = null;
6521
    if (line.styles) line.styles = null;
6522
    if (line.order != null) line.order = null;
0 ignored issues
show
Best Practice introduced by
Comparing line.order to null using the != operator is not safe. Consider using !== instead.
Loading history...
6523
    detachMarkedSpans(line);
6524
    attachMarkedSpans(line, markedSpans);
6525
    var estHeight = estimateHeight ? estimateHeight(line) : 1;
6526
    if (estHeight != line.height) updateLineHeight(line, estHeight);
6527
  }
6528
6529
  // Detach a line from the document tree and its markers.
6530
  function cleanUpLine(line) {
6531
    line.parent = null;
6532
    detachMarkedSpans(line);
6533
  }
6534
6535
  function extractLineClasses(type, output) {
6536
    if (type) for (;;) {
6537
      var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/);
6538
      if (!lineClass) break;
6539
      type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length);
6540
      var prop = lineClass[1] ? "bgClass" : "textClass";
6541
      if (output[prop] == null)
0 ignored issues
show
Best Practice introduced by
Comparing output.prop to null using the == operator is not safe. Consider using === instead.
Loading history...
6542
        output[prop] = lineClass[2];
6543
      else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop]))
6544
        output[prop] += " " + lineClass[2];
6545
    }
6546
    return type;
6547
  }
6548
6549
  function callBlankLine(mode, state) {
6550
    if (mode.blankLine) return mode.blankLine(state);
6551
    if (!mode.innerMode) 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...
6552
    var inner = CodeMirror.innerMode(mode, state);
6553
    if (inner.mode.blankLine) return inner.mode.blankLine(inner.state);
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if inner.mode.blankLine 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...
6554
  }
6555
6556
  function readToken(mode, stream, state, inner) {
6557
    for (var i = 0; i < 10; i++) {
6558
      if (inner) inner[0] = CodeMirror.innerMode(mode, state).mode;
6559
      var style = mode.token(stream, state);
6560
      if (stream.pos > stream.start) return style;
6561
    }
6562
    throw new Error("Mode " + mode.name + " failed to advance stream.");
6563
  }
6564
6565
  // Utility for getTokenAt and getLineTokens
6566
  function takeToken(cm, pos, precise, asArray) {
6567
    function getObj(copy) {
6568
      return {start: stream.start, end: stream.pos,
6569
              string: stream.current(),
6570
              type: style || null,
6571
              state: copy ? copyState(doc.mode, state) : state};
6572
    }
6573
6574
    var doc = cm.doc, mode = doc.mode, style;
6575
    pos = clipPos(doc, pos);
6576
    var line = getLine(doc, pos.line), state = getStateBefore(cm, pos.line, precise);
6577
    var stream = new StringStream(line.text, cm.options.tabSize), tokens;
6578
    if (asArray) tokens = [];
6579
    while ((asArray || stream.pos < pos.ch) && !stream.eol()) {
6580
      stream.start = stream.pos;
6581
      style = readToken(mode, stream, state);
6582
      if (asArray) tokens.push(getObj(true));
0 ignored issues
show
Bug introduced by
The variable tokens does not seem to be initialized in case asArray on line 6578 is false. Are you sure this can never be the case?
Loading history...
6583
    }
6584
    return asArray ? tokens : getObj();
6585
  }
6586
6587
  // Run the given mode's parser over a line, calling f for each token.
6588
  function runMode(cm, text, mode, state, f, lineClasses, forceToEnd) {
6589
    var flattenSpans = mode.flattenSpans;
6590
    if (flattenSpans == null) flattenSpans = cm.options.flattenSpans;
0 ignored issues
show
Best Practice introduced by
Comparing flattenSpans to null using the == operator is not safe. Consider using === instead.
Loading history...
6591
    var curStart = 0, curStyle = null;
6592
    var stream = new StringStream(text, cm.options.tabSize), style;
6593
    var inner = cm.options.addModeClass && [null];
6594
    if (text == "") extractLineClasses(callBlankLine(mode, state), lineClasses);
6595
    while (!stream.eol()) {
6596
      if (stream.pos > cm.options.maxHighlightLength) {
6597
        flattenSpans = false;
6598
        if (forceToEnd) processLine(cm, text, state, stream.pos);
6599
        stream.pos = text.length;
6600
        style = null;
6601
      } else {
6602
        style = extractLineClasses(readToken(mode, stream, state, inner), lineClasses);
6603
      }
6604
      if (inner) {
6605
        var mName = inner[0].name;
6606
        if (mName) style = "m-" + (style ? mName + " " + style : mName);
6607
      }
6608
      if (!flattenSpans || curStyle != style) {
6609
        while (curStart < stream.start) {
6610
          curStart = Math.min(stream.start, curStart + 50000);
6611
          f(curStart, curStyle);
6612
        }
6613
        curStyle = style;
6614
      }
6615
      stream.start = stream.pos;
6616
    }
6617
    while (curStart < stream.pos) {
6618
      // Webkit seems to refuse to render text nodes longer than 57444 characters
6619
      var pos = Math.min(stream.pos, curStart + 50000);
6620
      f(pos, curStyle);
6621
      curStart = pos;
6622
    }
6623
  }
6624
6625
  // Compute a style array (an array starting with a mode generation
6626
  // -- for invalidation -- followed by pairs of end positions and
6627
  // style strings), which is used to highlight the tokens on the
6628
  // line.
6629
  function highlightLine(cm, line, state, forceToEnd) {
6630
    // A styles array always starts with a number identifying the
6631
    // mode/overlays that it is based on (for easy invalidation).
6632
    var st = [cm.state.modeGen], lineClasses = {};
6633
    // Compute the base array of styles
6634
    runMode(cm, line.text, cm.doc.mode, state, function(end, style) {
6635
      st.push(end, style);
6636
    }, lineClasses, forceToEnd);
6637
6638
    // Run overlays, adjust style array.
6639
    for (var o = 0; o < cm.state.overlays.length; ++o) {
6640
      var overlay = cm.state.overlays[o], i = 1, at = 0;
6641
      runMode(cm, line.text, overlay.mode, true, function(end, style) {
6642
        var start = i;
0 ignored issues
show
Bug introduced by
The variable i is changed as part of the for loop for example by 1 on line 6640. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
6643
        // Ensure there's a token end at the current position, and that i points at it
6644
        while (at < end) {
0 ignored issues
show
Bug introduced by
The variable at is changed as part of the while loop for example by Math.min(end, i_end) on line 6649. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
6645
          var i_end = st[i];
6646
          if (i_end > end)
6647
            st.splice(i, 1, end, st[i+1], i_end);
6648
          i += 2;
6649
          at = Math.min(end, i_end);
6650
        }
6651
        if (!style) return;
6652
        if (overlay.opaque) {
0 ignored issues
show
Bug introduced by
The variable overlay is changed as part of the for loop for example by cm.state.overlays.o on line 6640. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
6653
          st.splice(start, i - start, end, "cm-overlay " + style);
6654
          i = start + 2;
6655
        } else {
6656
          for (; start < i; start += 2) {
6657
            var cur = st[start+1];
6658
            st[start+1] = (cur ? cur + " " : "") + "cm-overlay " + style;
6659
          }
6660
        }
6661
      }, lineClasses);
6662
    }
6663
6664
    return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null};
6665
  }
6666
6667
  function getLineStyles(cm, line, updateFrontier) {
6668
    if (!line.styles || line.styles[0] != cm.state.modeGen) {
6669
      var result = highlightLine(cm, line, line.stateAfter = getStateBefore(cm, lineNo(line)));
6670
      line.styles = result.styles;
6671
      if (result.classes) line.styleClasses = result.classes;
6672
      else if (line.styleClasses) line.styleClasses = null;
6673
      if (updateFrontier === cm.doc.frontier) cm.doc.frontier++;
6674
    }
6675
    return line.styles;
6676
  }
6677
6678
  // Lightweight form of highlight -- proceed over this line and
6679
  // update state, but don't save a style array. Used for lines that
6680
  // aren't currently visible.
6681
  function processLine(cm, text, state, startAt) {
6682
    var mode = cm.doc.mode;
6683
    var stream = new StringStream(text, cm.options.tabSize);
6684
    stream.start = stream.pos = startAt || 0;
6685
    if (text == "") callBlankLine(mode, state);
6686
    while (!stream.eol() && stream.pos <= cm.options.maxHighlightLength) {
6687
      readToken(mode, stream, state);
6688
      stream.start = stream.pos;
6689
    }
6690
  }
6691
6692
  // Convert a style as returned by a mode (either null, or a string
6693
  // containing one or more styles) to a CSS style. This is cached,
6694
  // and also looks for line-wide styles.
6695
  var styleToClassCache = {}, styleToClassCacheWithMode = {};
6696
  function interpretTokenStyle(style, options) {
6697
    if (!style || /^\s*$/.test(style)) return null;
6698
    var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache;
6699
    return cache[style] ||
6700
      (cache[style] = style.replace(/\S+/g, "cm-$&"));
6701
  }
6702
6703
  // Render the DOM representation of the text of a line. Also builds
6704
  // up a 'line map', which points at the DOM nodes that represent
6705
  // specific stretches of text, and is used by the measuring code.
6706
  // The returned object contains the DOM node, this map, and
6707
  // information about line-wide styles that were set by the mode.
6708
  function buildLineContent(cm, lineView) {
6709
    // The padding-right forces the element to have a 'border', which
6710
    // is needed on Webkit to be able to get line-level bounding
6711
    // rectangles for it (in measureChar).
6712
    var content = elt("span", null, null, webkit ? "padding-right: .1px" : null);
6713
    var builder = {pre: elt("pre", [content]), content: content, col: 0, pos: 0, cm: cm};
6714
    lineView.measure = {};
6715
6716
    // Iterate over the logical lines that make up this visual line.
6717
    for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {
6718
      var line = i ? lineView.rest[i - 1] : lineView.line, order;
6719
      builder.pos = 0;
6720
      builder.addToken = buildToken;
6721
      // Optionally wire in some hacks into the token-rendering
6722
      // algorithm, to deal with browser quirks.
6723
      if ((ie || webkit) && cm.getOption("lineWrapping"))
6724
        builder.addToken = buildTokenSplitSpaces(builder.addToken);
6725
      if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line)))
6726
        builder.addToken = buildTokenBadBidi(builder.addToken, order);
6727
      builder.map = [];
6728
      var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line);
6729
      insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate));
6730
      if (line.styleClasses) {
6731
        if (line.styleClasses.bgClass)
6732
          builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || "");
6733
        if (line.styleClasses.textClass)
6734
          builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || "");
6735
      }
6736
6737
      // Ensure at least a single node is present, for measuring.
6738
      if (builder.map.length == 0)
0 ignored issues
show
Best Practice introduced by
Comparing builder.map.length to 0 using the == operator is not safe. Consider using === instead.
Loading history...
6739
        builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure)));
6740
6741
      // Store the map and a cache object for the current logical line
6742
      if (i == 0) {
0 ignored issues
show
Best Practice introduced by
Comparing i to 0 using the == operator is not safe. Consider using === instead.
Loading history...
6743
        lineView.measure.map = builder.map;
6744
        lineView.measure.cache = {};
6745
      } else {
6746
        (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map);
6747
        (lineView.measure.caches || (lineView.measure.caches = [])).push({});
6748
      }
6749
    }
6750
6751
    // See issue #2901
6752
    if (webkit && /\bcm-tab\b/.test(builder.content.lastChild.className))
6753
      builder.content.className = "cm-tab-wrap-hack";
6754
6755
    signal(cm, "renderLine", cm, lineView.line, builder.pre);
6756
    if (builder.pre.className)
6757
      builder.textClass = joinClasses(builder.pre.className, builder.textClass || "");
6758
6759
    return builder;
6760
  }
6761
6762
  function defaultSpecialCharPlaceholder(ch) {
6763
    var token = elt("span", "\u2022", "cm-invalidchar");
6764
    token.title = "\\u" + ch.charCodeAt(0).toString(16);
6765
    token.setAttribute("aria-label", token.title);
6766
    return token;
6767
  }
6768
6769
  // Build up the DOM representation for a single token, and add it to
6770
  // the line map. Takes care to render special characters separately.
6771
  function buildToken(builder, text, style, startStyle, endStyle, title, css) {
6772
    if (!text) 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...
6773
    var special = builder.cm.options.specialChars, mustWrap = false;
6774
    if (!special.test(text)) {
6775
      builder.col += text.length;
6776
      var content = document.createTextNode(text);
6777
      builder.map.push(builder.pos, builder.pos + text.length, content);
6778
      if (ie && ie_version < 9) mustWrap = true;
6779
      builder.pos += text.length;
6780
    } else {
6781
      var content = document.createDocumentFragment(), pos = 0;
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable content already seems to be declared on line 6776. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
6782
      while (true) {
6783
        special.lastIndex = pos;
6784
        var m = special.exec(text);
6785
        var skipped = m ? m.index - pos : text.length - pos;
6786
        if (skipped) {
6787
          var txt = document.createTextNode(text.slice(pos, pos + skipped));
6788
          if (ie && ie_version < 9) content.appendChild(elt("span", [txt]));
6789
          else content.appendChild(txt);
6790
          builder.map.push(builder.pos, builder.pos + skipped, txt);
6791
          builder.col += skipped;
6792
          builder.pos += skipped;
6793
        }
6794
        if (!m) break;
6795
        pos += skipped + 1;
6796
        if (m[0] == "\t") {
6797
          var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
6798
          var txt = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable txt already seems to be declared on line 6787. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
6799
          txt.setAttribute("role", "presentation");
6800
          txt.setAttribute("cm-text", "\t");
6801
          builder.col += tabWidth;
6802
        } else {
6803
          var txt = builder.cm.options.specialCharPlaceholder(m[0]);
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable txt already seems to be declared on line 6787. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
6804
          txt.setAttribute("cm-text", m[0]);
6805
          if (ie && ie_version < 9) content.appendChild(elt("span", [txt]));
6806
          else content.appendChild(txt);
6807
          builder.col += 1;
6808
        }
6809
        builder.map.push(builder.pos, builder.pos + 1, txt);
6810
        builder.pos++;
6811
      }
6812
    }
6813
    if (style || startStyle || endStyle || mustWrap || css) {
6814
      var fullStyle = style || "";
6815
      if (startStyle) fullStyle += startStyle;
6816
      if (endStyle) fullStyle += endStyle;
6817
      var token = elt("span", [content], fullStyle, css);
6818
      if (title) token.title = title;
6819
      return builder.content.appendChild(token);
6820
    }
6821
    builder.content.appendChild(content);
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...
6822
  }
6823
6824
  function buildTokenSplitSpaces(inner) {
6825
    function split(old) {
6826
      var out = " ";
6827
      for (var i = 0; i < old.length - 2; ++i) out += i % 2 ? " " : "\u00a0";
6828
      out += " ";
6829
      return out;
6830
    }
6831
    return function(builder, text, style, startStyle, endStyle, title) {
6832
      inner(builder, text.replace(/ {3,}/g, split), style, startStyle, endStyle, title);
6833
    };
6834
  }
6835
6836
  // Work around nonsense dimensions being reported for stretches of
6837
  // right-to-left text.
6838
  function buildTokenBadBidi(inner, order) {
6839
    return function(builder, text, style, startStyle, endStyle, title) {
6840
      style = style ? style + " cm-force-border" : "cm-force-border";
6841
      var start = builder.pos, end = start + text.length;
6842
      for (;;) {
6843
        // Find the part that overlaps with the start of this text
6844
        for (var i = 0; i < order.length; i++) {
6845
          var part = order[i];
6846
          if (part.to > start && part.from <= start) break;
6847
        }
6848
        if (part.to >= end) return inner(builder, text, style, startStyle, endStyle, title);
0 ignored issues
show
Bug introduced by
The variable part seems to not be initialized for all possible execution paths.
Loading history...
6849
        inner(builder, text.slice(0, part.to - start), style, startStyle, null, title);
6850
        startStyle = null;
6851
        text = text.slice(part.to - start);
6852
        start = part.to;
6853
      }
6854
    };
6855
  }
6856
6857
  function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
6858
    var widget = !ignoreWidget && marker.widgetNode;
6859
    if (widget) builder.map.push(builder.pos, builder.pos + size, widget);
6860
    if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {
6861
      if (!widget)
6862
        widget = builder.content.appendChild(document.createElement("span"));
6863
      widget.setAttribute("cm-marker", marker.id);
6864
    }
6865
    if (widget) {
6866
      builder.cm.display.input.setUneditable(widget);
6867
      builder.content.appendChild(widget);
6868
    }
6869
    builder.pos += size;
6870
  }
6871
6872
  // Outputs a number of spans to make up a line, taking highlighting
6873
  // and marked text into account.
6874
  function insertLineContent(line, builder, styles) {
6875
    var spans = line.markedSpans, allText = line.text, at = 0;
6876
    if (!spans) {
6877
      for (var i = 1; i < styles.length; i+=2)
6878
        builder.addToken(builder, allText.slice(at, at = styles[i]), interpretTokenStyle(styles[i+1], builder.cm.options));
6879
      return;
6880
    }
6881
6882
    var len = allText.length, pos = 0, i = 1, text = "", style, css;
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 6877. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
6883
    var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed;
6884
    for (;;) {
6885
      if (nextChange == pos) { // Update current marker set
6886
        spanStyle = spanEndStyle = spanStartStyle = title = css = "";
6887
        collapsed = null; nextChange = Infinity;
0 ignored issues
show
Comprehensibility Best Practice introduced by
You seem to be aliasing the built-in name Infinity as nextChange. This makes your code very difficult to follow, consider using the built-in name directly.
Loading history...
6888
        var foundBookmarks = [];
6889
        for (var j = 0; j < spans.length; ++j) {
6890
          var sp = spans[j], m = sp.marker;
6891
          if (sp.from <= pos && (sp.to == null || sp.to > pos)) {
0 ignored issues
show
Best Practice introduced by
Comparing sp.to to null using the == operator is not safe. Consider using === instead.
Loading history...
6892
            if (sp.to != null && nextChange > sp.to) { nextChange = sp.to; spanEndStyle = ""; }
6893
            if (m.className) spanStyle += " " + m.className;
6894
            if (m.css) css = m.css;
6895
            if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle;
6896
            if (m.endStyle && sp.to == nextChange) spanEndStyle += " " + m.endStyle;
6897
            if (m.title && !title) title = m.title;
6898
            if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))
6899
              collapsed = sp;
6900
          } else if (sp.from > pos && nextChange > sp.from) {
6901
            nextChange = sp.from;
6902
          }
6903
          if (m.type == "bookmark" && sp.from == pos && m.widgetNode) foundBookmarks.push(m);
6904
        }
6905
        if (collapsed && (collapsed.from || 0) == pos) {
6906
          buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos,
0 ignored issues
show
Best Practice introduced by
Comparing collapsed.to to null using the == operator is not safe. Consider using === instead.
Loading history...
6907
                             collapsed.marker, collapsed.from == null);
0 ignored issues
show
Best Practice introduced by
Comparing collapsed.from to null using the == operator is not safe. Consider using === instead.
Loading history...
6908
          if (collapsed.to == null) return;
6909
        }
6910
        if (!collapsed && foundBookmarks.length) for (var j = 0; j < foundBookmarks.length; ++j)
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable j already seems to be declared on line 6889. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
6911
          buildCollapsedSpan(builder, 0, foundBookmarks[j]);
6912
      }
6913
      if (pos >= len) break;
6914
6915
      var upto = Math.min(len, nextChange);
6916
      while (true) {
6917
        if (text) {
6918
          var end = pos + text.length;
6919
          if (!collapsed) {
6920
            var tokenText = end > upto ? text.slice(0, upto - pos) : text;
6921
            builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
0 ignored issues
show
Bug introduced by
The variable spanStyle seems to not be initialized for all possible execution paths.
Loading history...
6922
                             spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", title, css);
0 ignored issues
show
Bug introduced by
The variable spanEndStyle seems to not be initialized for all possible execution paths.
Loading history...
Bug introduced by
The variable spanStartStyle seems to not be initialized for all possible execution paths. Are you sure addToken handles undefined variables?
Loading history...
Bug introduced by
The variable css seems to not be initialized for all possible execution paths. Are you sure addToken handles undefined variables?
Loading history...
Bug introduced by
The variable title seems to not be initialized for all possible execution paths. Are you sure addToken handles undefined variables?
Loading history...
6923
          }
6924
          if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
6925
          pos = end;
6926
          spanStartStyle = "";
6927
        }
6928
        text = allText.slice(at, at = styles[i++]);
6929
        style = interpretTokenStyle(styles[i++], builder.cm.options);
6930
      }
6931
    }
6932
  }
6933
6934
  // DOCUMENT DATA STRUCTURE
6935
6936
  // By default, updates that start and end at the beginning of a line
6937
  // are treated specially, in order to make the association of line
6938
  // widgets and marker elements with the text behave more intuitive.
6939
  function isWholeLineUpdate(doc, change) {
6940
    return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" &&
0 ignored issues
show
Best Practice introduced by
Comparing change.to.ch to 0 using the == operator is not safe. Consider using === instead.
Loading history...
Best Practice introduced by
Comparing change.from.ch to 0 using the == operator is not safe. Consider using === instead.
Loading history...
6941
      (!doc.cm || doc.cm.options.wholeLineUpdateBefore);
6942
  }
6943
6944
  // Perform a change on the document data structure.
6945
  function updateDoc(doc, change, markedSpans, estimateHeight) {
6946
    function spansFor(n) {return markedSpans ? markedSpans[n] : null;}
6947
    function update(line, text, spans) {
6948
      updateLine(line, text, spans, estimateHeight);
6949
      signalLater(line, "change", line, change);
6950
    }
6951
    function linesFor(start, end) {
6952
      for (var i = start, result = []; i < end; ++i)
6953
        result.push(new Line(text[i], spansFor(i), estimateHeight));
6954
      return result;
6955
    }
6956
6957
    var from = change.from, to = change.to, text = change.text;
6958
    var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
6959
    var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;
6960
6961
    // Adjust the line structure
6962
    if (change.full) {
6963
      doc.insert(0, linesFor(0, text.length));
6964
      doc.remove(text.length, doc.size - text.length);
6965
    } else if (isWholeLineUpdate(doc, change)) {
6966
      // This is a whole-line replace. Treated specially to make
6967
      // sure line objects move the way they are supposed to.
6968
      var added = linesFor(0, text.length - 1);
6969
      update(lastLine, lastLine.text, lastSpans);
6970
      if (nlines) doc.remove(from.line, nlines);
6971
      if (added.length) doc.insert(from.line, added);
6972
    } else if (firstLine == lastLine) {
6973
      if (text.length == 1) {
0 ignored issues
show
Best Practice introduced by
Comparing text.length to 1 using the == operator is not safe. Consider using === instead.
Loading history...
6974
        update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
6975
      } else {
6976
        var added = linesFor(1, text.length - 1);
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable added already seems to be declared on line 6968. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
6977
        added.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight));
6978
        update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
6979
        doc.insert(from.line + 1, added);
6980
      }
6981
    } else if (text.length == 1) {
6982
      update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0));
6983
      doc.remove(from.line + 1, nlines);
6984
    } else {
6985
      update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
6986
      update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
6987
      var added = linesFor(1, text.length - 1);
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable added already seems to be declared on line 6968. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
6988
      if (nlines > 1) doc.remove(from.line + 1, nlines - 1);
6989
      doc.insert(from.line + 1, added);
6990
    }
6991
6992
    signalLater(doc, "change", doc, change);
6993
  }
6994
6995
  // The document is represented as a BTree consisting of leaves, with
6996
  // chunk of lines in them, and branches, with up to ten leaves or
6997
  // other branch nodes below them. The top node is always a branch
6998
  // node, and is the document object itself (meaning it has
6999
  // additional methods and properties).
7000
  //
7001
  // All nodes have parent links. The tree is used both to go from
7002
  // line numbers to line objects, and to go from objects to numbers.
7003
  // It also indexes by height, and is used to convert between height
7004
  // and line object, and to find the total height of the document.
7005
  //
7006
  // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html
7007
7008
  function LeafChunk(lines) {
7009
    this.lines = lines;
7010
    this.parent = null;
7011
    for (var i = 0, height = 0; i < lines.length; ++i) {
7012
      lines[i].parent = this;
7013
      height += lines[i].height;
7014
    }
7015
    this.height = height;
7016
  }
7017
7018
  LeafChunk.prototype = {
7019
    chunkSize: function() { return this.lines.length; },
7020
    // Remove the n lines at offset 'at'.
7021
    removeInner: function(at, n) {
7022
      for (var i = at, e = at + n; i < e; ++i) {
7023
        var line = this.lines[i];
7024
        this.height -= line.height;
7025
        cleanUpLine(line);
7026
        signalLater(line, "delete");
7027
      }
7028
      this.lines.splice(at, n);
7029
    },
7030
    // Helper used to collapse a small branch into a single leaf.
7031
    collapse: function(lines) {
7032
      lines.push.apply(lines, this.lines);
7033
    },
7034
    // Insert the given array of lines at offset 'at', count them as
7035
    // having the given height.
7036
    insertInner: function(at, lines, height) {
7037
      this.height += height;
7038
      this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
7039
      for (var i = 0; i < lines.length; ++i) lines[i].parent = this;
7040
    },
7041
    // Used to iterate over a part of the tree.
7042
    iterN: function(at, n, op) {
7043
      for (var e = at + n; at < e; ++at)
7044
        if (op(this.lines[at])) return true;
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...
7045
    }
7046
  };
7047
7048
  function BranchChunk(children) {
7049
    this.children = children;
7050
    var size = 0, height = 0;
7051
    for (var i = 0; i < children.length; ++i) {
7052
      var ch = children[i];
7053
      size += ch.chunkSize(); height += ch.height;
7054
      ch.parent = this;
7055
    }
7056
    this.size = size;
7057
    this.height = height;
7058
    this.parent = null;
7059
  }
7060
7061
  BranchChunk.prototype = {
7062
    chunkSize: function() { return this.size; },
7063
    removeInner: function(at, n) {
7064
      this.size -= n;
7065
      for (var i = 0; i < this.children.length; ++i) {
7066
        var child = this.children[i], sz = child.chunkSize();
7067
        if (at < sz) {
7068
          var rm = Math.min(n, sz - at), oldHeight = child.height;
7069
          child.removeInner(at, rm);
7070
          this.height -= oldHeight - child.height;
7071
          if (sz == rm) { this.children.splice(i--, 1); child.parent = null; }
0 ignored issues
show
Complexity Coding Style introduced by
You seem to be assigning a new value to the loop variable i here. Please check if this was indeed your intention. Even if it was, consider using another kind of loop instead.
Loading history...
7072
          if ((n -= rm) == 0) break;
0 ignored issues
show
Best Practice introduced by
Comparing n -= rm to 0 using the == operator is not safe. Consider using === instead.
Loading history...
7073
          at = 0;
7074
        } else at -= sz;
7075
      }
7076
      // If the result is smaller than 25 lines, ensure that it is a
7077
      // single leaf node.
7078
      if (this.size - n < 25 &&
7079
          (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) {
7080
        var lines = [];
7081
        this.collapse(lines);
7082
        this.children = [new LeafChunk(lines)];
7083
        this.children[0].parent = this;
7084
      }
7085
    },
7086
    collapse: function(lines) {
7087
      for (var i = 0; i < this.children.length; ++i) this.children[i].collapse(lines);
7088
    },
7089
    insertInner: function(at, lines, height) {
7090
      this.size += lines.length;
7091
      this.height += height;
7092
      for (var i = 0; i < this.children.length; ++i) {
7093
        var child = this.children[i], sz = child.chunkSize();
7094
        if (at <= sz) {
7095
          child.insertInner(at, lines, height);
7096
          if (child.lines && child.lines.length > 50) {
7097
            while (child.lines.length > 50) {
7098
              var spilled = child.lines.splice(child.lines.length - 25, 25);
7099
              var newleaf = new LeafChunk(spilled);
7100
              child.height -= newleaf.height;
7101
              this.children.splice(i + 1, 0, newleaf);
7102
              newleaf.parent = this;
7103
            }
7104
            this.maybeSpill();
7105
          }
7106
          break;
7107
        }
7108
        at -= sz;
7109
      }
7110
    },
7111
    // When a node has grown, check whether it should be split.
7112
    maybeSpill: function() {
7113
      if (this.children.length <= 10) return;
7114
      var me = this;
7115
      do {
7116
        var spilled = me.children.splice(me.children.length - 5, 5);
7117
        var sibling = new BranchChunk(spilled);
7118
        if (!me.parent) { // Become the parent node
7119
          var copy = new BranchChunk(me.children);
7120
          copy.parent = me;
7121
          me.children = [copy, sibling];
7122
          me = copy;
7123
        } else {
7124
          me.size -= sibling.size;
7125
          me.height -= sibling.height;
7126
          var myIndex = indexOf(me.parent.children, me);
7127
          me.parent.children.splice(myIndex + 1, 0, sibling);
7128
        }
7129
        sibling.parent = me.parent;
7130
      } while (me.children.length > 10);
7131
      me.parent.maybeSpill();
7132
    },
7133
    iterN: function(at, n, op) {
7134
      for (var i = 0; i < this.children.length; ++i) {
7135
        var child = this.children[i], sz = child.chunkSize();
7136
        if (at < sz) {
7137
          var used = Math.min(n, sz - at);
7138
          if (child.iterN(at, used, op)) return true;
7139
          if ((n -= used) == 0) break;
0 ignored issues
show
Best Practice introduced by
Comparing n -= used to 0 using the == operator is not safe. Consider using === instead.
Loading history...
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...
7140
          at = 0;
7141
        } else at -= sz;
7142
      }
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...
7143
    }
7144
  };
7145
7146
  var nextDocId = 0;
7147
  var Doc = CodeMirror.Doc = function(text, mode, firstLine) {
7148
    if (!(this instanceof Doc)) return new Doc(text, mode, firstLine);
7149
    if (firstLine == null) firstLine = 0;
0 ignored issues
show
Best Practice introduced by
Comparing firstLine to null using the == operator is not safe. Consider using === instead.
Loading history...
7150
7151
    BranchChunk.call(this, [new LeafChunk([new Line("", null)])]);
7152
    this.first = firstLine;
7153
    this.scrollTop = this.scrollLeft = 0;
7154
    this.cantEdit = false;
7155
    this.cleanGeneration = 1;
7156
    this.frontier = firstLine;
7157
    var start = Pos(firstLine, 0);
7158
    this.sel = simpleSelection(start);
7159
    this.history = new History(null);
7160
    this.id = ++nextDocId;
7161
    this.modeOption = mode;
7162
7163
    if (typeof text == "string") text = splitLines(text);
7164
    updateDoc(this, {from: start, to: start, text: text});
7165
    setSelection(this, simpleSelection(start), sel_dontScroll);
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...
7166
  };
7167
7168
  Doc.prototype = createObj(BranchChunk.prototype, {
7169
    constructor: Doc,
7170
    // Iterate over the document. Supports two forms -- with only one
7171
    // argument, it calls that for each line in the document. With
7172
    // three, it iterates over the range given by the first two (with
7173
    // the second being non-inclusive).
7174
    iter: function(from, to, op) {
7175
      if (op) this.iterN(from - this.first, to - from, op);
7176
      else this.iterN(this.first, this.first + this.size, from);
7177
    },
7178
7179
    // Non-public interface for adding and removing lines.
7180
    insert: function(at, lines) {
7181
      var height = 0;
7182
      for (var i = 0; i < lines.length; ++i) height += lines[i].height;
7183
      this.insertInner(at - this.first, lines, height);
7184
    },
7185
    remove: function(at, n) { this.removeInner(at - this.first, n); },
7186
7187
    // From here, the methods are part of the public interface. Most
7188
    // are also available from CodeMirror (editor) instances.
7189
7190
    getValue: function(lineSep) {
7191
      var lines = getLines(this, this.first, this.first + this.size);
7192
      if (lineSep === false) return lines;
7193
      return lines.join(lineSep || "\n");
7194
    },
7195
    setValue: docMethodOp(function(code) {
7196
      var top = Pos(this.first, 0), last = this.first + this.size - 1;
7197
      makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
7198
                        text: splitLines(code), origin: "setValue", full: true}, true);
7199
      setSelection(this, simpleSelection(top));
7200
    }),
7201
    replaceRange: function(code, from, to, origin) {
7202
      from = clipPos(this, from);
7203
      to = to ? clipPos(this, to) : from;
7204
      replaceRange(this, code, from, to, origin);
7205
    },
7206
    getRange: function(from, to, lineSep) {
7207
      var lines = getBetween(this, clipPos(this, from), clipPos(this, to));
7208
      if (lineSep === false) return lines;
7209
      return lines.join(lineSep || "\n");
7210
    },
7211
7212
    getLine: function(line) {var l = this.getLineHandle(line); return l && l.text;},
7213
7214
    getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line);},
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if isLine(this, line) 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...
7215
    getLineNumber: function(line) {return lineNo(line);},
7216
7217
    getLineHandleVisualStart: function(line) {
7218
      if (typeof line == "number") line = getLine(this, line);
7219
      return visualLine(line);
7220
    },
7221
7222
    lineCount: function() {return this.size;},
7223
    firstLine: function() {return this.first;},
7224
    lastLine: function() {return this.first + this.size - 1;},
7225
7226
    clipPos: function(pos) {return clipPos(this, pos);},
7227
7228
    getCursor: function(start) {
7229
      var range = this.sel.primary(), pos;
7230
      if (start == null || start == "head") pos = range.head;
0 ignored issues
show
Best Practice introduced by
Comparing start to null using the == operator is not safe. Consider using === instead.
Loading history...
7231
      else if (start == "anchor") pos = range.anchor;
7232
      else if (start == "end" || start == "to" || start === false) pos = range.to();
7233
      else pos = range.from();
7234
      return pos;
7235
    },
7236
    listSelections: function() { return this.sel.ranges; },
7237
    somethingSelected: function() {return this.sel.somethingSelected();},
7238
7239
    setCursor: docMethodOp(function(line, ch, options) {
7240
      setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options);
7241
    }),
7242
    setSelection: docMethodOp(function(anchor, head, options) {
7243
      setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options);
7244
    }),
7245
    extendSelection: docMethodOp(function(head, other, options) {
7246
      extendSelection(this, clipPos(this, head), other && clipPos(this, other), options);
7247
    }),
7248
    extendSelections: docMethodOp(function(heads, options) {
7249
      extendSelections(this, clipPosArray(this, heads, options));
0 ignored issues
show
Bug introduced by
The call to clipPosArray seems to have too many arguments starting with options.
Loading history...
7250
    }),
7251
    extendSelectionsBy: docMethodOp(function(f, options) {
7252
      extendSelections(this, map(this.sel.ranges, f), options);
7253
    }),
7254
    setSelections: docMethodOp(function(ranges, primary, options) {
7255
      if (!ranges.length) return;
7256
      for (var i = 0, out = []; i < ranges.length; i++)
7257
        out[i] = new Range(clipPos(this, ranges[i].anchor),
7258
                           clipPos(this, ranges[i].head));
7259
      if (primary == null) primary = Math.min(ranges.length - 1, this.sel.primIndex);
0 ignored issues
show
Best Practice introduced by
Comparing primary to null using the == operator is not safe. Consider using === instead.
Loading history...
7260
      setSelection(this, normalizeSelection(out, primary), options);
7261
    }),
7262
    addSelection: docMethodOp(function(anchor, head, options) {
7263
      var ranges = this.sel.ranges.slice(0);
7264
      ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)));
7265
      setSelection(this, normalizeSelection(ranges, ranges.length - 1), options);
7266
    }),
7267
7268
    getSelection: function(lineSep) {
7269
      var ranges = this.sel.ranges, lines;
7270
      for (var i = 0; i < ranges.length; i++) {
7271
        var sel = getBetween(this, ranges[i].from(), ranges[i].to());
7272
        lines = lines ? lines.concat(sel) : sel;
7273
      }
7274
      if (lineSep === false) return lines;
0 ignored issues
show
Bug introduced by
The variable lines seems to not be initialized for all possible execution paths.
Loading history...
7275
      else return lines.join(lineSep || "\n");
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
7276
    },
7277
    getSelections: function(lineSep) {
7278
      var parts = [], ranges = this.sel.ranges;
7279
      for (var i = 0; i < ranges.length; i++) {
7280
        var sel = getBetween(this, ranges[i].from(), ranges[i].to());
7281
        if (lineSep !== false) sel = sel.join(lineSep || "\n");
7282
        parts[i] = sel;
7283
      }
7284
      return parts;
7285
    },
7286
    replaceSelection: function(code, collapse, origin) {
7287
      var dup = [];
7288
      for (var i = 0; i < this.sel.ranges.length; i++)
7289
        dup[i] = code;
7290
      this.replaceSelections(dup, collapse, origin || "+input");
7291
    },
7292
    replaceSelections: docMethodOp(function(code, collapse, origin) {
7293
      var changes = [], sel = this.sel;
7294
      for (var i = 0; i < sel.ranges.length; i++) {
7295
        var range = sel.ranges[i];
7296
        changes[i] = {from: range.from(), to: range.to(), text: splitLines(code[i]), origin: origin};
7297
      }
7298
      var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse);
7299
      for (var i = changes.length - 1; i >= 0; i--)
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 7294. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
7300
        makeChange(this, changes[i]);
7301
      if (newSel) setSelectionReplaceHistory(this, newSel);
7302
      else if (this.cm) ensureCursorVisible(this.cm);
7303
    }),
7304
    undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}),
7305
    redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}),
7306
    undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}),
7307
    redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true);}),
7308
7309
    setExtending: function(val) {this.extend = val;},
7310
    getExtending: function() {return this.extend;},
7311
7312
    historySize: function() {
7313
      var hist = this.history, done = 0, undone = 0;
7314
      for (var i = 0; i < hist.done.length; i++) if (!hist.done[i].ranges) ++done;
7315
      for (var i = 0; i < hist.undone.length; i++) if (!hist.undone[i].ranges) ++undone;
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 7314. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
7316
      return {undo: done, redo: undone};
7317
    },
7318
    clearHistory: function() {this.history = new History(this.history.maxGeneration);},
7319
7320
    markClean: function() {
7321
      this.cleanGeneration = this.changeGeneration(true);
7322
    },
7323
    changeGeneration: function(forceSplit) {
7324
      if (forceSplit)
7325
        this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null;
7326
      return this.history.generation;
7327
    },
7328
    isClean: function (gen) {
7329
      return this.history.generation == (gen || this.cleanGeneration);
7330
    },
7331
7332
    getHistory: function() {
7333
      return {done: copyHistoryArray(this.history.done),
7334
              undone: copyHistoryArray(this.history.undone)};
7335
    },
7336
    setHistory: function(histData) {
7337
      var hist = this.history = new History(this.history.maxGeneration);
7338
      hist.done = copyHistoryArray(histData.done.slice(0), null, true);
7339
      hist.undone = copyHistoryArray(histData.undone.slice(0), null, true);
7340
    },
7341
7342
    addLineClass: docMethodOp(function(handle, where, cls) {
7343
      return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function(line) {
7344
        var prop = where == "text" ? "textClass"
7345
                 : where == "background" ? "bgClass"
7346
                 : where == "gutter" ? "gutterClass" : "wrapClass";
7347
        if (!line[prop]) line[prop] = cls;
7348
        else if (classTest(cls).test(line[prop])) return false;
7349
        else line[prop] += " " + cls;
7350
        return true;
7351
      });
7352
    }),
7353
    removeLineClass: docMethodOp(function(handle, where, cls) {
7354
      return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function(line) {
7355
        var prop = where == "text" ? "textClass"
7356
                 : where == "background" ? "bgClass"
7357
                 : where == "gutter" ? "gutterClass" : "wrapClass";
7358
        var cur = line[prop];
7359
        if (!cur) return false;
7360
        else if (cls == null) line[prop] = null;
0 ignored issues
show
Best Practice introduced by
Comparing cls to null using the == operator is not safe. Consider using === instead.
Loading history...
7361
        else {
7362
          var found = cur.match(classTest(cls));
7363
          if (!found) return false;
7364
          var end = found.index + found[0].length;
7365
          line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null;
7366
        }
7367
        return true;
7368
      });
7369
    }),
7370
7371
    markText: function(from, to, options) {
7372
      return markText(this, clipPos(this, from), clipPos(this, to), options, "range");
7373
    },
7374
    setBookmark: function(pos, options) {
7375
      var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
0 ignored issues
show
Best Practice introduced by
Comparing options.nodeType to null using the == operator is not safe. Consider using === instead.
Loading history...
7376
                      insertLeft: options && options.insertLeft,
7377
                      clearWhenEmpty: false, shared: options && options.shared};
7378
      pos = clipPos(this, pos);
7379
      return markText(this, pos, pos, realOpts, "bookmark");
7380
    },
7381
    findMarksAt: function(pos) {
7382
      pos = clipPos(this, pos);
7383
      var markers = [], spans = getLine(this, pos.line).markedSpans;
7384
      if (spans) for (var i = 0; i < spans.length; ++i) {
7385
        var span = spans[i];
7386
        if ((span.from == null || span.from <= pos.ch) &&
0 ignored issues
show
Best Practice introduced by
Comparing span.from to null using the == operator is not safe. Consider using === instead.
Loading history...
7387
            (span.to == null || span.to >= pos.ch))
0 ignored issues
show
Best Practice introduced by
Comparing span.to to null using the == operator is not safe. Consider using === instead.
Loading history...
7388
          markers.push(span.marker.parent || span.marker);
7389
      }
7390
      return markers;
7391
    },
7392
    findMarks: function(from, to, filter) {
7393
      from = clipPos(this, from); to = clipPos(this, to);
7394
      var found = [], lineNo = from.line;
7395
      this.iter(from.line, to.line + 1, function(line) {
7396
        var spans = line.markedSpans;
7397
        if (spans) for (var i = 0; i < spans.length; i++) {
7398
          var span = spans[i];
7399
          if (!(lineNo == from.line && from.ch > span.to ||
7400
                span.from == null && lineNo != from.line||
0 ignored issues
show
Best Practice introduced by
Comparing span.from to null using the == operator is not safe. Consider using === instead.
Loading history...
7401
                lineNo == to.line && span.from > to.ch) &&
7402
              (!filter || filter(span.marker)))
7403
            found.push(span.marker.parent || span.marker);
7404
        }
7405
        ++lineNo;
7406
      });
7407
      return found;
7408
    },
7409
    getAllMarks: function() {
7410
      var markers = [];
7411
      this.iter(function(line) {
7412
        var sps = line.markedSpans;
7413
        if (sps) for (var i = 0; i < sps.length; ++i)
7414
          if (sps[i].from != null) markers.push(sps[i].marker);
0 ignored issues
show
Best Practice introduced by
Comparing sps.i.from to null using the != operator is not safe. Consider using !== instead.
Loading history...
7415
      });
7416
      return markers;
7417
    },
7418
7419
    posFromIndex: function(off) {
7420
      var ch, lineNo = this.first;
7421
      this.iter(function(line) {
7422
        var sz = line.text.length + 1;
7423
        if (sz > off) { ch = off; return true; }
7424
        off -= sz;
7425
        ++lineNo;
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...
7426
      });
7427
      return clipPos(this, Pos(lineNo, ch));
7428
    },
7429
    indexFromPos: function (coords) {
7430
      coords = clipPos(this, coords);
7431
      var index = coords.ch;
7432
      if (coords.line < this.first || coords.ch < 0) return 0;
7433
      this.iter(this.first, coords.line, function (line) {
7434
        index += line.text.length + 1;
7435
      });
7436
      return index;
7437
    },
7438
7439
    copy: function(copyHistory) {
7440
      var doc = new Doc(getLines(this, this.first, this.first + this.size), this.modeOption, this.first);
7441
      doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;
7442
      doc.sel = this.sel;
7443
      doc.extend = false;
7444
      if (copyHistory) {
7445
        doc.history.undoDepth = this.history.undoDepth;
7446
        doc.setHistory(this.getHistory());
7447
      }
7448
      return doc;
7449
    },
7450
7451
    linkedDoc: function(options) {
7452
      if (!options) options = {};
7453
      var from = this.first, to = this.first + this.size;
7454
      if (options.from != null && options.from > from) from = options.from;
0 ignored issues
show
Best Practice introduced by
Comparing options.from to null using the != operator is not safe. Consider using !== instead.
Loading history...
7455
      if (options.to != null && options.to < to) to = options.to;
0 ignored issues
show
Best Practice introduced by
Comparing options.to to null using the != operator is not safe. Consider using !== instead.
Loading history...
7456
      var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from);
7457
      if (options.sharedHist) copy.history = this.history;
7458
      (this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});
7459
      copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];
7460
      copySharedMarkers(copy, findSharedMarkers(this));
7461
      return copy;
7462
    },
7463
    unlinkDoc: function(other) {
7464
      if (other instanceof CodeMirror) other = other.doc;
7465
      if (this.linked) for (var i = 0; i < this.linked.length; ++i) {
7466
        var link = this.linked[i];
7467
        if (link.doc != other) continue;
7468
        this.linked.splice(i, 1);
7469
        other.unlinkDoc(this);
7470
        detachSharedMarkers(findSharedMarkers(this));
7471
        break;
7472
      }
7473
      // If the histories were shared, split them again
7474
      if (other.history == this.history) {
7475
        var splitIds = [other.id];
7476
        linkedDocs(other, function(doc) {splitIds.push(doc.id);}, true);
7477
        other.history = new History(null);
7478
        other.history.done = copyHistoryArray(this.history.done, splitIds);
7479
        other.history.undone = copyHistoryArray(this.history.undone, splitIds);
7480
      }
7481
    },
7482
    iterLinkedDocs: function(f) {linkedDocs(this, f);},
7483
7484
    getMode: function() {return this.mode;},
7485
    getEditor: function() {return this.cm;}
7486
  });
7487
7488
  // Public alias.
7489
  Doc.prototype.eachLine = Doc.prototype.iter;
7490
7491
  // Set up methods on CodeMirror's prototype to redirect to the editor's document.
7492
  var dontDelegate = "iter insert remove copy getEditor".split(" ");
7493
  for (var prop in Doc.prototype) if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
7494
    CodeMirror.prototype[prop] = (function(method) {
7495
      return function() {return method.apply(this.doc, arguments);};
7496
    })(Doc.prototype[prop]);
7497
7498
  eventMixin(Doc);
7499
7500
  // Call f for all linked documents.
7501
  function linkedDocs(doc, f, sharedHistOnly) {
7502
    function propagate(doc, skip, sharedHist) {
7503
      if (doc.linked) for (var i = 0; i < doc.linked.length; ++i) {
7504
        var rel = doc.linked[i];
7505
        if (rel.doc == skip) continue;
7506
        var shared = sharedHist && rel.sharedHist;
7507
        if (sharedHistOnly && !shared) continue;
7508
        f(rel.doc, shared);
7509
        propagate(rel.doc, doc, shared);
7510
      }
7511
    }
7512
    propagate(doc, null, true);
7513
  }
7514
7515
  // Attach a document to an editor.
7516
  function attachDoc(cm, doc) {
7517
    if (doc.cm) throw new Error("This document is already in use.");
7518
    cm.doc = doc;
7519
    doc.cm = cm;
7520
    estimateLineHeights(cm);
7521
    loadMode(cm);
7522
    if (!cm.options.lineWrapping) findMaxLine(cm);
7523
    cm.options.mode = doc.modeOption;
7524
    regChange(cm);
7525
  }
7526
7527
  // LINE UTILITIES
7528
7529
  // Find the line object corresponding to the given line number.
7530
  function getLine(doc, n) {
7531
    n -= doc.first;
7532
    if (n < 0 || n >= doc.size) throw new Error("There is no line " + (n + doc.first) + " in the document.");
7533
    for (var chunk = doc; !chunk.lines;) {
7534
      for (var i = 0;; ++i) {
7535
        var child = chunk.children[i], sz = child.chunkSize();
7536
        if (n < sz) { chunk = child; break; }
7537
        n -= sz;
7538
      }
7539
    }
7540
    return chunk.lines[n];
7541
  }
7542
7543
  // Get the part of a document between two positions, as an array of
7544
  // strings.
7545
  function getBetween(doc, start, end) {
7546
    var out = [], n = start.line;
7547
    doc.iter(start.line, end.line + 1, function(line) {
7548
      var text = line.text;
7549
      if (n == end.line) text = text.slice(0, end.ch);
7550
      if (n == start.line) text = text.slice(start.ch);
7551
      out.push(text);
7552
      ++n;
7553
    });
7554
    return out;
7555
  }
7556
  // Get the lines between from and to, as array of strings.
7557
  function getLines(doc, from, to) {
7558
    var out = [];
7559
    doc.iter(from, to, function(line) { out.push(line.text); });
7560
    return out;
7561
  }
7562
7563
  // Update the height of a line, propagating the height change
7564
  // upwards to parent nodes.
7565
  function updateLineHeight(line, height) {
7566
    var diff = height - line.height;
7567
    if (diff) for (var n = line; n; n = n.parent) n.height += diff;
7568
  }
7569
7570
  // Given a line object, find its line number by walking up through
7571
  // its parent links.
7572
  function lineNo(line) {
7573
    if (line.parent == null) return null;
0 ignored issues
show
Best Practice introduced by
Comparing line.parent to null using the == operator is not safe. Consider using === instead.
Loading history...
7574
    var cur = line.parent, no = indexOf(cur.lines, line);
7575
    for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
7576
      for (var i = 0;; ++i) {
7577
        if (chunk.children[i] == cur) break;
7578
        no += chunk.children[i].chunkSize();
7579
      }
7580
    }
7581
    return no + cur.first;
7582
  }
7583
7584
  // Find the line at the given vertical position, using the height
7585
  // information in the document tree.
7586
  function lineAtHeight(chunk, h) {
7587
    var n = chunk.first;
7588
    outer: do {
7589
      for (var i = 0; i < chunk.children.length; ++i) {
7590
        var child = chunk.children[i], ch = child.height;
7591
        if (h < ch) { chunk = child; continue outer; }
7592
        h -= ch;
7593
        n += child.chunkSize();
7594
      }
7595
      return n;
7596
    } while (!chunk.lines);
7597
    for (var i = 0; i < chunk.lines.length; ++i) {
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 7589. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
7598
      var line = chunk.lines[i], lh = line.height;
7599
      if (h < lh) break;
7600
      h -= lh;
7601
    }
7602
    return n + i;
7603
  }
7604
7605
7606
  // Find the height above the given line.
7607
  function heightAtLine(lineObj) {
7608
    lineObj = visualLine(lineObj);
7609
7610
    var h = 0, chunk = lineObj.parent;
7611
    for (var i = 0; i < chunk.lines.length; ++i) {
7612
      var line = chunk.lines[i];
7613
      if (line == lineObj) break;
7614
      else h += line.height;
7615
    }
7616
    for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
7617
      for (var i = 0; i < p.children.length; ++i) {
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 7611. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
7618
        var cur = p.children[i];
7619
        if (cur == chunk) break;
7620
        else h += cur.height;
7621
      }
7622
    }
7623
    return h;
7624
  }
7625
7626
  // Get the bidi ordering for the given line (and cache it). Returns
7627
  // false for lines that are fully left-to-right, and an array of
7628
  // BidiSpan objects otherwise.
7629
  function getOrder(line) {
7630
    var order = line.order;
7631
    if (order == null) order = line.order = bidiOrdering(line.text);
0 ignored issues
show
Best Practice introduced by
Comparing order to null using the == operator is not safe. Consider using === instead.
Loading history...
7632
    return order;
7633
  }
7634
7635
  // HISTORY
7636
7637
  function History(startGen) {
7638
    // Arrays of change events and selections. Doing something adds an
7639
    // event to done and clears undo. Undoing moves events from done
7640
    // to undone, redoing moves them in the other direction.
7641
    this.done = []; this.undone = [];
7642
    this.undoDepth = Infinity;
7643
    // Used to track when changes can be merged into a single undo
7644
    // event
7645
    this.lastModTime = this.lastSelTime = 0;
7646
    this.lastOp = this.lastSelOp = null;
7647
    this.lastOrigin = this.lastSelOrigin = null;
7648
    // Used by the isClean() method
7649
    this.generation = this.maxGeneration = startGen || 1;
7650
  }
7651
7652
  // Create a history change event from an updateDoc-style change
7653
  // object.
7654
  function historyChangeFromChange(doc, change) {
7655
    var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
7656
    attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
7657
    linkedDocs(doc, function(doc) {attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);}, true);
7658
    return histChange;
7659
  }
7660
7661
  // Pop all selection events off the end of a history array. Stop at
7662
  // a change event.
7663
  function clearSelectionEvents(array) {
7664
    while (array.length) {
7665
      var last = lst(array);
7666
      if (last.ranges) array.pop();
7667
      else break;
7668
    }
7669
  }
7670
7671
  // Find the top change event in the history. Pop off selection
7672
  // events that are in the way.
7673
  function lastChangeEvent(hist, force) {
7674
    if (force) {
7675
      clearSelectionEvents(hist.done);
7676
      return lst(hist.done);
7677
    } else if (hist.done.length && !lst(hist.done).ranges) {
7678
      return lst(hist.done);
7679
    } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if hist.done.length > 1 && ....done.length - 2.ranges 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...
7680
      hist.done.pop();
7681
      return lst(hist.done);
7682
    }
7683
  }
7684
7685
  // Register a change in the history. Merges changes that are within
7686
  // a single operation, ore are close together with an origin that
7687
  // allows merging (starting with "+") into a single event.
7688
  function addChangeToHistory(doc, change, selAfter, opId) {
7689
    var hist = doc.history;
7690
    hist.undone.length = 0;
7691
    var time = +new Date, cur;
7692
7693
    if ((hist.lastOp == opId ||
7694
         hist.lastOrigin == change.origin && change.origin &&
7695
         ((change.origin.charAt(0) == "+" && doc.cm && hist.lastModTime > time - doc.cm.options.historyEventDelay) ||
7696
          change.origin.charAt(0) == "*")) &&
7697
        (cur = lastChangeEvent(hist, hist.lastOp == opId))) {
7698
      // Merge this change into the last event
7699
      var last = lst(cur.changes);
7700
      if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) {
0 ignored issues
show
Best Practice introduced by
Comparing cmp(change.from, change.to) to 0 using the == operator is not safe. Consider using === instead.
Loading history...
Best Practice introduced by
Comparing cmp(change.from, last.to) to 0 using the == operator is not safe. Consider using === instead.
Loading history...
7701
        // Optimized case for simple insertion -- don't want to add
7702
        // new changesets for every character typed
7703
        last.to = changeEnd(change);
7704
      } else {
7705
        // Add new sub-event
7706
        cur.changes.push(historyChangeFromChange(doc, change));
7707
      }
7708
    } else {
7709
      // Can not be merged, start a new event.
7710
      var before = lst(hist.done);
7711
      if (!before || !before.ranges)
7712
        pushSelectionToHistory(doc.sel, hist.done);
7713
      cur = {changes: [historyChangeFromChange(doc, change)],
7714
             generation: hist.generation};
7715
      hist.done.push(cur);
7716
      while (hist.done.length > hist.undoDepth) {
7717
        hist.done.shift();
7718
        if (!hist.done[0].ranges) hist.done.shift();
7719
      }
7720
    }
7721
    hist.done.push(selAfter);
7722
    hist.generation = ++hist.maxGeneration;
7723
    hist.lastModTime = hist.lastSelTime = time;
7724
    hist.lastOp = hist.lastSelOp = opId;
7725
    hist.lastOrigin = hist.lastSelOrigin = change.origin;
7726
7727
    if (!last) signal(doc, "historyAdded");
7728
  }
7729
7730
  function selectionEventCanBeMerged(doc, origin, prev, sel) {
7731
    var ch = origin.charAt(0);
7732
    return ch == "*" ||
7733
      ch == "+" &&
7734
      prev.ranges.length == sel.ranges.length &&
7735
      prev.somethingSelected() == sel.somethingSelected() &&
7736
      new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500);
7737
  }
7738
7739
  // Called whenever the selection changes, sets the new selection as
7740
  // the pending selection in the history, and pushes the old pending
7741
  // selection into the 'done' array when it was significantly
7742
  // different (in number of selected ranges, emptiness, or time).
7743
  function addSelectionToHistory(doc, sel, opId, options) {
7744
    var hist = doc.history, origin = options && options.origin;
7745
7746
    // A new event is started when the previous origin does not match
7747
    // the current, or the origins don't allow matching. Origins
7748
    // starting with * are always merged, those starting with + are
7749
    // merged when similar and close together in time.
7750
    if (opId == hist.lastSelOp ||
7751
        (origin && hist.lastSelOrigin == origin &&
7752
         (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||
7753
          selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))
7754
      hist.done[hist.done.length - 1] = sel;
7755
    else
7756
      pushSelectionToHistory(sel, hist.done);
7757
7758
    hist.lastSelTime = +new Date;
7759
    hist.lastSelOrigin = origin;
7760
    hist.lastSelOp = opId;
7761
    if (options && options.clearRedo !== false)
7762
      clearSelectionEvents(hist.undone);
7763
  }
7764
7765
  function pushSelectionToHistory(sel, dest) {
7766
    var top = lst(dest);
7767
    if (!(top && top.ranges && top.equals(sel)))
7768
      dest.push(sel);
7769
  }
7770
7771
  // Used to store marked span information in the history.
7772
  function attachLocalSpans(doc, change, from, to) {
7773
    var existing = change["spans_" + doc.id], n = 0;
7774
    doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function(line) {
7775
      if (line.markedSpans)
7776
        (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans;
7777
      ++n;
7778
    });
7779
  }
7780
7781
  // When un/re-doing restores text containing marked spans, those
7782
  // that have been explicitly cleared should not be restored.
7783
  function removeClearedSpans(spans) {
7784
    if (!spans) return null;
7785
    for (var i = 0, out; i < spans.length; ++i) {
7786
      if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i); }
7787
      else if (out) out.push(spans[i]);
7788
    }
7789
    return !out ? spans : out.length ? out : null;
0 ignored issues
show
Bug introduced by
The variable out seems to not be initialized for all possible execution paths.
Loading history...
7790
  }
7791
7792
  // Retrieve and filter the old marked spans stored in a change event.
7793
  function getOldSpans(doc, change) {
7794
    var found = change["spans_" + doc.id];
7795
    if (!found) return null;
7796
    for (var i = 0, nw = []; i < change.text.length; ++i)
7797
      nw.push(removeClearedSpans(found[i]));
7798
    return nw;
7799
  }
7800
7801
  // Used both to provide a JSON-safe object in .getHistory, and, when
7802
  // detaching a document, to split the history in two
7803
  function copyHistoryArray(events, newGroup, instantiateSel) {
7804
    for (var i = 0, copy = []; i < events.length; ++i) {
7805
      var event = events[i];
7806
      if (event.ranges) {
7807
        copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event);
7808
        continue;
7809
      }
7810
      var changes = event.changes, newChanges = [];
7811
      copy.push({changes: newChanges});
7812
      for (var j = 0; j < changes.length; ++j) {
7813
        var change = changes[j], m;
7814
        newChanges.push({from: change.from, to: change.to, text: change.text});
7815
        if (newGroup) for (var prop in change) if (m = prop.match(/^spans_(\d+)$/)) {
0 ignored issues
show
Complexity introduced by
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
7816
          if (indexOf(newGroup, Number(m[1])) > -1) {
7817
            lst(newChanges)[prop] = change[prop];
7818
            delete change[prop];
7819
          }
7820
        }
7821
      }
7822
    }
7823
    return copy;
7824
  }
7825
7826
  // Rebasing/resetting history to deal with externally-sourced changes
7827
7828
  function rebaseHistSelSingle(pos, from, to, diff) {
7829
    if (to < pos.line) {
7830
      pos.line += diff;
7831
    } else if (from < pos.line) {
7832
      pos.line = from;
7833
      pos.ch = 0;
7834
    }
7835
  }
7836
7837
  // Tries to rebase an array of history events given a change in the
7838
  // document. If the change touches the same lines as the event, the
7839
  // event, and everything 'behind' it, is discarded. If the change is
7840
  // before the event, the event's positions are updated. Uses a
7841
  // copy-on-write scheme for the positions, to avoid having to
7842
  // reallocate them all on every rebase, but also avoid problems with
7843
  // shared position objects being unsafely updated.
7844
  function rebaseHistArray(array, from, to, diff) {
7845
    for (var i = 0; i < array.length; ++i) {
7846
      var sub = array[i], ok = true;
7847
      if (sub.ranges) {
7848
        if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; }
7849
        for (var j = 0; j < sub.ranges.length; j++) {
7850
          rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff);
7851
          rebaseHistSelSingle(sub.ranges[j].head, from, to, diff);
7852
        }
7853
        continue;
7854
      }
7855
      for (var j = 0; j < sub.changes.length; ++j) {
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable j already seems to be declared on line 7849. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
7856
        var cur = sub.changes[j];
7857
        if (to < cur.from.line) {
7858
          cur.from = Pos(cur.from.line + diff, cur.from.ch);
7859
          cur.to = Pos(cur.to.line + diff, cur.to.ch);
7860
        } else if (from <= cur.to.line) {
7861
          ok = false;
7862
          break;
7863
        }
7864
      }
7865
      if (!ok) {
7866
        array.splice(0, i + 1);
7867
        i = 0;
0 ignored issues
show
Complexity Coding Style introduced by
You seem to be assigning a new value to the loop variable i here. Please check if this was indeed your intention. Even if it was, consider using another kind of loop instead.
Loading history...
7868
      }
7869
    }
7870
  }
7871
7872
  function rebaseHist(hist, change) {
7873
    var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1;
7874
    rebaseHistArray(hist.done, from, to, diff);
7875
    rebaseHistArray(hist.undone, from, to, diff);
7876
  }
7877
7878
  // EVENT UTILITIES
7879
7880
  // Due to the fact that we still support jurassic IE versions, some
7881
  // compatibility wrappers are needed.
7882
7883
  var e_preventDefault = CodeMirror.e_preventDefault = function(e) {
7884
    if (e.preventDefault) e.preventDefault();
7885
    else e.returnValue = false;
7886
  };
7887
  var e_stopPropagation = CodeMirror.e_stopPropagation = function(e) {
7888
    if (e.stopPropagation) e.stopPropagation();
7889
    else e.cancelBubble = true;
7890
  };
7891
  function e_defaultPrevented(e) {
7892
    return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false;
0 ignored issues
show
Best Practice introduced by
Comparing e.defaultPrevented to null using the != operator is not safe. Consider using !== instead.
Loading history...
Best Practice introduced by
Comparing e.returnValue to false using the == operator is not safe. Consider using === instead.
Loading history...
7893
  }
7894
  var e_stop = CodeMirror.e_stop = function(e) {e_preventDefault(e); e_stopPropagation(e);};
7895
7896
  function e_target(e) {return e.target || e.srcElement;}
7897
  function e_button(e) {
7898
    var b = e.which;
7899
    if (b == null) {
0 ignored issues
show
Best Practice introduced by
Comparing b to null using the == operator is not safe. Consider using === instead.
Loading history...
7900
      if (e.button & 1) b = 1;
0 ignored issues
show
introduced by
You have used a bitwise operator & in a condition. Did you maybe want to use the logical operator &&
Loading history...
7901
      else if (e.button & 2) b = 3;
7902
      else if (e.button & 4) b = 2;
7903
    }
7904
    if (mac && e.ctrlKey && b == 1) b = 3;
7905
    return b;
7906
  }
7907
7908
  // EVENT HANDLING
7909
7910
  // Lightweight event framework. on/off also work on DOM nodes,
7911
  // registering native DOM handlers.
7912
7913
  var on = CodeMirror.on = function(emitter, type, f) {
7914
    if (emitter.addEventListener)
7915
      emitter.addEventListener(type, f, false);
7916
    else if (emitter.attachEvent)
7917
      emitter.attachEvent("on" + type, f);
7918
    else {
7919
      var map = emitter._handlers || (emitter._handlers = {});
7920
      var arr = map[type] || (map[type] = []);
7921
      arr.push(f);
7922
    }
7923
  };
7924
7925
  var off = CodeMirror.off = function(emitter, type, f) {
7926
    if (emitter.removeEventListener)
7927
      emitter.removeEventListener(type, f, false);
7928
    else if (emitter.detachEvent)
7929
      emitter.detachEvent("on" + type, f);
7930
    else {
7931
      var arr = emitter._handlers && emitter._handlers[type];
7932
      if (!arr) return;
7933
      for (var i = 0; i < arr.length; ++i)
7934
        if (arr[i] == f) { arr.splice(i, 1); break; }
7935
    }
7936
  };
7937
7938
  var signal = CodeMirror.signal = function(emitter, type /*, values...*/) {
7939
    var arr = emitter._handlers && emitter._handlers[type];
7940
    if (!arr) return;
7941
    var args = Array.prototype.slice.call(arguments, 2);
7942
    for (var i = 0; i < arr.length; ++i) arr[i].apply(null, args);
7943
  };
7944
7945
  var orphanDelayedCallbacks = null;
7946
7947
  // Often, we want to signal events at a point where we are in the
7948
  // middle of some work, but don't want the handler to start calling
7949
  // other methods on the editor, which might be in an inconsistent
7950
  // state or simply not expect any other events to happen.
7951
  // signalLater looks whether there are any handlers, and schedules
7952
  // them to be executed when the last operation ends, or, if no
7953
  // operation is active, when a timeout fires.
7954
  function signalLater(emitter, type /*, values...*/) {
7955
    var arr = emitter._handlers && emitter._handlers[type];
7956
    if (!arr) return;
7957
    var args = Array.prototype.slice.call(arguments, 2), list;
7958
    if (operationGroup) {
7959
      list = operationGroup.delayedCallbacks;
7960
    } else if (orphanDelayedCallbacks) {
7961
      list = orphanDelayedCallbacks;
7962
    } else {
7963
      list = orphanDelayedCallbacks = [];
7964
      setTimeout(fireOrphanDelayed, 0);
7965
    }
7966
    function bnd(f) {return function(){f.apply(null, args);};};
7967
    for (var i = 0; i < arr.length; ++i)
7968
      list.push(bnd(arr[i]));
7969
  }
7970
7971
  function fireOrphanDelayed() {
7972
    var delayed = orphanDelayedCallbacks;
7973
    orphanDelayedCallbacks = null;
7974
    for (var i = 0; i < delayed.length; ++i) delayed[i]();
7975
  }
7976
7977
  // The DOM events that CodeMirror handles can be overridden by
7978
  // registering a (non-DOM) handler on the editor for the event name,
7979
  // and preventDefault-ing the event in that handler.
7980
  function signalDOMEvent(cm, e, override) {
7981
    if (typeof e == "string")
7982
      e = {type: e, preventDefault: function() { this.defaultPrevented = true; }};
7983
    signal(cm, override || e.type, cm, e);
7984
    return e_defaultPrevented(e) || e.codemirrorIgnore;
7985
  }
7986
7987
  function signalCursorActivity(cm) {
7988
    var arr = cm._handlers && cm._handlers.cursorActivity;
7989
    if (!arr) return;
7990
    var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []);
7991
    for (var i = 0; i < arr.length; ++i) if (indexOf(set, arr[i]) == -1)
7992
      set.push(arr[i]);
7993
  }
7994
7995
  function hasHandler(emitter, type) {
7996
    var arr = emitter._handlers && emitter._handlers[type];
7997
    return arr && arr.length > 0;
7998
  }
7999
8000
  // Add on and off methods to a constructor's prototype, to make
8001
  // registering events on such objects more convenient.
8002
  function eventMixin(ctor) {
8003
    ctor.prototype.on = function(type, f) {on(this, type, f);};
8004
    ctor.prototype.off = function(type, f) {off(this, type, f);};
8005
  }
8006
8007
  // MISC UTILITIES
8008
8009
  // Number of pixels added to scroller and sizer to hide scrollbar
8010
  var scrollerGap = 30;
8011
8012
  // Returned or thrown by various protocols to signal 'I'm not
8013
  // handling this'.
8014
  var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}};
8015
8016
  // Reused option objects for setSelection & friends
8017
  var sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"};
8018
8019
  function Delayed() {this.id = null;}
8020
  Delayed.prototype.set = function(ms, f) {
8021
    clearTimeout(this.id);
8022
    this.id = setTimeout(f, ms);
8023
  };
8024
8025
  // Counts the column offset in a string, taking tabs into account.
8026
  // Used mostly to find indentation.
8027
  var countColumn = CodeMirror.countColumn = function(string, end, tabSize, startIndex, startValue) {
8028
    if (end == null) {
0 ignored issues
show
Best Practice introduced by
Comparing end to null using the == operator is not safe. Consider using === instead.
Loading history...
8029
      end = string.search(/[^\s\u00a0]/);
8030
      if (end == -1) end = string.length;
8031
    }
8032
    for (var i = startIndex || 0, n = startValue || 0;;) {
8033
      var nextTab = string.indexOf("\t", i);
8034
      if (nextTab < 0 || nextTab >= end)
8035
        return n + (end - i);
8036
      n += nextTab - i;
8037
      n += tabSize - (n % tabSize);
8038
      i = nextTab + 1;
8039
    }
8040
  };
8041
8042
  // The inverse of countColumn -- find the offset that corresponds to
8043
  // a particular column.
8044
  function findColumn(string, goal, tabSize) {
8045
    for (var pos = 0, col = 0;;) {
8046
      var nextTab = string.indexOf("\t", pos);
8047
      if (nextTab == -1) nextTab = string.length;
8048
      var skipped = nextTab - pos;
8049
      if (nextTab == string.length || col + skipped >= goal)
8050
        return pos + Math.min(skipped, goal - col);
8051
      col += nextTab - pos;
8052
      col += tabSize - (col % tabSize);
8053
      pos = nextTab + 1;
8054
      if (col >= goal) return pos;
8055
    }
8056
  }
8057
8058
  var spaceStrs = [""];
8059
  function spaceStr(n) {
8060
    while (spaceStrs.length <= n)
8061
      spaceStrs.push(lst(spaceStrs) + " ");
8062
    return spaceStrs[n];
8063
  }
8064
8065
  function lst(arr) { return arr[arr.length-1]; }
8066
8067
  var selectInput = function(node) { node.select(); };
8068
  if (ios) // Mobile Safari apparently has a bug where select() is broken.
8069
    selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; };
8070
  else if (ie) // Suppress mysterious IE10 errors
8071
    selectInput = function(node) { try { node.select(); } catch(_e) {} };
0 ignored issues
show
Coding Style Comprehensibility Best Practice introduced by
Empty catch clauses should be used with caution; consider adding a comment why this is needed.
Loading history...
8072
8073
  function indexOf(array, elt) {
8074
    for (var i = 0; i < array.length; ++i)
8075
      if (array[i] == elt) return i;
8076
    return -1;
8077
  }
8078
  function map(array, f) {
8079
    var out = [];
8080
    for (var i = 0; i < array.length; i++) out[i] = f(array[i], i);
8081
    return out;
8082
  }
8083
8084
  function nothing() {}
8085
8086
  function createObj(base, props) {
8087
    var inst;
8088
    if (Object.create) {
8089
      inst = Object.create(base);
8090
    } else {
8091
      nothing.prototype = base;
8092
      inst = new nothing();
0 ignored issues
show
Coding Style Best Practice introduced by
By convention, constructors like nothing should be capitalized.
Loading history...
8093
    }
8094
    if (props) copyObj(props, inst);
8095
    return inst;
8096
  };
8097
8098
  function copyObj(obj, target, overwrite) {
8099
    if (!target) target = {};
8100
    for (var prop in obj)
8101
      if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
8102
        target[prop] = obj[prop];
8103
    return target;
8104
  }
8105
8106
  function bind(f) {
8107
    var args = Array.prototype.slice.call(arguments, 1);
8108
    return function(){return f.apply(null, args);};
8109
  }
8110
8111
  var nonASCIISingleCaseWordChar = /[\u00df\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
8112
  var isWordCharBasic = CodeMirror.isWordChar = function(ch) {
8113
    return /\w/.test(ch) || ch > "\x80" &&
8114
      (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch));
8115
  };
8116
  function isWordChar(ch, helper) {
8117
    if (!helper) return isWordCharBasic(ch);
8118
    if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) return true;
8119
    return helper.test(ch);
8120
  }
8121
8122
  function isEmpty(obj) {
8123
    for (var n in obj) if (obj.hasOwnProperty(n) && obj[n]) return false;
8124
    return true;
8125
  }
8126
8127
  // Extending unicode characters. A series of a non-extending char +
8128
  // any number of extending chars is treated as a single unit as far
8129
  // as editing and measuring is concerned. This is not fully correct,
8130
  // since some scripts/fonts/browsers also treat other configurations
8131
  // of code points as a group.
8132
  var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/;
8133
  function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch); }
8134
8135
  // DOM UTILITIES
8136
8137
  function elt(tag, content, className, style) {
8138
    var e = document.createElement(tag);
8139
    if (className) e.className = className;
8140
    if (style) e.style.cssText = style;
8141
    if (typeof content == "string") e.appendChild(document.createTextNode(content));
8142
    else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]);
8143
    return e;
8144
  }
8145
8146
  var range;
8147
  if (document.createRange) range = function(node, start, end, endNode) {
8148
    var r = document.createRange();
8149
    r.setEnd(endNode || node, end);
8150
    r.setStart(node, start);
8151
    return r;
8152
  };
8153
  else range = function(node, start, end) {
8154
    var r = document.body.createTextRange();
8155
    try { r.moveToElementText(node.parentNode); }
8156
    catch(e) { return r; }
8157
    r.collapse(true);
8158
    r.moveEnd("character", end);
8159
    r.moveStart("character", start);
8160
    return r;
8161
  };
8162
8163
  function removeChildren(e) {
8164
    for (var count = e.childNodes.length; count > 0; --count)
8165
      e.removeChild(e.firstChild);
8166
    return e;
8167
  }
8168
8169
  function removeChildrenAndAdd(parent, e) {
8170
    return removeChildren(parent).appendChild(e);
8171
  }
8172
8173
  var contains = CodeMirror.contains = function(parent, child) {
8174
    if (child.nodeType == 3) // Android browser always returns false when child is a textnode
8175
      child = child.parentNode;
8176
    if (parent.contains)
8177
      return parent.contains(child);
8178
    do {
8179
      if (child.nodeType == 11) child = child.host;
8180
      if (child == parent) return true;
8181
    } while (child = child.parentNode);
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...
8182
  };
8183
8184
  function activeElt() { return document.activeElement; }
8185
  // Older versions of IE throws unspecified error when touching
8186
  // document.activeElement in some cases (during loading, in iframe)
8187
  if (ie && ie_version < 11) activeElt = function() {
0 ignored issues
show
Comprehensibility introduced by
It seems like you are trying to overwrite a function name here. activeElt is already defined in line 8184 as a function. While this will work, it can be very confusing.
Loading history...
8188
    try { return document.activeElement; }
8189
    catch(e) { return document.body; }
8190
  };
8191
8192
  function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*"); }
8193
  var rmClass = CodeMirror.rmClass = function(node, cls) {
8194
    var current = node.className;
8195
    var match = classTest(cls).exec(current);
8196
    if (match) {
8197
      var after = current.slice(match.index + match[0].length);
8198
      node.className = current.slice(0, match.index) + (after ? match[1] + after : "");
8199
    }
8200
  };
8201
  var addClass = CodeMirror.addClass = function(node, cls) {
8202
    var current = node.className;
8203
    if (!classTest(cls).test(current)) node.className += (current ? " " : "") + cls;
8204
  };
8205
  function joinClasses(a, b) {
8206
    var as = a.split(" ");
8207
    for (var i = 0; i < as.length; i++)
8208
      if (as[i] && !classTest(as[i]).test(b)) b += " " + as[i];
8209
    return b;
8210
  }
8211
8212
  // WINDOW-WIDE EVENTS
8213
8214
  // These must be handled carefully, because naively registering a
8215
  // handler for each editor will cause the editors to never be
8216
  // garbage collected.
8217
8218
  function forEachCodeMirror(f) {
8219
    if (!document.body.getElementsByClassName) return;
8220
    var byClass = document.body.getElementsByClassName("CodeMirror");
8221
    for (var i = 0; i < byClass.length; i++) {
8222
      var cm = byClass[i].CodeMirror;
8223
      if (cm) f(cm);
8224
    }
8225
  }
8226
8227
  var globalsRegistered = false;
8228
  function ensureGlobalHandlers() {
8229
    if (globalsRegistered) return;
8230
    registerGlobalHandlers();
8231
    globalsRegistered = true;
8232
  }
8233
  function registerGlobalHandlers() {
8234
    // When the window resizes, we need to refresh active editors.
8235
    var resizeTimer;
8236
    on(window, "resize", function() {
8237
      if (resizeTimer == null) resizeTimer = setTimeout(function() {
0 ignored issues
show
Best Practice introduced by
Comparing resizeTimer to null using the == operator is not safe. Consider using === instead.
Loading history...
8238
        resizeTimer = null;
8239
        forEachCodeMirror(onResize);
8240
      }, 100);
8241
    });
8242
    // When the window loses focus, we want to show the editor as blurred
8243
    on(window, "blur", function() {
8244
      forEachCodeMirror(onBlur);
8245
    });
8246
  }
8247
8248
  // FEATURE DETECTION
8249
8250
  // Detect drag-and-drop
8251
  var dragAndDrop = function() {
8252
    // There is *some* kind of drag-and-drop support in IE6-8, but I
8253
    // couldn't get it to work yet.
8254
    if (ie && ie_version < 9) return false;
8255
    var div = elt('div');
8256
    return "draggable" in div || "dragDrop" in div;
8257
  }();
8258
8259
  var zwspSupported;
8260
  function zeroWidthElement(measure) {
8261
    if (zwspSupported == null) {
0 ignored issues
show
Best Practice introduced by
Comparing zwspSupported to null using the == operator is not safe. Consider using === instead.
Loading history...
8262
      var test = elt("span", "\u200b");
8263
      removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]));
8264
      if (measure.firstChild.offsetHeight != 0)
0 ignored issues
show
Best Practice introduced by
Comparing measure.firstChild.offsetHeight to 0 using the != operator is not safe. Consider using !== instead.
Loading history...
8265
        zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8);
8266
    }
8267
    var node = zwspSupported ? elt("span", "\u200b") :
8268
      elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
8269
    node.setAttribute("cm-text", "");
8270
    return node;
8271
  }
8272
8273
  // Feature-detect IE's crummy client rect reporting for bidi text
8274
  var badBidiRects;
8275
  function hasBadBidiRects(measure) {
8276
    if (badBidiRects != null) return badBidiRects;
0 ignored issues
show
Best Practice introduced by
Comparing badBidiRects to null using the != operator is not safe. Consider using !== instead.
Loading history...
8277
    var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA"));
8278
    var r0 = range(txt, 0, 1).getBoundingClientRect();
8279
    if (!r0 || r0.left == r0.right) return false; // Safari returns null in some cases (#2780)
8280
    var r1 = range(txt, 1, 2).getBoundingClientRect();
8281
    return badBidiRects = (r1.right - r0.right < 3);
8282
  }
8283
8284
  // See if "".split is the broken IE version, if so, provide an
8285
  // alternative way to split lines.
8286
  var splitLines = CodeMirror.splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) {
8287
    var pos = 0, result = [], l = string.length;
8288
    while (pos <= l) {
8289
      var nl = string.indexOf("\n", pos);
8290
      if (nl == -1) nl = string.length;
8291
      var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
8292
      var rt = line.indexOf("\r");
8293
      if (rt != -1) {
8294
        result.push(line.slice(0, rt));
8295
        pos += rt + 1;
8296
      } else {
8297
        result.push(line);
8298
        pos = nl + 1;
8299
      }
8300
    }
8301
    return result;
8302
  } : function(string){return string.split(/\r\n?|\n/);};
8303
8304
  var hasSelection = window.getSelection ? function(te) {
8305
    try { return te.selectionStart != te.selectionEnd; }
8306
    catch(e) { return false; }
8307
  } : function(te) {
8308
    try {var range = te.ownerDocument.selection.createRange();}
8309
    catch(e) {}
0 ignored issues
show
Coding Style Comprehensibility Best Practice introduced by
Empty catch clauses should be used with caution; consider adding a comment why this is needed.
Loading history...
8310
    if (!range || range.parentElement() != te) return false;
8311
    return range.compareEndPoints("StartToEnd", range) != 0;
0 ignored issues
show
Best Practice introduced by
Comparing range.compareEndPoints("StartToEnd", range) to 0 using the != operator is not safe. Consider using !== instead.
Loading history...
8312
  };
8313
8314
  var hasCopyEvent = (function() {
8315
    var e = elt("div");
8316
    if ("oncopy" in e) return true;
8317
    e.setAttribute("oncopy", "return;");
8318
    return typeof e.oncopy == "function";
8319
  })();
8320
8321
  var badZoomedRects = null;
8322
  function hasBadZoomedRects(measure) {
8323
    if (badZoomedRects != null) return badZoomedRects;
0 ignored issues
show
Best Practice introduced by
Comparing badZoomedRects to null using the != operator is not safe. Consider using !== instead.
Loading history...
8324
    var node = removeChildrenAndAdd(measure, elt("span", "x"));
8325
    var normal = node.getBoundingClientRect();
8326
    var fromRange = range(node, 0, 1).getBoundingClientRect();
8327
    return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1;
8328
  }
8329
8330
  // KEY NAMES
8331
8332
  var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
8333
                  19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
8334
                  36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
8335
                  46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod", 107: "=", 109: "-", 127: "Delete",
8336
                  173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
8337
                  221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete",
8338
                  63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert"};
8339
  CodeMirror.keyNames = keyNames;
8340
  (function() {
8341
    // Number keys
8342
    for (var i = 0; i < 10; i++) keyNames[i + 48] = keyNames[i + 96] = String(i);
8343
    // Alphabetic keys
8344
    for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i);
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 8342. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
8345
    // Function keys
8346
    for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i;
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 8342. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
8347
  })();
8348
8349
  // BIDI HELPERS
8350
8351
  function iterateBidiSections(order, from, to, f) {
8352
    if (!order) return f(from, to, "ltr");
8353
    var found = false;
8354
    for (var i = 0; i < order.length; ++i) {
8355
      var part = order[i];
8356
      if (part.from < to && part.to > from || from == to && part.to == from) {
8357
        f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr");
0 ignored issues
show
Best Practice introduced by
Comparing part.level to 1 using the == operator is not safe. Consider using === instead.
Loading history...
8358
        found = true;
8359
      }
8360
    }
8361
    if (!found) f(from, to, "ltr");
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...
Complexity Best Practice introduced by
There is no return statement if !found 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...
8362
  }
8363
8364
  function bidiLeft(part) { return part.level % 2 ? part.to : part.from; }
8365
  function bidiRight(part) { return part.level % 2 ? part.from : part.to; }
8366
8367
  function lineLeft(line) { var order = getOrder(line); return order ? bidiLeft(order[0]) : 0; }
8368
  function lineRight(line) {
8369
    var order = getOrder(line);
8370
    if (!order) return line.text.length;
8371
    return bidiRight(lst(order));
8372
  }
8373
8374
  function lineStart(cm, lineN) {
8375
    var line = getLine(cm.doc, lineN);
8376
    var visual = visualLine(line);
8377
    if (visual != line) lineN = lineNo(visual);
8378
    var order = getOrder(visual);
8379
    var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual);
8380
    return Pos(lineN, ch);
8381
  }
8382
  function lineEnd(cm, lineN) {
8383
    var merged, line = getLine(cm.doc, lineN);
8384
    while (merged = collapsedSpanAtEnd(line)) {
8385
      line = merged.find(1, true).line;
8386
      lineN = null;
8387
    }
8388
    var order = getOrder(line);
8389
    var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line);
8390
    return Pos(lineN == null ? lineNo(line) : lineN, ch);
0 ignored issues
show
Best Practice introduced by
Comparing lineN to null using the == operator is not safe. Consider using === instead.
Loading history...
8391
  }
8392
  function lineStartSmart(cm, pos) {
8393
    var start = lineStart(cm, pos.line);
8394
    var line = getLine(cm.doc, start.line);
8395
    var order = getOrder(line);
8396
    if (!order || order[0].level == 0) {
0 ignored issues
show
Best Practice introduced by
Comparing order.0.level to 0 using the == operator is not safe. Consider using === instead.
Loading history...
8397
      var firstNonWS = Math.max(0, line.text.search(/\S/));
8398
      var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch;
8399
      return Pos(start.line, inWS ? 0 : firstNonWS);
8400
    }
8401
    return start;
8402
  }
8403
8404
  function compareBidiLevel(order, a, b) {
8405
    var linedir = order[0].level;
8406
    if (a == linedir) return true;
8407
    if (b == linedir) return false;
8408
    return a < b;
8409
  }
8410
  var bidiOther;
8411
  function getBidiPartAt(order, pos) {
8412
    bidiOther = null;
8413
    for (var i = 0, found; i < order.length; ++i) {
8414
      var cur = order[i];
8415
      if (cur.from < pos && cur.to > pos) return i;
8416
      if ((cur.from == pos || cur.to == pos)) {
8417
        if (found == null) {
0 ignored issues
show
Bug introduced by
The variable found seems to not be initialized for all possible execution paths.
Loading history...
Best Practice introduced by
Comparing found to null using the == operator is not safe. Consider using === instead.
Loading history...
8418
          found = i;
8419
        } else if (compareBidiLevel(order, cur.level, order[found].level)) {
8420
          if (cur.from != cur.to) bidiOther = found;
8421
          return i;
8422
        } else {
8423
          if (cur.from != cur.to) bidiOther = i;
8424
          return found;
8425
        }
8426
      }
8427
    }
8428
    return found;
8429
  }
8430
8431
  function moveInLine(line, pos, dir, byUnit) {
8432
    if (!byUnit) return pos + dir;
8433
    do pos += dir;
8434
    while (pos > 0 && isExtendingChar(line.text.charAt(pos)));
8435
    return pos;
8436
  }
8437
8438
  // This is needed in order to move 'visually' through bi-directional
8439
  // text -- i.e., pressing left should make the cursor go left, even
8440
  // when in RTL text. The tricky part is the 'jumps', where RTL and
8441
  // LTR text touch each other. This often requires the cursor offset
8442
  // to move more than one unit, in order to visually move one unit.
8443
  function moveVisually(line, start, dir, byUnit) {
8444
    var bidi = getOrder(line);
8445
    if (!bidi) return moveLogically(line, start, dir, byUnit);
8446
    var pos = getBidiPartAt(bidi, start), part = bidi[pos];
8447
    var target = moveInLine(line, start, part.level % 2 ? -dir : dir, byUnit);
8448
8449
    for (;;) {
8450
      if (target > part.from && target < part.to) return target;
8451
      if (target == part.from || target == part.to) {
8452
        if (getBidiPartAt(bidi, target) == pos) return target;
8453
        part = bidi[pos += dir];
0 ignored issues
show
Unused Code introduced by
The assignment to variable pos seems to be never used. Consider removing it.
Loading history...
8454
        return (dir > 0) == part.level % 2 ? part.to : part.from;
8455
      } else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
8456
        part = bidi[pos += dir];
8457
        if (!part) return null;
8458
        if ((dir > 0) == part.level % 2)
8459
          target = moveInLine(line, part.to, -1, byUnit);
8460
        else
8461
          target = moveInLine(line, part.from, 1, byUnit);
8462
      }
8463
    }
8464
  }
8465
8466
  function moveLogically(line, start, dir, byUnit) {
8467
    var target = start + dir;
8468
    if (byUnit) while (target > 0 && isExtendingChar(line.text.charAt(target))) target += dir;
8469
    return target < 0 || target > line.text.length ? null : target;
8470
  }
8471
8472
  // Bidirectional ordering algorithm
8473
  // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
8474
  // that this (partially) implements.
8475
8476
  // One-char codes used for character types:
8477
  // L (L):   Left-to-Right
8478
  // R (R):   Right-to-Left
8479
  // r (AL):  Right-to-Left Arabic
8480
  // 1 (EN):  European Number
8481
  // + (ES):  European Number Separator
8482
  // % (ET):  European Number Terminator
8483
  // n (AN):  Arabic Number
8484
  // , (CS):  Common Number Separator
8485
  // m (NSM): Non-Spacing Mark
8486
  // b (BN):  Boundary Neutral
8487
  // s (B):   Paragraph Separator
8488
  // t (S):   Segment Separator
8489
  // w (WS):  Whitespace
8490
  // N (ON):  Other Neutrals
8491
8492
  // Returns null if characters are ordered as they appear
8493
  // (left-to-right), or an array of sections ({from, to, level}
8494
  // objects) in the order in which they occur visually.
8495
  var bidiOrdering = (function() {
8496
    // Character types for codepoints 0 to 0xff
8497
    var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN";
8498
    // Character types for codepoints 0x600 to 0x6ff
8499
    var arabicTypes = "rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmm";
8500
    function charType(code) {
8501
      if (code <= 0xf7) return lowTypes.charAt(code);
8502
      else if (0x590 <= code && code <= 0x5f4) return "R";
8503
      else if (0x600 <= code && code <= 0x6ed) return arabicTypes.charAt(code - 0x600);
8504
      else if (0x6ee <= code && code <= 0x8ac) return "r";
8505
      else if (0x2000 <= code && code <= 0x200b) return "w";
8506
      else if (code == 0x200c) return "b";
8507
      else return "L";
8508
    }
8509
8510
    var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
8511
    var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
8512
    // Browsers seem to always treat the boundaries of block elements as being L.
8513
    var outerType = "L";
8514
8515
    function BidiSpan(level, from, to) {
8516
      this.level = level;
8517
      this.from = from; this.to = to;
8518
    }
8519
8520
    return function(str) {
8521
      if (!bidiRE.test(str)) return false;
8522
      var len = str.length, types = [];
8523
      for (var i = 0, type; i < len; ++i)
8524
        types.push(type = charType(str.charCodeAt(i)));
0 ignored issues
show
Unused Code introduced by
The assignment to variable type seems to be never used. Consider removing it.
Loading history...
8525
8526
      // W1. Examine each non-spacing mark (NSM) in the level run, and
8527
      // change the type of the NSM to the type of the previous
8528
      // character. If the NSM is at the start of the level run, it will
8529
      // get the type of sor.
8530
      for (var i = 0, prev = outerType; i < len; ++i) {
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 8523. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
8531
        var type = types[i];
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable type already seems to be declared on line 8523. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
8532
        if (type == "m") types[i] = prev;
8533
        else prev = type;
8534
      }
8535
8536
      // W2. Search backwards from each instance of a European number
8537
      // until the first strong type (R, L, AL, or sor) is found. If an
8538
      // AL is found, change the type of the European number to Arabic
8539
      // number.
8540
      // W3. Change all ALs to R.
8541
      for (var i = 0, cur = outerType; i < len; ++i) {
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 8523. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
8542
        var type = types[i];
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable type already seems to be declared on line 8523. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
8543
        if (type == "1" && cur == "r") types[i] = "n";
8544
        else if (isStrong.test(type)) { cur = type; if (type == "r") types[i] = "R"; }
8545
      }
8546
8547
      // W4. A single European separator between two European numbers
8548
      // changes to a European number. A single common separator between
8549
      // two numbers of the same type changes to that type.
8550
      for (var i = 1, prev = types[0]; i < len - 1; ++i) {
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 8523. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
Comprehensibility Naming Best Practice introduced by
The variable prev already seems to be declared on line 8530. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
8551
        var type = types[i];
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable type already seems to be declared on line 8523. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
8552
        if (type == "+" && prev == "1" && types[i+1] == "1") types[i] = "1";
8553
        else if (type == "," && prev == types[i+1] &&
8554
                 (prev == "1" || prev == "n")) types[i] = prev;
8555
        prev = type;
8556
      }
8557
8558
      // W5. A sequence of European terminators adjacent to European
8559
      // numbers changes to all European numbers.
8560
      // W6. Otherwise, separators and terminators change to Other
8561
      // Neutral.
8562
      for (var i = 0; i < len; ++i) {
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 8523. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
8563
        var type = types[i];
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable type already seems to be declared on line 8523. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
8564
        if (type == ",") types[i] = "N";
8565
        else if (type == "%") {
8566
          for (var end = i + 1; end < len && types[end] == "%"; ++end) {}
0 ignored issues
show
Comprehensibility Documentation Best Practice introduced by
This code block is empty. Consider removing it or adding a comment to explain.
Loading history...
8567
          var replace = (i && types[i-1] == "!") || (end < len && types[end] == "1") ? "1" : "N";
8568
          for (var j = i; j < end; ++j) types[j] = replace;
8569
          i = end - 1;
0 ignored issues
show
Complexity Coding Style introduced by
You seem to be assigning a new value to the loop variable i here. Please check if this was indeed your intention. Even if it was, consider using another kind of loop instead.
Loading history...
8570
        }
8571
      }
8572
8573
      // W7. Search backwards from each instance of a European number
8574
      // until the first strong type (R, L, or sor) is found. If an L is
8575
      // found, then change the type of the European number to L.
8576
      for (var i = 0, cur = outerType; i < len; ++i) {
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable cur already seems to be declared on line 8541. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 8523. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
8577
        var type = types[i];
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable type already seems to be declared on line 8523. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
8578
        if (cur == "L" && type == "1") types[i] = "L";
8579
        else if (isStrong.test(type)) cur = type;
8580
      }
8581
8582
      // N1. A sequence of neutrals takes the direction of the
8583
      // surrounding strong text if the text on both sides has the same
8584
      // direction. European and Arabic numbers act as if they were R in
8585
      // terms of their influence on neutrals. Start-of-level-run (sor)
8586
      // and end-of-level-run (eor) are used at level run boundaries.
8587
      // N2. Any remaining neutrals take the embedding direction.
8588
      for (var i = 0; i < len; ++i) {
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 8523. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
8589
        if (isNeutral.test(types[i])) {
8590
          for (var end = i + 1; end < len && isNeutral.test(types[end]); ++end) {}
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable end already seems to be declared on line 8566. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
Comprehensibility Documentation Best Practice introduced by
This code block is empty. Consider removing it or adding a comment to explain.
Loading history...
8591
          var before = (i ? types[i-1] : outerType) == "L";
8592
          var after = (end < len ? types[end] : outerType) == "L";
8593
          var replace = before || after ? "L" : "R";
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable replace already seems to be declared on line 8567. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
8594
          for (var j = i; j < end; ++j) types[j] = replace;
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable j already seems to be declared on line 8568. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
8595
          i = end - 1;
0 ignored issues
show
Complexity Coding Style introduced by
You seem to be assigning a new value to the loop variable i here. Please check if this was indeed your intention. Even if it was, consider using another kind of loop instead.
Loading history...
8596
        }
8597
      }
8598
8599
      // Here we depart from the documented algorithm, in order to avoid
8600
      // building up an actual levels array. Since there are only three
8601
      // levels (0, 1, 2) in an implementation that doesn't take
8602
      // explicit embedding into account, we can build up the order on
8603
      // the fly, without following the level-based algorithm.
8604
      var order = [], m;
8605
      for (var i = 0; i < len;) {
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 8523. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
8606
        if (countsAsLeft.test(types[i])) {
8607
          var start = i;
8608
          for (++i; i < len && countsAsLeft.test(types[i]); ++i) {}
0 ignored issues
show
Comprehensibility Documentation Best Practice introduced by
This code block is empty. Consider removing it or adding a comment to explain.
Loading history...
8609
          order.push(new BidiSpan(0, start, i));
8610
        } else {
8611
          var pos = i, at = order.length;
8612
          for (++i; i < len && types[i] != "L"; ++i) {}
0 ignored issues
show
Comprehensibility Documentation Best Practice introduced by
This code block is empty. Consider removing it or adding a comment to explain.
Loading history...
8613
          for (var j = pos; j < i;) {
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable j already seems to be declared on line 8568. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
8614
            if (countsAsNum.test(types[j])) {
8615
              if (pos < j) order.splice(at, 0, new BidiSpan(1, pos, j));
8616
              var nstart = j;
8617
              for (++j; j < i && countsAsNum.test(types[j]); ++j) {}
0 ignored issues
show
Comprehensibility Documentation Best Practice introduced by
This code block is empty. Consider removing it or adding a comment to explain.
Loading history...
8618
              order.splice(at, 0, new BidiSpan(2, nstart, j));
8619
              pos = j;
8620
            } else ++j;
0 ignored issues
show
Complexity Coding Style introduced by
You seem to be assigning a new value to the loop variable j here. Please check if this was indeed your intention. Even if it was, consider using another kind of loop instead.
Loading history...
8621
          }
8622
          if (pos < i) order.splice(at, 0, new BidiSpan(1, pos, i));
8623
        }
8624
      }
8625
      if (order[0].level == 1 && (m = str.match(/^\s+/))) {
0 ignored issues
show
Best Practice introduced by
Comparing order.0.level to 1 using the == operator is not safe. Consider using === instead.
Loading history...
8626
        order[0].from = m[0].length;
8627
        order.unshift(new BidiSpan(0, 0, m[0].length));
8628
      }
8629
      if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
0 ignored issues
show
Best Practice introduced by
Comparing lst(order).level to 1 using the == operator is not safe. Consider using === instead.
Loading history...
8630
        lst(order).to -= m[0].length;
8631
        order.push(new BidiSpan(0, len - m[0].length, len));
8632
      }
8633
      if (order[0].level != lst(order).level)
8634
        order.push(new BidiSpan(order[0].level, len, len));
8635
8636
      return order;
8637
    };
8638
  })();
8639
8640
  // THE END
8641
8642
  CodeMirror.version = "5.0.0";
8643
8644
  return CodeMirror;
8645
});
8646