public/lib/CodeMirror/lib/codemirror.js   F
last analyzed

Complexity

Total Complexity 3331
Complexity/F 3.88

Size

Lines of Code 8779
Function Count 858

Duplication

Duplicated Lines 52
Ratio 0.59 %

Importance

Changes 0
Metric Value
cc 0
eloc 5846
nc 0
dl 52
loc 8779
rs 0.8
c 0
b 0
f 0
wmc 3331
mnd 8
bc 1567
fnc 858
bpm 1.8263
cpm 3.8822
noi 416

1 Function

Rating   Name   Duplication   Size   Complexity  
B codemirror.js ➔ ?!? 0 8 4

How to fix   Duplicated Code    Complexity   

Duplicated Code

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

Common duplication problems, and corresponding solutions are:

Complexity

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

Complex classes like public/lib/CodeMirror/lib/codemirror.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
// CodeMirror, 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
(function(mod) {
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, null, options.lineSeparator);
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,
86
      delayingBlurEvent: false,
87
      focused: false,
88
      suppressEdits: false, // used to disable editing during key handlers when in readOnly mode
89
      pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edits in input.poll
90
      draggingText: false,
91
      highlight: new Delayed(), // stores highlight worker timeout
92
      keySeq: null,  // Unfinished key sequence
93
      specialChars: null
94
    };
95
96
    var cm = this;
97
98
    // Override magic textarea content restore that IE sometimes does
99
    // on our hidden textarea on reload
100
    if (ie && ie_version < 11) setTimeout(function() { cm.display.input.reset(true); }, 20);
101
102
    registerEventHandlers(this);
103
    ensureGlobalHandlers();
104
105
    startOperation(this);
106
    this.curOp.forceUpdate = true;
107
    attachDoc(this, doc);
108
109
    if ((options.autofocus && !mobile) || cm.hasFocus())
110
      setTimeout(bind(onFocus, this), 20);
111
    else
112
      onBlur(this);
113
114
    for (var opt in optionHandlers) if (optionHandlers.hasOwnProperty(opt))
115
      optionHandlers[opt](this, options[opt], Init);
116
    maybeUpdateLineNumberWidth(this);
117
    if (options.finishInit) options.finishInit(this);
118
    for (var i = 0; i < initHooks.length; ++i) initHooks[i](this);
119
    endOperation(this);
120
    // Suppress optimizelegibility in Webkit, since it breaks text
121
    // measuring on line wrapping boundaries.
122
    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...
123
        getComputedStyle(display.lineDiv).textRendering == "optimizelegibility")
124
      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...
125
  }
126
127
  // DISPLAY CONSTRUCTOR
128
129
  // The display handles the DOM integration, both for input reading
130
  // and content drawing. It holds references to DOM nodes and
131
  // display-related state.
132
133
  function Display(place, doc, input) {
134
    var d = this;
135
    this.input = input;
136
137
    // Covers bottom-right square when both scrollbars are present.
138
    d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
139
    d.scrollbarFiller.setAttribute("cm-not-content", "true");
140
    // Covers bottom of gutter when coverGutterNextToScrollbar is on
141
    // and h scrollbar is present.
142
    d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler");
143
    d.gutterFiller.setAttribute("cm-not-content", "true");
144
    // Will contain the actual code, positioned to cover the viewport.
145
    d.lineDiv = elt("div", null, "CodeMirror-code");
146
    // Elements are added to these to represent selection and cursors.
147
    d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
148
    d.cursorDiv = elt("div", null, "CodeMirror-cursors");
149
    // A visibility: hidden element used to find the size of things.
150
    d.measure = elt("div", null, "CodeMirror-measure");
151
    // When lines outside of the viewport are measured, they are drawn in this.
152
    d.lineMeasure = elt("div", null, "CodeMirror-measure");
153
    // Wraps everything that needs to exist inside the vertically-padded coordinate system
154
    d.lineSpace = elt("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],
155
                      null, "position: relative; outline: none");
156
    // Moved around its parent to cover visible view.
157
    d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null, "position: relative");
158
    // Set to the height of the document, allowing scrolling.
159
    d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
160
    d.sizerWidth = null;
161
    // Behavior of elts with overflow: auto and padding is
162
    // inconsistent across browsers. This is used to ensure the
163
    // scrollable area is big enough.
164
    d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;");
165
    // Will contain the gutters, if any.
166
    d.gutters = elt("div", null, "CodeMirror-gutters");
167
    d.lineGutter = null;
168
    // Actual scrollable element.
169
    d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll");
170
    d.scroller.setAttribute("tabIndex", "-1");
171
    // The element in which the editor lives.
172
    d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror");
173
174
    // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)
175
    if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
176
    if (!webkit && !(gecko && mobile)) d.scroller.draggable = true;
177
178
    if (place) {
179
      if (place.appendChild) place.appendChild(d.wrapper);
180
      else place(d.wrapper);
181
    }
182
183
    // Current rendered range (may be bigger than the view window).
184
    d.viewFrom = d.viewTo = doc.first;
185
    d.reportedViewFrom = d.reportedViewTo = doc.first;
186
    // Information about the rendered lines.
187
    d.view = [];
188
    d.renderedView = null;
189
    // Holds info about a single rendered line when it was rendered
190
    // for measurement, while not in view.
191
    d.externalMeasured = null;
192
    // Empty space (in pixels) above the view
193
    d.viewOffset = 0;
194
    d.lastWrapHeight = d.lastWrapWidth = 0;
195
    d.updateLineNumbers = null;
196
197
    d.nativeBarWidth = d.barHeight = d.barWidth = 0;
198
    d.scrollbarsClipped = false;
199
200
    // Used to only resize the line number gutter when necessary (when
201
    // the amount of lines crosses a boundary that makes its width change)
202
    d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
203
    // Set to true when a non-horizontal-scrolling line widget is
204
    // added. As an optimization, line widget aligning is skipped when
205
    // this is false.
206
    d.alignWidgets = false;
207
208
    d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
209
210
    // Tracks the maximum line length so that the horizontal scrollbar
211
    // can be kept static when scrolling.
212
    d.maxLine = null;
213
    d.maxLineLength = 0;
214
    d.maxLineChanged = false;
215
216
    // Used for measuring wheel scrolling granularity
217
    d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
218
219
    // True when shift is held down.
220
    d.shift = false;
221
222
    // Used to track whether anything happened since the context menu
223
    // was opened.
224
    d.selForContextMenu = null;
225
226
    d.activeTouch = null;
227
228
    input.init(d);
229
  }
230
231
  // STATE UPDATES
232
233
  // Used to get the editor into a consistent state again when options change.
234
235
  function loadMode(cm) {
236
    cm.doc.mode = CodeMirror.getMode(cm.options, cm.doc.modeOption);
237
    resetModeState(cm);
238
  }
239
240
  function resetModeState(cm) {
241
    cm.doc.iter(function(line) {
242
      if (line.stateAfter) line.stateAfter = null;
243
      if (line.styles) line.styles = null;
244
    });
245
    cm.doc.frontier = cm.doc.first;
246
    startWorker(cm, 100);
247
    cm.state.modeGen++;
248
    if (cm.curOp) regChange(cm);
249
  }
250
251
  function wrappingChanged(cm) {
252
    if (cm.options.lineWrapping) {
253
      addClass(cm.display.wrapper, "CodeMirror-wrap");
254
      cm.display.sizer.style.minWidth = "";
255
      cm.display.sizerWidth = null;
256
    } else {
257
      rmClass(cm.display.wrapper, "CodeMirror-wrap");
258
      findMaxLine(cm);
259
    }
260
    estimateLineHeights(cm);
261
    regChange(cm);
262
    clearCaches(cm);
263
    setTimeout(function(){updateScrollbars(cm);}, 100);
264
  }
265
266
  // Returns a function that estimates the height of a line, to use as
267
  // first approximation until the line becomes visible (and is thus
268
  // properly measurable).
269
  function estimateHeight(cm) {
270
    var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
271
    var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
272
    return function(line) {
273
      if (lineIsHidden(cm.doc, line)) return 0;
274
275
      var widgetsHeight = 0;
276
      if (line.widgets) for (var i = 0; i < line.widgets.length; i++) {
277
        if (line.widgets[i].height) widgetsHeight += line.widgets[i].height;
278
      }
279
280
      if (wrapping)
281
        return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th;
282
      else
283
        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...
284
    };
285
  }
286
287
  function estimateLineHeights(cm) {
288
    var doc = cm.doc, est = estimateHeight(cm);
289
    doc.iter(function(line) {
290
      var estHeight = est(line);
291
      if (estHeight != line.height) updateLineHeight(line, estHeight);
292
    });
293
  }
294
295
  function themeChanged(cm) {
296
    cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
297
      cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
298
    clearCaches(cm);
299
  }
300
301
  function guttersChanged(cm) {
302
    updateGutters(cm);
303
    regChange(cm);
304
    setTimeout(function(){alignHorizontally(cm);}, 20);
305
  }
306
307
  // Rebuild the gutter elements, ensure the margin to the left of the
308
  // code matches their width.
309
  function updateGutters(cm) {
310
    var gutters = cm.display.gutters, specs = cm.options.gutters;
311
    removeChildren(gutters);
312
    for (var i = 0; i < specs.length; ++i) {
313
      var gutterClass = specs[i];
314
      var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass));
315
      if (gutterClass == "CodeMirror-linenumbers") {
316
        cm.display.lineGutter = gElt;
317
        gElt.style.width = (cm.display.lineNumWidth || 1) + "px";
318
      }
319
    }
320
    gutters.style.display = i ? "" : "none";
321
    updateGutterSpace(cm);
322
  }
323
324
  function updateGutterSpace(cm) {
325
    var width = cm.display.gutters.offsetWidth;
326
    cm.display.sizer.style.marginLeft = width + "px";
327
  }
328
329
  // Compute the character length of a line, taking into account
330
  // collapsed ranges (see markText) that might hide parts, and join
331
  // other lines onto it.
332
  function lineLength(line) {
333
    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...
334
    var len = line.text.length, merged, cur = line;
335
    while (merged = collapsedSpanAtStart(cur)) {
336
      var found = merged.find(0, true);
337
      cur = found.from.line;
338
      len += found.from.ch - found.to.ch;
339
    }
340
    cur = line;
341
    while (merged = collapsedSpanAtEnd(cur)) {
342
      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 336. 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...
343
      len -= cur.text.length - found.from.ch;
344
      cur = found.to.line;
345
      len += cur.text.length - found.to.ch;
346
    }
347
    return len;
348
  }
349
350
  // Find the longest line in the document.
351
  function findMaxLine(cm) {
352
    var d = cm.display, doc = cm.doc;
353
    d.maxLine = getLine(doc, doc.first);
354
    d.maxLineLength = lineLength(d.maxLine);
355
    d.maxLineChanged = true;
356
    doc.iter(function(line) {
357
      var len = lineLength(line);
358
      if (len > d.maxLineLength) {
359
        d.maxLineLength = len;
360
        d.maxLine = line;
361
      }
362
    });
363
  }
364
365
  // Make sure the gutters options contains the element
366
  // "CodeMirror-linenumbers" when the lineNumbers option is true.
367
  function setGuttersForLineNumbers(options) {
368
    var found = indexOf(options.gutters, "CodeMirror-linenumbers");
369
    if (found == -1 && options.lineNumbers) {
370
      options.gutters = options.gutters.concat(["CodeMirror-linenumbers"]);
371
    } else if (found > -1 && !options.lineNumbers) {
372
      options.gutters = options.gutters.slice(0);
373
      options.gutters.splice(found, 1);
374
    }
375
  }
376
377
  // SCROLLBARS
378
379
  // Prepare DOM reads needed to update the scrollbars. Done in one
380
  // shot to minimize update/measure roundtrips.
381
  function measureForScrollbars(cm) {
382
    var d = cm.display, gutterW = d.gutters.offsetWidth;
383
    var docH = Math.round(cm.doc.height + paddingVert(cm.display));
384
    return {
385
      clientHeight: d.scroller.clientHeight,
386
      viewHeight: d.wrapper.clientHeight,
387
      scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,
388
      viewWidth: d.wrapper.clientWidth,
389
      barLeft: cm.options.fixedGutter ? gutterW : 0,
390
      docHeight: docH,
391
      scrollHeight: docH + scrollGap(cm) + d.barHeight,
392
      nativeBarWidth: d.nativeBarWidth,
393
      gutterWidth: gutterW
394
    };
395
  }
396
397
  function NativeScrollbars(place, scroll, cm) {
398
    this.cm = cm;
399
    var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar");
400
    var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar");
401
    place(vert); place(horiz);
402
403
    on(vert, "scroll", function() {
404
      if (vert.clientHeight) scroll(vert.scrollTop, "vertical");
405
    });
406
    on(horiz, "scroll", function() {
407
      if (horiz.clientWidth) scroll(horiz.scrollLeft, "horizontal");
408
    });
409
410
    this.checkedOverlay = false;
411
    // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
412
    if (ie && ie_version < 8) this.horiz.style.minHeight = this.vert.style.minWidth = "18px";
413
  }
414
415
  NativeScrollbars.prototype = copyObj({
416
    update: function(measure) {
417
      var needsH = measure.scrollWidth > measure.clientWidth + 1;
418
      var needsV = measure.scrollHeight > measure.clientHeight + 1;
419
      var sWidth = measure.nativeBarWidth;
420
421
      if (needsV) {
422
        this.vert.style.display = "block";
423
        this.vert.style.bottom = needsH ? sWidth + "px" : "0";
424
        var totalHeight = measure.viewHeight - (needsH ? sWidth : 0);
425
        // A bug in IE8 can cause this value to be negative, so guard it.
426
        this.vert.firstChild.style.height =
427
          Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px";
428
      } else {
429
        this.vert.style.display = "";
430
        this.vert.firstChild.style.height = "0";
431
      }
432
433
      if (needsH) {
434
        this.horiz.style.display = "block";
435
        this.horiz.style.right = needsV ? sWidth + "px" : "0";
436
        this.horiz.style.left = measure.barLeft + "px";
437
        var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0);
438
        this.horiz.firstChild.style.width =
439
          (measure.scrollWidth - measure.clientWidth + totalWidth) + "px";
440
      } else {
441
        this.horiz.style.display = "";
442
        this.horiz.firstChild.style.width = "0";
443
      }
444
445
      if (!this.checkedOverlay && measure.clientHeight > 0) {
446
        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...
447
        this.checkedOverlay = true;
448
      }
449
450
      return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0};
451
    },
452
    setScrollLeft: function(pos) {
453
      if (this.horiz.scrollLeft != pos) this.horiz.scrollLeft = pos;
454
    },
455
    setScrollTop: function(pos) {
456
      if (this.vert.scrollTop != pos) this.vert.scrollTop = pos;
457
    },
458
    overlayHack: function() {
459
      var w = mac && !mac_geMountainLion ? "12px" : "18px";
460
      this.horiz.style.minHeight = this.vert.style.minWidth = w;
461
      var self = this;
462
      var barMouseDown = function(e) {
463
        if (e_target(e) != self.vert && e_target(e) != self.horiz)
464
          operation(self.cm, onMouseDown)(e);
465
      };
466
      on(this.vert, "mousedown", barMouseDown);
467
      on(this.horiz, "mousedown", barMouseDown);
468
    },
469
    clear: function() {
470
      var parent = this.horiz.parentNode;
471
      parent.removeChild(this.horiz);
472
      parent.removeChild(this.vert);
473
    }
474
  }, NativeScrollbars.prototype);
475
476
  function NullScrollbars() {}
477
478
  NullScrollbars.prototype = copyObj({
479
    update: function() { return {bottom: 0, right: 0}; },
480
    setScrollLeft: function() {},
481
    setScrollTop: function() {},
482
    clear: function() {}
483
  }, NullScrollbars.prototype);
484
485
  CodeMirror.scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars};
486
487
  function initScrollbars(cm) {
488
    if (cm.display.scrollbars) {
489
      cm.display.scrollbars.clear();
490
      if (cm.display.scrollbars.addClass)
491
        rmClass(cm.display.wrapper, cm.display.scrollbars.addClass);
492
    }
493
494
    cm.display.scrollbars = new CodeMirror.scrollbarModel[cm.options.scrollbarStyle](function(node) {
495
      cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller);
496
      // Prevent clicks in the scrollbars from killing focus
497
      on(node, "mousedown", function() {
498
        if (cm.state.focused) setTimeout(function() { cm.display.input.focus(); }, 0);
499
      });
500
      node.setAttribute("cm-not-content", "true");
501
    }, function(pos, axis) {
502
      if (axis == "horizontal") setScrollLeft(cm, pos);
503
      else setScrollTop(cm, pos);
504
    }, cm);
505
    if (cm.display.scrollbars.addClass)
506
      addClass(cm.display.wrapper, cm.display.scrollbars.addClass);
507
  }
508
509
  function updateScrollbars(cm, measure) {
510
    if (!measure) measure = measureForScrollbars(cm);
511
    var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight;
512
    updateScrollbarsInner(cm, measure);
513
    for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) {
514
      if (startWidth != cm.display.barWidth && cm.options.lineWrapping)
515
        updateHeightsInViewport(cm);
516
      updateScrollbarsInner(cm, measureForScrollbars(cm));
517
      startWidth = cm.display.barWidth; startHeight = cm.display.barHeight;
518
    }
519
  }
520
521
  // Re-synchronize the fake scrollbars with the actual size of the
522
  // content.
523
  function updateScrollbarsInner(cm, measure) {
524
    var d = cm.display;
525
    var sizes = d.scrollbars.update(measure);
526
527
    d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px";
528
    d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px";
529
530
    if (sizes.right && sizes.bottom) {
531
      d.scrollbarFiller.style.display = "block";
532
      d.scrollbarFiller.style.height = sizes.bottom + "px";
533
      d.scrollbarFiller.style.width = sizes.right + "px";
534
    } else d.scrollbarFiller.style.display = "";
535
    if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
536
      d.gutterFiller.style.display = "block";
537
      d.gutterFiller.style.height = sizes.bottom + "px";
538
      d.gutterFiller.style.width = measure.gutterWidth + "px";
539
    } else d.gutterFiller.style.display = "";
540
  }
541
542
  // Compute the lines that are visible in a given viewport (defaults
543
  // the the current scroll position). viewport may contain top,
544
  // height, and ensure (see op.scrollToPos) properties.
545
  function visibleLines(display, doc, viewport) {
546
    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...
547
    top = Math.floor(top - paddingTop(display));
548
    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...
549
550
    var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom);
551
    // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
552
    // forces those lines into the viewport (if possible).
553
    if (viewport && viewport.ensure) {
554
      var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line;
555
      if (ensureFrom < from) {
556
        from = ensureFrom;
557
        to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight);
558
      } else if (Math.min(ensureTo, doc.lastLine()) >= to) {
559
        from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight);
560
        to = ensureTo;
561
      }
562
    }
563
    return {from: from, to: Math.max(to, from + 1)};
564
  }
565
566
  // LINE NUMBERS
567
568
  // Re-align line numbers and gutter marks to compensate for
569
  // horizontal scrolling.
570
  function alignHorizontally(cm) {
571
    var display = cm.display, view = display.view;
572
    if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) return;
573
    var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;
574
    var gutterW = display.gutters.offsetWidth, left = comp + "px";
575
    for (var i = 0; i < view.length; i++) if (!view[i].hidden) {
576
      if (cm.options.fixedGutter && view[i].gutter)
577
        view[i].gutter.style.left = left;
578
      var align = view[i].alignable;
579
      if (align) for (var j = 0; j < align.length; j++)
580
        align[j].style.left = left;
581
    }
582
    if (cm.options.fixedGutter)
583
      display.gutters.style.left = (comp + gutterW) + "px";
584
  }
585
586
  // Used to ensure that the line number gutter is still the right
587
  // size for the current document size. Returns true when an update
588
  // is needed.
589
  function maybeUpdateLineNumberWidth(cm) {
590
    if (!cm.options.lineNumbers) return false;
591
    var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;
592
    if (last.length != display.lineNumChars) {
593
      var test = display.measure.appendChild(elt("div", [elt("div", last)],
594
                                                 "CodeMirror-linenumber CodeMirror-gutter-elt"));
595
      var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
596
      display.lineGutter.style.width = "";
597
      display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1;
598
      display.lineNumWidth = display.lineNumInnerWidth + padding;
599
      display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
600
      display.lineGutter.style.width = display.lineNumWidth + "px";
601
      updateGutterSpace(cm);
602
      return true;
603
    }
604
    return false;
605
  }
606
607
  function lineNumberFor(options, i) {
608
    return String(options.lineNumberFormatter(i + options.firstLineNumber));
609
  }
610
611
  // Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
612
  // but using getBoundingClientRect to get a sub-pixel-accurate
613
  // result.
614
  function compensateForHScroll(display) {
615
    return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left;
616
  }
617
618
  // DISPLAY DRAWING
619
620
  function DisplayUpdate(cm, viewport, force) {
621
    var display = cm.display;
622
623
    this.viewport = viewport;
624
    // Store some values that we'll need later (but don't want to force a relayout for)
625
    this.visible = visibleLines(display, cm.doc, viewport);
626
    this.editorIsHidden = !display.wrapper.offsetWidth;
627
    this.wrapperHeight = display.wrapper.clientHeight;
628
    this.wrapperWidth = display.wrapper.clientWidth;
629
    this.oldDisplayWidth = displayWidth(cm);
630
    this.force = force;
631
    this.dims = getDimensions(cm);
632
    this.events = [];
633
  }
634
635
  DisplayUpdate.prototype.signal = function(emitter, type) {
636
    if (hasHandler(emitter, type))
637
      this.events.push(arguments);
638
  };
639
  DisplayUpdate.prototype.finish = function() {
640
    for (var i = 0; i < this.events.length; i++)
641
      signal.apply(null, this.events[i]);
642
  };
643
644
  function maybeClipScrollbars(cm) {
645
    var display = cm.display;
646
    if (!display.scrollbarsClipped && display.scroller.offsetWidth) {
647
      display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth;
648
      display.heightForcer.style.height = scrollGap(cm) + "px";
649
      display.sizer.style.marginBottom = -display.nativeBarWidth + "px";
650
      display.sizer.style.borderRightWidth = scrollGap(cm) + "px";
651
      display.scrollbarsClipped = true;
652
    }
653
  }
654
655
  // Does the actual updating of the line display. Bails out
656
  // (returning false) when there is nothing to be done and forced is
657
  // false.
658
  function updateDisplayIfNeeded(cm, update) {
659
    var display = cm.display, doc = cm.doc;
660
661
    if (update.editorIsHidden) {
662
      resetView(cm);
663
      return false;
664
    }
665
666
    // Bail out if the visible area is already rendered and nothing changed.
667
    if (!update.force &&
668
        update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&
669
        (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...
670
        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...
671
      return false;
672
673
    if (maybeUpdateLineNumberWidth(cm)) {
674
      resetView(cm);
675
      update.dims = getDimensions(cm);
676
    }
677
678
    // Compute a suitable new viewport (from & to)
679
    var end = doc.first + doc.size;
680
    var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first);
681
    var to = Math.min(end, update.visible.to + cm.options.viewportMargin);
682
    if (display.viewFrom < from && from - display.viewFrom < 20) from = Math.max(doc.first, display.viewFrom);
683
    if (display.viewTo > to && display.viewTo - to < 20) to = Math.min(end, display.viewTo);
684
    if (sawCollapsedSpans) {
685
      from = visualLineNo(cm.doc, from);
686
      to = visualLineEndNo(cm.doc, to);
687
    }
688
689
    var different = from != display.viewFrom || to != display.viewTo ||
690
      display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth;
691
    adjustView(cm, from, to);
692
693
    display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom));
694
    // Position the mover div to align with the current scroll position
695
    cm.display.mover.style.top = display.viewOffset + "px";
696
697
    var toUpdate = countDirtyView(cm);
698
    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...
699
        (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))
700
      return false;
701
702
    // For big changes, we hide the enclosing element during the
703
    // update, since that speeds up the operations on most browsers.
704
    var focused = activeElt();
705
    if (toUpdate > 4) display.lineDiv.style.display = "none";
706
    patchDisplay(cm, display.updateLineNumbers, update.dims);
707
    if (toUpdate > 4) display.lineDiv.style.display = "";
708
    display.renderedView = display.view;
709
    // There might have been a widget with a focused element that got
710
    // hidden or updated, if so re-focus it.
711
    if (focused && activeElt() != focused && focused.offsetHeight) focused.focus();
712
713
    // Prevent selection and cursors from interfering with the scroll
714
    // width and height.
715
    removeChildren(display.cursorDiv);
716
    removeChildren(display.selectionDiv);
717
    display.gutters.style.height = display.sizer.style.minHeight = 0;
718
719
    if (different) {
720
      display.lastWrapHeight = update.wrapperHeight;
721
      display.lastWrapWidth = update.wrapperWidth;
722
      startWorker(cm, 400);
723
    }
724
725
    display.updateLineNumbers = null;
726
727
    return true;
728
  }
729
730
  function postUpdateDisplay(cm, update) {
731
    var viewport = update.viewport;
732
    for (var first = true;; first = false) {
733
      if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) {
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
    if (lineView.gutterBackground) {
959
      lineView.node.removeChild(lineView.gutterBackground);
960
      lineView.gutterBackground = null;
961
    }
962
    if (lineView.line.gutterClass) {
963
      var wrap = ensureLineWrapped(lineView);
964
      lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass,
965
                                      "left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) +
966
                                      "px; width: " + dims.gutterTotalWidth + "px");
967
      wrap.insertBefore(lineView.gutterBackground, lineView.text);
968
    }
969
    var markers = lineView.line.gutterMarkers;
970
    if (cm.options.lineNumbers || markers) {
971
      var wrap = ensureLineWrapped(lineView);
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable wrap already seems to be declared on line 963. 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...
972
      var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", "left: " +
973
                                             (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px");
974
      cm.display.input.setUneditable(gutterWrap);
975
      wrap.insertBefore(gutterWrap, lineView.text);
976
      if (lineView.line.gutterClass)
977
        gutterWrap.className += " " + lineView.line.gutterClass;
978
      if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
979
        lineView.lineNumber = gutterWrap.appendChild(
980
          elt("div", lineNumberFor(cm.options, lineN),
981
              "CodeMirror-linenumber CodeMirror-gutter-elt",
982
              "left: " + dims.gutterLeft["CodeMirror-linenumbers"] + "px; width: "
983
              + cm.display.lineNumInnerWidth + "px"));
984
      if (markers) for (var k = 0; k < cm.options.gutters.length; ++k) {
985
        var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id];
986
        if (found)
987
          gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", "left: " +
988
                                     dims.gutterLeft[id] + "px; width: " + dims.gutterWidth[id] + "px"));
989
      }
990
    }
991
  }
992
993
  function updateLineWidgets(cm, lineView, dims) {
994
    if (lineView.alignable) lineView.alignable = null;
995
    for (var node = lineView.node.firstChild, next; node; node = next) {
996
      var next = node.nextSibling;
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable next already seems to be declared on line 995. 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...
997
      if (node.className == "CodeMirror-linewidget")
998
        lineView.node.removeChild(node);
999
    }
1000
    insertLineWidgets(cm, lineView, dims);
1001
  }
1002
1003
  // Build a line's DOM representation from scratch
1004
  function buildLineElement(cm, lineView, lineN, dims) {
1005
    var built = getLineContent(cm, lineView);
1006
    lineView.text = lineView.node = built.pre;
1007
    if (built.bgClass) lineView.bgClass = built.bgClass;
1008
    if (built.textClass) lineView.textClass = built.textClass;
1009
1010
    updateLineClasses(lineView);
1011
    updateLineGutter(cm, lineView, lineN, dims);
1012
    insertLineWidgets(cm, lineView, dims);
1013
    return lineView.node;
1014
  }
1015
1016
  // A lineView may contain multiple logical lines (when merged by
1017
  // collapsed spans). The widgets for all of them need to be drawn.
1018
  function insertLineWidgets(cm, lineView, dims) {
1019
    insertLineWidgetsFor(cm, lineView.line, lineView, dims, true);
1020
    if (lineView.rest) for (var i = 0; i < lineView.rest.length; i++)
1021
      insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false);
1022
  }
1023
1024
  function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {
1025
    if (!line.widgets) return;
1026
    var wrap = ensureLineWrapped(lineView);
1027
    for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
1028
      var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
1029
      if (!widget.handleMouseEvents) node.setAttribute("cm-ignore-events", "true");
1030
      positionLineWidget(widget, node, lineView, dims);
1031
      cm.display.input.setUneditable(node);
1032
      if (allowAbove && widget.above)
1033
        wrap.insertBefore(node, lineView.gutter || lineView.text);
1034
      else
1035
        wrap.appendChild(node);
1036
      signalLater(widget, "redraw");
1037
    }
1038
  }
1039
1040
  function positionLineWidget(widget, node, lineView, dims) {
1041
    if (widget.noHScroll) {
1042
      (lineView.alignable || (lineView.alignable = [])).push(node);
1043
      var width = dims.wrapperWidth;
1044
      node.style.left = dims.fixedPos + "px";
1045
      if (!widget.coverGutter) {
1046
        width -= dims.gutterTotalWidth;
1047
        node.style.paddingLeft = dims.gutterTotalWidth + "px";
1048
      }
1049
      node.style.width = width + "px";
1050
    }
1051
    if (widget.coverGutter) {
1052
      node.style.zIndex = 5;
1053
      node.style.position = "relative";
1054
      if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + "px";
1055
    }
1056
  }
1057
1058
  // POSITION OBJECT
1059
1060
  // A Pos instance represents a position within the text.
1061
  var Pos = CodeMirror.Pos = function(line, ch) {
1062
    if (!(this instanceof Pos)) return new Pos(line, ch);
1063
    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...
1064
  };
1065
1066
  // Compare two positions, return 0 if they are the same, a negative
1067
  // number when a is less, and a positive number otherwise.
1068
  var cmp = CodeMirror.cmpPos = function(a, b) { return a.line - b.line || a.ch - b.ch; };
1069
1070
  function copyPos(x) {return Pos(x.line, x.ch);}
1071
  function maxPos(a, b) { return cmp(a, b) < 0 ? b : a; }
1072
  function minPos(a, b) { return cmp(a, b) < 0 ? a : b; }
1073
1074
  // INPUT HANDLING
1075
1076
  function ensureFocus(cm) {
1077
    if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); }
1078
  }
1079
1080
  function isReadOnly(cm) {
1081
    return cm.options.readOnly || cm.doc.cantEdit;
1082
  }
1083
1084
  // This will be set to an array of strings when copying, so that,
1085
  // when pasting, we know what kind of selections the copied text
1086
  // was made out of.
1087
  var lastCopied = null;
1088
1089
  function applyTextInput(cm, inserted, deleted, sel, origin) {
1090
    var doc = cm.doc;
1091
    cm.display.shift = false;
1092
    if (!sel) sel = doc.sel;
1093
1094
    var paste = cm.state.pasteIncoming || origin == "paste";
1095
    var textLines = doc.splitLines(inserted), multiPaste = null;
1096
    // When pasing N lines into N selections, insert one line per selection
1097
    if (paste && sel.ranges.length > 1) {
1098
      if (lastCopied && lastCopied.join("\n") == inserted) {
1099
        if (sel.ranges.length % lastCopied.length == 0) {
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...
1100
          multiPaste = [];
1101
          for (var i = 0; i < lastCopied.length; i++)
1102
            multiPaste.push(doc.splitLines(lastCopied[i]));
1103
        }
1104
      } else if (textLines.length == sel.ranges.length) {
1105
        multiPaste = map(textLines, function(l) { return [l]; });
1106
      }
1107
    }
1108
1109
    // Normal behavior is to insert the new text into every selection
1110
    for (var i = sel.ranges.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 1101. 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...
1111
      var range = sel.ranges[i];
1112
      var from = range.from(), to = range.to();
1113
      if (range.empty()) {
1114
        if (deleted && deleted > 0) // Handle deletion
1115
          from = Pos(from.line, from.ch - deleted);
1116
        else if (cm.state.overwrite && !paste) // Handle overwrite
1117
          to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length));
1118
      }
1119
      var updateInput = cm.curOp.updateInput;
1120
      var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i % multiPaste.length] : textLines,
1121
                         origin: origin || (paste ? "paste" : cm.state.cutIncoming ? "cut" : "+input")};
1122
      makeChange(cm.doc, changeEvent);
1123
      signalLater(cm, "inputRead", cm, changeEvent);
1124
    }
1125
    if (inserted && !paste)
1126
      triggerElectric(cm, inserted);
1127
1128
    ensureCursorVisible(cm);
1129
    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...
1130
    cm.curOp.typing = true;
1131
    cm.state.pasteIncoming = cm.state.cutIncoming = false;
1132
  }
1133
1134
  function handlePaste(e, cm) {
1135
    var pasted = e.clipboardData && e.clipboardData.getData("text/plain");
1136
    if (pasted) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if pasted 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...
1137
      e.preventDefault();
1138
      runInOp(cm, function() { applyTextInput(cm, pasted, 0, null, "paste"); });
1139
      return true;
1140
    }
1141
  }
1142
1143
  function triggerElectric(cm, inserted) {
1144
    // When an 'electric' character is inserted, immediately trigger a reindent
1145
    if (!cm.options.electricChars || !cm.options.smartIndent) return;
1146
    var sel = cm.doc.sel;
1147
1148
    for (var i = sel.ranges.length - 1; i >= 0; i--) {
1149
      var range = sel.ranges[i];
1150
      if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head.line)) continue;
1151
      var mode = cm.getModeAt(range.head);
1152
      var indented = false;
1153
      if (mode.electricChars) {
1154
        for (var j = 0; j < mode.electricChars.length; j++)
1155
          if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
1156
            indented = indentLine(cm, range.head.line, "smart");
1157
            break;
1158
          }
1159
      } else if (mode.electricInput) {
1160
        if (mode.electricInput.test(getLine(cm.doc, range.head.line).text.slice(0, range.head.ch)))
1161
          indented = indentLine(cm, range.head.line, "smart");
1162
      }
1163
      if (indented) signalLater(cm, "electricInput", cm, range.head.line);
1164
    }
1165
  }
1166
1167
  function copyableRanges(cm) {
1168
    var text = [], ranges = [];
1169
    for (var i = 0; i < cm.doc.sel.ranges.length; i++) {
1170
      var line = cm.doc.sel.ranges[i].head.line;
1171
      var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)};
1172
      ranges.push(lineRange);
1173
      text.push(cm.getRange(lineRange.anchor, lineRange.head));
1174
    }
1175
    return {text: text, ranges: ranges};
1176
  }
1177
1178
  function disableBrowserMagic(field) {
1179
    field.setAttribute("autocorrect", "off");
1180
    field.setAttribute("autocapitalize", "off");
1181
    field.setAttribute("spellcheck", "false");
1182
  }
1183
1184
  // TEXTAREA INPUT STYLE
1185
1186
  function TextareaInput(cm) {
1187
    this.cm = cm;
1188
    // See input.poll and input.reset
1189
    this.prevInput = "";
1190
1191
    // Flag that indicates whether we expect input to appear real soon
1192
    // now (after some event like 'keypress' or 'input') and are
1193
    // polling intensively.
1194
    this.pollingFast = false;
1195
    // Self-resetting timeout for the poller
1196
    this.polling = new Delayed();
1197
    // Tracks when input.reset has punted to just putting a short
1198
    // string into the textarea instead of the full selection.
1199
    this.inaccurateSelection = false;
1200
    // Used to work around IE issue with selection being forgotten when focus moves away from textarea
1201
    this.hasSelection = false;
1202
    this.composing = null;
1203
  };
1204
1205
  function hiddenTextarea() {
1206
    var te = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none");
1207
    var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
1208
    // The textarea is kept positioned near the cursor to prevent the
1209
    // fact that it'll be scrolled into view on input from scrolling
1210
    // our fake cursor out of view. On webkit, when wrap=off, paste is
1211
    // very slow. So make the area wide instead.
1212
    if (webkit) te.style.width = "1000px";
1213
    else te.setAttribute("wrap", "off");
1214
    // If border: 0; -- iOS fails to open keyboard (issue #1287)
1215
    if (ios) te.style.border = "1px solid black";
1216
    disableBrowserMagic(te);
1217
    return div;
1218
  }
1219
1220
  TextareaInput.prototype = copyObj({
1221
    init: function(display) {
1222
      var input = this, cm = this.cm;
1223
1224
      // Wraps and hides input textarea
1225
      var div = this.wrapper = hiddenTextarea();
1226
      // The semihidden textarea that is focused when the editor is
1227
      // focused, and receives input.
1228
      var te = this.textarea = div.firstChild;
1229
      display.wrapper.insertBefore(div, display.wrapper.firstChild);
1230
1231
      // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)
1232
      if (ios) te.style.width = "0px";
1233
1234
      on(te, "input", function() {
1235
        if (ie && ie_version >= 9 && input.hasSelection) input.hasSelection = null;
1236
        input.poll();
1237
      });
1238
1239
      on(te, "paste", function(e) {
1240
        if (handlePaste(e, cm)) return true;
1241
1242
        cm.state.pasteIncoming = true;
1243
        input.fastPoll();
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...
1244
      });
1245
1246
      function prepareCopyCut(e) {
1247
        if (cm.somethingSelected()) {
1248
          lastCopied = cm.getSelections();
1249
          if (input.inaccurateSelection) {
1250
            input.prevInput = "";
1251
            input.inaccurateSelection = false;
1252
            te.value = lastCopied.join("\n");
1253
            selectInput(te);
1254
          }
1255
        } else if (!cm.options.lineWiseCopyCut) {
1256
          return;
1257
        } else {
1258
          var ranges = copyableRanges(cm);
1259
          lastCopied = ranges.text;
1260
          if (e.type == "cut") {
1261
            cm.setSelections(ranges.ranges, null, sel_dontScroll);
1262
          } else {
1263
            input.prevInput = "";
1264
            te.value = ranges.text.join("\n");
1265
            selectInput(te);
1266
          }
1267
        }
1268
        if (e.type == "cut") cm.state.cutIncoming = true;
1269
      }
1270
      on(te, "cut", prepareCopyCut);
1271
      on(te, "copy", prepareCopyCut);
1272
1273
      on(display.scroller, "paste", function(e) {
1274
        if (eventInWidget(display, e)) return;
1275
        cm.state.pasteIncoming = true;
1276
        input.focus();
1277
      });
1278
1279
      // Prevent normal selection in the editor (we handle our own)
1280
      on(display.lineSpace, "selectstart", function(e) {
1281
        if (!eventInWidget(display, e)) e_preventDefault(e);
1282
      });
1283
1284
      on(te, "compositionstart", function() {
1285
        var start = cm.getCursor("from");
1286
        input.composing = {
1287
          start: start,
1288
          range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"})
1289
        };
1290
      });
1291
      on(te, "compositionend", function() {
1292
        if (input.composing) {
1293
          input.poll();
1294
          input.composing.range.clear();
1295
          input.composing = null;
1296
        }
1297
      });
1298
    },
1299
1300
    prepareSelection: function() {
1301
      // Redraw the selection and/or cursor
1302
      var cm = this.cm, display = cm.display, doc = cm.doc;
1303
      var result = prepareSelection(cm);
1304
1305
      // Move the hidden textarea near the cursor to prevent scrolling artifacts
1306
      if (cm.options.moveInputWithCursor) {
1307
        var headPos = cursorCoords(cm, doc.sel.primary().head, "div");
1308
        var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();
1309
        result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
1310
                                            headPos.top + lineOff.top - wrapOff.top));
1311
        result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
1312
                                             headPos.left + lineOff.left - wrapOff.left));
1313
      }
1314
1315
      return result;
1316
    },
1317
1318
    showSelection: function(drawn) {
1319
      var cm = this.cm, display = cm.display;
1320
      removeChildrenAndAdd(display.cursorDiv, drawn.cursors);
1321
      removeChildrenAndAdd(display.selectionDiv, drawn.selection);
1322
      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...
1323
        this.wrapper.style.top = drawn.teTop + "px";
1324
        this.wrapper.style.left = drawn.teLeft + "px";
1325
      }
1326
    },
1327
1328
    // Reset the input to correspond to the selection (or to be empty,
1329
    // when not typing and nothing is selected)
1330
    reset: function(typing) {
1331
      if (this.contextMenuPending) return;
1332
      var minimal, selected, cm = this.cm, doc = cm.doc;
1333
      if (cm.somethingSelected()) {
1334
        this.prevInput = "";
1335
        var range = doc.sel.primary();
1336
        minimal = hasCopyEvent &&
1337
          (range.to().line - range.from().line > 100 || (selected = cm.getSelection()).length > 1000);
1338
        var content = minimal ? "-" : selected || cm.getSelection();
1339
        this.textarea.value = content;
1340
        if (cm.state.focused) selectInput(this.textarea);
1341
        if (ie && ie_version >= 9) this.hasSelection = content;
1342
      } else if (!typing) {
1343
        this.prevInput = this.textarea.value = "";
1344
        if (ie && ie_version >= 9) this.hasSelection = null;
1345
      }
1346
      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...
1347
    },
1348
1349
    getField: function() { return this.textarea; },
1350
1351
    supportsTouch: function() { return false; },
1352
1353
    focus: function() {
1354
      if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) {
1355
        try { this.textarea.focus(); }
1356
        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...
1357
      }
1358
    },
1359
1360
    blur: function() { this.textarea.blur(); },
1361
1362
    resetPosition: function() {
1363
      this.wrapper.style.top = this.wrapper.style.left = 0;
1364
    },
1365
1366
    receivedFocus: function() { this.slowPoll(); },
1367
1368
    // Poll for input changes, using the normal rate of polling. This
1369
    // runs as long as the editor is focused.
1370
    slowPoll: function() {
1371
      var input = this;
1372
      if (input.pollingFast) return;
1373
      input.polling.set(this.cm.options.pollInterval, function() {
1374
        input.poll();
1375
        if (input.cm.state.focused) input.slowPoll();
1376
      });
1377
    },
1378
1379
    // When an event has just come in that is likely to add or change
1380
    // something in the input textarea, we poll faster, to ensure that
1381
    // the change appears on the screen quickly.
1382
    fastPoll: function() {
1383
      var missed = false, input = this;
1384
      input.pollingFast = true;
1385
      function p() {
1386
        var changed = input.poll();
1387
        if (!changed && !missed) {missed = true; input.polling.set(60, p);}
1388
        else {input.pollingFast = false; input.slowPoll();}
1389
      }
1390
      input.polling.set(20, p);
1391
    },
1392
1393
    // Read input from the textarea, and update the document to match.
1394
    // When something is selected, it is present in the textarea, and
1395
    // selected (unless it is huge, in which case a placeholder is
1396
    // used). When nothing is selected, the cursor sits after previously
1397
    // seen text (can be empty), which is stored in prevInput (we must
1398
    // not reset the textarea when typing, because that breaks IME).
1399
    poll: function() {
1400
      var cm = this.cm, input = this.textarea, prevInput = this.prevInput;
1401
      // Since this is called a *lot*, try to bail out as cheaply as
1402
      // possible when it is clear that nothing happened. hasSelection
1403
      // will be the case when there is a lot of text in the textarea,
1404
      // in which case reading its value would be expensive.
1405
      if (this.contextMenuPending || !cm.state.focused ||
1406
          (hasSelection(input) && !prevInput && !this.composing) ||
1407
          isReadOnly(cm) || cm.options.disableInput || cm.state.keySeq)
1408
        return false;
1409
1410
      var text = input.value;
1411
      // If nothing changed, bail.
1412
      if (text == prevInput && !cm.somethingSelected()) return false;
1413
      // Work around nonsensical selection resetting in IE9/10, and
1414
      // inexplicable appearance of private area unicode characters on
1415
      // some key combos in Mac (#2689).
1416
      if (ie && ie_version >= 9 && this.hasSelection === text ||
1417
          mac && /[\uf700-\uf7ff]/.test(text)) {
1418
        cm.display.input.reset();
1419
        return false;
1420
      }
1421
1422
      if (cm.doc.sel == cm.display.selForContextMenu) {
1423
        var first = text.charCodeAt(0);
1424
        if (first == 0x200b && !prevInput) prevInput = "\u200b";
1425
        if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo"); }
1426
      }
1427
      // Find the part of the input that is actually new
1428
      var same = 0, l = Math.min(prevInput.length, text.length);
1429
      while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++same;
1430
1431
      var self = this;
1432
      runInOp(cm, function() {
1433
        applyTextInput(cm, text.slice(same), prevInput.length - same,
1434
                       null, self.composing ? "*compose" : null);
1435
1436
        // Don't leave long text in the textarea, since it makes further polling slow
1437
        if (text.length > 1000 || text.indexOf("\n") > -1) input.value = self.prevInput = "";
1438
        else self.prevInput = text;
1439
1440
        if (self.composing) {
1441
          self.composing.range.clear();
1442
          self.composing.range = cm.markText(self.composing.start, cm.getCursor("to"),
1443
                                             {className: "CodeMirror-composing"});
1444
        }
1445
      });
1446
      return true;
1447
    },
1448
1449
    ensurePolled: function() {
1450
      if (this.pollingFast && this.poll()) this.pollingFast = false;
1451
    },
1452
1453
    onKeyPress: function() {
1454
      if (ie && ie_version >= 9) this.hasSelection = null;
1455
      this.fastPoll();
1456
    },
1457
1458
    onContextMenu: function(e) {
1459
      var input = this, cm = input.cm, display = cm.display, te = input.textarea;
1460
      var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
1461
      if (!pos || presto) return; // Opera is difficult.
1462
1463
      // Reset the current text selection only if the click is done outside of the selection
1464
      // and 'resetSelectionOnContextMenu' option is true.
1465
      var reset = cm.options.resetSelectionOnContextMenu;
1466
      if (reset && cm.doc.sel.contains(pos) == -1)
1467
        operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll);
1468
1469
      var oldCSS = te.style.cssText;
1470
      input.wrapper.style.position = "absolute";
1471
      te.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
1472
        "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: " +
1473
        (ie ? "rgba(255, 255, 255, .05)" : "transparent") +
1474
        "; outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
1475
      if (webkit) var oldScrollY = window.scrollY; // Work around Chrome issue (#2712)
1476
      display.input.focus();
1477
      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 1475 is false. Are you sure the function scrollTo handles undefined variables?
Loading history...
1478
      display.input.reset();
1479
      // Adds "Select all" to context menu in FF
1480
      if (!cm.somethingSelected()) te.value = input.prevInput = " ";
1481
      input.contextMenuPending = true;
1482
      display.selForContextMenu = cm.doc.sel;
1483
      clearTimeout(display.detectingSelectAll);
1484
1485
      // Select-all will be greyed out if there's nothing to select, so
1486
      // this adds a zero-width space so that we can later check whether
1487
      // it got selected.
1488
      function prepareSelectAllHack() {
1489
        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...
1490
          var selected = cm.somethingSelected();
1491
          var extval = "\u200b" + (selected ? te.value : "");
1492
          te.value = "\u21da"; // Used to catch context-menu undo
1493
          te.value = extval;
1494
          input.prevInput = selected ? "" : "\u200b";
1495
          te.selectionStart = 1; te.selectionEnd = extval.length;
1496
          // Re-set this, in case some other handler touched the
1497
          // selection in the meantime.
1498
          display.selForContextMenu = cm.doc.sel;
1499
        }
1500
      }
1501
      function rehide() {
1502
        input.contextMenuPending = false;
1503
        input.wrapper.style.position = "relative";
1504
        te.style.cssText = oldCSS;
1505
        if (ie && ie_version < 9) display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos);
1506
1507
        // Try to detect the user choosing select-all
1508
        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...
1509
          if (!ie || (ie && ie_version < 9)) prepareSelectAllHack();
1510
          var i = 0, poll = function() {
1511
            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...
1512
                te.selectionEnd > 0 && input.prevInput == "\u200b")
1513
              operation(cm, commands.selectAll)(cm);
1514
            else if (i++ < 10) display.detectingSelectAll = setTimeout(poll, 500);
1515
            else display.input.reset();
1516
          };
1517
          display.detectingSelectAll = setTimeout(poll, 200);
1518
        }
1519
      }
1520
1521
      if (ie && ie_version >= 9) prepareSelectAllHack();
1522
      if (captureRightClick) {
1523
        e_stop(e);
1524
        var mouseup = function() {
1525
          off(window, "mouseup", mouseup);
1526
          setTimeout(rehide, 20);
1527
        };
1528
        on(window, "mouseup", mouseup);
1529
      } else {
1530
        setTimeout(rehide, 50);
1531
      }
1532
    },
1533
1534
    setUneditable: nothing,
1535
1536
    needsContentAttribute: false
1537
  }, TextareaInput.prototype);
1538
1539
  // CONTENTEDITABLE INPUT STYLE
1540
1541
  function ContentEditableInput(cm) {
1542
    this.cm = cm;
1543
    this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null;
1544
    this.polling = new Delayed();
1545
    this.gracePeriod = false;
1546
  }
1547
1548
  ContentEditableInput.prototype = copyObj({
1549
    init: function(display) {
1550
      var input = this, cm = input.cm;
1551
      var div = input.div = display.lineDiv;
1552
      div.contentEditable = "true";
1553
      disableBrowserMagic(div);
1554
1555
      on(div, "paste", function(e) { handlePaste(e, cm); })
1556
1557
      on(div, "compositionstart", function(e) {
1558
        var data = e.data;
1559
        input.composing = {sel: cm.doc.sel, data: data, startData: data};
1560
        if (!data) return;
1561
        var prim = cm.doc.sel.primary();
1562
        var line = cm.getLine(prim.head.line);
1563
        var found = line.indexOf(data, Math.max(0, prim.head.ch - data.length));
1564
        if (found > -1 && found <= prim.head.ch)
1565
          input.composing.sel = simpleSelection(Pos(prim.head.line, found),
1566
                                                Pos(prim.head.line, found + data.length));
1567
      });
1568
      on(div, "compositionupdate", function(e) {
1569
        input.composing.data = e.data;
1570
      });
1571
      on(div, "compositionend", function(e) {
1572
        var ours = input.composing;
1573
        if (!ours) return;
1574
        if (e.data != ours.startData && !/\u200b/.test(e.data))
1575
          ours.data = e.data;
1576
        // Need a small delay to prevent other code (input event,
1577
        // selection polling) from doing damage when fired right after
1578
        // compositionend.
1579
        setTimeout(function() {
1580
          if (!ours.handled)
1581
            input.applyComposition(ours);
1582
          if (input.composing == ours)
1583
            input.composing = null;
1584
        }, 50);
1585
      });
1586
1587
      on(div, "touchstart", function() {
1588
        input.forceCompositionEnd();
1589
      });
1590
1591
      on(div, "input", function() {
1592
        if (input.composing) return;
1593
        if (!input.pollContent())
1594
          runInOp(input.cm, function() {regChange(cm);});
1595
      });
1596
1597
      function onCopyCut(e) {
1598
        if (cm.somethingSelected()) {
1599
          lastCopied = cm.getSelections();
1600
          if (e.type == "cut") cm.replaceSelection("", null, "cut");
1601
        } else if (!cm.options.lineWiseCopyCut) {
1602
          return;
1603
        } else {
1604
          var ranges = copyableRanges(cm);
1605
          lastCopied = ranges.text;
1606
          if (e.type == "cut") {
1607
            cm.operation(function() {
1608
              cm.setSelections(ranges.ranges, 0, sel_dontScroll);
1609
              cm.replaceSelection("", null, "cut");
1610
            });
1611
          }
1612
        }
1613
        // iOS exposes the clipboard API, but seems to discard content inserted into it
1614
        if (e.clipboardData && !ios) {
1615
          e.preventDefault();
1616
          e.clipboardData.clearData();
1617
          e.clipboardData.setData("text/plain", lastCopied.join("\n"));
1618
        } else {
1619
          // Old-fashioned briefly-focus-a-textarea hack
1620
          var kludge = hiddenTextarea(), te = kludge.firstChild;
1621
          cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild);
1622
          te.value = lastCopied.join("\n");
1623
          var hadFocus = document.activeElement;
1624
          selectInput(te);
1625
          setTimeout(function() {
1626
            cm.display.lineSpace.removeChild(kludge);
1627
            hadFocus.focus();
1628
          }, 50);
1629
        }
1630
      }
1631
      on(div, "copy", onCopyCut);
1632
      on(div, "cut", onCopyCut);
1633
    },
1634
1635
    prepareSelection: function() {
1636
      var result = prepareSelection(this.cm, false);
1637
      result.focus = this.cm.state.focused;
1638
      return result;
1639
    },
1640
1641
    showSelection: function(info) {
1642
      if (!info || !this.cm.display.view.length) return;
1643
      if (info.focus) this.showPrimarySelection();
1644
      this.showMultipleSelections(info);
1645
    },
1646
1647
    showPrimarySelection: function() {
1648
      var sel = window.getSelection(), prim = this.cm.doc.sel.primary();
1649
      var curAnchor = domToPos(this.cm, sel.anchorNode, sel.anchorOffset);
1650
      var curFocus = domToPos(this.cm, sel.focusNode, sel.focusOffset);
1651
      if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad &&
1652
          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...
1653
          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...
1654
        return;
1655
1656
      var start = posToDOM(this.cm, prim.from());
1657
      var end = posToDOM(this.cm, prim.to());
1658
      if (!start && !end) return;
1659
1660
      var view = this.cm.display.view;
1661
      var old = sel.rangeCount && sel.getRangeAt(0);
1662
      if (!start) {
1663
        start = {node: view[0].measure.map[2], offset: 0};
1664
      } else if (!end) { // FIXME dangerously hacky
1665
        var measure = view[view.length - 1].measure;
1666
        var map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map;
1667
        end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map.length - 3]};
1668
      }
1669
1670
      try { var rng = range(start.node, start.offset, end.offset, end.node); }
1671
      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...
1672
      if (rng) {
1673
        sel.removeAllRanges();
1674
        sel.addRange(rng);
1675
        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...
1676
        else if (gecko) this.startGracePeriod();
1677
      }
1678
      this.rememberSelection();
1679
    },
1680
1681
    startGracePeriod: function() {
1682
      var input = this;
1683
      clearTimeout(this.gracePeriod);
1684
      this.gracePeriod = setTimeout(function() {
1685
        input.gracePeriod = false;
1686
        if (input.selectionChanged())
1687
          input.cm.operation(function() { input.cm.curOp.selectionChanged = true; });
1688
      }, 20);
1689
    },
1690
1691
    showMultipleSelections: function(info) {
1692
      removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors);
1693
      removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection);
1694
    },
1695
1696
    rememberSelection: function() {
1697
      var sel = window.getSelection();
1698
      this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset;
1699
      this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset;
1700
    },
1701
1702
    selectionInEditor: function() {
1703
      var sel = window.getSelection();
1704
      if (!sel.rangeCount) return false;
1705
      var node = sel.getRangeAt(0).commonAncestorContainer;
1706
      return contains(this.div, node);
1707
    },
1708
1709
    focus: function() {
1710
      if (this.cm.options.readOnly != "nocursor") this.div.focus();
1711
    },
1712
    blur: function() { this.div.blur(); },
1713
    getField: function() { return this.div; },
1714
1715
    supportsTouch: function() { return true; },
1716
1717
    receivedFocus: function() {
1718
      var input = this;
1719
      if (this.selectionInEditor())
1720
        this.pollSelection();
1721
      else
1722
        runInOp(this.cm, function() { input.cm.curOp.selectionChanged = true; });
1723
1724
      function poll() {
1725
        if (input.cm.state.focused) {
1726
          input.pollSelection();
1727
          input.polling.set(input.cm.options.pollInterval, poll);
1728
        }
1729
      }
1730
      this.polling.set(this.cm.options.pollInterval, poll);
1731
    },
1732
1733
    selectionChanged: function() {
1734
      var sel = window.getSelection();
1735
      return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||
1736
        sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset;
1737
    },
1738
1739
    pollSelection: function() {
1740
      if (!this.composing && !this.gracePeriod && this.selectionChanged()) {
1741
        var sel = window.getSelection(), cm = this.cm;
1742
        this.rememberSelection();
1743
        var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
1744
        var head = domToPos(cm, sel.focusNode, sel.focusOffset);
1745
        if (anchor && head) runInOp(cm, function() {
1746
          setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll);
1747
          if (anchor.bad || head.bad) cm.curOp.selectionChanged = true;
1748
        });
1749
      }
1750
    },
1751
1752
    pollContent: function() {
1753
      var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary();
1754
      var from = sel.from(), to = sel.to();
1755
      if (from.line < display.viewFrom || to.line > display.viewTo - 1) return false;
1756
1757
      var fromIndex;
1758
      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...
1759
        var fromLine = lineNo(display.view[0].line);
1760
        var fromNode = display.view[0].node;
1761
      } else {
1762
        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 1759. 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...
1763
        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 1760. 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...
1764
      }
1765
      var toIndex = findViewIndex(cm, to.line);
1766
      if (toIndex == display.view.length - 1) {
1767
        var toLine = display.viewTo - 1;
1768
        var toNode = display.lineDiv.lastChild;
1769
      } else {
1770
        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 1767. 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...
1771
        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 1768. 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...
1772
      }
1773
1774
      var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine));
1775
      var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length));
1776
      while (newText.length > 1 && oldText.length > 1) {
1777
        if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; }
1778
        else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; }
1779
        else break;
1780
      }
1781
1782
      var cutFront = 0, cutEnd = 0;
1783
      var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length);
1784
      while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront))
1785
        ++cutFront;
1786
      var newBot = lst(newText), oldBot = lst(oldText);
1787
      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...
1788
                               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...
1789
      while (cutEnd < maxCutEnd &&
1790
             newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1))
1791
        ++cutEnd;
1792
1793
      newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd);
1794
      newText[0] = newText[0].slice(cutFront);
1795
1796
      var chFrom = Pos(fromLine, cutFront);
1797
      var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0);
1798
      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...
1799
        replaceRange(cm.doc, newText, chFrom, chTo, "+input");
1800
        return true;
1801
      }
1802
    },
1803
1804
    ensurePolled: function() {
1805
      this.forceCompositionEnd();
1806
    },
1807
    reset: function() {
1808
      this.forceCompositionEnd();
1809
    },
1810
    forceCompositionEnd: function() {
1811
      if (!this.composing || this.composing.handled) return;
1812
      this.applyComposition(this.composing);
1813
      this.composing.handled = true;
1814
      this.div.blur();
1815
      this.div.focus();
1816
    },
1817
    applyComposition: function(composing) {
1818
      if (composing.data && composing.data != composing.startData)
1819
        operation(this.cm, applyTextInput)(this.cm, composing.data, 0, composing.sel);
1820
    },
1821
1822
    setUneditable: function(node) {
1823
      node.setAttribute("contenteditable", "false");
1824
    },
1825
1826
    onKeyPress: function(e) {
1827
      e.preventDefault();
1828
      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...
1829
    },
1830
1831
    onContextMenu: nothing,
1832
    resetPosition: nothing,
1833
1834
    needsContentAttribute: true
1835
  }, ContentEditableInput.prototype);
1836
1837
  function posToDOM(cm, pos) {
1838
    var view = findViewForLine(cm, pos.line);
1839
    if (!view || view.hidden) return null;
1840
    var line = getLine(cm.doc, pos.line);
1841
    var info = mapFromLineView(view, line, pos.line);
1842
1843
    var order = getOrder(line), side = "left";
1844
    if (order) {
1845
      var partPos = getBidiPartAt(order, pos.ch);
1846
      side = partPos % 2 ? "right" : "left";
1847
    }
1848
    var result = nodeAndOffsetInLineMap(info.map, pos.ch, side);
1849
    result.offset = result.collapse == "right" ? result.end : result.start;
1850
    return result;
1851
  }
1852
1853
  function badPos(pos, bad) { if (bad) pos.bad = true; return pos; }
1854
1855
  function domToPos(cm, node, offset) {
1856
    var lineNode;
1857
    if (node == cm.display.lineDiv) {
1858
      lineNode = cm.display.lineDiv.childNodes[offset];
1859
      if (!lineNode) return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true);
1860
      node = null; offset = 0;
1861
    } else {
1862
      for (lineNode = node;; lineNode = lineNode.parentNode) {
1863
        if (!lineNode || lineNode == cm.display.lineDiv) return null;
1864
        if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) break;
1865
      }
1866
    }
1867
    for (var i = 0; i < cm.display.view.length; i++) {
1868
      var lineView = cm.display.view[i];
1869
      if (lineView.node == lineNode)
1870
        return locateNodeInLineView(lineView, node, offset);
1871
    }
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...
1872
  }
1873
1874
  function locateNodeInLineView(lineView, node, offset) {
1875
    var wrapper = lineView.text.firstChild, bad = false;
1876
    if (!node || !contains(wrapper, node)) return badPos(Pos(lineNo(lineView.line), 0), true);
1877
    if (node == wrapper) {
1878
      bad = true;
1879
      node = wrapper.childNodes[offset];
1880
      offset = 0;
1881
      if (!node) {
1882
        var line = lineView.rest ? lst(lineView.rest) : lineView.line;
1883
        return badPos(Pos(lineNo(line), line.text.length), bad);
1884
      }
1885
    }
1886
1887
    var textNode = node.nodeType == 3 ? node : null, topNode = node;
1888
    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...
1889
      textNode = node.firstChild;
1890
      if (offset) offset = textNode.nodeValue.length;
1891
    }
1892
    while (topNode.parentNode != wrapper) topNode = topNode.parentNode;
1893
    var measure = lineView.measure, maps = measure.maps;
1894
1895
    function find(textNode, topNode, offset) {
1896
      for (var i = -1; i < (maps ? maps.length : 0); i++) {
1897
        var map = i < 0 ? measure.map : maps[i];
1898
        for (var j = 0; j < map.length; j += 3) {
1899
          var curNode = map[j + 2];
1900
          if (curNode == textNode || curNode == topNode) {
1901
            var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]);
1902
            var ch = map[j] + offset;
1903
            if (offset < 0 || curNode != textNode) ch = map[j + (offset ? 1 : 0)];
1904
            return Pos(line, ch);
1905
          }
1906
        }
1907
      }
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...
1908
    }
1909
    var found = find(textNode, topNode, offset);
1910
    if (found) return badPos(found, bad);
1911
1912
    // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems
1913
    for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) {
1914
      found = find(after, after.firstChild, 0);
1915
      if (found)
1916
        return badPos(Pos(found.line, found.ch - dist), bad);
1917
      else
1918
        dist += after.textContent.length;
1919
    }
1920
    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 1913. 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...
1921
      found = find(before, before.firstChild, -1);
1922
      if (found)
1923
        return badPos(Pos(found.line, found.ch + dist), bad);
1924
      else
1925
        dist += after.textContent.length;
1926
    }
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...
1927
  }
1928
1929
  function domTextBetween(cm, from, to, fromLine, toLine) {
1930
    var text = "", closing = false, lineSep = cm.doc.lineSeparator();
1931
    function recognizeMarker(id) { return function(marker) { return marker.id == id; }; }
1932
    function walk(node) {
1933
      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...
1934
        var cmText = node.getAttribute("cm-text");
1935
        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...
1936
          if (cmText == "") cmText = node.textContent.replace(/\u200b/g, "");
1937
          text += cmText;
1938
          return;
1939
        }
1940
        var markerID = node.getAttribute("cm-marker"), range;
1941
        if (markerID) {
1942
          var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID));
1943
          if (found.length && (range = found[0].find()))
1944
            text += getBetween(cm.doc, range.from, range.to).join(lineSep);
1945
          return;
1946
        }
1947
        if (node.getAttribute("contenteditable") == "false") return;
1948
        for (var i = 0; i < node.childNodes.length; i++)
1949
          walk(node.childNodes[i]);
1950
        if (/^(pre|div|p)$/i.test(node.nodeName))
1951
          closing = true;
1952
      } else if (node.nodeType == 3) {
1953
        var val = node.nodeValue;
1954
        if (!val) return;
1955
        if (closing) {
1956
          text += lineSep;
1957
          closing = false;
1958
        }
1959
        text += val;
1960
      }
1961
    }
1962
    for (;;) {
1963
      walk(from);
1964
      if (from == to) break;
1965
      from = from.nextSibling;
1966
    }
1967
    return text;
1968
  }
1969
1970
  CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput};
1971
1972
  // SELECTION / CURSOR
1973
1974
  // Selection objects are immutable. A new one is created every time
1975
  // the selection changes. A selection is one or more non-overlapping
1976
  // (and non-touching) ranges, sorted, and an integer that indicates
1977
  // which one is the primary selection (the one that's scrolled into
1978
  // view, that getCursor returns, etc).
1979
  function Selection(ranges, primIndex) {
1980
    this.ranges = ranges;
1981
    this.primIndex = primIndex;
1982
  }
1983
1984
  Selection.prototype = {
1985
    primary: function() { return this.ranges[this.primIndex]; },
1986
    equals: function(other) {
1987
      if (other == this) return true;
1988
      if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) return false;
1989
      for (var i = 0; i < this.ranges.length; i++) {
1990
        var here = this.ranges[i], there = other.ranges[i];
1991
        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...
1992
      }
1993
      return true;
1994
    },
1995
    deepCopy: function() {
1996
      for (var out = [], i = 0; i < this.ranges.length; i++)
1997
        out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head));
1998
      return new Selection(out, this.primIndex);
1999
    },
2000
    somethingSelected: function() {
2001
      for (var i = 0; i < this.ranges.length; i++)
2002
        if (!this.ranges[i].empty()) return true;
2003
      return false;
2004
    },
2005
    contains: function(pos, end) {
2006
      if (!end) end = pos;
2007
      for (var i = 0; i < this.ranges.length; i++) {
2008
        var range = this.ranges[i];
2009
        if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)
2010
          return i;
2011
      }
2012
      return -1;
2013
    }
2014
  };
2015
2016
  function Range(anchor, head) {
2017
    this.anchor = anchor; this.head = head;
2018
  }
2019
2020
  Range.prototype = {
2021
    from: function() { return minPos(this.anchor, this.head); },
2022
    to: function() { return maxPos(this.anchor, this.head); },
2023
    empty: function() {
2024
      return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch;
2025
    }
2026
  };
2027
2028
  // Take an unsorted, potentially overlapping set of ranges, and
2029
  // build a selection out of it. 'Consumes' ranges array (modifying
2030
  // it).
2031
  function normalizeSelection(ranges, primIndex) {
2032
    var prim = ranges[primIndex];
2033
    ranges.sort(function(a, b) { return cmp(a.from(), b.from()); });
2034
    primIndex = indexOf(ranges, prim);
2035
    for (var i = 1; i < ranges.length; i++) {
2036
      var cur = ranges[i], prev = ranges[i - 1];
2037
      if (cmp(prev.to(), cur.from()) >= 0) {
2038
        var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to());
2039
        var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head;
2040
        if (i <= primIndex) --primIndex;
2041
        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...
2042
      }
2043
    }
2044
    return new Selection(ranges, primIndex);
2045
  }
2046
2047
  function simpleSelection(anchor, head) {
2048
    return new Selection([new Range(anchor, head || anchor)], 0);
2049
  }
2050
2051
  // Most of the external API clips given positions to make sure they
2052
  // actually exist within the document.
2053
  function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1));}
2054
  function clipPos(doc, pos) {
2055
    if (pos.line < doc.first) return Pos(doc.first, 0);
2056
    var last = doc.first + doc.size - 1;
2057
    if (pos.line > last) return Pos(last, getLine(doc, last).text.length);
2058
    return clipToLen(pos, getLine(doc, pos.line).text.length);
2059
  }
2060
  function clipToLen(pos, linelen) {
2061
    var ch = pos.ch;
2062
    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...
2063
    else if (ch < 0) return Pos(pos.line, 0);
2064
    else return pos;
2065
  }
2066
  function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size;}
2067
  function clipPosArray(doc, array) {
2068
    for (var out = [], i = 0; i < array.length; i++) out[i] = clipPos(doc, array[i]);
2069
    return out;
2070
  }
2071
2072
  // SELECTION UPDATES
2073
2074
  // The 'scroll' parameter given to many of these indicated whether
2075
  // the new cursor position should be scrolled into view after
2076
  // modifying the selection.
2077
2078
  // If shift is held or the extend flag is set, extends a range to
2079
  // include a given position (and optionally a second position).
2080
  // Otherwise, simply returns the range between the given positions.
2081
  // Used for cursor motion and such.
2082
  function extendRange(doc, range, head, other) {
2083
    if (doc.cm && doc.cm.display.shift || doc.extend) {
2084
      var anchor = range.anchor;
2085
      if (other) {
2086
        var posBefore = cmp(head, anchor) < 0;
2087
        if (posBefore != (cmp(other, anchor) < 0)) {
2088
          anchor = head;
2089
          head = other;
2090
        } else if (posBefore != (cmp(head, other) < 0)) {
2091
          head = other;
2092
        }
2093
      }
2094
      return new Range(anchor, head);
2095
    } 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...
2096
      return new Range(other || head, head);
2097
    }
2098
  }
2099
2100
  // Extend the primary selection range, discard the rest.
2101
  function extendSelection(doc, head, other, options) {
2102
    setSelection(doc, new Selection([extendRange(doc, doc.sel.primary(), head, other)], 0), options);
2103
  }
2104
2105
  // Extend all selections (pos is an array of selections with length
2106
  // equal the number of selections)
2107
  function extendSelections(doc, heads, options) {
2108
    for (var out = [], i = 0; i < doc.sel.ranges.length; i++)
2109
      out[i] = extendRange(doc, doc.sel.ranges[i], heads[i], null);
2110
    var newSel = normalizeSelection(out, doc.sel.primIndex);
2111
    setSelection(doc, newSel, options);
2112
  }
2113
2114
  // Updates a single range in the selection.
2115
  function replaceOneSelection(doc, i, range, options) {
2116
    var ranges = doc.sel.ranges.slice(0);
2117
    ranges[i] = range;
2118
    setSelection(doc, normalizeSelection(ranges, doc.sel.primIndex), options);
2119
  }
2120
2121
  // Reset the selection to a single range.
2122
  function setSimpleSelection(doc, anchor, head, options) {
2123
    setSelection(doc, simpleSelection(anchor, head), options);
2124
  }
2125
2126
  // Give beforeSelectionChange handlers a change to influence a
2127
  // selection update.
2128
  function filterSelectionChange(doc, sel) {
2129
    var obj = {
2130
      ranges: sel.ranges,
2131
      update: function(ranges) {
2132
        this.ranges = [];
2133
        for (var i = 0; i < ranges.length; i++)
2134
          this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),
2135
                                     clipPos(doc, ranges[i].head));
2136
      }
2137
    };
2138
    signal(doc, "beforeSelectionChange", doc, obj);
2139
    if (doc.cm) signal(doc.cm, "beforeSelectionChange", doc.cm, obj);
2140
    if (obj.ranges != sel.ranges) return normalizeSelection(obj.ranges, obj.ranges.length - 1);
2141
    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...
2142
  }
2143
2144
  function setSelectionReplaceHistory(doc, sel, options) {
2145
    var done = doc.history.done, last = lst(done);
2146
    if (last && last.ranges) {
2147
      done[done.length - 1] = sel;
2148
      setSelectionNoUndo(doc, sel, options);
2149
    } else {
2150
      setSelection(doc, sel, options);
2151
    }
2152
  }
2153
2154
  // Set a new selection.
2155
  function setSelection(doc, sel, options) {
2156
    setSelectionNoUndo(doc, sel, options);
2157
    addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options);
2158
  }
2159
2160
  function setSelectionNoUndo(doc, sel, options) {
2161
    if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange"))
2162
      sel = filterSelectionChange(doc, sel);
2163
2164
    var bias = options && options.bias ||
2165
      (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1);
2166
    setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true));
2167
2168
    if (!(options && options.scroll === false) && doc.cm)
2169
      ensureCursorVisible(doc.cm);
2170
  }
2171
2172
  function setSelectionInner(doc, sel) {
2173
    if (sel.equals(doc.sel)) return;
2174
2175
    doc.sel = sel;
2176
2177
    if (doc.cm) {
2178
      doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true;
2179
      signalCursorActivity(doc.cm);
2180
    }
2181
    signalLater(doc, "cursorActivity", doc);
2182
  }
2183
2184
  // Verify that the selection does not partially select any atomic
2185
  // marked ranges.
2186
  function reCheckSelection(doc) {
2187
    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...
2188
  }
2189
2190
  // Return a selection that does not partially select any atomic
2191
  // ranges.
2192
  function skipAtomicInSelection(doc, sel, bias, mayClear) {
2193
    var out;
2194
    for (var i = 0; i < sel.ranges.length; i++) {
2195
      var range = sel.ranges[i];
2196
      var newAnchor = skipAtomic(doc, range.anchor, bias, mayClear);
2197
      var newHead = skipAtomic(doc, range.head, bias, mayClear);
2198
      if (out || newAnchor != range.anchor || newHead != range.head) {
2199
        if (!out) out = sel.ranges.slice(0, i);
2200
        out[i] = new Range(newAnchor, newHead);
2201
      }
2202
    }
2203
    return out ? normalizeSelection(out, sel.primIndex) : sel;
2204
  }
2205
2206
  // Ensure a given position is not inside an atomic range.
2207
  function skipAtomic(doc, pos, bias, mayClear) {
2208
    var flipped = false, curPos = pos;
2209
    var dir = bias || 1;
2210
    doc.cantEdit = false;
2211
    search: for (;;) {
2212
      var line = getLine(doc, curPos.line);
2213
      if (line.markedSpans) {
2214
        for (var i = 0; i < line.markedSpans.length; ++i) {
2215
          var sp = line.markedSpans[i], m = sp.marker;
2216
          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...
2217
              (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...
2218
            if (mayClear) {
2219
              signal(m, "beforeCursorEnter");
2220
              if (m.explicitlyCleared) {
2221
                if (!line.markedSpans) break;
2222
                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...
2223
              }
2224
            }
2225
            if (!m.atomic) continue;
2226
            var newPos = m.find(dir < 0 ? -1 : 1);
2227
            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...
2228
              newPos.ch += dir;
2229
              if (newPos.ch < 0) {
2230
                if (newPos.line > doc.first) newPos = clipPos(doc, Pos(newPos.line - 1));
2231
                else newPos = null;
2232
              } else if (newPos.ch > line.text.length) {
2233
                if (newPos.line < doc.first + doc.size - 1) newPos = Pos(newPos.line + 1, 0);
2234
                else newPos = null;
2235
              }
2236
              if (!newPos) {
2237
                if (flipped) {
2238
                  // Driven in a corner -- no valid cursor position found at all
2239
                  // -- try again *with* clearing, if we didn't already
2240
                  if (!mayClear) return skipAtomic(doc, pos, bias, true);
2241
                  // Otherwise, turn off editing until further notice, and return the start of the doc
2242
                  doc.cantEdit = true;
2243
                  return Pos(doc.first, 0);
2244
                }
2245
                flipped = true; newPos = pos; dir = -dir;
2246
              }
2247
            }
2248
            curPos = newPos;
2249
            continue search;
2250
          }
2251
        }
2252
      }
2253
      return curPos;
2254
    }
2255
  }
2256
2257
  // SELECTION DRAWING
2258
2259
  function updateSelection(cm) {
2260
    cm.display.input.showSelection(cm.display.input.prepareSelection());
2261
  }
2262
2263
  function prepareSelection(cm, primary) {
2264
    var doc = cm.doc, result = {};
2265
    var curFragment = result.cursors = document.createDocumentFragment();
2266
    var selFragment = result.selection = document.createDocumentFragment();
2267
2268
    for (var i = 0; i < doc.sel.ranges.length; i++) {
2269
      if (primary === false && i == doc.sel.primIndex) continue;
2270
      var range = doc.sel.ranges[i];
2271
      var collapsed = range.empty();
2272
      if (collapsed || cm.options.showCursorWhenSelecting)
2273
        drawSelectionCursor(cm, range, curFragment);
2274
      if (!collapsed)
2275
        drawSelectionRange(cm, range, selFragment);
2276
    }
2277
    return result;
2278
  }
2279
2280
  // Draws a cursor for the given range
2281
  function drawSelectionCursor(cm, range, output) {
2282
    var pos = cursorCoords(cm, range.head, "div", null, null, !cm.options.singleCursorHeightPerLine);
2283
2284
    var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor"));
2285
    cursor.style.left = pos.left + "px";
2286
    cursor.style.top = pos.top + "px";
2287
    cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
2288
2289
    if (pos.other) {
2290
      // Secondary cursor, shown when on a 'jump' in bi-directional text
2291
      var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"));
2292
      otherCursor.style.display = "";
2293
      otherCursor.style.left = pos.other.left + "px";
2294
      otherCursor.style.top = pos.other.top + "px";
2295
      otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
2296
    }
2297
  }
2298
2299
  // Draws the given range as a highlighted selection
2300
  function drawSelectionRange(cm, range, output) {
2301
    var display = cm.display, doc = cm.doc;
2302
    var fragment = document.createDocumentFragment();
2303
    var padding = paddingH(cm.display), leftSide = padding.left;
2304
    var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right;
2305
2306
    function add(left, top, width, bottom) {
2307
      if (top < 0) top = 0;
2308
      top = Math.round(top);
2309
      bottom = Math.round(bottom);
2310
      fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left +
2311
                               "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...
2312
                               "px; height: " + (bottom - top) + "px"));
2313
    }
2314
2315
    function drawForLine(line, fromArg, toArg) {
2316
      var lineObj = getLine(doc, line);
2317
      var lineLen = lineObj.text.length;
2318
      var start, end;
2319
      function coords(ch, bias) {
2320
        return charCoords(cm, Pos(line, ch), "div", lineObj, bias);
2321
      }
2322
2323
      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...
2324
        var leftPos = coords(from, "left"), rightPos, left, right;
2325
        if (from == to) {
2326
          rightPos = leftPos;
2327
          left = right = leftPos.left;
2328
        } else {
2329
          rightPos = coords(to - 1, "right");
2330
          if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp; }
2331
          left = leftPos.left;
2332
          right = rightPos.right;
2333
        }
2334
        if (fromArg == null && from == 0) left = leftSide;
0 ignored issues
show
Best Practice introduced by
Comparing from to 0 using the == operator is not safe. Consider using === instead.
Loading history...
Best Practice introduced by
Comparing fromArg to null using the == operator is not safe. Consider using === instead.
Loading history...
2335
        if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
2336
          add(left, leftPos.top, null, leftPos.bottom);
2337
          left = leftSide;
2338
          if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rightPos.top);
2339
        }
2340
        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...
2341
        if (!start || leftPos.top < start.top || leftPos.top == start.top && leftPos.left < start.left)
2342
          start = leftPos;
2343
        if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.bottom && rightPos.right > end.right)
2344
          end = rightPos;
2345
        if (left < leftSide + 1) left = leftSide;
2346
        add(left, rightPos.top, right - left, rightPos.bottom);
2347
      });
2348
      return {start: start, end: end};
2349
    }
2350
2351
    var sFrom = range.from(), sTo = range.to();
2352
    if (sFrom.line == sTo.line) {
2353
      drawForLine(sFrom.line, sFrom.ch, sTo.ch);
2354
    } else {
2355
      var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line);
2356
      var singleVLine = visualLine(fromLine) == visualLine(toLine);
2357
      var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end;
2358
      var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start;
2359
      if (singleVLine) {
2360
        if (leftEnd.top < rightStart.top - 2) {
2361
          add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);
2362
          add(leftSide, rightStart.top, rightStart.left, rightStart.bottom);
2363
        } else {
2364
          add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);
2365
        }
2366
      }
2367
      if (leftEnd.bottom < rightStart.top)
2368
        add(leftSide, leftEnd.bottom, null, rightStart.top);
2369
    }
2370
2371
    output.appendChild(fragment);
2372
  }
2373
2374
  // Cursor-blinking
2375
  function restartBlink(cm) {
2376
    if (!cm.state.focused) return;
2377
    var display = cm.display;
2378
    clearInterval(display.blinker);
2379
    var on = true;
2380
    display.cursorDiv.style.visibility = "";
2381
    if (cm.options.cursorBlinkRate > 0)
2382
      display.blinker = setInterval(function() {
2383
        display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden";
2384
      }, cm.options.cursorBlinkRate);
2385
    else if (cm.options.cursorBlinkRate < 0)
2386
      display.cursorDiv.style.visibility = "hidden";
2387
  }
2388
2389
  // HIGHLIGHT WORKER
2390
2391
  function startWorker(cm, time) {
2392
    if (cm.doc.mode.startState && cm.doc.frontier < cm.display.viewTo)
2393
      cm.state.highlight.set(time, bind(highlightWorker, cm));
2394
  }
2395
2396
  function highlightWorker(cm) {
2397
    var doc = cm.doc;
2398
    if (doc.frontier < doc.first) doc.frontier = doc.first;
2399
    if (doc.frontier >= cm.display.viewTo) return;
2400
    var end = +new Date + cm.options.workTime;
2401
    var state = copyState(doc.mode, getStateBefore(cm, doc.frontier));
2402
    var changedLines = [];
2403
2404
    doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function(line) {
2405
      if (doc.frontier >= cm.display.viewFrom) { // Visible
2406
        var oldStyles = line.styles;
2407
        var highlighted = highlightLine(cm, line, state, true);
2408
        line.styles = highlighted.styles;
2409
        var oldCls = line.styleClasses, newCls = highlighted.classes;
2410
        if (newCls) line.styleClasses = newCls;
2411
        else if (oldCls) line.styleClasses = null;
2412
        var ischange = !oldStyles || oldStyles.length != line.styles.length ||
2413
          oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass);
2414
        for (var i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldStyles[i] != line.styles[i];
2415
        if (ischange) changedLines.push(doc.frontier);
2416
        line.stateAfter = copyState(doc.mode, state);
2417
      } else {
2418
        processLine(cm, line.text, state);
2419
        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...
2420
      }
2421
      ++doc.frontier;
2422
      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...
2423
        startWorker(cm, cm.options.workDelay);
2424
        return true;
2425
      }
2426
    });
2427
    if (changedLines.length) runInOp(cm, function() {
2428
      for (var i = 0; i < changedLines.length; i++)
2429
        regLineChange(cm, changedLines[i], "text");
2430
    });
2431
  }
2432
2433
  // Finds the line to start with when starting a parse. Tries to
2434
  // find a line with a stateAfter, so that it can start with a
2435
  // valid state. If that fails, it returns the line with the
2436
  // smallest indentation, which tends to need the least context to
2437
  // parse correctly.
2438
  function findStartLine(cm, n, precise) {
2439
    var minindent, minline, doc = cm.doc;
2440
    var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100);
2441
    for (var search = n; search > lim; --search) {
2442
      if (search <= doc.first) return doc.first;
2443
      var line = getLine(doc, search - 1);
2444
      if (line.stateAfter && (!precise || search <= doc.frontier)) return search;
2445
      var indented = countColumn(line.text, null, cm.options.tabSize);
2446
      if (minline == null || minindent > indented) {
0 ignored issues
show
Bug introduced by
The variable minindent seems to not be initialized for all possible execution paths.
Loading history...
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...
2447
        minline = search - 1;
2448
        minindent = indented;
2449
      }
2450
    }
2451
    return minline;
2452
  }
2453
2454
  function getStateBefore(cm, n, precise) {
2455
    var doc = cm.doc, display = cm.display;
2456
    if (!doc.mode.startState) return true;
2457
    var pos = findStartLine(cm, n, precise), state = pos > doc.first && getLine(doc, pos-1).stateAfter;
2458
    if (!state) state = startState(doc.mode);
2459
    else state = copyState(doc.mode, state);
2460
    doc.iter(pos, n, function(line) {
2461
      processLine(cm, line.text, state);
2462
      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...
2463
      line.stateAfter = save ? copyState(doc.mode, state) : null;
2464
      ++pos;
2465
    });
2466
    if (precise) doc.frontier = pos;
2467
    return state;
2468
  }
2469
2470
  // POSITION MEASUREMENT
2471
2472
  function paddingTop(display) {return display.lineSpace.offsetTop;}
2473
  function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight;}
2474
  function paddingH(display) {
2475
    if (display.cachedPaddingH) return display.cachedPaddingH;
2476
    var e = removeChildrenAndAdd(display.measure, elt("pre", "x"));
2477
    var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle;
2478
    var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)};
2479
    if (!isNaN(data.left) && !isNaN(data.right)) display.cachedPaddingH = data;
2480
    return data;
2481
  }
2482
2483
  function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth; }
2484
  function displayWidth(cm) {
2485
    return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth;
2486
  }
2487
  function displayHeight(cm) {
2488
    return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight;
2489
  }
2490
2491
  // Ensure the lineView.wrapping.heights array is populated. This is
2492
  // an array of bottom offsets for the lines that make up a drawn
2493
  // line. When lineWrapping is on, there might be more than one
2494
  // height.
2495
  function ensureLineHeights(cm, lineView, rect) {
2496
    var wrapping = cm.options.lineWrapping;
2497
    var curWidth = wrapping && displayWidth(cm);
2498
    if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {
2499
      var heights = lineView.measure.heights = [];
2500
      if (wrapping) {
2501
        lineView.measure.width = curWidth;
2502
        var rects = lineView.text.firstChild.getClientRects();
2503
        for (var i = 0; i < rects.length - 1; i++) {
2504
          var cur = rects[i], next = rects[i + 1];
2505
          if (Math.abs(cur.bottom - next.bottom) > 2)
2506
            heights.push((cur.bottom + next.top) / 2 - rect.top);
2507
        }
2508
      }
2509
      heights.push(rect.bottom - rect.top);
2510
    }
2511
  }
2512
2513
  // Find a line map (mapping character offsets to text nodes) and a
2514
  // measurement cache for the given line number. (A line view might
2515
  // contain multiple lines when collapsed ranges are present.)
2516
  function mapFromLineView(lineView, line, lineN) {
2517
    if (lineView.line == line)
2518
      return {map: lineView.measure.map, cache: lineView.measure.cache};
2519
    for (var i = 0; i < lineView.rest.length; i++)
2520
      if (lineView.rest[i] == line)
2521
        return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]};
2522
    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 2519. 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...
2523
      if (lineNo(lineView.rest[i]) > lineN)
2524
        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...
2525
  }
2526
2527
  // Render a line into the hidden node display.externalMeasured. Used
2528
  // when measurement is needed for a line that's not in the viewport.
2529
  function updateExternalMeasurement(cm, line) {
2530
    line = visualLine(line);
2531
    var lineN = lineNo(line);
2532
    var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN);
2533
    view.lineN = lineN;
2534
    var built = view.built = buildLineContent(cm, view);
2535
    view.text = built.pre;
2536
    removeChildrenAndAdd(cm.display.lineMeasure, built.pre);
2537
    return view;
2538
  }
2539
2540
  // Get a {top, bottom, left, right} box (in line-local coordinates)
2541
  // for a given character.
2542
  function measureChar(cm, line, ch, bias) {
2543
    return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias);
2544
  }
2545
2546
  // Find a line view that corresponds to the given line number.
2547
  function findViewForLine(cm, lineN) {
2548
    if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo)
2549
      return cm.display.view[findViewIndex(cm, lineN)];
2550
    var ext = cm.display.externalMeasured;
2551
    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...
2552
      return ext;
2553
  }
2554
2555
  // Measurement can be split in two steps, the set-up work that
2556
  // applies to the whole line, and the measurement of the actual
2557
  // character. Functions like coordsChar, that need to do a lot of
2558
  // measurements in a row, can thus ensure that the set-up work is
2559
  // only done once.
2560
  function prepareMeasureForLine(cm, line) {
2561
    var lineN = lineNo(line);
2562
    var view = findViewForLine(cm, lineN);
2563
    if (view && !view.text) {
2564
      view = null;
2565
    } else if (view && view.changes) {
2566
      updateLineForChanges(cm, view, lineN, getDimensions(cm));
2567
      cm.curOp.forceUpdate = true;
2568
    }
2569
    if (!view)
2570
      view = updateExternalMeasurement(cm, line);
2571
2572
    var info = mapFromLineView(view, line, lineN);
2573
    return {
2574
      line: line, view: view, rect: null,
2575
      map: info.map, cache: info.cache, before: info.before,
2576
      hasHeights: false
2577
    };
2578
  }
2579
2580
  // Given a prepared measurement object, measures the position of an
2581
  // actual character (or fetches it from the cache).
2582
  function measureCharPrepared(cm, prepared, ch, bias, varHeight) {
2583
    if (prepared.before) ch = -1;
2584
    var key = ch + (bias || ""), found;
2585
    if (prepared.cache.hasOwnProperty(key)) {
2586
      found = prepared.cache[key];
2587
    } else {
2588
      if (!prepared.rect)
2589
        prepared.rect = prepared.view.text.getBoundingClientRect();
2590
      if (!prepared.hasHeights) {
2591
        ensureLineHeights(cm, prepared.view, prepared.rect);
2592
        prepared.hasHeights = true;
2593
      }
2594
      found = measureCharInner(cm, prepared, ch, bias);
2595
      if (!found.bogus) prepared.cache[key] = found;
2596
    }
2597
    return {left: found.left, right: found.right,
2598
            top: varHeight ? found.rtop : found.top,
2599
            bottom: varHeight ? found.rbottom : found.bottom};
2600
  }
2601
2602
  var nullRect = {left: 0, right: 0, top: 0, bottom: 0};
2603
2604
  function nodeAndOffsetInLineMap(map, ch, bias) {
2605
    var node, start, end, collapse;
2606
    // First, search the line map for the text node corresponding to,
2607
    // or closest to, the target character.
2608
    for (var i = 0; i < map.length; i += 3) {
2609
      var mStart = map[i], mEnd = map[i + 1];
2610
      if (ch < mStart) {
2611
        start = 0; end = 1;
2612
        collapse = "left";
2613
      } else if (ch < mEnd) {
2614
        start = ch - mStart;
2615
        end = start + 1;
2616
      } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) {
2617
        end = mEnd - mStart;
2618
        start = end - 1;
2619
        if (ch >= mEnd) collapse = "right";
2620
      }
2621
      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...
2622
        node = map[i + 2];
2623
        if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right"))
2624
          collapse = bias;
2625
        if (bias == "left" && start == 0)
2626
          while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) {
2627
            node = map[(i -= 3) + 2];
2628
            collapse = "left";
2629
          }
2630
        if (bias == "right" && start == mEnd - mStart)
2631
          while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].insertLeft) {
2632
            node = map[(i += 3) + 2];
2633
            collapse = "right";
2634
          }
2635
        break;
2636
      }
2637
    }
2638
    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 mStart 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...
Bug introduced by
The variable collapse 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...
2639
  }
2640
2641
  function measureCharInner(cm, prepared, ch, bias) {
2642
    var place = nodeAndOffsetInLineMap(prepared.map, ch, bias);
2643
    var node = place.node, start = place.start, end = place.end, collapse = place.collapse;
2644
2645
    var rect;
2646
    if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.
2647
      for (var i = 0; i < 4; i++) { // Retry a maximum of 4 times when nonsense rectangles are returned
2648
        while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) --start;
2649
        while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) ++end;
2650
        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...
2651
          rect = node.parentNode.getBoundingClientRect();
2652
        } else if (ie && cm.options.lineWrapping) {
2653
          var rects = range(node, start, end).getClientRects();
2654
          if (rects.length)
2655
            rect = rects[bias == "right" ? rects.length - 1 : 0];
2656
          else
2657
            rect = nullRect;
2658
        } else {
2659
          rect = range(node, start, end).getBoundingClientRect() || nullRect;
2660
        }
2661
        if (rect.left || rect.right || start == 0) break;
2662
        end = start;
2663
        start = start - 1;
2664
        collapse = "right";
2665
      }
2666
      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...
2667
    } else { // If it is a widget, simply get the box for the whole widget.
2668
      if (start > 0) collapse = bias = "right";
2669
      var rects;
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable rects already seems to be declared on line 2653. 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...
2670
      if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)
2671
        rect = rects[bias == "right" ? rects.length - 1 : 0];
2672
      else
2673
        rect = node.getBoundingClientRect();
2674
    }
2675
    if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {
2676
      var rSpan = node.parentNode.getClientRects()[0];
2677
      if (rSpan)
2678
        rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom};
2679
      else
2680
        rect = nullRect;
2681
    }
2682
2683
    var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top;
2684
    var mid = (rtop + rbot) / 2;
2685
    var heights = prepared.view.measure.heights;
2686
    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 2647. 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...
2687
      if (mid < heights[i]) break;
2688
    var top = i ? heights[i - 1] : 0, bot = heights[i];
2689
    var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left,
2690
                  right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left,
2691
                  top: top, bottom: bot};
2692
    if (!rect.left && !rect.right) result.bogus = true;
2693
    if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; }
2694
2695
    return result;
2696
  }
2697
2698
  // Work around problem with bounding client rects on ranges being
2699
  // returned incorrectly when zoomed on IE10 and below.
2700
  function maybeUpdateRectForZooming(measure, rect) {
2701
    if (!window.screen || screen.logicalXDPI == null ||
0 ignored issues
show
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...
Best Practice introduced by
Comparing screen.logicalXDPI to null using the == operator is not safe. Consider using === instead.
Loading history...
2702
        screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))
2703
      return rect;
2704
    var scaleX = screen.logicalXDPI / screen.deviceXDPI;
2705
    var scaleY = screen.logicalYDPI / screen.deviceYDPI;
2706
    return {left: rect.left * scaleX, right: rect.right * scaleX,
2707
            top: rect.top * scaleY, bottom: rect.bottom * scaleY};
2708
  }
2709
2710
  function clearLineMeasurementCacheFor(lineView) {
2711
    if (lineView.measure) {
2712
      lineView.measure.cache = {};
2713
      lineView.measure.heights = null;
2714
      if (lineView.rest) for (var i = 0; i < lineView.rest.length; i++)
2715
        lineView.measure.caches[i] = {};
2716
    }
2717
  }
2718
2719
  function clearLineMeasurementCache(cm) {
2720
    cm.display.externalMeasure = null;
2721
    removeChildren(cm.display.lineMeasure);
2722
    for (var i = 0; i < cm.display.view.length; i++)
2723
      clearLineMeasurementCacheFor(cm.display.view[i]);
2724
  }
2725
2726
  function clearCaches(cm) {
2727
    clearLineMeasurementCache(cm);
2728
    cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null;
2729
    if (!cm.options.lineWrapping) cm.display.maxLineChanged = true;
2730
    cm.display.lineNumChars = null;
2731
  }
2732
2733
  function pageScrollX() { return window.pageXOffset || (document.documentElement || document.body).scrollLeft; }
2734
  function pageScrollY() { return window.pageYOffset || (document.documentElement || document.body).scrollTop; }
2735
2736
  // Converts a {top, bottom, left, right} box from line-local
2737
  // coordinates into another coordinate system. Context may be one of
2738
  // "line", "div" (display.lineDiv), "local"/null (editor), "window",
2739
  // or "page".
2740
  function intoCoordSystem(cm, lineObj, rect, context) {
2741
    if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {
2742
      var size = widgetHeight(lineObj.widgets[i]);
2743
      rect.top += size; rect.bottom += size;
2744
    }
2745
    if (context == "line") return rect;
2746
    if (!context) context = "local";
2747
    var yOff = heightAtLine(lineObj);
2748
    if (context == "local") yOff += paddingTop(cm.display);
2749
    else yOff -= cm.display.viewOffset;
2750
    if (context == "page" || context == "window") {
2751
      var lOff = cm.display.lineSpace.getBoundingClientRect();
2752
      yOff += lOff.top + (context == "window" ? 0 : pageScrollY());
2753
      var xOff = lOff.left + (context == "window" ? 0 : pageScrollX());
2754
      rect.left += xOff; rect.right += xOff;
2755
    }
2756
    rect.top += yOff; rect.bottom += yOff;
2757
    return rect;
2758
  }
2759
2760
  // Coverts a box from "div" coords to another coordinate system.
2761
  // Context may be "window", "page", "div", or "local"/null.
2762
  function fromCoordSystem(cm, coords, context) {
2763
    if (context == "div") return coords;
2764
    var left = coords.left, top = coords.top;
2765
    // First move into "page" coordinate system
2766
    if (context == "page") {
2767
      left -= pageScrollX();
2768
      top -= pageScrollY();
2769
    } else if (context == "local" || !context) {
2770
      var localBox = cm.display.sizer.getBoundingClientRect();
2771
      left += localBox.left;
2772
      top += localBox.top;
2773
    }
2774
2775
    var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect();
2776
    return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top};
2777
  }
2778
2779
  function charCoords(cm, pos, context, lineObj, bias) {
2780
    if (!lineObj) lineObj = getLine(cm.doc, pos.line);
2781
    return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context);
2782
  }
2783
2784
  // Returns a box for a given cursor position, which may have an
2785
  // 'other' property containing the position of the secondary cursor
2786
  // on a bidi boundary.
2787
  function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
2788
    lineObj = lineObj || getLine(cm.doc, pos.line);
2789
    if (!preparedMeasure) preparedMeasure = prepareMeasureForLine(cm, lineObj);
2790
    function get(ch, right) {
2791
      var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight);
2792
      if (right) m.left = m.right; else m.right = m.left;
2793
      return intoCoordSystem(cm, lineObj, m, context);
2794
    }
2795
    function getBidi(ch, partPos) {
2796
      var part = order[partPos], right = part.level % 2;
2797
      if (ch == bidiLeft(part) && partPos && part.level < order[partPos - 1].level) {
2798
        part = order[--partPos];
2799
        ch = bidiRight(part) - (part.level % 2 ? 0 : 1);
2800
        right = true;
2801
      } else if (ch == bidiRight(part) && partPos < order.length - 1 && part.level < order[partPos + 1].level) {
2802
        part = order[++partPos];
2803
        ch = bidiLeft(part) - part.level % 2;
2804
        right = false;
2805
      }
2806
      if (right && ch == part.to && ch > part.from) return get(ch - 1);
2807
      return get(ch, right);
2808
    }
2809
    var order = getOrder(lineObj), ch = pos.ch;
2810
    if (!order) return get(ch);
2811
    var partPos = getBidiPartAt(order, ch);
2812
    var val = getBidi(ch, partPos);
2813
    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...
2814
    return val;
2815
  }
2816
2817
  // Used to cheaply estimate the coordinates for a position. Used for
2818
  // intermediate scroll updates.
2819
  function estimateCoords(cm, pos) {
2820
    var left = 0, pos = clipPos(cm.doc, pos);
2821
    if (!cm.options.lineWrapping) left = charWidth(cm.display) * pos.ch;
2822
    var lineObj = getLine(cm.doc, pos.line);
2823
    var top = heightAtLine(lineObj) + paddingTop(cm.display);
2824
    return {left: left, right: left, top: top, bottom: top + lineObj.height};
2825
  }
2826
2827
  // Positions returned by coordsChar contain some extra information.
2828
  // xRel is the relative x position of the input coordinates compared
2829
  // to the found position (so xRel > 0 means the coordinates are to
2830
  // the right of the character position, for example). When outside
2831
  // is true, that means the coordinates lie outside the line's
2832
  // vertical range.
2833
  function PosWithInfo(line, ch, outside, xRel) {
2834
    var pos = Pos(line, ch);
2835
    pos.xRel = xRel;
2836
    if (outside) pos.outside = true;
2837
    return pos;
2838
  }
2839
2840
  // Compute the character position closest to the given coordinates.
2841
  // Input must be lineSpace-local ("div" coordinate system).
2842
  function coordsChar(cm, x, y) {
2843
    var doc = cm.doc;
2844
    y += cm.display.viewOffset;
2845
    if (y < 0) return PosWithInfo(doc.first, 0, true, -1);
2846
    var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
2847
    if (lineN > last)
2848
      return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, true, 1);
2849
    if (x < 0) x = 0;
2850
2851
    var lineObj = getLine(doc, lineN);
2852
    for (;;) {
2853
      var found = coordsCharInner(cm, lineObj, lineN, x, y);
2854
      var merged = collapsedSpanAtEnd(lineObj);
2855
      var mergedPos = merged && merged.find(0, true);
2856
      if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0))
2857
        lineN = lineNo(lineObj = mergedPos.to.line);
2858
      else
2859
        return found;
2860
    }
2861
  }
2862
2863
  function coordsCharInner(cm, lineObj, lineNo, x, y) {
2864
    var innerOff = y - heightAtLine(lineObj);
2865
    var wrongLine = false, adjust = 2 * cm.display.wrapper.clientWidth;
2866
    var preparedMeasure = prepareMeasureForLine(cm, lineObj);
2867
2868
    function getX(ch) {
2869
      var sp = cursorCoords(cm, Pos(lineNo, ch), "line", lineObj, preparedMeasure);
2870
      wrongLine = true;
2871
      if (innerOff > sp.bottom) return sp.left - adjust;
2872
      else if (innerOff < sp.top) return sp.left + adjust;
2873
      else wrongLine = false;
2874
      return sp.left;
2875
    }
2876
2877
    var bidi = getOrder(lineObj), dist = lineObj.text.length;
2878
    var from = lineLeft(lineObj), to = lineRight(lineObj);
2879
    var fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine;
2880
2881
    if (x > toX) return PosWithInfo(lineNo, to, toOutside, 1);
2882
    // Do a binary search between these bounds.
2883
    for (;;) {
2884
      if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
2885
        var ch = x < fromX || x - fromX <= toX - x ? from : to;
2886
        var xDiff = x - (ch == from ? fromX : toX);
2887
        while (isExtendingChar(lineObj.text.charAt(ch))) ++ch;
2888
        var pos = PosWithInfo(lineNo, ch, ch == from ? fromOutside : toOutside,
2889
                              xDiff < -1 ? -1 : xDiff > 1 ? 1 : 0);
2890
        return pos;
2891
      }
2892
      var step = Math.ceil(dist / 2), middle = from + step;
2893
      if (bidi) {
2894
        middle = from;
2895
        for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1);
2896
      }
2897
      var middleX = getX(middle);
2898
      if (middleX > x) {to = middle; toX = middleX; if (toOutside = wrongLine) toX += 1000; dist = step;}
2899
      else {from = middle; fromX = middleX; fromOutside = wrongLine; dist -= step;}
2900
    }
2901
  }
2902
2903
  var measureText;
2904
  // Compute the default text height.
2905
  function textHeight(display) {
2906
    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...
2907
    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...
2908
      measureText = elt("pre");
2909
      // Measure a bunch of lines, for browsers that compute
2910
      // fractional heights.
2911
      for (var i = 0; i < 49; ++i) {
2912
        measureText.appendChild(document.createTextNode("x"));
2913
        measureText.appendChild(elt("br"));
2914
      }
2915
      measureText.appendChild(document.createTextNode("x"));
2916
    }
2917
    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 2907 is false. Are you sure the function removeChildrenAndAdd handles undefined variables?
Loading history...
2918
    var height = measureText.offsetHeight / 50;
2919
    if (height > 3) display.cachedTextHeight = height;
2920
    removeChildren(display.measure);
2921
    return height || 1;
2922
  }
2923
2924
  // Compute the default character width.
2925
  function charWidth(display) {
2926
    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...
2927
    var anchor = elt("span", "xxxxxxxxxx");
2928
    var pre = elt("pre", [anchor]);
2929
    removeChildrenAndAdd(display.measure, pre);
2930
    var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10;
2931
    if (width > 2) display.cachedCharWidth = width;
2932
    return width || 10;
2933
  }
2934
2935
  // OPERATIONS
2936
2937
  // Operations are used to wrap a series of changes to the editor
2938
  // state in such a way that each change won't have to update the
2939
  // cursor and display (which would be awkward, slow, and
2940
  // error-prone). Instead, display updates are batched and then all
2941
  // combined and executed at once.
2942
2943
  var operationGroup = null;
2944
2945
  var nextOpId = 0;
2946
  // Start a new operation.
2947
  function startOperation(cm) {
2948
    cm.curOp = {
2949
      cm: cm,
2950
      viewChanged: false,      // Flag that indicates that lines might need to be redrawn
2951
      startHeight: cm.doc.height, // Used to detect need to update scrollbar
2952
      forceUpdate: false,      // Used to force a redraw
2953
      updateInput: null,       // Whether to reset the input textarea
2954
      typing: false,           // Whether this reset should be careful to leave existing text (for compositing)
2955
      changeObjs: null,        // Accumulated changes, for firing change events
2956
      cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on
2957
      cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
2958
      selectionChanged: false, // Whether the selection needs to be redrawn
2959
      updateMaxLine: false,    // Set when the widest line needs to be determined anew
2960
      scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
2961
      scrollToPos: null,       // Used to scroll to a specific position
2962
      focus: false,
2963
      id: ++nextOpId           // Unique ID
2964
    };
2965
    if (operationGroup) {
2966
      operationGroup.ops.push(cm.curOp);
2967
    } else {
2968
      cm.curOp.ownsGroup = operationGroup = {
2969
        ops: [cm.curOp],
2970
        delayedCallbacks: []
2971
      };
2972
    }
2973
  }
2974
2975
  function fireCallbacksForOps(group) {
2976
    // Calls delayed callbacks and cursorActivity handlers until no
2977
    // new ones appear
2978
    var callbacks = group.delayedCallbacks, i = 0;
2979
    do {
2980
      for (; i < callbacks.length; i++)
2981
        callbacks[i].call(null);
2982
      for (var j = 0; j < group.ops.length; j++) {
2983
        var op = group.ops[j];
2984
        if (op.cursorActivityHandlers)
2985
          while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
2986
            op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm);
2987
      }
2988
    } while (i < callbacks.length);
2989
  }
2990
2991
  // Finish an operation, updating the display and signalling delayed events
2992
  function endOperation(cm) {
2993
    var op = cm.curOp, group = op.ownsGroup;
2994
    if (!group) return;
2995
2996
    try { fireCallbacksForOps(group); }
2997
    finally {
2998
      operationGroup = null;
2999
      for (var i = 0; i < group.ops.length; i++)
3000
        group.ops[i].cm.curOp = null;
3001
      endOperations(group);
3002
    }
3003
  }
3004
3005
  // The DOM updates done when an operation finishes are batched so
3006
  // that the minimum number of relayouts are required.
3007
  function endOperations(group) {
3008
    var ops = group.ops;
3009
    for (var i = 0; i < ops.length; i++) // Read DOM
3010
      endOperation_R1(ops[i]);
3011
    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 3009. 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...
3012
      endOperation_W1(ops[i]);
3013
    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 3009. 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...
3014
      endOperation_R2(ops[i]);
3015
    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 3009. 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...
3016
      endOperation_W2(ops[i]);
3017
    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 3009. 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...
3018
      endOperation_finish(ops[i]);
3019
  }
3020
3021
  function endOperation_R1(op) {
3022
    var cm = op.cm, display = cm.display;
3023
    maybeClipScrollbars(cm);
3024
    if (op.updateMaxLine) findMaxLine(cm);
3025
3026
    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...
3027
      op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
3028
                         op.scrollToPos.to.line >= display.viewTo) ||
3029
      display.maxLineChanged && cm.options.lineWrapping;
3030
    op.update = op.mustUpdate &&
3031
      new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate);
3032
  }
3033
3034
  function endOperation_W1(op) {
3035
    op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update);
3036
  }
3037
3038
  function endOperation_R2(op) {
3039
    var cm = op.cm, display = cm.display;
3040
    if (op.updatedDisplay) updateHeightsInViewport(cm);
3041
3042
    op.barMeasure = measureForScrollbars(cm);
3043
3044
    // If the max line changed since it was last measured, measure it,
3045
    // and ensure the document's width matches it.
3046
    // updateDisplay_W2 will use these properties to do the actual resizing
3047
    if (display.maxLineChanged && !cm.options.lineWrapping) {
3048
      op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3;
3049
      cm.display.sizerWidth = op.adjustWidthTo;
3050
      op.barMeasure.scrollWidth =
3051
        Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth);
3052
      op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm));
3053
    }
3054
3055
    if (op.updatedDisplay || op.selectionChanged)
3056
      op.preparedSelection = display.input.prepareSelection();
3057
  }
3058
3059
  function endOperation_W2(op) {
3060
    var cm = op.cm;
3061
3062
    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...
3063
      cm.display.sizer.style.minWidth = op.adjustWidthTo + "px";
3064
      if (op.maxScrollLeft < cm.doc.scrollLeft)
3065
        setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true);
3066
      cm.display.maxLineChanged = false;
3067
    }
3068
3069
    if (op.preparedSelection)
3070
      cm.display.input.showSelection(op.preparedSelection);
3071
    if (op.updatedDisplay)
3072
      setDocumentHeight(cm, op.barMeasure);
3073
    if (op.updatedDisplay || op.startHeight != cm.doc.height)
3074
      updateScrollbars(cm, op.barMeasure);
3075
3076
    if (op.selectionChanged) restartBlink(cm);
3077
3078
    if (cm.state.focused && op.updateInput)
3079
      cm.display.input.reset(op.typing);
3080
    if (op.focus && op.focus == activeElt()) ensureFocus(op.cm);
3081
  }
3082
3083
  function endOperation_finish(op) {
3084
    var cm = op.cm, display = cm.display, doc = cm.doc;
3085
3086
    if (op.updatedDisplay) postUpdateDisplay(cm, op.update);
3087
3088
    // Abort mouse wheel delta measurement, when scrolling explicitly
3089
    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...
3090
      display.wheelStartX = display.wheelStartY = null;
3091
3092
    // Propagate the scroll position to the actual DOM scroller
3093
    if (op.scrollTop != null && (display.scroller.scrollTop != op.scrollTop || op.forceScroll)) {
3094
      doc.scrollTop = Math.max(0, Math.min(display.scroller.scrollHeight - display.scroller.clientHeight, op.scrollTop));
3095
      display.scrollbars.setScrollTop(doc.scrollTop);
3096
      display.scroller.scrollTop = doc.scrollTop;
3097
    }
3098
    if (op.scrollLeft != null && (display.scroller.scrollLeft != op.scrollLeft || op.forceScroll)) {
3099
      doc.scrollLeft = Math.max(0, Math.min(display.scroller.scrollWidth - displayWidth(cm), op.scrollLeft));
3100
      display.scrollbars.setScrollLeft(doc.scrollLeft);
3101
      display.scroller.scrollLeft = doc.scrollLeft;
3102
      alignHorizontally(cm);
3103
    }
3104
    // If we need to scroll a specific position into view, do so.
3105
    if (op.scrollToPos) {
3106
      var coords = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
3107
                                     clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin);
3108
      if (op.scrollToPos.isCursor && cm.state.focused) maybeScrollWindow(cm, coords);
3109
    }
3110
3111
    // Fire events for markers that are hidden/unidden by editing or
3112
    // undoing
3113
    var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
3114
    if (hidden) for (var i = 0; i < hidden.length; ++i)
3115
      if (!hidden[i].lines.length) signal(hidden[i], "hide");
3116
    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 3114. 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...
3117
      if (unhidden[i].lines.length) signal(unhidden[i], "unhide");
3118
3119
    if (display.wrapper.offsetHeight)
3120
      doc.scrollTop = cm.display.scroller.scrollTop;
3121
3122
    // Fire change events, and delayed event handlers
3123
    if (op.changeObjs)
3124
      signal(cm, "changes", cm, op.changeObjs);
3125
    if (op.update)
3126
      op.update.finish();
3127
  }
3128
3129
  // Run the given function in an operation
3130
  function runInOp(cm, f) {
3131
    if (cm.curOp) return f();
3132
    startOperation(cm);
3133
    try { return f(); }
3134
    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...
3135
  }
3136
  // Wraps a function in an operation. Returns the wrapped function.
3137
  function operation(cm, f) {
3138
    return function() {
3139
      if (cm.curOp) return f.apply(cm, arguments);
3140
      startOperation(cm);
3141
      try { return f.apply(cm, arguments); }
3142
      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...
3143
    };
3144
  }
3145
  // Used to add methods to editor and doc instances, wrapping them in
3146
  // operations.
3147
  function methodOp(f) {
3148
    return function() {
3149
      if (this.curOp) return f.apply(this, arguments);
3150
      startOperation(this);
3151
      try { return f.apply(this, arguments); }
3152
      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...
3153
    };
3154
  }
3155
  function docMethodOp(f) {
3156
    return function() {
3157
      var cm = this.cm;
3158
      if (!cm || cm.curOp) return f.apply(this, arguments);
3159
      startOperation(cm);
3160
      try { return f.apply(this, arguments); }
3161
      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...
3162
    };
3163
  }
3164
3165
  // VIEW TRACKING
3166
3167
  // These objects are used to represent the visible (currently drawn)
3168
  // part of the document. A LineView may correspond to multiple
3169
  // logical lines, if those are connected by collapsed ranges.
3170
  function LineView(doc, line, lineN) {
3171
    // The starting line
3172
    this.line = line;
3173
    // Continuing lines, if any
3174
    this.rest = visualLineContinued(line);
3175
    // Number of logical lines in this visual line
3176
    this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1;
3177
    this.node = this.text = null;
3178
    this.hidden = lineIsHidden(doc, line);
3179
  }
3180
3181
  // Create a range of LineView objects for the given lines.
3182
  function buildViewArray(cm, from, to) {
3183
    var array = [], nextPos;
3184
    for (var pos = from; pos < to; pos = nextPos) {
3185
      var view = new LineView(cm.doc, getLine(cm.doc, pos), pos);
3186
      nextPos = pos + view.size;
3187
      array.push(view);
3188
    }
3189
    return array;
3190
  }
3191
3192
  // Updates the display.view data structure for a given change to the
3193
  // document. From and to are in pre-change coordinates. Lendiff is
3194
  // the amount of lines added or subtracted by the change. This is
3195
  // used for changes that span multiple lines, or change the way
3196
  // lines are divided into visual lines. regLineChange (below)
3197
  // registers single-line changes.
3198
  function regChange(cm, from, to, lendiff) {
3199
    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...
3200
    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...
3201
    if (!lendiff) lendiff = 0;
3202
3203
    var display = cm.display;
3204
    if (lendiff && to < display.viewTo &&
3205
        (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...
3206
      display.updateLineNumbers = from;
3207
3208
    cm.curOp.viewChanged = true;
3209
3210
    if (from >= display.viewTo) { // Change after
3211
      if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)
3212
        resetView(cm);
3213
    } else if (to <= display.viewFrom) { // Change before
3214
      if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {
3215
        resetView(cm);
3216
      } else {
3217
        display.viewFrom += lendiff;
3218
        display.viewTo += lendiff;
3219
      }
3220
    } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap
3221
      resetView(cm);
3222
    } else if (from <= display.viewFrom) { // Top overlap
3223
      var cut = viewCuttingPoint(cm, to, to + lendiff, 1);
3224
      if (cut) {
3225
        display.view = display.view.slice(cut.index);
3226
        display.viewFrom = cut.lineN;
3227
        display.viewTo += lendiff;
3228
      } else {
3229
        resetView(cm);
3230
      }
3231
    } else if (to >= display.viewTo) { // Bottom overlap
3232
      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 3223. 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...
3233
      if (cut) {
3234
        display.view = display.view.slice(0, cut.index);
3235
        display.viewTo = cut.lineN;
3236
      } else {
3237
        resetView(cm);
3238
      }
3239
    } else { // Gap in the middle
3240
      var cutTop = viewCuttingPoint(cm, from, from, -1);
3241
      var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1);
3242
      if (cutTop && cutBot) {
3243
        display.view = display.view.slice(0, cutTop.index)
3244
          .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))
3245
          .concat(display.view.slice(cutBot.index));
3246
        display.viewTo += lendiff;
3247
      } else {
3248
        resetView(cm);
3249
      }
3250
    }
3251
3252
    var ext = display.externalMeasured;
3253
    if (ext) {
3254
      if (to < ext.lineN)
3255
        ext.lineN += lendiff;
3256
      else if (from < ext.lineN + ext.size)
3257
        display.externalMeasured = null;
3258
    }
3259
  }
3260
3261
  // Register a change to a single line. Type must be one of "text",
3262
  // "gutter", "class", "widget"
3263
  function regLineChange(cm, line, type) {
3264
    cm.curOp.viewChanged = true;
3265
    var display = cm.display, ext = cm.display.externalMeasured;
3266
    if (ext && line >= ext.lineN && line < ext.lineN + ext.size)
3267
      display.externalMeasured = null;
3268
3269
    if (line < display.viewFrom || line >= display.viewTo) return;
3270
    var lineView = display.view[findViewIndex(cm, line)];
3271
    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...
3272
    var arr = lineView.changes || (lineView.changes = []);
3273
    if (indexOf(arr, type) == -1) arr.push(type);
3274
  }
3275
3276
  // Clear the view.
3277
  function resetView(cm) {
3278
    cm.display.viewFrom = cm.display.viewTo = cm.doc.first;
3279
    cm.display.view = [];
3280
    cm.display.viewOffset = 0;
3281
  }
3282
3283
  // Find the view element corresponding to a given line. Return null
3284
  // when the line isn't visible.
3285
  function findViewIndex(cm, n) {
3286
    if (n >= cm.display.viewTo) return null;
3287
    n -= cm.display.viewFrom;
3288
    if (n < 0) return null;
3289
    var view = cm.display.view;
3290
    for (var i = 0; i < view.length; i++) {
3291
      n -= view[i].size;
3292
      if (n < 0) return i;
3293
    }
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...
3294
  }
3295
3296
  function viewCuttingPoint(cm, oldN, newN, dir) {
3297
    var index = findViewIndex(cm, oldN), diff, view = cm.display.view;
3298
    if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)
3299
      return {index: index, lineN: newN};
3300
    for (var i = 0, n = cm.display.viewFrom; i < index; i++)
3301
      n += view[i].size;
3302
    if (n != oldN) {
3303
      if (dir > 0) {
3304
        if (index == view.length - 1) return null;
3305
        diff = (n + view[index].size) - oldN;
3306
        index++;
3307
      } else {
3308
        diff = n - oldN;
3309
      }
3310
      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...
3311
    }
3312
    while (visualLineNo(cm.doc, newN) != newN) {
3313
      if (index == (dir < 0 ? 0 : view.length - 1)) return null;
3314
      newN += dir * view[index - (dir < 0 ? 1 : 0)].size;
3315
      index += dir;
3316
    }
3317
    return {index: index, lineN: newN};
3318
  }
3319
3320
  // Force the view to cover a given range, adding empty view element
3321
  // or clipping off existing ones as needed.
3322
  function adjustView(cm, from, to) {
3323
    var display = cm.display, view = display.view;
3324
    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...
3325
      display.view = buildViewArray(cm, from, to);
3326
      display.viewFrom = from;
3327
    } else {
3328
      if (display.viewFrom > from)
3329
        display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view);
3330
      else if (display.viewFrom < from)
3331
        display.view = display.view.slice(findViewIndex(cm, from));
3332
      display.viewFrom = from;
3333
      if (display.viewTo < to)
3334
        display.view = display.view.concat(buildViewArray(cm, display.viewTo, to));
3335
      else if (display.viewTo > to)
3336
        display.view = display.view.slice(0, findViewIndex(cm, to));
3337
    }
3338
    display.viewTo = to;
3339
  }
3340
3341
  // Count the number of lines in the view whose DOM representation is
3342
  // out of date (or nonexistent).
3343
  function countDirtyView(cm) {
3344
    var view = cm.display.view, dirty = 0;
3345
    for (var i = 0; i < view.length; i++) {
3346
      var lineView = view[i];
3347
      if (!lineView.hidden && (!lineView.node || lineView.changes)) ++dirty;
3348
    }
3349
    return dirty;
3350
  }
3351
3352
  // EVENT HANDLERS
3353
3354
  // Attach the necessary event handlers when initializing the editor
3355
  function registerEventHandlers(cm) {
3356
    var d = cm.display;
3357
    on(d.scroller, "mousedown", operation(cm, onMouseDown));
3358
    // Older IE's will not fire a second mousedown for a double click
3359
    if (ie && ie_version < 11)
3360
      on(d.scroller, "dblclick", operation(cm, function(e) {
3361
        if (signalDOMEvent(cm, e)) return;
3362
        var pos = posFromMouse(cm, e);
3363
        if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) return;
3364
        e_preventDefault(e);
3365
        var word = cm.findWordAt(pos);
3366
        extendSelection(cm.doc, word.anchor, word.head);
3367
      }));
3368
    else
3369
      on(d.scroller, "dblclick", function(e) { signalDOMEvent(cm, e) || e_preventDefault(e); });
3370
    // Some browsers fire contextmenu *after* opening the menu, at
3371
    // which point we can't mess with it anymore. Context menu is
3372
    // handled in onMouseDown for these browsers.
3373
    if (!captureRightClick) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);});
3374
3375
    // Used to suppress mouse event handling when a touch happens
3376
    var touchFinished, prevTouch = {end: 0};
3377
    function finishTouch() {
3378
      if (d.activeTouch) {
3379
        touchFinished = setTimeout(function() {d.activeTouch = null;}, 1000);
3380
        prevTouch = d.activeTouch;
3381
        prevTouch.end = +new Date;
3382
      }
3383
    };
3384
    function isMouseLikeTouchEvent(e) {
3385
      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...
3386
      var touch = e.touches[0];
3387
      return touch.radiusX <= 1 && touch.radiusY <= 1;
3388
    }
3389
    function farAway(touch, other) {
3390
      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...
3391
      var dx = other.left - touch.left, dy = other.top - touch.top;
3392
      return dx * dx + dy * dy > 20 * 20;
3393
    }
3394
    on(d.scroller, "touchstart", function(e) {
3395
      if (!isMouseLikeTouchEvent(e)) {
3396
        clearTimeout(touchFinished);
3397
        var now = +new Date;
3398
        d.activeTouch = {start: now, moved: false,
3399
                         prev: now - prevTouch.end <= 300 ? prevTouch : null};
3400
        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...
3401
          d.activeTouch.left = e.touches[0].pageX;
3402
          d.activeTouch.top = e.touches[0].pageY;
3403
        }
3404
      }
3405
    });
3406
    on(d.scroller, "touchmove", function() {
3407
      if (d.activeTouch) d.activeTouch.moved = true;
3408
    });
3409
    on(d.scroller, "touchend", function(e) {
3410
      var touch = d.activeTouch;
3411
      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...
3412
          !touch.moved && new Date - touch.start < 300) {
3413
        var pos = cm.coordsChar(d.activeTouch, "page"), range;
3414
        if (!touch.prev || farAway(touch, touch.prev)) // Single tap
3415
          range = new Range(pos, pos);
3416
        else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap
3417
          range = cm.findWordAt(pos);
3418
        else // Triple tap
3419
          range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0)));
3420
        cm.setSelection(range.anchor, range.head);
3421
        cm.focus();
3422
        e_preventDefault(e);
3423
      }
3424
      finishTouch();
3425
    });
3426
    on(d.scroller, "touchcancel", finishTouch);
3427
3428
    // Sync scrolling between fake scrollbars and real scrollable
3429
    // area, ensure viewport is updated when scrolling.
3430
    on(d.scroller, "scroll", function() {
3431
      if (d.scroller.clientHeight) {
3432
        setScrollTop(cm, d.scroller.scrollTop);
3433
        setScrollLeft(cm, d.scroller.scrollLeft, true);
3434
        signal(cm, "scroll", cm);
3435
      }
3436
    });
3437
3438
    // Listen to wheel events in order to try and update the viewport on time.
3439
    on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);});
3440
    on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);});
3441
3442
    // Prevent wrapper from ever scrolling
3443
    on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
3444
3445
    d.dragFunctions = {
3446
      simple: function(e) {if (!signalDOMEvent(cm, e)) e_stop(e);},
3447
      start: function(e){onDragStart(cm, e);},
3448
      drop: operation(cm, onDrop)
3449
    };
3450
3451
    var inp = d.input.getField();
3452
    on(inp, "keyup", function(e) { onKeyUp.call(cm, e); });
3453
    on(inp, "keydown", operation(cm, onKeyDown));
3454
    on(inp, "keypress", operation(cm, onKeyPress));
3455
    on(inp, "focus", bind(onFocus, cm));
3456
    on(inp, "blur", bind(onBlur, cm));
3457
  }
3458
3459
  function dragDropChanged(cm, value, old) {
3460
    var wasOn = old && old != CodeMirror.Init;
3461
    if (!value != !wasOn) {
3462
      var funcs = cm.display.dragFunctions;
3463
      var toggle = value ? on : off;
3464
      toggle(cm.display.scroller, "dragstart", funcs.start);
3465
      toggle(cm.display.scroller, "dragenter", funcs.simple);
3466
      toggle(cm.display.scroller, "dragover", funcs.simple);
3467
      toggle(cm.display.scroller, "drop", funcs.drop);
3468
    }
3469
  }
3470
3471
  // Called when the window resizes
3472
  function onResize(cm) {
3473
    var d = cm.display;
3474
    if (d.lastWrapHeight == d.wrapper.clientHeight && d.lastWrapWidth == d.wrapper.clientWidth)
3475
      return;
3476
    // Might be a text scaling operation, clear size caches.
3477
    d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
3478
    d.scrollbarsClipped = false;
3479
    cm.setSize();
3480
  }
3481
3482
  // MOUSE EVENTS
3483
3484
  // Return true when the given mouse event happened in a widget
3485
  function eventInWidget(display, e) {
3486
    for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
3487
      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...
3488
          (n.parentNode == display.sizer && n != display.mover))
3489
        return true;
3490
    }
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...
3491
  }
3492
3493
  // Given a mouse event, find the corresponding position. If liberal
3494
  // is false, it checks whether a gutter or scrollbar was clicked,
3495
  // and returns null if it was. forRect is used by rectangular
3496
  // selections, and tries to estimate a character position even for
3497
  // coordinates beyond the right of the text.
3498
  function posFromMouse(cm, e, liberal, forRect) {
3499
    var display = cm.display;
3500
    if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") return null;
3501
3502
    var x, y, space = display.lineSpace.getBoundingClientRect();
3503
    // Fails unpredictably on IE[67] when mouse is dragged around quickly.
3504
    try { x = e.clientX - space.left; y = e.clientY - space.top; }
3505
    catch (e) { return null; }
3506
    var coords = coordsChar(cm, x, y), line;
3507
    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...
3508
      var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length;
3509
      coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff));
3510
    }
3511
    return coords;
3512
  }
3513
3514
  // A mouse down can be a single click, double click, triple click,
3515
  // start of selection drag, start of text drag, new cursor
3516
  // (ctrl-click), rectangle drag (alt-drag), or xwin
3517
  // middle-click-paste. Or it might be a click on something we should
3518
  // not interfere with, such as a scrollbar or widget.
3519
  function onMouseDown(e) {
3520
    var cm = this, display = cm.display;
3521
    if (display.activeTouch && display.input.supportsTouch() || signalDOMEvent(cm, e)) return;
3522
    display.shift = e.shiftKey;
3523
3524
    if (eventInWidget(display, e)) {
3525
      if (!webkit) {
3526
        // Briefly turn off draggability, to allow widgets to do
3527
        // normal dragging things.
3528
        display.scroller.draggable = false;
3529
        setTimeout(function(){display.scroller.draggable = true;}, 100);
3530
      }
3531
      return;
3532
    }
3533
    if (clickInGutter(cm, e)) return;
3534
    var start = posFromMouse(cm, e);
3535
    window.focus();
3536
3537
    switch (e_button(e)) {
3538
    case 1:
3539
      if (start)
3540
        leftButtonDown(cm, e, start);
3541
      else if (e_target(e) == display.scroller)
3542
        e_preventDefault(e);
3543
      break;
3544
    case 2:
3545
      if (webkit) cm.state.lastMiddleDown = +new Date;
3546
      if (start) extendSelection(cm.doc, start);
3547
      setTimeout(function() {display.input.focus();}, 20);
3548
      e_preventDefault(e);
3549
      break;
3550
    case 3:
3551
      if (captureRightClick) onContextMenu(cm, e);
3552
      else delayBlurEvent(cm);
3553
      break;
3554
    }
3555
  }
3556
3557
  var lastClick, lastDoubleClick;
3558
  function leftButtonDown(cm, e, start) {
3559
    if (ie) setTimeout(bind(ensureFocus, cm), 0);
3560
    else cm.curOp.focus = activeElt();
3561
3562
    var now = +new Date, type;
3563
    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...
3564
      type = "triple";
3565
    } 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...
3566
      type = "double";
3567
      lastDoubleClick = {time: now, pos: start};
3568
    } else {
3569
      type = "single";
3570
      lastClick = {time: now, pos: start};
3571
    }
3572
3573
    var sel = cm.doc.sel, modifier = mac ? e.metaKey : e.ctrlKey, contained;
3574
    if (cm.options.dragDrop && dragAndDrop && !isReadOnly(cm) &&
3575
        type == "single" && (contained = sel.contains(start)) > -1 &&
3576
        (cmp((contained = sel.ranges[contained]).from(), start) < 0 || start.xRel > 0) &&
3577
        (cmp(contained.to(), start) > 0 || start.xRel < 0))
3578
      leftButtonStartDrag(cm, e, start, modifier);
3579
    else
3580
      leftButtonSelect(cm, e, start, type, modifier);
3581
  }
3582
3583
  // Start a text drag. When it ends, see if any dragging actually
3584
  // happen, and treat as a click if it didn't.
3585
  function leftButtonStartDrag(cm, e, start, modifier) {
3586
    var display = cm.display, startTime = +new Date;
3587
    var dragEnd = operation(cm, function(e2) {
3588
      if (webkit) display.scroller.draggable = false;
3589
      cm.state.draggingText = false;
3590
      off(document, "mouseup", dragEnd);
3591
      off(display.scroller, "drop", dragEnd);
3592
      if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
3593
        e_preventDefault(e2);
3594
        if (!modifier && +new Date - 200 < startTime)
3595
          extendSelection(cm.doc, start);
3596
        // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)
3597
        if (webkit || ie && ie_version == 9)
3598
          setTimeout(function() {document.body.focus(); display.input.focus();}, 20);
3599
        else
3600
          display.input.focus();
3601
      }
3602
    });
3603
    // Let the drag handler handle this.
3604
    if (webkit) display.scroller.draggable = true;
3605
    cm.state.draggingText = dragEnd;
3606
    // IE's approach to draggable
3607
    if (display.scroller.dragDrop) display.scroller.dragDrop();
3608
    on(document, "mouseup", dragEnd);
3609
    on(display.scroller, "drop", dragEnd);
3610
  }
3611
3612
  // Normal selection, as opposed to text dragging.
3613
  function leftButtonSelect(cm, e, start, type, addNew) {
3614
    var display = cm.display, doc = cm.doc;
3615
    e_preventDefault(e);
3616
3617
    var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges;
3618
    if (addNew && !e.shiftKey) {
3619
      ourIndex = doc.sel.contains(start);
3620
      if (ourIndex > -1)
3621
        ourRange = ranges[ourIndex];
3622
      else
3623
        ourRange = new Range(start, start);
3624
    } else {
3625
      ourRange = doc.sel.primary();
3626
      ourIndex = doc.sel.primIndex;
3627
    }
3628
3629
    if (e.altKey) {
3630
      type = "rect";
3631
      if (!addNew) ourRange = new Range(start, start);
3632
      start = posFromMouse(cm, e, true, true);
3633
      ourIndex = -1;
3634
    } else if (type == "double") {
3635
      var word = cm.findWordAt(start);
3636
      if (cm.display.shift || doc.extend)
3637
        ourRange = extendRange(doc, ourRange, word.anchor, word.head);
3638
      else
3639
        ourRange = word;
3640
    } else if (type == "triple") {
3641
      var line = new Range(Pos(start.line, 0), clipPos(doc, Pos(start.line + 1, 0)));
3642
      if (cm.display.shift || doc.extend)
3643
        ourRange = extendRange(doc, ourRange, line.anchor, line.head);
3644
      else
3645
        ourRange = line;
3646
    } else {
3647
      ourRange = extendRange(doc, ourRange, start);
3648
    }
3649
3650
    if (!addNew) {
3651
      ourIndex = 0;
3652
      setSelection(doc, new Selection([ourRange], 0), sel_mouse);
3653
      startSel = doc.sel;
3654
    } else if (ourIndex == -1) {
3655
      ourIndex = ranges.length;
3656
      setSelection(doc, normalizeSelection(ranges.concat([ourRange]), ourIndex),
3657
                   {scroll: false, origin: "*mouse"});
3658
    } else if (ranges.length > 1 && ranges[ourIndex].empty() && type == "single" && !e.shiftKey) {
3659
      setSelection(doc, normalizeSelection(ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0));
3660
      startSel = doc.sel;
3661
    } else {
3662
      replaceOneSelection(doc, ourIndex, ourRange, sel_mouse);
3663
    }
3664
3665
    var lastPos = start;
3666
    function extendTo(pos) {
3667
      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...
3668
      lastPos = pos;
3669
3670
      if (type == "rect") {
3671
        var ranges = [], tabSize = cm.options.tabSize;
3672
        var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize);
3673
        var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize);
3674
        var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol);
3675
        for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line));
3676
             line <= end; line++) {
3677
          var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize);
3678
          if (left == right)
3679
            ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos)));
3680
          else if (text.length > leftPos)
3681
            ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize))));
3682
        }
3683
        if (!ranges.length) ranges.push(new Range(start, start));
3684
        setSelection(doc, normalizeSelection(startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex),
3685
                     {origin: "*mouse", scroll: false});
3686
        cm.scrollIntoView(pos);
3687
      } else {
3688
        var oldRange = ourRange;
3689
        var anchor = oldRange.anchor, head = pos;
3690
        if (type != "single") {
3691
          if (type == "double")
3692
            var range = cm.findWordAt(pos);
3693
          else
3694
            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 3692. 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...
3695
          if (cmp(range.anchor, anchor) > 0) {
3696
            head = range.head;
3697
            anchor = minPos(oldRange.from(), range.anchor);
3698
          } else {
3699
            head = range.anchor;
3700
            anchor = maxPos(oldRange.to(), range.head);
3701
          }
3702
        }
3703
        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 3671. 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...
3704
        ranges[ourIndex] = new Range(clipPos(doc, anchor), head);
3705
        setSelection(doc, normalizeSelection(ranges, ourIndex), sel_mouse);
3706
      }
3707
    }
3708
3709
    var editorSize = display.wrapper.getBoundingClientRect();
3710
    // Used to ensure timeout re-tries don't fire when another extend
3711
    // happened in the meantime (clearTimeout isn't reliable -- at
3712
    // least on Chrome, the timeouts still happen even when cleared,
3713
    // if the clear happens after their scheduled firing time).
3714
    var counter = 0;
3715
3716
    function extend(e) {
3717
      var curCount = ++counter;
3718
      var cur = posFromMouse(cm, e, true, type == "rect");
3719
      if (!cur) return;
3720
      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...
3721
        cm.curOp.focus = activeElt();
3722
        extendTo(cur);
3723
        var visible = visibleLines(display, doc);
3724
        if (cur.line >= visible.to || cur.line < visible.from)
3725
          setTimeout(operation(cm, function(){if (counter == curCount) extend(e);}), 150);
3726
      } else {
3727
        var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;
3728
        if (outside) setTimeout(operation(cm, function() {
3729
          if (counter != curCount) return;
3730
          display.scroller.scrollTop += outside;
3731
          extend(e);
3732
        }), 50);
3733
      }
3734
    }
3735
3736
    function done(e) {
3737
      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...
3738
      e_preventDefault(e);
3739
      display.input.focus();
3740
      off(document, "mousemove", move);
3741
      off(document, "mouseup", up);
3742
      doc.history.lastSelOrigin = null;
3743
    }
3744
3745
    var move = operation(cm, function(e) {
3746
      if (!e_button(e)) done(e);
3747
      else extend(e);
3748
    });
3749
    var up = operation(cm, done);
3750
    on(document, "mousemove", move);
3751
    on(document, "mouseup", up);
3752
  }
3753
3754
  // Determines whether an event happened in the gutter, and fires the
3755
  // handlers for the corresponding event.
3756
  function gutterEvent(cm, e, type, prevent, signalfn) {
3757
    try { var mX = e.clientX, mY = e.clientY; }
3758
    catch(e) { return false; }
3759
    if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) return false;
3760
    if (prevent) e_preventDefault(e);
3761
3762
    var display = cm.display;
3763
    var lineBox = display.lineDiv.getBoundingClientRect();
3764
3765
    if (mY > lineBox.bottom || !hasHandler(cm, type)) return e_defaultPrevented(e);
3766
    mY -= lineBox.top - display.viewOffset;
3767
3768
    for (var i = 0; i < cm.options.gutters.length; ++i) {
3769
      var g = display.gutters.childNodes[i];
3770
      if (g && g.getBoundingClientRect().right >= mX) {
3771
        var line = lineAtHeight(cm.doc, mY);
3772
        var gutter = cm.options.gutters[i];
3773
        signalfn(cm, type, cm, line, gutter, e);
3774
        return e_defaultPrevented(e);
3775
      }
3776
    }
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...
3777
  }
3778
3779
  function clickInGutter(cm, e) {
3780
    return gutterEvent(cm, e, "gutterClick", true, signalLater);
3781
  }
3782
3783
  // Kludge to work around strange IE behavior where it'll sometimes
3784
  // re-fire a series of drag-related events right after the drop (#1551)
3785
  var lastDrop = 0;
3786
3787
  function onDrop(e) {
3788
    var cm = this;
3789
    if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))
3790
      return;
3791
    e_preventDefault(e);
3792
    if (ie) lastDrop = +new Date;
3793
    var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
3794
    if (!pos || isReadOnly(cm)) return;
3795
    // Might be a file drop, in which case we simply extract the text
3796
    // and insert it.
3797
    if (files && files.length && window.FileReader && window.File) {
3798
      var n = files.length, text = Array(n), read = 0;
3799
      var loadFile = function(file, i) {
3800
        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...
3801
        reader.onload = operation(cm, function() {
3802
          text[i] = reader.result;
3803
          if (++read == n) {
3804
            pos = clipPos(cm.doc, pos);
3805
            var change = {from: pos, to: pos,
3806
                          text: cm.doc.splitLines(text.join(cm.doc.lineSeparator())),
3807
                          origin: "paste"};
3808
            makeChange(cm.doc, change);
3809
            setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change)));
3810
          }
3811
        });
3812
        reader.readAsText(file);
3813
      };
3814
      for (var i = 0; i < n; ++i) loadFile(files[i], i);
3815
    } else { // Normal drop
3816
      // Don't do a replace if the drop happened inside of the selected text.
3817
      if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {
3818
        cm.state.draggingText(e);
3819
        // Ensure the editor is re-focused
3820
        setTimeout(function() {cm.display.input.focus();}, 20);
3821
        return;
3822
      }
3823
      try {
3824
        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 3798. 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...
3825
        if (text) {
3826
          if (cm.state.draggingText && !(mac ? e.altKey : e.ctrlKey))
3827
            var selected = cm.listSelections();
3828
          setSelectionNoUndo(cm.doc, simpleSelection(pos, pos));
3829
          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 3814. 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...
3830
            replaceRange(cm.doc, "", selected[i].anchor, selected[i].head, "drag");
3831
          cm.replaceSelection(text, "around", "paste");
3832
          cm.display.input.focus();
3833
        }
3834
      }
3835
      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...
3836
    }
3837
  }
3838
3839
  function onDragStart(cm, e) {
3840
    if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return; }
3841
    if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) return;
3842
3843
    e.dataTransfer.setData("Text", cm.getSelection());
3844
3845
    // Use dummy image instead of default browsers image.
3846
    // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
3847
    if (e.dataTransfer.setDragImage && !safari) {
3848
      var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
3849
      img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
3850
      if (presto) {
3851
        img.width = img.height = 1;
3852
        cm.display.wrapper.appendChild(img);
3853
        // Force a relayout, or Opera won't use our image for some obscure reason
3854
        img._top = img.offsetTop;
3855
      }
3856
      e.dataTransfer.setDragImage(img, 0, 0);
3857
      if (presto) img.parentNode.removeChild(img);
3858
    }
3859
  }
3860
3861
  // SCROLL EVENTS
3862
3863
  // Sync the scrollable area and scrollbars, ensure the viewport
3864
  // covers the visible area.
3865
  function setScrollTop(cm, val) {
3866
    if (Math.abs(cm.doc.scrollTop - val) < 2) return;
3867
    cm.doc.scrollTop = val;
3868
    if (!gecko) updateDisplaySimple(cm, {top: val});
3869
    if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val;
3870
    cm.display.scrollbars.setScrollTop(val);
3871
    if (gecko) updateDisplaySimple(cm);
3872
    startWorker(cm, 100);
3873
  }
3874
  // Sync scroller and scrollbar, ensure the gutter elements are
3875
  // aligned.
3876
  function setScrollLeft(cm, val, isScroller) {
3877
    if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) return;
3878
    val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth);
3879
    cm.doc.scrollLeft = val;
3880
    alignHorizontally(cm);
3881
    if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val;
3882
    cm.display.scrollbars.setScrollLeft(val);
3883
  }
3884
3885
  // Since the delta values reported on mouse wheel events are
3886
  // unstandardized between browsers and even browser versions, and
3887
  // generally horribly unpredictable, this code starts by measuring
3888
  // the scroll effect that the first few mouse wheel events have,
3889
  // and, from that, detects the way it can convert deltas to pixel
3890
  // offsets afterwards.
3891
  //
3892
  // The reason we want to know the amount a wheel event will scroll
3893
  // is that it gives us a chance to update the display before the
3894
  // actual scrolling happens, reducing flickering.
3895
3896
  var wheelSamples = 0, wheelPixelsPerUnit = null;
3897
  // Fill in a browser-detected starting value on browsers where we
3898
  // know one. These don't have to be accurate -- the result of them
3899
  // being wrong would just be a slight flicker on the first wheel
3900
  // scroll (if it is large enough).
3901
  if (ie) wheelPixelsPerUnit = -.53;
3902
  else if (gecko) wheelPixelsPerUnit = 15;
3903
  else if (chrome) wheelPixelsPerUnit = -.7;
3904
  else if (safari) wheelPixelsPerUnit = -1/3;
3905
3906
  var wheelEventDelta = function(e) {
3907
    var dx = e.wheelDeltaX, dy = e.wheelDeltaY;
3908
    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...
3909
    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...
3910
    else if (dy == null) dy = e.wheelDelta;
3911
    return {x: dx, y: dy};
3912
  };
3913
  CodeMirror.wheelEventPixels = function(e) {
3914
    var delta = wheelEventDelta(e);
3915
    delta.x *= wheelPixelsPerUnit;
3916
    delta.y *= wheelPixelsPerUnit;
3917
    return delta;
3918
  };
3919
3920
  function onScrollWheel(cm, e) {
3921
    var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y;
3922
3923
    var display = cm.display, scroll = display.scroller;
3924
    // Quit if there's nothing to scroll here
3925
    if (!(dx && scroll.scrollWidth > scroll.clientWidth ||
3926
          dy && scroll.scrollHeight > scroll.clientHeight)) return;
3927
3928
    // Webkit browsers on OS X abort momentum scrolls when the target
3929
    // of the scroll event is removed from the scrollable element.
3930
    // This hack (see related code in patchDisplay) makes sure the
3931
    // element is kept around.
3932
    if (dy && mac && webkit) {
3933
      outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {
3934
        for (var i = 0; i < view.length; i++) {
3935
          if (view[i].node == cur) {
3936
            cm.display.currentWheelTarget = cur;
3937
            break outer;
3938
          }
3939
        }
3940
      }
3941
    }
3942
3943
    // On some browsers, horizontal scrolling will cause redraws to
3944
    // happen before the gutter has been realigned, causing it to
3945
    // wriggle around in a most unseemly way. When we have an
3946
    // estimated pixels/delta value, we just handle horizontal
3947
    // scrolling entirely here. It'll be slightly off from native, but
3948
    // better than glitching out.
3949
    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...
3950
      if (dy)
3951
        setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight)));
3952
      setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth)));
3953
      e_preventDefault(e);
3954
      display.wheelStartX = null; // Abort measurement, if in progress
3955
      return;
3956
    }
3957
3958
    // 'Project' the visible viewport to cover the area that is being
3959
    // scrolled into view (if we know enough to estimate it).
3960
    if (dy && wheelPixelsPerUnit != null) {
3961
      var pixels = dy * wheelPixelsPerUnit;
3962
      var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
3963
      if (pixels < 0) top = Math.max(0, top + pixels - 50);
3964
      else bot = Math.min(cm.doc.height, bot + pixels + 50);
3965
      updateDisplaySimple(cm, {top: top, bottom: bot});
3966
    }
3967
3968
    if (wheelSamples < 20) {
3969
      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...
3970
        display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;
3971
        display.wheelDX = dx; display.wheelDY = dy;
3972
        setTimeout(function() {
3973
          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...
3974
          var movedX = scroll.scrollLeft - display.wheelStartX;
3975
          var movedY = scroll.scrollTop - display.wheelStartY;
3976
          var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
3977
            (movedX && display.wheelDX && movedX / display.wheelDX);
3978
          display.wheelStartX = display.wheelStartY = null;
3979
          if (!sample) return;
3980
          wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
3981
          ++wheelSamples;
3982
        }, 200);
3983
      } else {
3984
        display.wheelDX += dx; display.wheelDY += dy;
3985
      }
3986
    }
3987
  }
3988
3989
  // KEY EVENTS
3990
3991
  // Run a handler that was bound to a key.
3992
  function doHandleBinding(cm, bound, dropShift) {
3993
    if (typeof bound == "string") {
3994
      bound = commands[bound];
3995
      if (!bound) return false;
3996
    }
3997
    // Ensure previous input has been read, so that the handler sees a
3998
    // consistent view of the document
3999
    cm.display.input.ensurePolled();
4000
    var prevShift = cm.display.shift, done = false;
4001
    try {
4002
      if (isReadOnly(cm)) cm.state.suppressEdits = true;
4003
      if (dropShift) cm.display.shift = false;
4004
      done = bound(cm) != Pass;
4005
    } finally {
4006
      cm.display.shift = prevShift;
4007
      cm.state.suppressEdits = false;
4008
    }
4009
    return done;
4010
  }
4011
4012
  function lookupKeyForEditor(cm, name, handle) {
4013
    for (var i = 0; i < cm.state.keyMaps.length; i++) {
4014
      var result = lookupKey(name, cm.state.keyMaps[i], handle, cm);
4015
      if (result) return result;
4016
    }
4017
    return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm))
4018
      || lookupKey(name, cm.options.keyMap, handle, cm);
4019
  }
4020
4021
  var stopSeq = new Delayed;
4022
  function dispatchKey(cm, name, e, handle) {
4023
    var seq = cm.state.keySeq;
4024
    if (seq) {
4025
      if (isModifierKey(name)) return "handled";
4026
      stopSeq.set(50, function() {
4027
        if (cm.state.keySeq == seq) {
4028
          cm.state.keySeq = null;
4029
          cm.display.input.reset();
4030
        }
4031
      });
4032
      name = seq + " " + name;
4033
    }
4034
    var result = lookupKeyForEditor(cm, name, handle);
4035
4036
    if (result == "multi")
4037
      cm.state.keySeq = name;
4038
    if (result == "handled")
4039
      signalLater(cm, "keyHandled", cm, name, e);
4040
4041
    if (result == "handled" || result == "multi") {
4042
      e_preventDefault(e);
4043
      restartBlink(cm);
4044
    }
4045
4046
    if (seq && !result && /\'$/.test(name)) {
4047
      e_preventDefault(e);
4048
      return true;
4049
    }
4050
    return !!result;
4051
  }
4052
4053
  // Handle a key from the keydown event.
4054
  function handleKeyBinding(cm, e) {
4055
    var name = keyName(e, true);
4056
    if (!name) return false;
4057
4058
    if (e.shiftKey && !cm.state.keySeq) {
4059
      // First try to resolve full name (including 'Shift-'). Failing
4060
      // that, see if there is a cursor-motion command (starting with
4061
      // 'go') bound to the keyname without 'Shift-'.
4062
      return dispatchKey(cm, "Shift-" + name, e, function(b) {return doHandleBinding(cm, b, true);})
4063
          || dispatchKey(cm, name, e, function(b) {
4064
               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...
4065
                 return doHandleBinding(cm, b);
4066
             });
4067
    } 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...
4068
      return dispatchKey(cm, name, e, function(b) { return doHandleBinding(cm, b); });
4069
    }
4070
  }
4071
4072
  // Handle a key from the keypress event
4073
  function handleCharBinding(cm, e, ch) {
4074
    return dispatchKey(cm, "'" + ch + "'", e,
4075
                       function(b) { return doHandleBinding(cm, b, true); });
4076
  }
4077
4078
  var lastStoppedKey = null;
4079
  function onKeyDown(e) {
4080
    var cm = this;
4081
    cm.curOp.focus = activeElt();
4082
    if (signalDOMEvent(cm, e)) return;
4083
    // IE does strange things with escape.
4084
    if (ie && ie_version < 11 && e.keyCode == 27) e.returnValue = false;
4085
    var code = e.keyCode;
4086
    cm.display.shift = code == 16 || e.shiftKey;
4087
    var handled = handleKeyBinding(cm, e);
4088
    if (presto) {
4089
      lastStoppedKey = handled ? code : null;
4090
      // Opera has no cut event... we try to at least catch the key combo
4091
      if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
4092
        cm.replaceSelection("", null, "cut");
4093
    }
4094
4095
    // Turn mouse into crosshair when Alt is held on Mac.
4096
    if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className))
4097
      showCrossHair(cm);
4098
  }
4099
4100
  function showCrossHair(cm) {
4101
    var lineDiv = cm.display.lineDiv;
4102
    addClass(lineDiv, "CodeMirror-crosshair");
4103
4104
    function up(e) {
4105
      if (e.keyCode == 18 || !e.altKey) {
4106
        rmClass(lineDiv, "CodeMirror-crosshair");
4107
        off(document, "keyup", up);
4108
        off(document, "mouseover", up);
4109
      }
4110
    }
4111
    on(document, "keyup", up);
4112
    on(document, "mouseover", up);
4113
  }
4114
4115
  function onKeyUp(e) {
4116
    if (e.keyCode == 16) this.doc.sel.shift = false;
4117
    signalDOMEvent(this, e);
4118
  }
4119
4120
  function onKeyPress(e) {
4121
    var cm = this;
4122
    if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) return;
4123
    var keyCode = e.keyCode, charCode = e.charCode;
4124
    if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
4125
    if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) return;
4126
    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...
4127
    if (handleCharBinding(cm, e, ch)) return;
4128
    cm.display.input.onKeyPress(e);
4129
  }
4130
4131
  // FOCUS/BLUR EVENTS
4132
4133
  function delayBlurEvent(cm) {
4134
    cm.state.delayingBlurEvent = true;
4135
    setTimeout(function() {
4136
      if (cm.state.delayingBlurEvent) {
4137
        cm.state.delayingBlurEvent = false;
4138
        onBlur(cm);
4139
      }
4140
    }, 100);
4141
  }
4142
4143
  function onFocus(cm) {
4144
    if (cm.state.delayingBlurEvent) cm.state.delayingBlurEvent = false;
4145
4146
    if (cm.options.readOnly == "nocursor") return;
4147
    if (!cm.state.focused) {
4148
      signal(cm, "focus", cm);
4149
      cm.state.focused = true;
4150
      addClass(cm.display.wrapper, "CodeMirror-focused");
4151
      // This test prevents this from firing when a context
4152
      // menu is closed (since the input reset would kill the
4153
      // select-all detection hack)
4154
      if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {
4155
        cm.display.input.reset();
4156
        if (webkit) setTimeout(function() { cm.display.input.reset(true); }, 20); // Issue #1730
4157
      }
4158
      cm.display.input.receivedFocus();
4159
    }
4160
    restartBlink(cm);
4161
  }
4162
  function onBlur(cm) {
4163
    if (cm.state.delayingBlurEvent) return;
4164
4165
    if (cm.state.focused) {
4166
      signal(cm, "blur", cm);
4167
      cm.state.focused = false;
4168
      rmClass(cm.display.wrapper, "CodeMirror-focused");
4169
    }
4170
    clearInterval(cm.display.blinker);
4171
    setTimeout(function() {if (!cm.state.focused) cm.display.shift = false;}, 150);
4172
  }
4173
4174
  // CONTEXT MENU HANDLING
4175
4176
  // To make the context menu work, we need to briefly unhide the
4177
  // textarea (making it as unobtrusive as possible) to let the
4178
  // right-click take effect on it.
4179
  function onContextMenu(cm, e) {
4180
    if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) return;
4181
    cm.display.input.onContextMenu(e);
4182
  }
4183
4184
  function contextMenuInGutter(cm, e) {
4185
    if (!hasHandler(cm, "gutterContextMenu")) return false;
4186
    return gutterEvent(cm, e, "gutterContextMenu", false, signal);
4187
  }
4188
4189
  // UPDATING
4190
4191
  // Compute the position of the end of a change (its 'to' property
4192
  // refers to the pre-change end).
4193
  var changeEnd = CodeMirror.changeEnd = function(change) {
4194
    if (!change.text) return change.to;
4195
    return Pos(change.from.line + change.text.length - 1,
4196
               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...
4197
  };
4198
4199
  // Adjust a position to refer to the post-change position of the
4200
  // same text, or the end of the change if the change covers it.
4201
  function adjustForChange(pos, change) {
4202
    if (cmp(pos, change.from) < 0) return pos;
4203
    if (cmp(pos, change.to) <= 0) return changeEnd(change);
4204
4205
    var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;
4206
    if (pos.line == change.to.line) ch += changeEnd(change).ch - change.to.ch;
4207
    return Pos(line, ch);
4208
  }
4209
4210
  function computeSelAfterChange(doc, change) {
4211
    var out = [];
4212
    for (var i = 0; i < doc.sel.ranges.length; i++) {
4213
      var range = doc.sel.ranges[i];
4214
      out.push(new Range(adjustForChange(range.anchor, change),
4215
                         adjustForChange(range.head, change)));
4216
    }
4217
    return normalizeSelection(out, doc.sel.primIndex);
4218
  }
4219
4220
  function offsetPos(pos, old, nw) {
4221
    if (pos.line == old.line)
4222
      return Pos(nw.line, pos.ch - old.ch + nw.ch);
4223
    else
4224
      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...
4225
  }
4226
4227
  // Used by replaceSelections to allow moving the selection to the
4228
  // start or around the replaced test. Hint may be "start" or "around".
4229
  function computeReplacedSel(doc, changes, hint) {
4230
    var out = [];
4231
    var oldPrev = Pos(doc.first, 0), newPrev = oldPrev;
4232
    for (var i = 0; i < changes.length; i++) {
4233
      var change = changes[i];
4234
      var from = offsetPos(change.from, oldPrev, newPrev);
4235
      var to = offsetPos(changeEnd(change), oldPrev, newPrev);
4236
      oldPrev = change.to;
4237
      newPrev = to;
4238
      if (hint == "around") {
4239
        var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0;
4240
        out[i] = new Range(inv ? to : from, inv ? from : to);
4241
      } else {
4242
        out[i] = new Range(from, from);
4243
      }
4244
    }
4245
    return new Selection(out, doc.sel.primIndex);
4246
  }
4247
4248
  // Allow "beforeChange" event handlers to influence a change
4249
  function filterChange(doc, change, update) {
4250
    var obj = {
4251
      canceled: false,
4252
      from: change.from,
4253
      to: change.to,
4254
      text: change.text,
4255
      origin: change.origin,
4256
      cancel: function() { this.canceled = true; }
4257
    };
4258
    if (update) obj.update = function(from, to, text, origin) {
4259
      if (from) this.from = clipPos(doc, from);
4260
      if (to) this.to = clipPos(doc, to);
4261
      if (text) this.text = text;
4262
      if (origin !== undefined) this.origin = origin;
4263
    };
4264
    signal(doc, "beforeChange", doc, obj);
4265
    if (doc.cm) signal(doc.cm, "beforeChange", doc.cm, obj);
4266
4267
    if (obj.canceled) return null;
4268
    return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin};
4269
  }
4270
4271
  // Apply a change to a document, and add it to the document's
4272
  // history, and propagating it to all linked documents.
4273
  function makeChange(doc, change, ignoreReadOnly) {
4274
    if (doc.cm) {
4275
      if (!doc.cm.curOp) return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly);
4276
      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...
4277
    }
4278
4279
    if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
4280
      change = filterChange(doc, change, true);
4281
      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...
4282
    }
4283
4284
    // Possibly split or suppress the update based on the presence
4285
    // of read-only spans in its range.
4286
    var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);
4287
    if (split) {
4288
      for (var i = split.length - 1; i >= 0; --i)
4289
        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...
4290
    } else {
4291
      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...
4292
    }
4293
  }
4294
4295
  function makeChangeInner(doc, change) {
4296
    if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) return;
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...
Best Practice introduced by
Comparing cmp(change.from, change.to) to 0 using the == operator is not safe. Consider using === instead.
Loading history...
4297
    var selAfter = computeSelAfterChange(doc, change);
4298
    addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);
4299
4300
    makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));
4301
    var rebased = [];
4302
4303
    linkedDocs(doc, function(doc, sharedHist) {
4304
      if (!sharedHist && indexOf(rebased, doc.history) == -1) {
4305
        rebaseHist(doc.history, change);
4306
        rebased.push(doc.history);
4307
      }
4308
      makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change));
4309
    });
4310
  }
4311
4312
  // Revert a change stored in a document's history.
4313
  function makeChangeFromHistory(doc, type, allowSelectionOnly) {
4314
    if (doc.cm && doc.cm.state.suppressEdits) return;
4315
4316
    var hist = doc.history, event, selAfter = doc.sel;
4317
    var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done;
4318
4319
    // Verify that there is a useable event (so that ctrl-z won't
4320
    // needlessly clear selection events)
4321
    for (var i = 0; i < source.length; i++) {
4322
      event = source[i];
4323
      if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges)
4324
        break;
4325
    }
4326
    if (i == source.length) return;
4327
    hist.lastOrigin = hist.lastSelOrigin = null;
4328
4329
    for (;;) {
4330
      event = source.pop();
4331
      if (event.ranges) {
4332
        pushSelectionToHistory(event, dest);
4333
        if (allowSelectionOnly && !event.equals(doc.sel)) {
4334
          setSelection(doc, event, {clearRedo: false});
4335
          return;
4336
        }
4337
        selAfter = event;
4338
      }
4339
      else break;
4340
    }
4341
4342
    // Build up a reverse change object to add to the opposite history
4343
    // stack (redo when undoing, and vice versa).
4344
    var antiChanges = [];
4345
    pushSelectionToHistory(selAfter, dest);
4346
    dest.push({changes: antiChanges, generation: hist.generation});
4347
    hist.generation = event.generation || ++hist.maxGeneration;
4348
4349
    var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange");
4350
4351
    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 4321. 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...
4352
      var change = event.changes[i];
4353
      change.origin = type;
4354
      if (filter && !filterChange(doc, change, false)) {
4355
        source.length = 0;
4356
        return;
4357
      }
4358
4359
      antiChanges.push(historyChangeFromChange(doc, change));
4360
4361
      var after = i ? computeSelAfterChange(doc, change) : lst(source);
4362
      makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
4363
      if (!i && doc.cm) doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)});
4364
      var rebased = [];
4365
4366
      // Propagate to the linked documents
4367
      linkedDocs(doc, function(doc, sharedHist) {
4368
        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 4364. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
4369
          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 4352. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
4370
          rebased.push(doc.history);
4371
        }
4372
        makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change));
4373
      });
4374
    }
4375
  }
4376
4377
  // Sub-views need their line numbers shifted when text is added
4378
  // above or below them in the parent document.
4379
  function shiftDoc(doc, distance) {
4380
    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...
4381
    doc.first += distance;
4382
    doc.sel = new Selection(map(doc.sel.ranges, function(range) {
4383
      return new Range(Pos(range.anchor.line + distance, range.anchor.ch),
4384
                       Pos(range.head.line + distance, range.head.ch));
4385
    }), doc.sel.primIndex);
4386
    if (doc.cm) {
4387
      regChange(doc.cm, doc.first, doc.first - distance, distance);
4388
      for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++)
4389
        regLineChange(doc.cm, l, "gutter");
4390
    }
4391
  }
4392
4393
  // More lower-level change function, handling only a single document
4394
  // (not linked ones).
4395
  function makeChangeSingleDoc(doc, change, selAfter, spans) {
4396
    if (doc.cm && !doc.cm.curOp)
4397
      return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans);
4398
4399
    if (change.to.line < doc.first) {
4400
      shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line));
4401
      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...
4402
    }
4403
    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...
4404
4405
    // Clip the change to the size of this doc
4406
    if (change.from.line < doc.first) {
4407
      var shift = change.text.length - 1 - (doc.first - change.from.line);
4408
      shiftDoc(doc, shift);
4409
      change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
4410
                text: [lst(change.text)], origin: change.origin};
4411
    }
4412
    var last = doc.lastLine();
4413
    if (change.to.line > last) {
4414
      change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
4415
                text: [change.text[0]], origin: change.origin};
4416
    }
4417
4418
    change.removed = getBetween(doc, change.from, change.to);
4419
4420
    if (!selAfter) selAfter = computeSelAfterChange(doc, change);
4421
    if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans);
4422
    else updateDoc(doc, change, spans);
4423
    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...
4424
  }
4425
4426
  // Handle the interaction of a change to a document with the editor
4427
  // that this document is part of.
4428
  function makeChangeSingleDocInEditor(cm, change, spans) {
4429
    var doc = cm.doc, display = cm.display, from = change.from, to = change.to;
4430
4431
    var recomputeMaxLength = false, checkWidthStart = from.line;
4432
    if (!cm.options.lineWrapping) {
4433
      checkWidthStart = lineNo(visualLine(getLine(doc, from.line)));
4434
      doc.iter(checkWidthStart, to.line + 1, function(line) {
4435
        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...
4436
          recomputeMaxLength = true;
4437
          return true;
4438
        }
4439
      });
4440
    }
4441
4442
    if (doc.sel.contains(change.from, change.to) > -1)
4443
      signalCursorActivity(cm);
4444
4445
    updateDoc(doc, change, spans, estimateHeight(cm));
4446
4447
    if (!cm.options.lineWrapping) {
4448
      doc.iter(checkWidthStart, from.line + change.text.length, function(line) {
4449
        var len = lineLength(line);
4450
        if (len > display.maxLineLength) {
4451
          display.maxLine = line;
4452
          display.maxLineLength = len;
4453
          display.maxLineChanged = true;
4454
          recomputeMaxLength = false;
4455
        }
4456
      });
4457
      if (recomputeMaxLength) cm.curOp.updateMaxLine = true;
4458
    }
4459
4460
    // Adjust frontier, schedule worker
4461
    doc.frontier = Math.min(doc.frontier, from.line);
4462
    startWorker(cm, 400);
4463
4464
    var lendiff = change.text.length - (to.line - from.line) - 1;
4465
    // Remember that these lines changed, for updating the display
4466
    if (change.full)
4467
      regChange(cm);
4468
    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...
4469
      regLineChange(cm, from.line, "text");
4470
    else
4471
      regChange(cm, from.line, to.line + 1, lendiff);
4472
4473
    var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change");
4474
    if (changeHandler || changesHandler) {
4475
      var obj = {
4476
        from: from, to: to,
4477
        text: change.text,
4478
        removed: change.removed,
4479
        origin: change.origin
4480
      };
4481
      if (changeHandler) signalLater(cm, "change", cm, obj);
4482
      if (changesHandler) (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj);
4483
    }
4484
    cm.display.selForContextMenu = null;
4485
  }
4486
4487
  function replaceRange(doc, code, from, to, origin) {
4488
    if (!to) to = from;
4489
    if (cmp(to, from) < 0) { var tmp = to; to = from; from = tmp; }
4490
    if (typeof code == "string") code = doc.splitLines(code);
4491
    makeChange(doc, {from: from, to: to, text: code, origin: origin});
4492
  }
4493
4494
  // SCROLLING THINGS INTO VIEW
4495
4496
  // If an editor sits on the top or bottom of the window, partially
4497
  // scrolled out of view, this ensures that the cursor is visible.
4498
  function maybeScrollWindow(cm, coords) {
4499
    if (signalDOMEvent(cm, "scrollCursorIntoView")) return;
4500
4501
    var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;
4502
    if (coords.top + box.top < 0) doScroll = true;
4503
    else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
4504
    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...
4505
      var scrollNode = elt("div", "\u200b", null, "position: absolute; top: " +
4506
                           (coords.top - display.viewOffset - paddingTop(cm.display)) + "px; height: " +
4507
                           (coords.bottom - coords.top + scrollGap(cm) + display.barHeight) + "px; left: " +
4508
                           coords.left + "px; width: 2px;");
4509
      cm.display.lineSpace.appendChild(scrollNode);
4510
      scrollNode.scrollIntoView(doScroll);
4511
      cm.display.lineSpace.removeChild(scrollNode);
4512
    }
4513
  }
4514
4515
  // Scroll a given position into view (immediately), verifying that
4516
  // it actually became visible (as line heights are accurately
4517
  // measured, the position of something may 'drift' during drawing).
4518
  function scrollPosIntoView(cm, pos, end, margin) {
4519
    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...
4520
    for (var limit = 0; limit < 5; limit++) {
4521
      var changed = false, coords = cursorCoords(cm, pos);
4522
      var endCoords = !end || end == pos ? coords : cursorCoords(cm, end);
4523
      var scrollPos = calculateScrollPos(cm, Math.min(coords.left, endCoords.left),
4524
                                         Math.min(coords.top, endCoords.top) - margin,
4525
                                         Math.max(coords.left, endCoords.left),
4526
                                         Math.max(coords.bottom, endCoords.bottom) + margin);
4527
      var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
4528
      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...
4529
        setScrollTop(cm, scrollPos.scrollTop);
4530
        if (Math.abs(cm.doc.scrollTop - startTop) > 1) changed = true;
4531
      }
4532
      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...
4533
        setScrollLeft(cm, scrollPos.scrollLeft);
4534
        if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) changed = true;
4535
      }
4536
      if (!changed) break;
4537
    }
4538
    return coords;
0 ignored issues
show
Bug introduced by
The variable coords seems to not be initialized for all possible execution paths.
Loading history...
4539
  }
4540
4541
  // Scroll a given set of coordinates into view (immediately).
4542
  function scrollIntoView(cm, x1, y1, x2, y2) {
4543
    var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2);
4544
    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...
4545
    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...
4546
  }
4547
4548
  // Calculate a new scroll position needed to scroll the given
4549
  // rectangle into view. Returns an object with scrollTop and
4550
  // scrollLeft properties. When these are undefined, the
4551
  // vertical/horizontal position does not need to be adjusted.
4552
  function calculateScrollPos(cm, x1, y1, x2, y2) {
4553
    var display = cm.display, snapMargin = textHeight(cm.display);
4554
    if (y1 < 0) y1 = 0;
4555
    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...
4556
    var screen = displayHeight(cm), result = {};
4557
    if (y2 - y1 > screen) y2 = y1 + screen;
4558
    var docBottom = cm.doc.height + paddingVert(display);
4559
    var atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin;
4560
    if (y1 < screentop) {
4561
      result.scrollTop = atTop ? 0 : y1;
4562
    } else if (y2 > screentop + screen) {
4563
      var newTop = Math.min(y1, (atBottom ? docBottom : y2) - screen);
4564
      if (newTop != screentop) result.scrollTop = newTop;
4565
    }
4566
4567
    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...
4568
    var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0);
4569
    var tooWide = x2 - x1 > screenw;
4570
    if (tooWide) x2 = x1 + screenw;
4571
    if (x1 < 10)
4572
      result.scrollLeft = 0;
4573
    else if (x1 < screenleft)
4574
      result.scrollLeft = Math.max(0, x1 - (tooWide ? 0 : 10));
4575
    else if (x2 > screenw + screenleft - 3)
4576
      result.scrollLeft = x2 + (tooWide ? 0 : 10) - screenw;
4577
    return result;
4578
  }
4579
4580
  // Store a relative adjustment to the scroll position in the current
4581
  // operation (to be applied when the operation finishes).
4582
  function addToScrollPos(cm, left, top) {
4583
    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...
4584
    if (left != null)
4585
      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...
4586
    if (top != null)
4587
      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...
4588
  }
4589
4590
  // Make sure that at the end of the operation the current cursor is
4591
  // shown.
4592
  function ensureCursorVisible(cm) {
4593
    resolveScrollToPos(cm);
4594
    var cur = cm.getCursor(), from = cur, to = cur;
4595
    if (!cm.options.lineWrapping) {
4596
      from = cur.ch ? Pos(cur.line, cur.ch - 1) : cur;
4597
      to = Pos(cur.line, cur.ch + 1);
4598
    }
4599
    cm.curOp.scrollToPos = {from: from, to: to, margin: cm.options.cursorScrollMargin, isCursor: true};
4600
  }
4601
4602
  // When an operation has its scrollToPos property set, and another
4603
  // scroll action is applied before the end of the operation, this
4604
  // 'simulates' scrolling that position into view in a cheap way, so
4605
  // that the effect of intermediate scroll commands is not ignored.
4606
  function resolveScrollToPos(cm) {
4607
    var range = cm.curOp.scrollToPos;
4608
    if (range) {
4609
      cm.curOp.scrollToPos = null;
4610
      var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to);
4611
      var sPos = calculateScrollPos(cm, Math.min(from.left, to.left),
4612
                                    Math.min(from.top, to.top) - range.margin,
4613
                                    Math.max(from.right, to.right),
4614
                                    Math.max(from.bottom, to.bottom) + range.margin);
4615
      cm.scrollTo(sPos.scrollLeft, sPos.scrollTop);
4616
    }
4617
  }
4618
4619
  // API UTILITIES
4620
4621
  // Indent the given line. The how parameter can be "smart",
4622
  // "add"/null, "subtract", or "prev". When aggressive is false
4623
  // (typically set to true for forced single-line indents), empty
4624
  // lines are not indented, and places where the mode returns Pass
4625
  // are left alone.
4626
  function indentLine(cm, n, how, aggressive) {
4627
    var doc = cm.doc, state;
4628
    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...
4629
    if (how == "smart") {
4630
      // Fall back to "prev" when the mode doesn't have an indentation
4631
      // method.
4632
      if (!doc.mode.indent) how = "prev";
4633
      else state = getStateBefore(cm, n);
4634
    }
4635
4636
    var tabSize = cm.options.tabSize;
4637
    var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
4638
    if (line.stateAfter) line.stateAfter = null;
4639
    var curSpaceString = line.text.match(/^\s*/)[0], indentation;
4640
    if (!aggressive && !/\S/.test(line.text)) {
4641
      indentation = 0;
4642
      how = "not";
4643
    } else if (how == "smart") {
4644
      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...
4645
      if (indentation == Pass || indentation > 150) {
4646
        if (!aggressive) 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...
4647
        how = "prev";
4648
      }
4649
    }
4650
    if (how == "prev") {
4651
      if (n > doc.first) indentation = countColumn(getLine(doc, n-1).text, null, tabSize);
4652
      else indentation = 0;
4653
    } else if (how == "add") {
4654
      indentation = curSpace + cm.options.indentUnit;
4655
    } else if (how == "subtract") {
4656
      indentation = curSpace - cm.options.indentUnit;
4657
    } else if (typeof how == "number") {
4658
      indentation = curSpace + how;
4659
    }
4660
    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 4643 is false. Are you sure the function max handles undefined variables?
Loading history...
4661
4662
    var indentString = "", pos = 0;
4663
    if (cm.options.indentWithTabs)
4664
      for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";}
4665
    if (pos < indentation) indentString += spaceStr(indentation - pos);
4666
4667
    if (indentString != curSpaceString) {
4668
      replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
4669
      line.stateAfter = null;
4670
      return true;
4671
    } 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...
4672
      // Ensure that, if the cursor was in the whitespace at the start
4673
      // of the line, it is moved to the end of that space.
4674
      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 4664. 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...
4675
        var range = doc.sel.ranges[i];
4676
        if (range.head.line == n && range.head.ch < curSpaceString.length) {
4677
          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 4662. 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...
4678
          replaceOneSelection(doc, i, new Range(pos, pos));
4679
          break;
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...
4680
        }
4681
      }
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...
4682
    }
4683
  }
4684
4685
  // Utility for applying a change to a line by handle or number,
4686
  // returning the number and optionally registering the line as
4687
  // changed.
4688
  function changeLine(doc, handle, changeType, op) {
4689
    var no = handle, line = handle;
4690
    if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle));
4691
    else no = lineNo(handle);
4692
    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...
4693
    if (op(line, no) && doc.cm) regLineChange(doc.cm, no, changeType);
4694
    return line;
4695
  }
4696
4697
  // Helper for deleting text near the selection(s), used to implement
4698
  // backspace, delete, and similar functionality.
4699
  function deleteNearSelection(cm, compute) {
4700
    var ranges = cm.doc.sel.ranges, kill = [];
4701
    // Build up a set of ranges to kill first, merging overlapping
4702
    // ranges.
4703
    for (var i = 0; i < ranges.length; i++) {
4704
      var toKill = compute(ranges[i]);
4705
      while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {
4706
        var replaced = kill.pop();
4707
        if (cmp(replaced.from, toKill.from) < 0) {
4708
          toKill.from = replaced.from;
4709
          break;
4710
        }
4711
      }
4712
      kill.push(toKill);
4713
    }
4714
    // Next, remove those actual ranges.
4715
    runInOp(cm, function() {
4716
      for (var i = kill.length - 1; i >= 0; i--)
4717
        replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete");
4718
      ensureCursorVisible(cm);
4719
    });
4720
  }
4721
4722
  // Used for horizontal relative motion. Dir is -1 or 1 (left or
4723
  // right), unit can be "char", "column" (like char, but doesn't
4724
  // cross line boundaries), "word" (across next word), or "group" (to
4725
  // the start of next group of word or non-word-non-whitespace
4726
  // chars). The visually param controls whether, in right-to-left
4727
  // text, direction 1 means to move towards the next index in the
4728
  // string, or towards the character to the right of the current
4729
  // position. The resulting position will have a hitSide=true
4730
  // property if it reached the end of the document.
4731
  function findPosH(doc, pos, dir, unit, visually) {
4732
    var line = pos.line, ch = pos.ch, origDir = dir;
4733
    var lineObj = getLine(doc, line);
4734
    var possible = true;
4735
    function findNextLine() {
4736
      var l = line + dir;
4737
      if (l < doc.first || l >= doc.first + doc.size) return (possible = false);
4738
      line = l;
4739
      return lineObj = getLine(doc, l);
4740
    }
4741
    function moveOnce(boundToLine) {
4742
      var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, true);
4743
      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...
4744
        if (!boundToLine && findNextLine()) {
4745
          if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj);
4746
          else ch = dir < 0 ? lineObj.text.length : 0;
4747
        } else return (possible = false);
4748
      } else ch = next;
4749
      return true;
4750
    }
4751
4752
    if (unit == "char") moveOnce();
4753
    else if (unit == "column") moveOnce(true);
4754
    else if (unit == "word" || unit == "group") {
4755
      var sawType = null, group = unit == "group";
4756
      var helper = doc.cm && doc.cm.getHelper(pos, "wordChars");
4757
      for (var first = true;; first = false) {
4758
        if (dir < 0 && !moveOnce(!first)) break;
4759
        var cur = lineObj.text.charAt(ch) || "\n";
4760
        var type = isWordChar(cur, helper) ? "w"
4761
          : group && cur == "\n" ? "n"
4762
          : !group || /\s/.test(cur) ? null
4763
          : "p";
4764
        if (group && !first && !type) type = "s";
4765
        if (sawType && sawType != type) {
4766
          if (dir < 0) {dir = 1; moveOnce();}
4767
          break;
4768
        }
4769
4770
        if (type) sawType = type;
4771
        if (dir > 0 && !moveOnce(!first)) break;
4772
      }
4773
    }
4774
    var result = skipAtomic(doc, Pos(line, ch), origDir, true);
4775
    if (!possible) result.hitSide = true;
4776
    return result;
4777
  }
4778
4779
  // For relative vertical movement. Dir may be -1 or 1. Unit can be
4780
  // "page" or "line". The resulting position will have a hitSide=true
4781
  // property if it reached the end of the document.
4782
  function findPosV(cm, pos, dir, unit) {
4783
    var doc = cm.doc, x = pos.left, y;
4784
    if (unit == "page") {
4785
      var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
4786
      y = pos.top + dir * (pageSize - (dir < 0 ? 1.5 : .5) * textHeight(cm.display));
4787
    } else if (unit == "line") {
4788
      y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
4789
    }
4790
    for (;;) {
4791
      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 4787 is false. Are you sure the function coordsChar handles undefined variables?
Loading history...
4792
      if (!target.outside) break;
4793
      if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break; }
4794
      y += dir * 5;
4795
    }
4796
    return target;
4797
  }
4798
4799
  // EDITOR METHODS
4800
4801
  // The publicly visible API. Note that methodOp(f) means
4802
  // 'wrap f in an operation, performed on its `this` parameter'.
4803
4804
  // This is not the complete set of editor methods. Most of the
4805
  // methods defined on the Doc type are also injected into
4806
  // CodeMirror.prototype, for backwards compatibility and
4807
  // convenience.
4808
4809
  CodeMirror.prototype = {
4810
    constructor: CodeMirror,
4811
    focus: function(){window.focus(); this.display.input.focus();},
4812
4813
    setOption: function(option, value) {
4814
      var options = this.options, old = options[option];
4815
      if (options[option] == value && option != "mode") return;
4816
      options[option] = value;
4817
      if (optionHandlers.hasOwnProperty(option))
4818
        operation(this, optionHandlers[option])(this, value, old);
4819
    },
4820
4821
    getOption: function(option) {return this.options[option];},
4822
    getDoc: function() {return this.doc;},
4823
4824
    addKeyMap: function(map, bottom) {
4825
      this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map));
4826
    },
4827
    removeKeyMap: function(map) {
4828
      var maps = this.state.keyMaps;
4829
      for (var i = 0; i < maps.length; ++i)
4830
        if (maps[i] == map || maps[i].name == map) {
4831
          maps.splice(i, 1);
4832
          return true;
4833
        }
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...
4834
    },
4835
4836
    addOverlay: methodOp(function(spec, options) {
4837
      var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
4838
      if (mode.startState) throw new Error("Overlays may not be stateful.");
4839
      this.state.overlays.push({mode: mode, modeSpec: spec, opaque: options && options.opaque});
4840
      this.state.modeGen++;
4841
      regChange(this);
4842
    }),
4843
    removeOverlay: methodOp(function(spec) {
4844
      var overlays = this.state.overlays;
4845
      for (var i = 0; i < overlays.length; ++i) {
4846
        var cur = overlays[i].modeSpec;
4847
        if (cur == spec || typeof spec == "string" && cur.name == spec) {
4848
          overlays.splice(i, 1);
4849
          this.state.modeGen++;
4850
          regChange(this);
4851
          return;
4852
        }
4853
      }
4854
    }),
4855
4856
    indentLine: methodOp(function(n, dir, aggressive) {
4857
      if (typeof dir != "string" && typeof dir != "number") {
4858
        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...
4859
        else dir = dir ? "add" : "subtract";
4860
      }
4861
      if (isLine(this.doc, n)) indentLine(this, n, dir, aggressive);
4862
    }),
4863
    indentSelection: methodOp(function(how) {
4864
      var ranges = this.doc.sel.ranges, end = -1;
4865
      for (var i = 0; i < ranges.length; i++) {
4866
        var range = ranges[i];
4867
        if (!range.empty()) {
4868
          var from = range.from(), to = range.to();
4869
          var start = Math.max(end, from.line);
4870
          end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1;
4871
          for (var j = start; j < end; ++j)
4872
            indentLine(this, j, how);
4873
          var newRanges = this.doc.sel.ranges;
4874
          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...
4875
            replaceOneSelection(this.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll);
4876
        } else if (range.head.line > end) {
4877
          indentLine(this, range.head.line, how, true);
4878
          end = range.head.line;
4879
          if (i == this.doc.sel.primIndex) ensureCursorVisible(this);
4880
        }
4881
      }
4882
    }),
4883
4884
    // Fetch the parser token for a given character. Useful for hacks
4885
    // that want to inspect the mode state (say, for completion).
4886
    getTokenAt: function(pos, precise) {
4887
      return takeToken(this, pos, precise);
4888
    },
4889
4890
    getLineTokens: function(line, precise) {
4891
      return takeToken(this, Pos(line), precise, true);
4892
    },
4893
4894
    getTokenTypeAt: function(pos) {
4895
      pos = clipPos(this.doc, pos);
4896
      var styles = getLineStyles(this, getLine(this.doc, pos.line));
4897
      var before = 0, after = (styles.length - 1) / 2, ch = pos.ch;
4898
      var type;
4899
      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...
4900
      else for (;;) {
4901
        var mid = (before + after) >> 1;
4902
        if ((mid ? styles[mid * 2 - 1] : 0) >= ch) after = mid;
4903
        else if (styles[mid * 2 + 1] < ch) before = mid + 1;
4904
        else { type = styles[mid * 2 + 2]; break; }
4905
      }
4906
      var cut = type ? type.indexOf("cm-overlay ") : -1;
4907
      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...
4908
    },
4909
4910
    getModeAt: function(pos) {
4911
      var mode = this.doc.mode;
4912
      if (!mode.innerMode) return mode;
4913
      return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode;
4914
    },
4915
4916
    getHelper: function(pos, type) {
4917
      return this.getHelpers(pos, type)[0];
4918
    },
4919
4920
    getHelpers: function(pos, type) {
4921
      var found = [];
4922
      if (!helpers.hasOwnProperty(type)) return found;
4923
      var help = helpers[type], mode = this.getModeAt(pos);
4924
      if (typeof mode[type] == "string") {
4925
        if (help[mode[type]]) found.push(help[mode[type]]);
4926
      } else if (mode[type]) {
4927
        for (var i = 0; i < mode[type].length; i++) {
4928
          var val = help[mode[type][i]];
4929
          if (val) found.push(val);
4930
        }
4931
      } else if (mode.helperType && help[mode.helperType]) {
4932
        found.push(help[mode.helperType]);
4933
      } else if (help[mode.name]) {
4934
        found.push(help[mode.name]);
4935
      }
4936
      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 4927. 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...
4937
        var cur = help._global[i];
4938
        if (cur.pred(mode, this) && indexOf(found, cur.val) == -1)
4939
          found.push(cur.val);
4940
      }
4941
      return found;
4942
    },
4943
4944
    getStateAfter: function(line, precise) {
4945
      var doc = this.doc;
4946
      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...
4947
      return getStateBefore(this, line + 1, precise);
4948
    },
4949
4950
    cursorCoords: function(start, mode) {
4951
      var pos, range = this.doc.sel.primary();
4952
      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...
4953
      else if (typeof start == "object") pos = clipPos(this.doc, start);
4954
      else pos = start ? range.from() : range.to();
4955
      return cursorCoords(this, pos, mode || "page");
4956
    },
4957
4958
    charCoords: function(pos, mode) {
4959
      return charCoords(this, clipPos(this.doc, pos), mode || "page");
4960
    },
4961
4962
    coordsChar: function(coords, mode) {
4963
      coords = fromCoordSystem(this, coords, mode || "page");
4964
      return coordsChar(this, coords.left, coords.top);
4965
    },
4966
4967
    lineAtHeight: function(height, mode) {
4968
      height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top;
4969
      return lineAtHeight(this.doc, height + this.display.viewOffset);
4970
    },
4971
    heightAtLine: function(line, mode) {
4972
      var end = false, lineObj;
4973
      if (typeof line == "number") {
4974
        var last = this.doc.first + this.doc.size - 1;
4975
        if (line < this.doc.first) line = this.doc.first;
4976
        else if (line > last) { line = last; end = true; }
4977
        lineObj = getLine(this.doc, line);
4978
      } else {
4979
        lineObj = line;
4980
      }
4981
      return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page").top +
4982
        (end ? this.doc.height - heightAtLine(lineObj) : 0);
4983
    },
4984
4985
    defaultTextHeight: function() { return textHeight(this.display); },
4986
    defaultCharWidth: function() { return charWidth(this.display); },
4987
4988
    setGutterMarker: methodOp(function(line, gutterID, value) {
4989
      return changeLine(this.doc, line, "gutter", function(line) {
4990
        var markers = line.gutterMarkers || (line.gutterMarkers = {});
4991
        markers[gutterID] = value;
4992
        if (!value && isEmpty(markers)) line.gutterMarkers = null;
4993
        return true;
4994
      });
4995
    }),
4996
4997
    clearGutter: methodOp(function(gutterID) {
4998
      var cm = this, doc = cm.doc, i = doc.first;
4999
      doc.iter(function(line) {
5000
        if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
5001
          line.gutterMarkers[gutterID] = null;
5002
          regLineChange(cm, i, "gutter");
5003
          if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null;
5004
        }
5005
        ++i;
5006
      });
5007
    }),
5008
5009
    lineInfo: function(line) {
5010
      if (typeof line == "number") {
5011
        if (!isLine(this.doc, line)) return null;
5012
        var n = line;
5013
        line = getLine(this.doc, line);
5014
        if (!line) return null;
5015
      } else {
5016
        var n = lineNo(line);
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable n already seems to be declared on line 5012. 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...
5017
        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...
5018
      }
5019
      return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
5020
              textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
5021
              widgets: line.widgets};
5022
    },
5023
5024
    getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo};},
5025
5026
    addWidget: function(pos, node, scroll, vert, horiz) {
5027
      var display = this.display;
5028
      pos = cursorCoords(this, clipPos(this.doc, pos));
5029
      var top = pos.bottom, left = pos.left;
5030
      node.style.position = "absolute";
5031
      node.setAttribute("cm-ignore-events", "true");
5032
      this.display.input.setUneditable(node);
5033
      display.sizer.appendChild(node);
5034
      if (vert == "over") {
5035
        top = pos.top;
5036
      } else if (vert == "above" || vert == "near") {
5037
        var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
5038
        hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
5039
        // Default to positioning above (if specified and possible); otherwise default to positioning below
5040
        if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
5041
          top = pos.top - node.offsetHeight;
5042
        else if (pos.bottom + node.offsetHeight <= vspace)
5043
          top = pos.bottom;
5044
        if (left + node.offsetWidth > hspace)
5045
          left = hspace - node.offsetWidth;
5046
      }
5047
      node.style.top = top + "px";
5048
      node.style.left = node.style.right = "";
5049
      if (horiz == "right") {
5050
        left = display.sizer.clientWidth - node.offsetWidth;
5051
        node.style.right = "0px";
5052
      } else {
5053
        if (horiz == "left") left = 0;
5054
        else if (horiz == "middle") left = (display.sizer.clientWidth - node.offsetWidth) / 2;
5055
        node.style.left = left + "px";
5056
      }
5057
      if (scroll)
5058
        scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight);
5059
    },
5060
5061
    triggerOnKeyDown: methodOp(onKeyDown),
5062
    triggerOnKeyPress: methodOp(onKeyPress),
5063
    triggerOnKeyUp: onKeyUp,
5064
5065
    execCommand: function(cmd) {
5066
      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...
5067
        return commands[cmd].call(null, this);
5068
    },
5069
5070
    triggerElectric: methodOp(function(text) { triggerElectric(this, text); }),
5071
5072
    findPosH: function(from, amount, unit, visually) {
5073
      var dir = 1;
5074
      if (amount < 0) { dir = -1; amount = -amount; }
5075
      for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
5076
        cur = findPosH(this.doc, cur, dir, unit, visually);
5077
        if (cur.hitSide) break;
5078
      }
5079
      return cur;
5080
    },
5081
5082
    moveH: methodOp(function(dir, unit) {
5083
      var cm = this;
5084
      cm.extendSelectionsBy(function(range) {
5085
        if (cm.display.shift || cm.doc.extend || range.empty())
5086
          return findPosH(cm.doc, range.head, dir, unit, cm.options.rtlMoveVisually);
5087
        else
5088
          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...
5089
      }, sel_move);
5090
    }),
5091
5092
    deleteH: methodOp(function(dir, unit) {
5093
      var sel = this.doc.sel, doc = this.doc;
5094
      if (sel.somethingSelected())
5095
        doc.replaceSelection("", null, "+delete");
5096
      else
5097
        deleteNearSelection(this, function(range) {
5098
          var other = findPosH(doc, range.head, dir, unit, false);
5099
          return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other};
5100
        });
5101
    }),
5102
5103
    findPosV: function(from, amount, unit, goalColumn) {
5104
      var dir = 1, x = goalColumn;
5105
      if (amount < 0) { dir = -1; amount = -amount; }
5106
      for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
5107
        var coords = cursorCoords(this, cur, "div");
5108
        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...
5109
        else coords.left = x;
5110
        cur = findPosV(this, coords, dir, unit);
5111
        if (cur.hitSide) break;
5112
      }
5113
      return cur;
5114
    },
5115
5116
    moveV: methodOp(function(dir, unit) {
5117
      var cm = this, doc = this.doc, goals = [];
5118
      var collapse = !cm.display.shift && !doc.extend && doc.sel.somethingSelected();
5119
      doc.extendSelectionsBy(function(range) {
5120
        if (collapse)
5121
          return dir < 0 ? range.from() : range.to();
5122
        var headPos = cursorCoords(cm, range.head, "div");
5123
        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...
5124
        goals.push(headPos.left);
5125
        var pos = findPosV(cm, headPos, dir, unit);
5126
        if (unit == "page" && range == doc.sel.primary())
5127
          addToScrollPos(cm, null, charCoords(cm, pos, "div").top - headPos.top);
5128
        return pos;
5129
      }, sel_move);
5130
      if (goals.length) for (var i = 0; i < doc.sel.ranges.length; i++)
5131
        doc.sel.ranges[i].goalColumn = goals[i];
5132
    }),
5133
5134
    // Find the word at the given position (as returned by coordsChar).
5135
    findWordAt: function(pos) {
5136
      var doc = this.doc, line = getLine(doc, pos.line).text;
5137
      var start = pos.ch, end = pos.ch;
5138
      if (line) {
5139
        var helper = this.getHelper(pos, "wordChars");
5140
        if ((pos.xRel < 0 || end == line.length) && start) --start; else ++end;
5141
        var startChar = line.charAt(start);
5142
        var check = isWordChar(startChar, helper)
5143
          ? function(ch) { return isWordChar(ch, helper); }
5144
          : /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);}
5145
          : function(ch) {return !/\s/.test(ch) && !isWordChar(ch);};
5146
        while (start > 0 && check(line.charAt(start - 1))) --start;
5147
        while (end < line.length && check(line.charAt(end))) ++end;
5148
      }
5149
      return new Range(Pos(pos.line, start), Pos(pos.line, end));
5150
    },
5151
5152
    toggleOverwrite: function(value) {
5153
      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...
5154
      if (this.state.overwrite = !this.state.overwrite)
5155
        addClass(this.display.cursorDiv, "CodeMirror-overwrite");
5156
      else
5157
        rmClass(this.display.cursorDiv, "CodeMirror-overwrite");
5158
5159
      signal(this, "overwriteToggle", this, this.state.overwrite);
5160
    },
5161
    hasFocus: function() { return this.display.input.getField() == activeElt(); },
5162
5163
    scrollTo: methodOp(function(x, y) {
5164
      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...
5165
      if (x != null) this.curOp.scrollLeft = x;
5166
      if (y != null) this.curOp.scrollTop = y;
5167
    }),
5168
    getScrollInfo: function() {
5169
      var scroller = this.display.scroller;
5170
      return {left: scroller.scrollLeft, top: scroller.scrollTop,
5171
              height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight,
5172
              width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth,
5173
              clientHeight: displayHeight(this), clientWidth: displayWidth(this)};
5174
    },
5175
5176
    scrollIntoView: methodOp(function(range, margin) {
5177
      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...
5178
        range = {from: this.doc.sel.primary().head, to: null};
5179
        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...
5180
      } else if (typeof range == "number") {
5181
        range = {from: Pos(range, 0), to: null};
5182
      } 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...
5183
        range = {from: range, to: null};
5184
      }
5185
      if (!range.to) range.to = range.from;
5186
      range.margin = margin || 0;
5187
5188
      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...
5189
        resolveScrollToPos(this);
5190
        this.curOp.scrollToPos = range;
5191
      } else {
5192
        var sPos = calculateScrollPos(this, Math.min(range.from.left, range.to.left),
5193
                                      Math.min(range.from.top, range.to.top) - range.margin,
5194
                                      Math.max(range.from.right, range.to.right),
5195
                                      Math.max(range.from.bottom, range.to.bottom) + range.margin);
5196
        this.scrollTo(sPos.scrollLeft, sPos.scrollTop);
5197
      }
5198
    }),
5199
5200
    setSize: methodOp(function(width, height) {
5201
      var cm = this;
5202
      function interpret(val) {
5203
        return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val;
5204
      }
5205
      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...
5206
      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...
5207
      if (cm.options.lineWrapping) clearLineMeasurementCache(this);
5208
      var lineNo = cm.display.viewFrom;
5209
      cm.doc.iter(lineNo, cm.display.viewTo, function(line) {
5210
        if (line.widgets) for (var i = 0; i < line.widgets.length; i++)
5211
          if (line.widgets[i].noHScroll) { regLineChange(cm, lineNo, "widget"); break; }
5212
        ++lineNo;
5213
      });
5214
      cm.curOp.forceUpdate = true;
5215
      signal(cm, "refresh", this);
5216
    }),
5217
5218
    operation: function(f){return runInOp(this, f);},
5219
5220
    refresh: methodOp(function() {
5221
      var oldHeight = this.display.cachedTextHeight;
5222
      regChange(this);
5223
      this.curOp.forceUpdate = true;
5224
      clearCaches(this);
5225
      this.scrollTo(this.doc.scrollLeft, this.doc.scrollTop);
5226
      updateGutterSpace(this);
5227
      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...
5228
        estimateLineHeights(this);
5229
      signal(this, "refresh", this);
5230
    }),
5231
5232
    swapDoc: methodOp(function(doc) {
5233
      var old = this.doc;
5234
      old.cm = null;
5235
      attachDoc(this, doc);
5236
      clearCaches(this);
5237
      this.display.input.reset();
5238
      this.scrollTo(doc.scrollLeft, doc.scrollTop);
5239
      this.curOp.forceScroll = true;
5240
      signalLater(this, "swapDoc", this, old);
5241
      return old;
5242
    }),
5243
5244
    getInputField: function(){return this.display.input.getField();},
5245
    getWrapperElement: function(){return this.display.wrapper;},
5246
    getScrollerElement: function(){return this.display.scroller;},
5247
    getGutterElement: function(){return this.display.gutters;}
5248
  };
5249
  eventMixin(CodeMirror);
5250
5251
  // OPTION DEFAULTS
5252
5253
  // The default configuration options.
5254
  var defaults = CodeMirror.defaults = {};
5255
  // Functions to run when options are changed.
5256
  var optionHandlers = CodeMirror.optionHandlers = {};
5257
5258
  function option(name, deflt, handle, notOnInit) {
5259
    CodeMirror.defaults[name] = deflt;
5260
    if (handle) optionHandlers[name] =
5261
      notOnInit ? function(cm, val, old) {if (old != Init) handle(cm, val, old);} : handle;
5262
  }
5263
5264
  // Passed to option handlers when there is no old value.
5265
  var Init = CodeMirror.Init = {toString: function(){return "CodeMirror.Init";}};
5266
5267
  // These two are, on init, called from the constructor because they
5268
  // have to be initialized before the editor can start at all.
5269
  option("value", "", function(cm, val) {
5270
    cm.setValue(val);
5271
  }, true);
5272
  option("mode", null, function(cm, val) {
5273
    cm.doc.modeOption = val;
5274
    loadMode(cm);
5275
  }, true);
5276
5277
  option("indentUnit", 2, loadMode, true);
5278
  option("indentWithTabs", false);
5279
  option("smartIndent", true);
5280
  option("tabSize", 4, function(cm) {
5281
    resetModeState(cm);
5282
    clearCaches(cm);
5283
    regChange(cm);
5284
  }, true);
5285
  option("lineSeparator", null, function(cm, val) {
5286
    cm.doc.lineSep = val;
5287
    if (!val) return;
5288
    var newBreaks = [], lineNo = cm.doc.first;
5289
    cm.doc.iter(function(line) {
5290
      for (var pos = 0;;) {
5291
        var found = line.text.indexOf(val, pos);
5292
        if (found == -1) break;
5293
        pos = found + val.length;
5294
        newBreaks.push(Pos(lineNo, found));
5295
      }
5296
      lineNo++;
5297
    });
5298
    for (var i = newBreaks.length - 1; i >= 0; i--)
5299
      replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length))
5300
  });
5301
  option("specialChars", /[\t\u0000-\u0019\u00ad\u200b-\u200f\u2028\u2029\ufeff]/g, function(cm, val, old) {
5302
    cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g");
5303
    if (old != CodeMirror.Init) cm.refresh();
5304
  });
5305
  option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function(cm) {cm.refresh();}, true);
5306
  option("electricChars", true);
5307
  option("inputStyle", mobile ? "contenteditable" : "textarea", function() {
5308
    throw new Error("inputStyle can not (yet) be changed in a running editor"); // FIXME
5309
  }, true);
5310
  option("rtlMoveVisually", !windows);
5311
  option("wholeLineUpdateBefore", true);
5312
5313
  option("theme", "default", function(cm) {
5314
    themeChanged(cm);
5315
    guttersChanged(cm);
5316
  }, true);
5317
  option("keyMap", "default", function(cm, val, old) {
5318
    var next = getKeyMap(val);
5319
    var prev = old != CodeMirror.Init && getKeyMap(old);
5320
    if (prev && prev.detach) prev.detach(cm, next);
5321
    if (next.attach) next.attach(cm, prev || null);
5322
  });
5323
  option("extraKeys", null);
5324
5325
  option("lineWrapping", false, wrappingChanged, true);
5326
  option("gutters", [], function(cm) {
5327
    setGuttersForLineNumbers(cm.options);
5328
    guttersChanged(cm);
5329
  }, true);
5330
  option("fixedGutter", true, function(cm, val) {
5331
    cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
5332
    cm.refresh();
5333
  }, true);
5334
  option("coverGutterNextToScrollbar", false, function(cm) {updateScrollbars(cm);}, true);
5335
  option("scrollbarStyle", "native", function(cm) {
5336
    initScrollbars(cm);
5337
    updateScrollbars(cm);
5338
    cm.display.scrollbars.setScrollTop(cm.doc.scrollTop);
5339
    cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft);
5340
  }, true);
5341
  option("lineNumbers", false, function(cm) {
5342
    setGuttersForLineNumbers(cm.options);
5343
    guttersChanged(cm);
5344
  }, true);
5345
  option("firstLineNumber", 1, guttersChanged, true);
5346
  option("lineNumberFormatter", function(integer) {return integer;}, guttersChanged, true);
5347
  option("showCursorWhenSelecting", false, updateSelection, true);
5348
5349
  option("resetSelectionOnContextMenu", true);
5350
  option("lineWiseCopyCut", true);
5351
5352
  option("readOnly", false, function(cm, val) {
5353
    if (val == "nocursor") {
5354
      onBlur(cm);
5355
      cm.display.input.blur();
5356
      cm.display.disabled = true;
5357
    } else {
5358
      cm.display.disabled = false;
5359
      if (!val) cm.display.input.reset();
5360
    }
5361
  });
5362
  option("disableInput", false, function(cm, val) {if (!val) cm.display.input.reset();}, true);
5363
  option("dragDrop", true, dragDropChanged);
5364
5365
  option("cursorBlinkRate", 530);
5366
  option("cursorScrollMargin", 0);
5367
  option("cursorHeight", 1, updateSelection, true);
5368
  option("singleCursorHeightPerLine", true, updateSelection, true);
5369
  option("workTime", 100);
5370
  option("workDelay", 100);
5371
  option("flattenSpans", true, resetModeState, true);
5372
  option("addModeClass", false, resetModeState, true);
5373
  option("pollInterval", 100);
5374
  option("undoDepth", 200, function(cm, val){cm.doc.history.undoDepth = val;});
5375
  option("historyEventDelay", 1250);
5376
  option("viewportMargin", 10, function(cm){cm.refresh();}, true);
5377
  option("maxHighlightLength", 10000, resetModeState, true);
5378
  option("moveInputWithCursor", true, function(cm, val) {
5379
    if (!val) cm.display.input.resetPosition();
5380
  });
5381
5382
  option("tabindex", null, function(cm, val) {
5383
    cm.display.input.getField().tabIndex = val || "";
5384
  });
5385
  option("autofocus", null);
5386
5387
  // MODE DEFINITION AND QUERYING
5388
5389
  // Known modes, by name and by MIME
5390
  var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {};
5391
5392
  // Extra arguments are stored as the mode's dependencies, which is
5393
  // used by (legacy) mechanisms like loadmode.js to automatically
5394
  // load a mode. (Preferred mechanism is the require/define calls.)
5395
  CodeMirror.defineMode = function(name, mode) {
5396
    if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
5397
    if (arguments.length > 2)
5398
      mode.dependencies = Array.prototype.slice.call(arguments, 2);
5399
    modes[name] = mode;
5400
  };
5401
5402
  CodeMirror.defineMIME = function(mime, spec) {
5403
    mimeModes[mime] = spec;
5404
  };
5405
5406
  // Given a MIME type, a {name, ...options} config object, or a name
5407
  // string, return a mode config object.
5408
  CodeMirror.resolveMode = function(spec) {
5409
    if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
5410
      spec = mimeModes[spec];
5411
    } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
5412
      var found = mimeModes[spec.name];
5413
      if (typeof found == "string") found = {name: found};
5414
      spec = createObj(found, spec);
5415
      spec.name = found.name;
5416
    } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
5417
      return CodeMirror.resolveMode("application/xml");
5418
    }
5419
    if (typeof spec == "string") return {name: spec};
5420
    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...
5421
  };
5422
5423
  // Given a mode spec (anything that resolveMode accepts), find and
5424
  // initialize an actual mode object.
5425
  CodeMirror.getMode = function(options, spec) {
5426
    var spec = CodeMirror.resolveMode(spec);
5427
    var mfactory = modes[spec.name];
5428
    if (!mfactory) return CodeMirror.getMode(options, "text/plain");
5429
    var modeObj = mfactory(options, spec);
5430
    if (modeExtensions.hasOwnProperty(spec.name)) {
5431
      var exts = modeExtensions[spec.name];
5432
      for (var prop in exts) {
5433
        if (!exts.hasOwnProperty(prop)) continue;
5434
        if (modeObj.hasOwnProperty(prop)) modeObj["_" + prop] = modeObj[prop];
5435
        modeObj[prop] = exts[prop];
5436
      }
5437
    }
5438
    modeObj.name = spec.name;
5439
    if (spec.helperType) modeObj.helperType = spec.helperType;
5440
    if (spec.modeProps) for (var prop in spec.modeProps)
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable prop already seems to be declared on line 5432. 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...
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...
5441
      modeObj[prop] = spec.modeProps[prop];
5442
5443
    return modeObj;
5444
  };
5445
5446
  // Minimal default mode.
5447
  CodeMirror.defineMode("null", function() {
5448
    return {token: function(stream) {stream.skipToEnd();}};
5449
  });
5450
  CodeMirror.defineMIME("text/plain", "null");
5451
5452
  // This can be used to attach properties to mode objects from
5453
  // outside the actual mode definition.
5454
  var modeExtensions = CodeMirror.modeExtensions = {};
5455
  CodeMirror.extendMode = function(mode, properties) {
5456
    var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
5457
    copyObj(properties, exts);
5458
  };
5459
5460
  // EXTENSIONS
5461
5462
  CodeMirror.defineExtension = function(name, func) {
5463
    CodeMirror.prototype[name] = func;
5464
  };
5465
  CodeMirror.defineDocExtension = function(name, func) {
5466
    Doc.prototype[name] = func;
5467
  };
5468
  CodeMirror.defineOption = option;
5469
5470
  var initHooks = [];
5471
  CodeMirror.defineInitHook = function(f) {initHooks.push(f);};
5472
5473
  var helpers = CodeMirror.helpers = {};
5474
  CodeMirror.registerHelper = function(type, name, value) {
5475
    if (!helpers.hasOwnProperty(type)) helpers[type] = CodeMirror[type] = {_global: []};
5476
    helpers[type][name] = value;
5477
  };
5478
  CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {
5479
    CodeMirror.registerHelper(type, name, value);
5480
    helpers[type]._global.push({pred: predicate, val: value});
5481
  };
5482
5483
  // MODE STATE HANDLING
5484
5485
  // Utility functions for working with state. Exported because nested
5486
  // modes need to do this for their inner modes.
5487
5488
  var copyState = CodeMirror.copyState = function(mode, state) {
5489
    if (state === true) return state;
5490
    if (mode.copyState) return mode.copyState(state);
5491
    var nstate = {};
5492
    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...
5493
      var val = state[n];
5494
      if (val instanceof Array) val = val.concat([]);
5495
      nstate[n] = val;
5496
    }
5497
    return nstate;
5498
  };
5499
5500
  var startState = CodeMirror.startState = function(mode, a1, a2) {
5501
    return mode.startState ? mode.startState(a1, a2) : true;
5502
  };
5503
5504
  // Given a mode and a state (for that mode), find the inner mode and
5505
  // state at the position that the state refers to.
5506
  CodeMirror.innerMode = function(mode, state) {
5507
    while (mode.innerMode) {
5508
      var info = mode.innerMode(state);
5509
      if (!info || info.mode == mode) break;
5510
      state = info.state;
5511
      mode = info.mode;
5512
    }
5513
    return info || {mode: mode, state: state};
5514
  };
5515
5516
  // STANDARD COMMANDS
5517
5518
  // Commands are parameter-less actions that can be performed on an
5519
  // editor, mostly used for keybindings.
5520
  var commands = CodeMirror.commands = {
5521
    selectAll: function(cm) {cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll);},
5522
    singleSelection: function(cm) {
5523
      cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll);
5524
    },
5525
    killLine: function(cm) {
5526
      deleteNearSelection(cm, function(range) {
5527
        if (range.empty()) {
5528
          var len = getLine(cm.doc, range.head.line).text.length;
5529
          if (range.head.ch == len && range.head.line < cm.lastLine())
5530
            return {from: range.head, to: Pos(range.head.line + 1, 0)};
5531
          else
5532
            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...
5533
        } 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...
5534
          return {from: range.from(), to: range.to()};
5535
        }
5536
      });
5537
    },
5538
    deleteLine: function(cm) {
5539
      deleteNearSelection(cm, function(range) {
5540
        return {from: Pos(range.from().line, 0),
5541
                to: clipPos(cm.doc, Pos(range.to().line + 1, 0))};
5542
      });
5543
    },
5544
    delLineLeft: function(cm) {
5545
      deleteNearSelection(cm, function(range) {
5546
        return {from: Pos(range.from().line, 0), to: range.from()};
5547
      });
5548
    },
5549
    delWrappedLineLeft: function(cm) {
5550
      deleteNearSelection(cm, function(range) {
5551
        var top = cm.charCoords(range.head, "div").top + 5;
5552
        var leftPos = cm.coordsChar({left: 0, top: top}, "div");
5553
        return {from: leftPos, to: range.from()};
5554
      });
5555
    },
5556
    delWrappedLineRight: function(cm) {
5557
      deleteNearSelection(cm, function(range) {
5558
        var top = cm.charCoords(range.head, "div").top + 5;
5559
        var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div");
5560
        return {from: range.from(), to: rightPos };
5561
      });
5562
    },
5563
    undo: function(cm) {cm.undo();},
5564
    redo: function(cm) {cm.redo();},
5565
    undoSelection: function(cm) {cm.undoSelection();},
5566
    redoSelection: function(cm) {cm.redoSelection();},
5567
    goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));},
5568
    goDocEnd: function(cm) {cm.extendSelection(Pos(cm.lastLine()));},
5569
    goLineStart: function(cm) {
5570
      cm.extendSelectionsBy(function(range) { return lineStart(cm, range.head.line); },
5571
                            {origin: "+move", bias: 1});
5572
    },
5573
    goLineStartSmart: function(cm) {
5574
      cm.extendSelectionsBy(function(range) {
5575
        return lineStartSmart(cm, range.head);
5576
      }, {origin: "+move", bias: 1});
5577
    },
5578
    goLineEnd: function(cm) {
5579
      cm.extendSelectionsBy(function(range) { return lineEnd(cm, range.head.line); },
5580
                            {origin: "+move", bias: -1});
5581
    },
5582
    goLineRight: function(cm) {
5583
      cm.extendSelectionsBy(function(range) {
5584
        var top = cm.charCoords(range.head, "div").top + 5;
5585
        return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div");
5586
      }, sel_move);
5587
    },
5588
    goLineLeft: function(cm) {
5589
      cm.extendSelectionsBy(function(range) {
5590
        var top = cm.charCoords(range.head, "div").top + 5;
5591
        return cm.coordsChar({left: 0, top: top}, "div");
5592
      }, sel_move);
5593
    },
5594
    goLineLeftSmart: function(cm) {
5595
      cm.extendSelectionsBy(function(range) {
5596
        var top = cm.charCoords(range.head, "div").top + 5;
5597
        var pos = cm.coordsChar({left: 0, top: top}, "div");
5598
        if (pos.ch < cm.getLine(pos.line).search(/\S/)) return lineStartSmart(cm, range.head);
5599
        return pos;
5600
      }, sel_move);
5601
    },
5602
    goLineUp: function(cm) {cm.moveV(-1, "line");},
5603
    goLineDown: function(cm) {cm.moveV(1, "line");},
5604
    goPageUp: function(cm) {cm.moveV(-1, "page");},
5605
    goPageDown: function(cm) {cm.moveV(1, "page");},
5606
    goCharLeft: function(cm) {cm.moveH(-1, "char");},
5607
    goCharRight: function(cm) {cm.moveH(1, "char");},
5608
    goColumnLeft: function(cm) {cm.moveH(-1, "column");},
5609
    goColumnRight: function(cm) {cm.moveH(1, "column");},
5610
    goWordLeft: function(cm) {cm.moveH(-1, "word");},
5611
    goGroupRight: function(cm) {cm.moveH(1, "group");},
5612
    goGroupLeft: function(cm) {cm.moveH(-1, "group");},
5613
    goWordRight: function(cm) {cm.moveH(1, "word");},
5614
    delCharBefore: function(cm) {cm.deleteH(-1, "char");},
5615
    delCharAfter: function(cm) {cm.deleteH(1, "char");},
5616
    delWordBefore: function(cm) {cm.deleteH(-1, "word");},
5617
    delWordAfter: function(cm) {cm.deleteH(1, "word");},
5618
    delGroupBefore: function(cm) {cm.deleteH(-1, "group");},
5619
    delGroupAfter: function(cm) {cm.deleteH(1, "group");},
5620
    indentAuto: function(cm) {cm.indentSelection("smart");},
5621
    indentMore: function(cm) {cm.indentSelection("add");},
5622
    indentLess: function(cm) {cm.indentSelection("subtract");},
5623
    insertTab: function(cm) {cm.replaceSelection("\t");},
5624
    insertSoftTab: function(cm) {
5625
      var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize;
5626
      for (var i = 0; i < ranges.length; i++) {
5627
        var pos = ranges[i].from();
5628
        var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize);
5629
        spaces.push(new Array(tabSize - col % tabSize + 1).join(" "));
5630
      }
5631
      cm.replaceSelections(spaces);
5632
    },
5633
    defaultTab: function(cm) {
5634
      if (cm.somethingSelected()) cm.indentSelection("add");
5635
      else cm.execCommand("insertTab");
5636
    },
5637
    transposeChars: function(cm) {
5638
      runInOp(cm, function() {
5639
        var ranges = cm.listSelections(), newSel = [];
5640
        for (var i = 0; i < ranges.length; i++) {
5641
          var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text;
5642
          if (line) {
5643
            if (cur.ch == line.length) cur = new Pos(cur.line, cur.ch - 1);
5644
            if (cur.ch > 0) {
5645
              cur = new Pos(cur.line, cur.ch + 1);
5646
              cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),
5647
                              Pos(cur.line, cur.ch - 2), cur, "+transpose");
5648
            } else if (cur.line > cm.doc.first) {
5649
              var prev = getLine(cm.doc, cur.line - 1).text;
5650
              if (prev)
5651
                cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() +
5652
                                prev.charAt(prev.length - 1),
5653
                                Pos(cur.line - 1, prev.length - 1), Pos(cur.line, 1), "+transpose");
5654
            }
5655
          }
5656
          newSel.push(new Range(cur, cur));
5657
        }
5658
        cm.setSelections(newSel);
5659
      });
5660
    },
5661
    newlineAndIndent: function(cm) {
5662
      runInOp(cm, function() {
5663
        var len = cm.listSelections().length;
5664
        for (var i = 0; i < len; i++) {
5665
          var range = cm.listSelections()[i];
5666
          cm.replaceRange(cm.doc.lineSeparator(), range.anchor, range.head, "+input");
5667
          cm.indentLine(range.from().line + 1, null, true);
5668
          ensureCursorVisible(cm);
5669
        }
5670
      });
5671
    },
5672
    toggleOverwrite: function(cm) {cm.toggleOverwrite();}
5673
  };
5674
5675
5676
  // STANDARD KEYMAPS
5677
5678
  var keyMap = CodeMirror.keyMap = {};
5679
5680
  keyMap.basic = {
5681
    "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
5682
    "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
5683
    "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore",
5684
    "Tab": "defaultTab", "Shift-Tab": "indentAuto",
5685
    "Enter": "newlineAndIndent", "Insert": "toggleOverwrite",
5686
    "Esc": "singleSelection"
5687
  };
5688
  // Note that the save and find-related commands aren't defined by
5689
  // default. User code or addons can define them. Unknown commands
5690
  // are simply ignored.
5691
  keyMap.pcDefault = {
5692
    "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
5693
    "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown",
5694
    "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
5695
    "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
5696
    "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
5697
    "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
5698
    "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection",
5699
    fallthrough: "basic"
5700
  };
5701
  // Very basic readline/emacs-style bindings, which are standard on Mac.
5702
  keyMap.emacsy = {
5703
    "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
5704
    "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
5705
    "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
5706
    "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars"
5707
  };
5708
  keyMap.macDefault = {
5709
    "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
5710
    "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
5711
    "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore",
5712
    "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
5713
    "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
5714
    "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight",
5715
    "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd",
5716
    fallthrough: ["basic", "emacsy"]
5717
  };
5718
  keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
5719
5720
  // KEYMAP DISPATCH
5721
5722
  function normalizeKeyName(name) {
5723
    var parts = name.split(/-(?!$)/), name = parts[parts.length - 1];
5724
    var alt, ctrl, shift, cmd;
5725
    for (var i = 0; i < parts.length - 1; i++) {
5726
      var mod = parts[i];
5727
      if (/^(cmd|meta|m)$/i.test(mod)) cmd = true;
5728
      else if (/^a(lt)?$/i.test(mod)) alt = true;
5729
      else if (/^(c|ctrl|control)$/i.test(mod)) ctrl = true;
5730
      else if (/^s(hift)$/i.test(mod)) shift = true;
5731
      else throw new Error("Unrecognized modifier name: " + mod);
5732
    }
5733
    if (alt) name = "Alt-" + name;
5734
    if (ctrl) name = "Ctrl-" + name;
5735
    if (cmd) name = "Cmd-" + name;
5736
    if (shift) name = "Shift-" + name;
5737
    return name;
5738
  }
5739
5740
  // This is a kludge to keep keymaps mostly working as raw objects
5741
  // (backwards compatibility) while at the same time support features
5742
  // like normalization and multi-stroke key bindings. It compiles a
5743
  // new normalized keymap, and then updates the old object to reflect
5744
  // this.
5745
  CodeMirror.normalizeKeyMap = function(keymap) {
5746
    var copy = {};
5747
    for (var keyname in keymap) if (keymap.hasOwnProperty(keyname)) {
5748
      var value = keymap[keyname];
5749
      if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) continue;
5750
      if (value == "...") { delete keymap[keyname]; continue; }
5751
5752
      var keys = map(keyname.split(" "), normalizeKeyName);
5753
      for (var i = 0; i < keys.length; i++) {
5754
        var val, name;
5755
        if (i == keys.length - 1) {
5756
          name = keys.join(" ");
5757
          val = value;
5758
        } else {
5759
          name = keys.slice(0, i + 1).join(" ");
5760
          val = "...";
5761
        }
5762
        var prev = copy[name];
5763
        if (!prev) copy[name] = val;
5764
        else if (prev != val) throw new Error("Inconsistent bindings for " + name);
5765
      }
5766
      delete keymap[keyname];
5767
    }
5768
    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...
5769
    return keymap;
5770
  };
5771
5772
  var lookupKey = CodeMirror.lookupKey = function(key, map, handle, context) {
5773
    map = getKeyMap(map);
5774
    var found = map.call ? map.call(key, context) : map[key];
5775
    if (found === false) return "nothing";
5776
    if (found === "...") return "multi";
5777
    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...
5778
5779
    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...
5780
      if (Object.prototype.toString.call(map.fallthrough) != "[object Array]")
5781
        return lookupKey(key, map.fallthrough, handle, context);
5782
      for (var i = 0; i < map.fallthrough.length; i++) {
5783
        var result = lookupKey(key, map.fallthrough[i], handle, context);
5784
        if (result) return result;
5785
      }
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...
5786
    }
5787
  };
5788
5789
  // Modifier key presses don't count as 'real' key presses for the
5790
  // purpose of keymap fallthrough.
5791
  var isModifierKey = CodeMirror.isModifierKey = function(value) {
5792
    var name = typeof value == "string" ? value : keyNames[value.keyCode];
5793
    return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
5794
  };
5795
5796
  // Look up the name of a key as indicated by an event object.
5797
  var keyName = CodeMirror.keyName = function(event, noShift) {
5798
    if (presto && event.keyCode == 34 && event["char"]) return false;
5799
    var base = keyNames[event.keyCode], name = base;
5800
    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...
5801
    if (event.altKey && base != "Alt") name = "Alt-" + name;
5802
    if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") name = "Ctrl-" + name;
5803
    if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") name = "Cmd-" + name;
5804
    if (!noShift && event.shiftKey && base != "Shift") name = "Shift-" + name;
5805
    return name;
5806
  };
5807
5808
  function getKeyMap(val) {
5809
    return typeof val == "string" ? keyMap[val] : val;
5810
  }
5811
5812
  // FROMTEXTAREA
5813
5814
  CodeMirror.fromTextArea = function(textarea, options) {
5815
    options = options ? copyObj(options) : {};
5816
    options.value = textarea.value;
5817
    if (!options.tabindex && textarea.tabIndex)
5818
      options.tabindex = textarea.tabIndex;
5819
    if (!options.placeholder && textarea.placeholder)
5820
      options.placeholder = textarea.placeholder;
5821
    // Set autofocus to true if this textarea is focused, or if it has
5822
    // autofocus and no other element is focused.
5823
    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...
5824
      var hasFocus = activeElt();
5825
      options.autofocus = hasFocus == textarea ||
5826
        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...
5827
    }
5828
5829
    function save() {textarea.value = cm.getValue();}
5830
    if (textarea.form) {
5831
      on(textarea.form, "submit", save);
5832
      // Deplorable hack to make the submit method do the right thing.
5833
      if (!options.leaveSubmitMethodAlone) {
5834
        var form = textarea.form, realSubmit = form.submit;
5835
        try {
5836
          var wrappedSubmit = form.submit = function() {
5837
            save();
5838
            form.submit = realSubmit;
5839
            form.submit();
5840
            form.submit = wrappedSubmit;
5841
          };
5842
        } 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...
5843
      }
5844
    }
5845
5846
    options.finishInit = function(cm) {
5847
      cm.save = save;
5848
      cm.getTextArea = function() { return textarea; };
5849
      cm.toTextArea = function() {
5850
        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...
5851
        save();
5852
        textarea.parentNode.removeChild(cm.getWrapperElement());
5853
        textarea.style.display = "";
5854
        if (textarea.form) {
5855
          off(textarea.form, "submit", save);
5856
          if (typeof textarea.form.submit == "function")
5857
            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...
5858
        }
5859
      };
5860
    };
5861
5862
    textarea.style.display = "none";
5863
    var cm = CodeMirror(function(node) {
5864
      textarea.parentNode.insertBefore(node, textarea.nextSibling);
5865
    }, options);
5866
    return cm;
5867
  };
5868
5869
  // STRING STREAM
5870
5871
  // Fed to the mode parsers, provides helper functions to make
5872
  // parsers more succinct.
5873
5874
  var StringStream = CodeMirror.StringStream = function(string, tabSize) {
5875
    this.pos = this.start = 0;
5876
    this.string = string;
5877
    this.tabSize = tabSize || 8;
5878
    this.lastColumnPos = this.lastColumnValue = 0;
5879
    this.lineStart = 0;
5880
  };
5881
5882
  StringStream.prototype = {
5883
    eol: function() {return this.pos >= this.string.length;},
5884
    sol: function() {return this.pos == this.lineStart;},
5885
    peek: function() {return this.string.charAt(this.pos) || undefined;},
5886
    next: function() {
5887
      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...
5888
        return this.string.charAt(this.pos++);
5889
    },
5890
    eat: function(match) {
5891
      var ch = this.string.charAt(this.pos);
5892
      if (typeof match == "string") var ok = ch == match;
5893
      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 5892. 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...
5894
      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...
5895
    },
5896
    eatWhile: function(match) {
5897
      var start = this.pos;
5898
      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...
5899
      return this.pos > start;
5900
    },
5901
    eatSpace: function() {
5902
      var start = this.pos;
5903
      while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
5904
      return this.pos > start;
5905
    },
5906
    skipToEnd: function() {this.pos = this.string.length;},
5907
    skipTo: function(ch) {
5908
      var found = this.string.indexOf(ch, this.pos);
5909
      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...
5910
    },
5911
    backUp: function(n) {this.pos -= n;},
5912
    column: function() {
5913
      if (this.lastColumnPos < this.start) {
5914
        this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
5915
        this.lastColumnPos = this.start;
5916
      }
5917
      return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0);
5918
    },
5919
    indentation: function() {
5920
      return countColumn(this.string, null, this.tabSize) -
5921
        (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0);
5922
    },
5923 View Code Duplication
    match: function(pattern, consume, caseInsensitive) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
5924
      if (typeof pattern == "string") {
5925
        var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
5926
        var substr = this.string.substr(this.pos, pattern.length);
5927
        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...
5928
          if (consume !== false) this.pos += pattern.length;
5929
          return true;
5930
        }
5931
      } else {
5932
        var match = this.string.slice(this.pos).match(pattern);
5933
        if (match && match.index > 0) return null;
5934
        if (match && consume !== false) this.pos += match[0].length;
5935
        return match;
5936
      }
5937
    },
5938
    current: function(){return this.string.slice(this.start, this.pos);},
5939
    hideFirstChars: function(n, inner) {
5940
      this.lineStart += n;
5941
      try { return inner(); }
5942
      finally { this.lineStart -= n; }
5943
    }
5944
  };
5945
5946
  // TEXTMARKERS
5947
5948
  // Created with markText and setBookmark methods. A TextMarker is a
5949
  // handle that can be used to clear or find a marked position in the
5950
  // document. Line objects hold arrays (markedSpans) containing
5951
  // {from, to, marker} object pointing to such marker objects, and
5952
  // indicating that such a marker is present on that line. Multiple
5953
  // lines may point to the same marker when it spans across lines.
5954
  // The spans will have null for their from/to properties when the
5955
  // marker continues beyond the start/end of the line. Markers have
5956
  // links back to the lines they currently touch.
5957
5958
  var nextMarkerId = 0;
5959
5960
  var TextMarker = CodeMirror.TextMarker = function(doc, type) {
5961
    this.lines = [];
5962
    this.type = type;
5963
    this.doc = doc;
5964
    this.id = ++nextMarkerId;
5965
  };
5966
  eventMixin(TextMarker);
5967
5968
  // Clear the marker.
5969
  TextMarker.prototype.clear = function() {
5970
    if (this.explicitlyCleared) return;
5971
    var cm = this.doc.cm, withOp = cm && !cm.curOp;
5972
    if (withOp) startOperation(cm);
5973
    if (hasHandler(this, "clear")) {
5974
      var found = this.find();
5975
      if (found) signalLater(this, "clear", found.from, found.to);
5976
    }
5977
    var min = null, max = null;
5978
    for (var i = 0; i < this.lines.length; ++i) {
5979
      var line = this.lines[i];
5980
      var span = getMarkedSpanFor(line.markedSpans, this);
5981
      if (cm && !this.collapsed) regLineChange(cm, lineNo(line), "text");
5982
      else if (cm) {
5983
        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...
5984
        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...
5985
      }
5986
      line.markedSpans = removeMarkedSpan(line.markedSpans, span);
5987
      if (span.from == null && this.collapsed && !lineIsHidden(this.doc, line) && cm)
5988
        updateLineHeight(line, textHeight(cm.display));
5989
    }
5990
    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 5978. 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...
5991
      var visual = visualLine(this.lines[i]), len = lineLength(visual);
5992
      if (len > cm.display.maxLineLength) {
5993
        cm.display.maxLine = visual;
5994
        cm.display.maxLineLength = len;
5995
        cm.display.maxLineChanged = true;
5996
      }
5997
    }
5998
5999
    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...
6000
    this.lines.length = 0;
6001
    this.explicitlyCleared = true;
6002
    if (this.atomic && this.doc.cantEdit) {
6003
      this.doc.cantEdit = false;
6004
      if (cm) reCheckSelection(cm.doc);
6005
    }
6006
    if (cm) signalLater(cm, "markerCleared", cm, this);
6007
    if (withOp) endOperation(cm);
6008
    if (this.parent) this.parent.clear();
6009
  };
6010
6011
  // Find the position of the marker in the document. Returns a {from,
6012
  // to} object by default. Side can be passed to get a specific side
6013
  // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the
6014
  // Pos objects returned contain a line object, rather than a line
6015
  // number (used to prevent looking up the same line twice).
6016
  TextMarker.prototype.find = function(side, lineObj) {
6017
    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...
6018
    var from, to;
6019
    for (var i = 0; i < this.lines.length; ++i) {
6020
      var line = this.lines[i];
6021
      var span = getMarkedSpanFor(line.markedSpans, this);
6022
      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...
6023
        from = Pos(lineObj ? line : lineNo(line), span.from);
6024
        if (side == -1) return from;
6025
      }
6026
      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...
6027
        to = Pos(lineObj ? line : lineNo(line), span.to);
6028
        if (side == 1) return to;
6029
      }
6030
    }
6031
    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...
6032
  };
6033
6034
  // Signals that the marker's widget changed, and surrounding layout
6035
  // should be recomputed.
6036
  TextMarker.prototype.changed = function() {
6037
    var pos = this.find(-1, true), widget = this, cm = this.doc.cm;
6038
    if (!pos || !cm) return;
6039
    runInOp(cm, function() {
6040
      var line = pos.line, lineN = lineNo(pos.line);
6041
      var view = findViewForLine(cm, lineN);
6042
      if (view) {
6043
        clearLineMeasurementCacheFor(view);
6044
        cm.curOp.selectionChanged = cm.curOp.forceUpdate = true;
6045
      }
6046
      cm.curOp.updateMaxLine = true;
6047
      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...
6048
        var oldHeight = widget.height;
6049
        widget.height = null;
6050
        var dHeight = widgetHeight(widget) - oldHeight;
6051
        if (dHeight)
6052
          updateLineHeight(line, line.height + dHeight);
6053
      }
6054
    });
6055
  };
6056
6057
  TextMarker.prototype.attachLine = function(line) {
6058
    if (!this.lines.length && this.doc.cm) {
6059
      var op = this.doc.cm.curOp;
6060
      if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
6061
        (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this);
6062
    }
6063
    this.lines.push(line);
6064
  };
6065
  TextMarker.prototype.detachLine = function(line) {
6066
    this.lines.splice(indexOf(this.lines, line), 1);
6067
    if (!this.lines.length && this.doc.cm) {
6068
      var op = this.doc.cm.curOp;
6069
      (op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this);
6070
    }
6071
  };
6072
6073
  // Collapsed markers have unique ids, in order to be able to order
6074
  // them, which is needed for uniquely determining an outer marker
6075
  // when they overlap (they may nest, but not partially overlap).
6076
  var nextMarkerId = 0;
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable nextMarkerId already seems to be declared on line 5958. 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...
6077
6078
  // Create a marker, wire it up to the right lines, and
6079
  function markText(doc, from, to, options, type) {
6080
    // Shared markers (across linked documents) are handled separately
6081
    // (markTextShared will call out to this again, once per
6082
    // document).
6083
    if (options && options.shared) return markTextShared(doc, from, to, options, type);
6084
    // Ensure we are in an operation.
6085
    if (doc.cm && !doc.cm.curOp) return operation(doc.cm, markText)(doc, from, to, options, type);
6086
6087
    var marker = new TextMarker(doc, type), diff = cmp(from, to);
6088
    if (options) copyObj(options, marker, false);
6089
    // Don't connect empty markers unless clearWhenEmpty is false
6090
    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...
6091
      return marker;
6092
    if (marker.replacedWith) {
6093
      // Showing up as a widget implies collapsed (widget replaces text)
6094
      marker.collapsed = true;
6095
      marker.widgetNode = elt("span", [marker.replacedWith], "CodeMirror-widget");
6096
      if (!options.handleMouseEvents) marker.widgetNode.setAttribute("cm-ignore-events", "true");
6097
      if (options.insertLeft) marker.widgetNode.insertLeft = true;
6098
    }
6099
    if (marker.collapsed) {
6100
      if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||
6101
          from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker))
6102
        throw new Error("Inserting collapsed marker partially overlapping an existing one");
6103
      sawCollapsedSpans = true;
6104
    }
6105
6106
    if (marker.addToHistory)
6107
      addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN);
6108
6109
    var curLine = from.line, cm = doc.cm, updateMaxLine;
6110
    doc.iter(curLine, to.line + 1, function(line) {
6111
      if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine)
6112
        updateMaxLine = true;
6113
      if (marker.collapsed && curLine != from.line) updateLineHeight(line, 0);
6114
      addMarkedSpan(line, new MarkedSpan(marker,
6115
                                         curLine == from.line ? from.ch : null,
6116
                                         curLine == to.line ? to.ch : null));
6117
      ++curLine;
6118
    });
6119
    // lineIsHidden depends on the presence of the spans, so needs a second pass
6120
    if (marker.collapsed) doc.iter(from.line, to.line + 1, function(line) {
6121
      if (lineIsHidden(doc, line)) updateLineHeight(line, 0);
6122
    });
6123
6124
    if (marker.clearOnEnter) on(marker, "beforeCursorEnter", function() { marker.clear(); });
6125
6126
    if (marker.readOnly) {
6127
      sawReadOnlySpans = true;
6128
      if (doc.history.done.length || doc.history.undone.length)
6129
        doc.clearHistory();
6130
    }
6131
    if (marker.collapsed) {
6132
      marker.id = ++nextMarkerId;
6133
      marker.atomic = true;
6134
    }
6135
    if (cm) {
6136
      // Sync editor state
6137
      if (updateMaxLine) cm.curOp.updateMaxLine = true;
6138
      if (marker.collapsed)
6139
        regChange(cm, from.line, to.line + 1);
6140
      else if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.css)
6141
        for (var i = from.line; i <= to.line; i++) regLineChange(cm, i, "text");
6142
      if (marker.atomic) reCheckSelection(cm.doc);
6143
      signalLater(cm, "markerAdded", cm, marker);
6144
    }
6145
    return marker;
6146
  }
6147
6148
  // SHARED TEXTMARKERS
6149
6150
  // A shared marker spans multiple linked documents. It is
6151
  // implemented as a meta-marker-object controlling multiple normal
6152
  // markers.
6153
  var SharedTextMarker = CodeMirror.SharedTextMarker = function(markers, primary) {
6154
    this.markers = markers;
6155
    this.primary = primary;
6156
    for (var i = 0; i < markers.length; ++i)
6157
      markers[i].parent = this;
6158
  };
6159
  eventMixin(SharedTextMarker);
6160
6161
  SharedTextMarker.prototype.clear = function() {
6162
    if (this.explicitlyCleared) return;
6163
    this.explicitlyCleared = true;
6164
    for (var i = 0; i < this.markers.length; ++i)
6165
      this.markers[i].clear();
6166
    signalLater(this, "clear");
6167
  };
6168
  SharedTextMarker.prototype.find = function(side, lineObj) {
6169
    return this.primary.find(side, lineObj);
6170
  };
6171
6172
  function markTextShared(doc, from, to, options, type) {
6173
    options = copyObj(options);
6174
    options.shared = false;
6175
    var markers = [markText(doc, from, to, options, type)], primary = markers[0];
6176
    var widget = options.widgetNode;
6177
    linkedDocs(doc, function(doc) {
6178
      if (widget) options.widgetNode = widget.cloneNode(true);
6179
      markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));
6180
      for (var i = 0; i < doc.linked.length; ++i)
6181
        if (doc.linked[i].isParent) return;
6182
      primary = lst(markers);
6183
    });
6184
    return new SharedTextMarker(markers, primary);
6185
  }
6186
6187
  function findSharedMarkers(doc) {
6188
    return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())),
6189
                         function(m) { return m.parent; });
6190
  }
6191
6192
  function copySharedMarkers(doc, markers) {
6193
    for (var i = 0; i < markers.length; i++) {
6194
      var marker = markers[i], pos = marker.find();
6195
      var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to);
6196
      if (cmp(mFrom, mTo)) {
6197
        var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type);
6198
        marker.markers.push(subMark);
6199
        subMark.parent = marker;
6200
      }
6201
    }
6202
  }
6203
6204
  function detachSharedMarkers(markers) {
6205
    for (var i = 0; i < markers.length; i++) {
6206
      var marker = markers[i], linked = [marker.primary.doc];;
6207
      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 6206. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
6208
      for (var j = 0; j < marker.markers.length; j++) {
6209
        var subMarker = marker.markers[j];
6210
        if (indexOf(linked, subMarker.doc) == -1) {
6211
          subMarker.parent = null;
6212
          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...
6213
        }
6214
      }
6215
    }
6216
  }
6217
6218
  // TEXTMARKER SPANS
6219
6220
  function MarkedSpan(marker, from, to) {
6221
    this.marker = marker;
6222
    this.from = from; this.to = to;
6223
  }
6224
6225
  // Search an array of spans for a span matching the given marker.
6226
  function getMarkedSpanFor(spans, marker) {
6227
    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...
6228
      var span = spans[i];
6229
      if (span.marker == marker) return span;
6230
    }
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...
6231
  }
6232
  // Remove a span from an array, returning undefined if no spans are
6233
  // left (we don't store arrays for lines without spans).
6234
  function removeMarkedSpan(spans, span) {
6235
    for (var r, i = 0; i < spans.length; ++i)
6236
      if (spans[i] != span) (r || (r = [])).push(spans[i]);
6237
    return r;
0 ignored issues
show
Bug introduced by
The variable r seems to not be initialized for all possible execution paths.
Loading history...
6238
  }
6239
  // Add a span to a line.
6240
  function addMarkedSpan(line, span) {
6241
    line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
6242
    span.marker.attachLine(line);
6243
  }
6244
6245
  // Used for the algorithm that adjusts markers for a change in the
6246
  // document. These functions cut an array of spans at a given
6247
  // character position, returning an array of remaining chunks (or
6248
  // undefined if nothing remains).
6249 View Code Duplication
  function markedSpansBefore(old, startCh, isInsert) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
6250
    if (old) for (var i = 0, nw; i < old.length; ++i) {
6251
      var span = old[i], marker = span.marker;
6252
      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...
6253
      if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) {
6254
        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...
6255
        (nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to));
6256
      }
6257
    }
6258
    return nw;
0 ignored issues
show
Bug introduced by
The variable nw seems to not be initialized for all possible execution paths.
Loading history...
6259
  }
6260 View Code Duplication
  function markedSpansAfter(old, endCh, isInsert) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
6261
    if (old) for (var i = 0, nw; i < old.length; ++i) {
6262
      var span = old[i], marker = span.marker;
6263
      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...
6264
      if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) {
6265
        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...
6266
        (nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh,
6267
                                              span.to == null ? null : span.to - endCh));
6268
      }
6269
    }
6270
    return nw;
0 ignored issues
show
Bug introduced by
The variable nw seems to not be initialized for all possible execution paths.
Loading history...
6271
  }
6272
6273
  // Given a change object, compute the new set of marker spans that
6274
  // cover the line in which the change took place. Removes spans
6275
  // entirely within the change, reconnects spans belonging to the
6276
  // same marker that appear on both sides of the change, and cuts off
6277
  // spans partially within the change. Returns an array of span
6278
  // arrays with one element for each line in (after) the change.
6279
  function stretchSpansOverChange(doc, change) {
6280
    if (change.full) return null;
6281
    var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans;
6282
    var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans;
6283
    if (!oldFirst && !oldLast) return null;
6284
6285
    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...
6286
    // Get the spans that 'stick out' on both sides
6287
    var first = markedSpansBefore(oldFirst, startCh, isInsert);
6288
    var last = markedSpansAfter(oldLast, endCh, isInsert);
6289
6290
    // Next, merge those two ends
6291
    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...
6292
    if (first) {
6293
      // Fix up .to properties of first
6294
      for (var i = 0; i < first.length; ++i) {
6295
        var span = first[i];
6296
        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...
6297
          var found = getMarkedSpanFor(last, span.marker);
6298
          if (!found) span.to = startCh;
6299
          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...
6300
        }
6301
      }
6302
    }
6303
    if (last) {
6304
      // Fix up .from in last (or move them into first in case of sameLine)
6305
      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 6294. 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...
6306
        var span = last[i];
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable span already seems to be declared on line 6295. 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...
6307
        if (span.to != null) span.to += offset;
6308
        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...
6309
          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 6297. 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...
6310
          if (!found) {
6311
            span.from = offset;
6312
            if (sameLine) (first || (first = [])).push(span);
6313
          }
6314
        } else {
6315
          span.from += offset;
6316
          if (sameLine) (first || (first = [])).push(span);
6317
        }
6318
      }
6319
    }
6320
    // Make sure we didn't create any zero-length spans
6321
    if (first) first = clearEmptySpans(first);
6322
    if (last && last != first) last = clearEmptySpans(last);
6323
6324
    var newMarkers = [first];
6325
    if (!sameLine) {
6326
      // Fill gap with whole-line-spans
6327
      var gap = change.text.length - 2, gapMarkers;
6328
      if (gap > 0 && first)
6329
        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 6294. 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...
6330
          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...
6331
            (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i].marker, null, null));
6332
      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 6294. 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...
6333
        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...
6334
      newMarkers.push(last);
6335
    }
6336
    return newMarkers;
6337
  }
6338
6339
  // Remove spans that are empty and don't have a clearWhenEmpty
6340
  // option of false.
6341
  function clearEmptySpans(spans) {
6342
    for (var i = 0; i < spans.length; ++i) {
6343
      var span = spans[i];
6344
      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...
6345
        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...
6346
    }
6347
    if (!spans.length) return null;
6348
    return spans;
6349
  }
6350
6351
  // Used for un/re-doing changes from the history. Combines the
6352
  // result of computing the existing spans with the set of spans that
6353
  // existed in the history (so that deleting around a span and then
6354
  // undoing brings back the span).
6355
  function mergeOldSpans(doc, change) {
6356
    var old = getOldSpans(doc, change);
6357
    var stretched = stretchSpansOverChange(doc, change);
6358
    if (!old) return stretched;
6359
    if (!stretched) return old;
6360
6361
    for (var i = 0; i < old.length; ++i) {
6362
      var oldCur = old[i], stretchCur = stretched[i];
6363
      if (oldCur && stretchCur) {
6364
        spans: for (var j = 0; j < stretchCur.length; ++j) {
6365
          var span = stretchCur[j];
6366
          for (var k = 0; k < oldCur.length; ++k)
6367
            if (oldCur[k].marker == span.marker) continue spans;
6368
          oldCur.push(span);
6369
        }
6370
      } else if (stretchCur) {
6371
        old[i] = stretchCur;
6372
      }
6373
    }
6374
    return old;
6375
  }
6376
6377
  // Used to 'clip' out readOnly ranges when making a change.
6378
  function removeReadOnlyRanges(doc, from, to) {
6379
    var markers = null;
6380
    doc.iter(from.line, to.line + 1, function(line) {
6381
      if (line.markedSpans) for (var i = 0; i < line.markedSpans.length; ++i) {
6382
        var mark = line.markedSpans[i].marker;
6383
        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 6384. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
6384
          (markers || (markers = [])).push(mark);
6385
      }
6386
    });
6387
    if (!markers) return null;
6388
    var parts = [{from: from, to: to}];
6389
    for (var i = 0; i < markers.length; ++i) {
6390
      var mk = markers[i], m = mk.find(0);
6391
      for (var j = 0; j < parts.length; ++j) {
6392
        var p = parts[j];
6393
        if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) continue;
6394
        var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to);
6395
        if (dfrom < 0 || !mk.inclusiveLeft && !dfrom)
6396
          newParts.push({from: p.from, to: m.from});
6397
        if (dto > 0 || !mk.inclusiveRight && !dto)
6398
          newParts.push({from: m.to, to: p.to});
6399
        parts.splice.apply(parts, newParts);
6400
        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...
6401
      }
6402
    }
6403
    return parts;
6404
  }
6405
6406
  // Connect or disconnect spans from a line.
6407
  function detachMarkedSpans(line) {
6408
    var spans = line.markedSpans;
6409
    if (!spans) return;
6410
    for (var i = 0; i < spans.length; ++i)
6411
      spans[i].marker.detachLine(line);
6412
    line.markedSpans = null;
6413
  }
6414
  function attachMarkedSpans(line, spans) {
6415
    if (!spans) return;
6416
    for (var i = 0; i < spans.length; ++i)
6417
      spans[i].marker.attachLine(line);
6418
    line.markedSpans = spans;
6419
  }
6420
6421
  // Helpers used when computing which overlapping collapsed span
6422
  // counts as the larger one.
6423
  function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0; }
6424
  function extraRight(marker) { return marker.inclusiveRight ? 1 : 0; }
6425
6426
  // Returns a number indicating which of two overlapping collapsed
6427
  // spans is larger (and thus includes the other). Falls back to
6428
  // comparing ids when the spans cover exactly the same range.
6429
  function compareCollapsedMarkers(a, b) {
6430
    var lenDiff = a.lines.length - b.lines.length;
6431
    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...
6432
    var aPos = a.find(), bPos = b.find();
6433
    var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b);
6434
    if (fromCmp) return -fromCmp;
6435
    var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b);
6436
    if (toCmp) return toCmp;
6437
    return b.id - a.id;
6438
  }
6439
6440
  // Find out whether a line ends or starts in a collapsed span. If
6441
  // so, return the marker for that span.
6442
  function collapsedSpanAtSide(line, start) {
6443
    var sps = sawCollapsedSpans && line.markedSpans, found;
6444
    if (sps) for (var sp, i = 0; i < sps.length; ++i) {
6445
      sp = sps[i];
6446
      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...
6447
          (!found || compareCollapsedMarkers(found, sp.marker) < 0))
6448
        found = sp.marker;
6449
    }
6450
    return found;
0 ignored issues
show
Bug introduced by
The variable found seems to not be initialized for all possible execution paths.
Loading history...
6451
  }
6452
  function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true); }
6453
  function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false); }
6454
6455
  // Test whether there exists a collapsed span that partially
6456
  // overlaps (covers the start or end, but not both) of a new span.
6457
  // Such overlap is not allowed.
6458
  function conflictingCollapsedRange(doc, lineNo, from, to, marker) {
6459
    var line = getLine(doc, lineNo);
6460
    var sps = sawCollapsedSpans && line.markedSpans;
6461
    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...
6462
      var sp = sps[i];
6463
      if (!sp.marker.collapsed) continue;
6464
      var found = sp.marker.find(0);
6465
      var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker);
6466
      var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker);
6467
      if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) continue;
6468
      if (fromCmp <= 0 && (cmp(found.to, from) > 0 || (sp.marker.inclusiveRight && marker.inclusiveLeft)) ||
6469
          fromCmp >= 0 && (cmp(found.from, to) < 0 || (sp.marker.inclusiveLeft && marker.inclusiveRight)))
6470
        return true;
6471
    }
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...
6472
  }
6473
6474
  // A visual line is a line as drawn on the screen. Folding, for
6475
  // example, can cause multiple logical lines to appear on the same
6476
  // visual line. This finds the start of the visual line that the
6477
  // given line is part of (usually that is the line itself).
6478
  function visualLine(line) {
6479
    var merged;
6480
    while (merged = collapsedSpanAtStart(line))
6481
      line = merged.find(-1, true).line;
6482
    return line;
6483
  }
6484
6485
  // Returns an array of logical lines that continue the visual line
6486
  // started by the argument, or undefined if there are no such lines.
6487
  function visualLineContinued(line) {
6488
    var merged, lines;
6489
    while (merged = collapsedSpanAtEnd(line)) {
6490
      line = merged.find(1, true).line;
6491
      (lines || (lines = [])).push(line);
6492
    }
6493
    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 6489 is not entered. Are you sure this can never be the case?
Loading history...
6494
  }
6495
6496
  // Get the line number of the start of the visual line that the
6497
  // given line number is part of.
6498
  function visualLineNo(doc, lineN) {
6499
    var line = getLine(doc, lineN), vis = visualLine(line);
6500
    if (line == vis) return lineN;
6501
    return lineNo(vis);
6502
  }
6503
  // Get the line number of the start of the next visual line after
6504
  // the given line.
6505
  function visualLineEndNo(doc, lineN) {
6506
    if (lineN > doc.lastLine()) return lineN;
6507
    var line = getLine(doc, lineN), merged;
6508
    if (!lineIsHidden(doc, line)) return lineN;
6509
    while (merged = collapsedSpanAtEnd(line))
6510
      line = merged.find(1, true).line;
6511
    return lineNo(line) + 1;
6512
  }
6513
6514
  // Compute whether a line is hidden. Lines count as hidden when they
6515
  // are part of a visual line that starts with another line, or when
6516
  // they are entirely covered by collapsed, non-widget span.
6517
  function lineIsHidden(doc, line) {
6518
    var sps = sawCollapsedSpans && line.markedSpans;
6519
    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...
6520
      sp = sps[i];
6521
      if (!sp.marker.collapsed) continue;
6522
      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...
6523
      if (sp.marker.widgetNode) continue;
6524
      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...
6525
        return true;
6526
    }
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...
6527
  }
6528
  function lineIsHiddenInner(doc, line, span) {
6529
    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...
6530
      var end = span.marker.find(1, true);
6531
      return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker));
6532
    }
6533
    if (span.marker.inclusiveRight && span.to == line.text.length)
6534
      return true;
6535
    for (var sp, i = 0; i < line.markedSpans.length; ++i) {
6536
      sp = line.markedSpans[i];
6537
      if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to &&
6538
          (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...
6539
          (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
6540
          lineIsHiddenInner(doc, line, sp)) return true;
6541
    }
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...
6542
  }
6543
6544
  // LINE WIDGETS
6545
6546
  // Line widgets are block elements displayed above or below a line.
6547
6548
  var LineWidget = CodeMirror.LineWidget = function(doc, node, options) {
6549
    if (options) for (var opt in options) if (options.hasOwnProperty(opt))
6550
      this[opt] = options[opt];
6551
    this.doc = doc;
6552
    this.node = node;
6553
  };
6554
  eventMixin(LineWidget);
6555
6556
  function adjustScrollWhenAboveVisible(cm, line, diff) {
6557
    if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))
6558
      addToScrollPos(cm, null, diff);
6559
  }
6560
6561
  LineWidget.prototype.clear = function() {
6562
    var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line);
6563
    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...
6564
    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...
6565
    if (!ws.length) line.widgets = null;
6566
    var height = widgetHeight(this);
6567
    updateLineHeight(line, Math.max(0, line.height - height));
6568
    if (cm) runInOp(cm, function() {
6569
      adjustScrollWhenAboveVisible(cm, line, -height);
6570
      regLineChange(cm, no, "widget");
6571
    });
6572
  };
6573
  LineWidget.prototype.changed = function() {
6574
    var oldH = this.height, cm = this.doc.cm, line = this.line;
6575
    this.height = null;
6576
    var diff = widgetHeight(this) - oldH;
6577
    if (!diff) return;
6578
    updateLineHeight(line, line.height + diff);
6579
    if (cm) runInOp(cm, function() {
6580
      cm.curOp.forceUpdate = true;
6581
      adjustScrollWhenAboveVisible(cm, line, diff);
6582
    });
6583
  };
6584
6585
  function widgetHeight(widget) {
6586
    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...
6587
    var cm = widget.doc.cm;
6588
    if (!cm) return 0;
6589
    if (!contains(document.body, widget.node)) {
6590
      var parentStyle = "position: relative;";
6591
      if (widget.coverGutter)
6592
        parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;";
6593
      if (widget.noHScroll)
6594
        parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;";
6595
      removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle));
6596
    }
6597
    return widget.height = widget.node.offsetHeight;
6598
  }
6599
6600
  function addLineWidget(doc, handle, node, options) {
6601
    var widget = new LineWidget(doc, node, options);
6602
    var cm = doc.cm;
6603
    if (cm && widget.noHScroll) cm.display.alignWidgets = true;
6604
    changeLine(doc, handle, "widget", function(line) {
6605
      var widgets = line.widgets || (line.widgets = []);
6606
      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...
6607
      else widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget);
6608
      widget.line = line;
6609
      if (cm && !lineIsHidden(doc, line)) {
6610
        var aboveVisible = heightAtLine(line) < doc.scrollTop;
6611
        updateLineHeight(line, line.height + widgetHeight(widget));
6612
        if (aboveVisible) addToScrollPos(cm, null, widget.height);
6613
        cm.curOp.forceUpdate = true;
6614
      }
6615
      return true;
6616
    });
6617
    return widget;
6618
  }
6619
6620
  // LINE DATA STRUCTURE
6621
6622
  // Line objects. These hold state related to a line, including
6623
  // highlighting info (the styles array).
6624
  var Line = CodeMirror.Line = function(text, markedSpans, estimateHeight) {
6625
    this.text = text;
6626
    attachMarkedSpans(this, markedSpans);
6627
    this.height = estimateHeight ? estimateHeight(this) : 1;
6628
  };
6629
  eventMixin(Line);
6630
  Line.prototype.lineNo = function() { return lineNo(this); };
6631
6632
  // Change the content (text, markers) of a line. Automatically
6633
  // invalidates cached information and tries to re-estimate the
6634
  // line's height.
6635
  function updateLine(line, text, markedSpans, estimateHeight) {
6636
    line.text = text;
6637
    if (line.stateAfter) line.stateAfter = null;
6638
    if (line.styles) line.styles = null;
6639
    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...
6640
    detachMarkedSpans(line);
6641
    attachMarkedSpans(line, markedSpans);
6642
    var estHeight = estimateHeight ? estimateHeight(line) : 1;
6643
    if (estHeight != line.height) updateLineHeight(line, estHeight);
6644
  }
6645
6646
  // Detach a line from the document tree and its markers.
6647
  function cleanUpLine(line) {
6648
    line.parent = null;
6649
    detachMarkedSpans(line);
6650
  }
6651
6652
  function extractLineClasses(type, output) {
6653
    if (type) for (;;) {
6654
      var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/);
6655
      if (!lineClass) break;
6656
      type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length);
6657
      var prop = lineClass[1] ? "bgClass" : "textClass";
6658
      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...
6659
        output[prop] = lineClass[2];
6660
      else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop]))
6661
        output[prop] += " " + lineClass[2];
6662
    }
6663
    return type;
6664
  }
6665
6666
  function callBlankLine(mode, state) {
6667
    if (mode.blankLine) return mode.blankLine(state);
6668
    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...
6669
    var inner = CodeMirror.innerMode(mode, state);
6670
    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...
6671
  }
6672
6673
  function readToken(mode, stream, state, inner) {
6674
    for (var i = 0; i < 10; i++) {
6675
      if (inner) inner[0] = CodeMirror.innerMode(mode, state).mode;
6676
      var style = mode.token(stream, state);
6677
      if (stream.pos > stream.start) return style;
6678
    }
6679
    throw new Error("Mode " + mode.name + " failed to advance stream.");
6680
  }
6681
6682
  // Utility for getTokenAt and getLineTokens
6683
  function takeToken(cm, pos, precise, asArray) {
6684
    function getObj(copy) {
6685
      return {start: stream.start, end: stream.pos,
6686
              string: stream.current(),
6687
              type: style || null,
6688
              state: copy ? copyState(doc.mode, state) : state};
6689
    }
6690
6691
    var doc = cm.doc, mode = doc.mode, style;
6692
    pos = clipPos(doc, pos);
6693
    var line = getLine(doc, pos.line), state = getStateBefore(cm, pos.line, precise);
6694
    var stream = new StringStream(line.text, cm.options.tabSize), tokens;
6695
    if (asArray) tokens = [];
6696
    while ((asArray || stream.pos < pos.ch) && !stream.eol()) {
6697
      stream.start = stream.pos;
6698
      style = readToken(mode, stream, state);
6699
      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 6695 is false. Are you sure this can never be the case?
Loading history...
6700
    }
6701
    return asArray ? tokens : getObj();
6702
  }
6703
6704
  // Run the given mode's parser over a line, calling f for each token.
6705
  function runMode(cm, text, mode, state, f, lineClasses, forceToEnd) {
6706
    var flattenSpans = mode.flattenSpans;
6707
    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...
6708
    var curStart = 0, curStyle = null;
6709
    var stream = new StringStream(text, cm.options.tabSize), style;
6710
    var inner = cm.options.addModeClass && [null];
6711
    if (text == "") extractLineClasses(callBlankLine(mode, state), lineClasses);
6712
    while (!stream.eol()) {
6713
      if (stream.pos > cm.options.maxHighlightLength) {
6714
        flattenSpans = false;
6715
        if (forceToEnd) processLine(cm, text, state, stream.pos);
6716
        stream.pos = text.length;
6717
        style = null;
6718
      } else {
6719
        style = extractLineClasses(readToken(mode, stream, state, inner), lineClasses);
6720
      }
6721
      if (inner) {
6722
        var mName = inner[0].name;
6723
        if (mName) style = "m-" + (style ? mName + " " + style : mName);
6724
      }
6725
      if (!flattenSpans || curStyle != style) {
6726
        while (curStart < stream.start) {
6727
          curStart = Math.min(stream.start, curStart + 50000);
6728
          f(curStart, curStyle);
6729
        }
6730
        curStyle = style;
6731
      }
6732
      stream.start = stream.pos;
6733
    }
6734
    while (curStart < stream.pos) {
6735
      // Webkit seems to refuse to render text nodes longer than 57444 characters
6736
      var pos = Math.min(stream.pos, curStart + 50000);
6737
      f(pos, curStyle);
6738
      curStart = pos;
6739
    }
6740
  }
6741
6742
  // Compute a style array (an array starting with a mode generation
6743
  // -- for invalidation -- followed by pairs of end positions and
6744
  // style strings), which is used to highlight the tokens on the
6745
  // line.
6746
  function highlightLine(cm, line, state, forceToEnd) {
6747
    // A styles array always starts with a number identifying the
6748
    // mode/overlays that it is based on (for easy invalidation).
6749
    var st = [cm.state.modeGen], lineClasses = {};
6750
    // Compute the base array of styles
6751
    runMode(cm, line.text, cm.doc.mode, state, function(end, style) {
6752
      st.push(end, style);
6753
    }, lineClasses, forceToEnd);
6754
6755
    // Run overlays, adjust style array.
6756
    for (var o = 0; o < cm.state.overlays.length; ++o) {
6757
      var overlay = cm.state.overlays[o], i = 1, at = 0;
6758
      runMode(cm, line.text, overlay.mode, true, function(end, style) {
6759
        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 6757. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
6760
        // Ensure there's a token end at the current position, and that i points at it
6761
        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 6766. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
6762
          var i_end = st[i];
6763
          if (i_end > end)
6764
            st.splice(i, 1, end, st[i+1], i_end);
6765
          i += 2;
6766
          at = Math.min(end, i_end);
6767
        }
6768
        if (!style) return;
6769
        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 6757. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
6770
          st.splice(start, i - start, end, "cm-overlay " + style);
6771
          i = start + 2;
6772
        } else {
6773
          for (; start < i; start += 2) {
6774
            var cur = st[start+1];
6775
            st[start+1] = (cur ? cur + " " : "") + "cm-overlay " + style;
6776
          }
6777
        }
6778
      }, lineClasses);
6779
    }
6780
6781
    return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null};
6782
  }
6783
6784
  function getLineStyles(cm, line, updateFrontier) {
6785
    if (!line.styles || line.styles[0] != cm.state.modeGen) {
6786
      var result = highlightLine(cm, line, line.stateAfter = getStateBefore(cm, lineNo(line)));
6787
      line.styles = result.styles;
6788
      if (result.classes) line.styleClasses = result.classes;
6789
      else if (line.styleClasses) line.styleClasses = null;
6790
      if (updateFrontier === cm.doc.frontier) cm.doc.frontier++;
6791
    }
6792
    return line.styles;
6793
  }
6794
6795
  // Lightweight form of highlight -- proceed over this line and
6796
  // update state, but don't save a style array. Used for lines that
6797
  // aren't currently visible.
6798
  function processLine(cm, text, state, startAt) {
6799
    var mode = cm.doc.mode;
6800
    var stream = new StringStream(text, cm.options.tabSize);
6801
    stream.start = stream.pos = startAt || 0;
6802
    if (text == "") callBlankLine(mode, state);
6803
    while (!stream.eol() && stream.pos <= cm.options.maxHighlightLength) {
6804
      readToken(mode, stream, state);
6805
      stream.start = stream.pos;
6806
    }
6807
  }
6808
6809
  // Convert a style as returned by a mode (either null, or a string
6810
  // containing one or more styles) to a CSS style. This is cached,
6811
  // and also looks for line-wide styles.
6812
  var styleToClassCache = {}, styleToClassCacheWithMode = {};
6813
  function interpretTokenStyle(style, options) {
6814
    if (!style || /^\s*$/.test(style)) return null;
6815
    var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache;
6816
    return cache[style] ||
6817
      (cache[style] = style.replace(/\S+/g, "cm-$&"));
6818
  }
6819
6820
  // Render the DOM representation of the text of a line. Also builds
6821
  // up a 'line map', which points at the DOM nodes that represent
6822
  // specific stretches of text, and is used by the measuring code.
6823
  // The returned object contains the DOM node, this map, and
6824
  // information about line-wide styles that were set by the mode.
6825
  function buildLineContent(cm, lineView) {
6826
    // The padding-right forces the element to have a 'border', which
6827
    // is needed on Webkit to be able to get line-level bounding
6828
    // rectangles for it (in measureChar).
6829
    var content = elt("span", null, null, webkit ? "padding-right: .1px" : null);
6830
    var builder = {pre: elt("pre", [content], "CodeMirror-line"), content: content,
6831
                   col: 0, pos: 0, cm: cm,
6832
                   splitSpaces: (ie || webkit) && cm.getOption("lineWrapping")};
6833
    lineView.measure = {};
6834
6835
    // Iterate over the logical lines that make up this visual line.
6836
    for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {
6837
      var line = i ? lineView.rest[i - 1] : lineView.line, order;
6838
      builder.pos = 0;
6839
      builder.addToken = buildToken;
6840
      // Optionally wire in some hacks into the token-rendering
6841
      // algorithm, to deal with browser quirks.
6842
      if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line)))
6843
        builder.addToken = buildTokenBadBidi(builder.addToken, order);
6844
      builder.map = [];
6845
      var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line);
6846
      insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate));
6847
      if (line.styleClasses) {
6848
        if (line.styleClasses.bgClass)
6849
          builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || "");
6850
        if (line.styleClasses.textClass)
6851
          builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || "");
6852
      }
6853
6854
      // Ensure at least a single node is present, for measuring.
6855
      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...
6856
        builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure)));
6857
6858
      // Store the map and a cache object for the current logical line
6859
      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...
6860
        lineView.measure.map = builder.map;
6861
        lineView.measure.cache = {};
6862
      } else {
6863
        (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map);
6864
        (lineView.measure.caches || (lineView.measure.caches = [])).push({});
6865
      }
6866
    }
6867
6868
    // See issue #2901
6869
    if (webkit && /\bcm-tab\b/.test(builder.content.lastChild.className))
6870
      builder.content.className = "cm-tab-wrap-hack";
6871
6872
    signal(cm, "renderLine", cm, lineView.line, builder.pre);
6873
    if (builder.pre.className)
6874
      builder.textClass = joinClasses(builder.pre.className, builder.textClass || "");
6875
6876
    return builder;
6877
  }
6878
6879
  function defaultSpecialCharPlaceholder(ch) {
6880
    var token = elt("span", "\u2022", "cm-invalidchar");
6881
    token.title = "\\u" + ch.charCodeAt(0).toString(16);
6882
    token.setAttribute("aria-label", token.title);
6883
    return token;
6884
  }
6885
6886
  // Build up the DOM representation for a single token, and add it to
6887
  // the line map. Takes care to render special characters separately.
6888
  function buildToken(builder, text, style, startStyle, endStyle, title, css) {
6889
    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...
6890
    var displayText = builder.splitSpaces ? text.replace(/ {3,}/g, splitSpaces) : text;
6891
    var special = builder.cm.state.specialChars, mustWrap = false;
6892
    if (!special.test(text)) {
6893
      builder.col += text.length;
6894
      var content = document.createTextNode(displayText);
6895
      builder.map.push(builder.pos, builder.pos + text.length, content);
6896
      if (ie && ie_version < 9) mustWrap = true;
6897
      builder.pos += text.length;
6898
    } else {
6899
      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 6894. 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...
6900
      while (true) {
6901
        special.lastIndex = pos;
6902
        var m = special.exec(text);
6903
        var skipped = m ? m.index - pos : text.length - pos;
6904
        if (skipped) {
6905
          var txt = document.createTextNode(displayText.slice(pos, pos + skipped));
6906
          if (ie && ie_version < 9) content.appendChild(elt("span", [txt]));
6907
          else content.appendChild(txt);
6908
          builder.map.push(builder.pos, builder.pos + skipped, txt);
6909
          builder.col += skipped;
6910
          builder.pos += skipped;
6911
        }
6912
        if (!m) break;
6913
        pos += skipped + 1;
6914
        if (m[0] == "\t") {
6915
          var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
6916
          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 6905. 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...
6917
          txt.setAttribute("role", "presentation");
6918
          txt.setAttribute("cm-text", "\t");
6919
          builder.col += tabWidth;
6920
        } else if (m[0] == "\r" || m[0] == "\n") {
6921
          var txt = content.appendChild(elt("span", m[0] == "\r" ? "␍" : "␤", "cm-invalidchar"));
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable txt already seems to be declared on line 6905. 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...
6922
          txt.setAttribute("cm-text", m[0]);
6923
          builder.col += 1;
6924
        } else {
6925
          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 6905. 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...
6926
          txt.setAttribute("cm-text", m[0]);
6927
          if (ie && ie_version < 9) content.appendChild(elt("span", [txt]));
6928
          else content.appendChild(txt);
6929
          builder.col += 1;
6930
        }
6931
        builder.map.push(builder.pos, builder.pos + 1, txt);
6932
        builder.pos++;
6933
      }
6934
    }
6935
    if (style || startStyle || endStyle || mustWrap || css) {
6936
      var fullStyle = style || "";
6937
      if (startStyle) fullStyle += startStyle;
6938
      if (endStyle) fullStyle += endStyle;
6939
      var token = elt("span", [content], fullStyle, css);
6940
      if (title) token.title = title;
6941
      return builder.content.appendChild(token);
6942
    }
6943
    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...
6944
  }
6945
6946
  function splitSpaces(old) {
6947
    var out = " ";
6948
    for (var i = 0; i < old.length - 2; ++i) out += i % 2 ? " " : "\u00a0";
6949
    out += " ";
6950
    return out;
6951
  }
6952
6953
  // Work around nonsense dimensions being reported for stretches of
6954
  // right-to-left text.
6955
  function buildTokenBadBidi(inner, order) {
6956
    return function(builder, text, style, startStyle, endStyle, title, css) {
6957
      style = style ? style + " cm-force-border" : "cm-force-border";
6958
      var start = builder.pos, end = start + text.length;
6959
      for (;;) {
6960
        // Find the part that overlaps with the start of this text
6961
        for (var i = 0; i < order.length; i++) {
6962
          var part = order[i];
6963
          if (part.to > start && part.from <= start) break;
6964
        }
6965
        if (part.to >= end) return inner(builder, text, style, startStyle, endStyle, title, css);
0 ignored issues
show
Bug introduced by
The variable part seems to not be initialized for all possible execution paths.
Loading history...
6966
        inner(builder, text.slice(0, part.to - start), style, startStyle, null, title, css);
6967
        startStyle = null;
6968
        text = text.slice(part.to - start);
6969
        start = part.to;
6970
      }
6971
    };
6972
  }
6973
6974
  function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
6975
    var widget = !ignoreWidget && marker.widgetNode;
6976
    if (widget) builder.map.push(builder.pos, builder.pos + size, widget);
6977
    if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {
6978
      if (!widget)
6979
        widget = builder.content.appendChild(document.createElement("span"));
6980
      widget.setAttribute("cm-marker", marker.id);
6981
    }
6982
    if (widget) {
6983
      builder.cm.display.input.setUneditable(widget);
6984
      builder.content.appendChild(widget);
6985
    }
6986
    builder.pos += size;
6987
  }
6988
6989
  // Outputs a number of spans to make up a line, taking highlighting
6990
  // and marked text into account.
6991
  function insertLineContent(line, builder, styles) {
6992
    var spans = line.markedSpans, allText = line.text, at = 0;
6993
    if (!spans) {
6994
      for (var i = 1; i < styles.length; i+=2)
6995
        builder.addToken(builder, allText.slice(at, at = styles[i]), interpretTokenStyle(styles[i+1], builder.cm.options));
6996
      return;
6997
    }
6998
6999
    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 6994. 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...
7000
    var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed;
7001
    for (;;) {
7002
      if (nextChange == pos) { // Update current marker set
7003
        spanStyle = spanEndStyle = spanStartStyle = title = css = "";
7004
        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...
7005
        var foundBookmarks = [];
7006
        for (var j = 0; j < spans.length; ++j) {
7007
          var sp = spans[j], m = sp.marker;
7008
          if (m.type == "bookmark" && sp.from == pos && m.widgetNode) {
7009
            foundBookmarks.push(m);
7010
          } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == 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...
7011
            if (sp.to != null && sp.to != pos && nextChange > sp.to) {
7012
              nextChange = sp.to;
7013
              spanEndStyle = "";
7014
            }
7015
            if (m.className) spanStyle += " " + m.className;
7016
            if (m.css) css = m.css;
7017
            if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle;
7018
            if (m.endStyle && sp.to == nextChange) spanEndStyle += " " + m.endStyle;
7019
            if (m.title && !title) title = m.title;
7020
            if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))
7021
              collapsed = sp;
7022
          } else if (sp.from > pos && nextChange > sp.from) {
7023
            nextChange = sp.from;
7024
          }
7025
        }
7026
        if (collapsed && (collapsed.from || 0) == pos) {
7027
          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...
7028
                             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...
7029
          if (collapsed.to == null) return;
7030
          if (collapsed.to == pos) collapsed = false;
7031
        }
7032
        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 7006. 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...
7033
          buildCollapsedSpan(builder, 0, foundBookmarks[j]);
7034
      }
7035
      if (pos >= len) break;
7036
7037
      var upto = Math.min(len, nextChange);
7038
      while (true) {
7039
        if (text) {
7040
          var end = pos + text.length;
7041
          if (!collapsed) {
7042
            var tokenText = end > upto ? text.slice(0, upto - pos) : text;
7043
            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...
7044
                             spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", title, css);
0 ignored issues
show
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...
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...
7045
          }
7046
          if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
7047
          pos = end;
7048
          spanStartStyle = "";
7049
        }
7050
        text = allText.slice(at, at = styles[i++]);
7051
        style = interpretTokenStyle(styles[i++], builder.cm.options);
7052
      }
7053
    }
7054
  }
7055
7056
  // DOCUMENT DATA STRUCTURE
7057
7058
  // By default, updates that start and end at the beginning of a line
7059
  // are treated specially, in order to make the association of line
7060
  // widgets and marker elements with the text behave more intuitive.
7061
  function isWholeLineUpdate(doc, change) {
7062
    return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" &&
0 ignored issues
show
Best Practice introduced by
Comparing change.from.ch to 0 using the == operator is not safe. Consider using === instead.
Loading history...
Best Practice introduced by
Comparing change.to.ch to 0 using the == operator is not safe. Consider using === instead.
Loading history...
7063
      (!doc.cm || doc.cm.options.wholeLineUpdateBefore);
7064
  }
7065
7066
  // Perform a change on the document data structure.
7067
  function updateDoc(doc, change, markedSpans, estimateHeight) {
7068
    function spansFor(n) {return markedSpans ? markedSpans[n] : null;}
7069
    function update(line, text, spans) {
7070
      updateLine(line, text, spans, estimateHeight);
7071
      signalLater(line, "change", line, change);
7072
    }
7073
    function linesFor(start, end) {
7074
      for (var i = start, result = []; i < end; ++i)
7075
        result.push(new Line(text[i], spansFor(i), estimateHeight));
7076
      return result;
7077
    }
7078
7079
    var from = change.from, to = change.to, text = change.text;
7080
    var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
7081
    var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;
7082
7083
    // Adjust the line structure
7084
    if (change.full) {
7085
      doc.insert(0, linesFor(0, text.length));
7086
      doc.remove(text.length, doc.size - text.length);
7087
    } else if (isWholeLineUpdate(doc, change)) {
7088
      // This is a whole-line replace. Treated specially to make
7089
      // sure line objects move the way they are supposed to.
7090
      var added = linesFor(0, text.length - 1);
7091
      update(lastLine, lastLine.text, lastSpans);
7092
      if (nlines) doc.remove(from.line, nlines);
7093
      if (added.length) doc.insert(from.line, added);
7094
    } else if (firstLine == lastLine) {
7095
      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...
7096
        update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
7097
      } else {
7098
        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 7090. 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...
7099
        added.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight));
7100
        update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
7101
        doc.insert(from.line + 1, added);
7102
      }
7103
    } else if (text.length == 1) {
7104
      update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0));
7105
      doc.remove(from.line + 1, nlines);
7106
    } else {
7107
      update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
7108
      update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
7109
      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 7090. 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...
7110
      if (nlines > 1) doc.remove(from.line + 1, nlines - 1);
7111
      doc.insert(from.line + 1, added);
7112
    }
7113
7114
    signalLater(doc, "change", doc, change);
7115
  }
7116
7117
  // The document is represented as a BTree consisting of leaves, with
7118
  // chunk of lines in them, and branches, with up to ten leaves or
7119
  // other branch nodes below them. The top node is always a branch
7120
  // node, and is the document object itself (meaning it has
7121
  // additional methods and properties).
7122
  //
7123
  // All nodes have parent links. The tree is used both to go from
7124
  // line numbers to line objects, and to go from objects to numbers.
7125
  // It also indexes by height, and is used to convert between height
7126
  // and line object, and to find the total height of the document.
7127
  //
7128
  // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html
7129
7130
  function LeafChunk(lines) {
7131
    this.lines = lines;
7132
    this.parent = null;
7133
    for (var i = 0, height = 0; i < lines.length; ++i) {
7134
      lines[i].parent = this;
7135
      height += lines[i].height;
7136
    }
7137
    this.height = height;
7138
  }
7139
7140
  LeafChunk.prototype = {
7141
    chunkSize: function() { return this.lines.length; },
7142
    // Remove the n lines at offset 'at'.
7143
    removeInner: function(at, n) {
7144
      for (var i = at, e = at + n; i < e; ++i) {
7145
        var line = this.lines[i];
7146
        this.height -= line.height;
7147
        cleanUpLine(line);
7148
        signalLater(line, "delete");
7149
      }
7150
      this.lines.splice(at, n);
7151
    },
7152
    // Helper used to collapse a small branch into a single leaf.
7153
    collapse: function(lines) {
7154
      lines.push.apply(lines, this.lines);
7155
    },
7156
    // Insert the given array of lines at offset 'at', count them as
7157
    // having the given height.
7158
    insertInner: function(at, lines, height) {
7159
      this.height += height;
7160
      this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
7161
      for (var i = 0; i < lines.length; ++i) lines[i].parent = this;
7162
    },
7163
    // Used to iterate over a part of the tree.
7164
    iterN: function(at, n, op) {
7165
      for (var e = at + n; at < e; ++at)
7166
        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...
7167
    }
7168
  };
7169
7170
  function BranchChunk(children) {
7171
    this.children = children;
7172
    var size = 0, height = 0;
7173
    for (var i = 0; i < children.length; ++i) {
7174
      var ch = children[i];
7175
      size += ch.chunkSize(); height += ch.height;
7176
      ch.parent = this;
7177
    }
7178
    this.size = size;
7179
    this.height = height;
7180
    this.parent = null;
7181
  }
7182
7183
  BranchChunk.prototype = {
7184
    chunkSize: function() { return this.size; },
7185
    removeInner: function(at, n) {
7186
      this.size -= n;
7187
      for (var i = 0; i < this.children.length; ++i) {
7188
        var child = this.children[i], sz = child.chunkSize();
7189
        if (at < sz) {
7190
          var rm = Math.min(n, sz - at), oldHeight = child.height;
7191
          child.removeInner(at, rm);
7192
          this.height -= oldHeight - child.height;
7193
          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...
7194
          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...
7195
          at = 0;
7196
        } else at -= sz;
7197
      }
7198
      // If the result is smaller than 25 lines, ensure that it is a
7199
      // single leaf node.
7200
      if (this.size - n < 25 &&
7201
          (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) {
7202
        var lines = [];
7203
        this.collapse(lines);
7204
        this.children = [new LeafChunk(lines)];
7205
        this.children[0].parent = this;
7206
      }
7207
    },
7208
    collapse: function(lines) {
7209
      for (var i = 0; i < this.children.length; ++i) this.children[i].collapse(lines);
7210
    },
7211
    insertInner: function(at, lines, height) {
7212
      this.size += lines.length;
7213
      this.height += height;
7214
      for (var i = 0; i < this.children.length; ++i) {
7215
        var child = this.children[i], sz = child.chunkSize();
7216
        if (at <= sz) {
7217
          child.insertInner(at, lines, height);
7218
          if (child.lines && child.lines.length > 50) {
7219
            while (child.lines.length > 50) {
7220
              var spilled = child.lines.splice(child.lines.length - 25, 25);
7221
              var newleaf = new LeafChunk(spilled);
7222
              child.height -= newleaf.height;
7223
              this.children.splice(i + 1, 0, newleaf);
7224
              newleaf.parent = this;
7225
            }
7226
            this.maybeSpill();
7227
          }
7228
          break;
7229
        }
7230
        at -= sz;
7231
      }
7232
    },
7233
    // When a node has grown, check whether it should be split.
7234
    maybeSpill: function() {
7235
      if (this.children.length <= 10) return;
7236
      var me = this;
7237
      do {
7238
        var spilled = me.children.splice(me.children.length - 5, 5);
7239
        var sibling = new BranchChunk(spilled);
7240
        if (!me.parent) { // Become the parent node
7241
          var copy = new BranchChunk(me.children);
7242
          copy.parent = me;
7243
          me.children = [copy, sibling];
7244
          me = copy;
7245
        } else {
7246
          me.size -= sibling.size;
7247
          me.height -= sibling.height;
7248
          var myIndex = indexOf(me.parent.children, me);
7249
          me.parent.children.splice(myIndex + 1, 0, sibling);
7250
        }
7251
        sibling.parent = me.parent;
7252
      } while (me.children.length > 10);
7253
      me.parent.maybeSpill();
7254
    },
7255
    iterN: function(at, n, op) {
7256
      for (var i = 0; i < this.children.length; ++i) {
7257
        var child = this.children[i], sz = child.chunkSize();
7258
        if (at < sz) {
7259
          var used = Math.min(n, sz - at);
7260
          if (child.iterN(at, used, op)) return true;
7261
          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...
7262
          at = 0;
7263
        } else at -= sz;
7264
      }
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...
7265
    }
7266
  };
7267
7268
  var nextDocId = 0;
7269
  var Doc = CodeMirror.Doc = function(text, mode, firstLine, lineSep) {
7270
    if (!(this instanceof Doc)) return new Doc(text, mode, firstLine, lineSep);
7271
    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...
7272
7273
    BranchChunk.call(this, [new LeafChunk([new Line("", null)])]);
7274
    this.first = firstLine;
7275
    this.scrollTop = this.scrollLeft = 0;
7276
    this.cantEdit = false;
7277
    this.cleanGeneration = 1;
7278
    this.frontier = firstLine;
7279
    var start = Pos(firstLine, 0);
7280
    this.sel = simpleSelection(start);
7281
    this.history = new History(null);
7282
    this.id = ++nextDocId;
7283
    this.modeOption = mode;
7284
    this.lineSep = lineSep;
7285
7286
    if (typeof text == "string") text = this.splitLines(text);
7287
    updateDoc(this, {from: start, to: start, text: text});
7288
    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...
7289
  };
7290
7291
  Doc.prototype = createObj(BranchChunk.prototype, {
7292
    constructor: Doc,
7293
    // Iterate over the document. Supports two forms -- with only one
7294
    // argument, it calls that for each line in the document. With
7295
    // three, it iterates over the range given by the first two (with
7296
    // the second being non-inclusive).
7297
    iter: function(from, to, op) {
7298
      if (op) this.iterN(from - this.first, to - from, op);
7299
      else this.iterN(this.first, this.first + this.size, from);
7300
    },
7301
7302
    // Non-public interface for adding and removing lines.
7303
    insert: function(at, lines) {
7304
      var height = 0;
7305
      for (var i = 0; i < lines.length; ++i) height += lines[i].height;
7306
      this.insertInner(at - this.first, lines, height);
7307
    },
7308
    remove: function(at, n) { this.removeInner(at - this.first, n); },
7309
7310
    // From here, the methods are part of the public interface. Most
7311
    // are also available from CodeMirror (editor) instances.
7312
7313
    getValue: function(lineSep) {
7314
      var lines = getLines(this, this.first, this.first + this.size);
7315
      if (lineSep === false) return lines;
7316
      return lines.join(lineSep || this.lineSeparator());
7317
    },
7318
    setValue: docMethodOp(function(code) {
7319
      var top = Pos(this.first, 0), last = this.first + this.size - 1;
7320
      makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
7321
                        text: this.splitLines(code), origin: "setValue", full: true}, true);
7322
      setSelection(this, simpleSelection(top));
7323
    }),
7324
    replaceRange: function(code, from, to, origin) {
7325
      from = clipPos(this, from);
7326
      to = to ? clipPos(this, to) : from;
7327
      replaceRange(this, code, from, to, origin);
7328
    },
7329
    getRange: function(from, to, lineSep) {
7330
      var lines = getBetween(this, clipPos(this, from), clipPos(this, to));
7331
      if (lineSep === false) return lines;
7332
      return lines.join(lineSep || this.lineSeparator());
7333
    },
7334
7335
    getLine: function(line) {var l = this.getLineHandle(line); return l && l.text;},
7336
7337
    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...
7338
    getLineNumber: function(line) {return lineNo(line);},
7339
7340
    getLineHandleVisualStart: function(line) {
7341
      if (typeof line == "number") line = getLine(this, line);
7342
      return visualLine(line);
7343
    },
7344
7345
    lineCount: function() {return this.size;},
7346
    firstLine: function() {return this.first;},
7347
    lastLine: function() {return this.first + this.size - 1;},
7348
7349
    clipPos: function(pos) {return clipPos(this, pos);},
7350
7351
    getCursor: function(start) {
7352
      var range = this.sel.primary(), pos;
7353
      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...
7354
      else if (start == "anchor") pos = range.anchor;
7355
      else if (start == "end" || start == "to" || start === false) pos = range.to();
7356
      else pos = range.from();
7357
      return pos;
7358
    },
7359
    listSelections: function() { return this.sel.ranges; },
7360
    somethingSelected: function() {return this.sel.somethingSelected();},
7361
7362
    setCursor: docMethodOp(function(line, ch, options) {
7363
      setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options);
7364
    }),
7365
    setSelection: docMethodOp(function(anchor, head, options) {
7366
      setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options);
7367
    }),
7368
    extendSelection: docMethodOp(function(head, other, options) {
7369
      extendSelection(this, clipPos(this, head), other && clipPos(this, other), options);
7370
    }),
7371
    extendSelections: docMethodOp(function(heads, options) {
7372
      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...
7373
    }),
7374
    extendSelectionsBy: docMethodOp(function(f, options) {
7375
      extendSelections(this, map(this.sel.ranges, f), options);
7376
    }),
7377
    setSelections: docMethodOp(function(ranges, primary, options) {
7378
      if (!ranges.length) return;
7379
      for (var i = 0, out = []; i < ranges.length; i++)
7380
        out[i] = new Range(clipPos(this, ranges[i].anchor),
7381
                           clipPos(this, ranges[i].head));
7382
      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...
7383
      setSelection(this, normalizeSelection(out, primary), options);
7384
    }),
7385
    addSelection: docMethodOp(function(anchor, head, options) {
7386
      var ranges = this.sel.ranges.slice(0);
7387
      ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)));
7388
      setSelection(this, normalizeSelection(ranges, ranges.length - 1), options);
7389
    }),
7390
7391
    getSelection: function(lineSep) {
7392
      var ranges = this.sel.ranges, lines;
7393
      for (var i = 0; i < ranges.length; i++) {
7394
        var sel = getBetween(this, ranges[i].from(), ranges[i].to());
7395
        lines = lines ? lines.concat(sel) : sel;
7396
      }
7397
      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...
7398
      else return lines.join(lineSep || this.lineSeparator());
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...
7399
    },
7400
    getSelections: function(lineSep) {
7401
      var parts = [], ranges = this.sel.ranges;
7402
      for (var i = 0; i < ranges.length; i++) {
7403
        var sel = getBetween(this, ranges[i].from(), ranges[i].to());
7404
        if (lineSep !== false) sel = sel.join(lineSep || this.lineSeparator());
7405
        parts[i] = sel;
7406
      }
7407
      return parts;
7408
    },
7409
    replaceSelection: function(code, collapse, origin) {
7410
      var dup = [];
7411
      for (var i = 0; i < this.sel.ranges.length; i++)
7412
        dup[i] = code;
7413
      this.replaceSelections(dup, collapse, origin || "+input");
7414
    },
7415
    replaceSelections: docMethodOp(function(code, collapse, origin) {
7416
      var changes = [], sel = this.sel;
7417
      for (var i = 0; i < sel.ranges.length; i++) {
7418
        var range = sel.ranges[i];
7419
        changes[i] = {from: range.from(), to: range.to(), text: this.splitLines(code[i]), origin: origin};
7420
      }
7421
      var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse);
7422
      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 7417. 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...
7423
        makeChange(this, changes[i]);
7424
      if (newSel) setSelectionReplaceHistory(this, newSel);
7425
      else if (this.cm) ensureCursorVisible(this.cm);
7426
    }),
7427
    undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}),
7428
    redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}),
7429
    undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}),
7430
    redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true);}),
7431
7432
    setExtending: function(val) {this.extend = val;},
7433
    getExtending: function() {return this.extend;},
7434
7435
    historySize: function() {
7436
      var hist = this.history, done = 0, undone = 0;
7437
      for (var i = 0; i < hist.done.length; i++) if (!hist.done[i].ranges) ++done;
7438
      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 7437. 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...
7439
      return {undo: done, redo: undone};
7440
    },
7441
    clearHistory: function() {this.history = new History(this.history.maxGeneration);},
7442
7443
    markClean: function() {
7444
      this.cleanGeneration = this.changeGeneration(true);
7445
    },
7446
    changeGeneration: function(forceSplit) {
7447
      if (forceSplit)
7448
        this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null;
7449
      return this.history.generation;
7450
    },
7451
    isClean: function (gen) {
7452
      return this.history.generation == (gen || this.cleanGeneration);
7453
    },
7454
7455
    getHistory: function() {
7456
      return {done: copyHistoryArray(this.history.done),
7457
              undone: copyHistoryArray(this.history.undone)};
7458
    },
7459
    setHistory: function(histData) {
7460
      var hist = this.history = new History(this.history.maxGeneration);
7461
      hist.done = copyHistoryArray(histData.done.slice(0), null, true);
7462
      hist.undone = copyHistoryArray(histData.undone.slice(0), null, true);
7463
    },
7464
7465
    addLineClass: docMethodOp(function(handle, where, cls) {
7466
      return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function(line) {
7467
        var prop = where == "text" ? "textClass"
7468
                 : where == "background" ? "bgClass"
7469
                 : where == "gutter" ? "gutterClass" : "wrapClass";
7470
        if (!line[prop]) line[prop] = cls;
7471
        else if (classTest(cls).test(line[prop])) return false;
7472
        else line[prop] += " " + cls;
7473
        return true;
7474
      });
7475
    }),
7476
    removeLineClass: docMethodOp(function(handle, where, cls) {
7477
      return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function(line) {
7478
        var prop = where == "text" ? "textClass"
7479
                 : where == "background" ? "bgClass"
7480
                 : where == "gutter" ? "gutterClass" : "wrapClass";
7481
        var cur = line[prop];
7482
        if (!cur) return false;
7483
        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...
7484
        else {
7485
          var found = cur.match(classTest(cls));
7486
          if (!found) return false;
7487
          var end = found.index + found[0].length;
7488
          line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null;
7489
        }
7490
        return true;
7491
      });
7492
    }),
7493
7494
    addLineWidget: docMethodOp(function(handle, node, options) {
7495
      return addLineWidget(this, handle, node, options);
7496
    }),
7497
    removeLineWidget: function(widget) { widget.clear(); },
7498
7499
    markText: function(from, to, options) {
7500
      return markText(this, clipPos(this, from), clipPos(this, to), options, "range");
7501
    },
7502
    setBookmark: function(pos, options) {
7503
      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...
7504
                      insertLeft: options && options.insertLeft,
7505
                      clearWhenEmpty: false, shared: options && options.shared,
7506
                      handleMouseEvents: options && options.handleMouseEvents};
7507
      pos = clipPos(this, pos);
7508
      return markText(this, pos, pos, realOpts, "bookmark");
7509
    },
7510
    findMarksAt: function(pos) {
7511
      pos = clipPos(this, pos);
7512
      var markers = [], spans = getLine(this, pos.line).markedSpans;
7513
      if (spans) for (var i = 0; i < spans.length; ++i) {
7514
        var span = spans[i];
7515
        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...
7516
            (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...
7517
          markers.push(span.marker.parent || span.marker);
7518
      }
7519
      return markers;
7520
    },
7521
    findMarks: function(from, to, filter) {
7522
      from = clipPos(this, from); to = clipPos(this, to);
7523
      var found = [], lineNo = from.line;
7524
      this.iter(from.line, to.line + 1, function(line) {
7525
        var spans = line.markedSpans;
7526
        if (spans) for (var i = 0; i < spans.length; i++) {
7527
          var span = spans[i];
7528
          if (!(lineNo == from.line && from.ch > span.to ||
7529
                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...
7530
                lineNo == to.line && span.from > to.ch) &&
7531
              (!filter || filter(span.marker)))
7532
            found.push(span.marker.parent || span.marker);
7533
        }
7534
        ++lineNo;
7535
      });
7536
      return found;
7537
    },
7538
    getAllMarks: function() {
7539
      var markers = [];
7540
      this.iter(function(line) {
7541
        var sps = line.markedSpans;
7542
        if (sps) for (var i = 0; i < sps.length; ++i)
7543
          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...
7544
      });
7545
      return markers;
7546
    },
7547
7548
    posFromIndex: function(off) {
7549
      var ch, lineNo = this.first;
7550
      this.iter(function(line) {
7551
        var sz = line.text.length + 1;
7552
        if (sz > off) { ch = off; return true; }
7553
        off -= sz;
7554
        ++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...
7555
      });
7556
      return clipPos(this, Pos(lineNo, ch));
7557
    },
7558
    indexFromPos: function (coords) {
7559
      coords = clipPos(this, coords);
7560
      var index = coords.ch;
7561
      if (coords.line < this.first || coords.ch < 0) return 0;
7562
      this.iter(this.first, coords.line, function (line) {
7563
        index += line.text.length + 1;
7564
      });
7565
      return index;
7566
    },
7567
7568
    copy: function(copyHistory) {
7569
      var doc = new Doc(getLines(this, this.first, this.first + this.size),
7570
                        this.modeOption, this.first, this.lineSep);
7571
      doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;
7572
      doc.sel = this.sel;
7573
      doc.extend = false;
7574
      if (copyHistory) {
7575
        doc.history.undoDepth = this.history.undoDepth;
7576
        doc.setHistory(this.getHistory());
7577
      }
7578
      return doc;
7579
    },
7580
7581
    linkedDoc: function(options) {
7582
      if (!options) options = {};
7583
      var from = this.first, to = this.first + this.size;
7584
      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...
7585
      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...
7586
      var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep);
7587
      if (options.sharedHist) copy.history = this.history;
7588
      (this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});
7589
      copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];
7590
      copySharedMarkers(copy, findSharedMarkers(this));
7591
      return copy;
7592
    },
7593
    unlinkDoc: function(other) {
7594
      if (other instanceof CodeMirror) other = other.doc;
7595
      if (this.linked) for (var i = 0; i < this.linked.length; ++i) {
7596
        var link = this.linked[i];
7597
        if (link.doc != other) continue;
7598
        this.linked.splice(i, 1);
7599
        other.unlinkDoc(this);
7600
        detachSharedMarkers(findSharedMarkers(this));
7601
        break;
7602
      }
7603
      // If the histories were shared, split them again
7604
      if (other.history == this.history) {
7605
        var splitIds = [other.id];
7606
        linkedDocs(other, function(doc) {splitIds.push(doc.id);}, true);
7607
        other.history = new History(null);
7608
        other.history.done = copyHistoryArray(this.history.done, splitIds);
7609
        other.history.undone = copyHistoryArray(this.history.undone, splitIds);
7610
      }
7611
    },
7612
    iterLinkedDocs: function(f) {linkedDocs(this, f);},
7613
7614
    getMode: function() {return this.mode;},
7615
    getEditor: function() {return this.cm;},
7616
7617
    splitLines: function(str) {
7618
      if (this.lineSep) return str.split(this.lineSep);
7619
      return splitLinesAuto(str);
7620
    },
7621
    lineSeparator: function() { return this.lineSep || "\n"; }
7622
  });
7623
7624
  // Public alias.
7625
  Doc.prototype.eachLine = Doc.prototype.iter;
7626
7627
  // Set up methods on CodeMirror's prototype to redirect to the editor's document.
7628
  var dontDelegate = "iter insert remove copy getEditor constructor".split(" ");
7629
  for (var prop in Doc.prototype) if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
7630
    CodeMirror.prototype[prop] = (function(method) {
7631
      return function() {return method.apply(this.doc, arguments);};
7632
    })(Doc.prototype[prop]);
7633
7634
  eventMixin(Doc);
7635
7636
  // Call f for all linked documents.
7637
  function linkedDocs(doc, f, sharedHistOnly) {
7638
    function propagate(doc, skip, sharedHist) {
7639
      if (doc.linked) for (var i = 0; i < doc.linked.length; ++i) {
7640
        var rel = doc.linked[i];
7641
        if (rel.doc == skip) continue;
7642
        var shared = sharedHist && rel.sharedHist;
7643
        if (sharedHistOnly && !shared) continue;
7644
        f(rel.doc, shared);
7645
        propagate(rel.doc, doc, shared);
7646
      }
7647
    }
7648
    propagate(doc, null, true);
7649
  }
7650
7651
  // Attach a document to an editor.
7652
  function attachDoc(cm, doc) {
7653
    if (doc.cm) throw new Error("This document is already in use.");
7654
    cm.doc = doc;
7655
    doc.cm = cm;
7656
    estimateLineHeights(cm);
7657
    loadMode(cm);
7658
    if (!cm.options.lineWrapping) findMaxLine(cm);
7659
    cm.options.mode = doc.modeOption;
7660
    regChange(cm);
7661
  }
7662
7663
  // LINE UTILITIES
7664
7665
  // Find the line object corresponding to the given line number.
7666
  function getLine(doc, n) {
7667
    n -= doc.first;
7668
    if (n < 0 || n >= doc.size) throw new Error("There is no line " + (n + doc.first) + " in the document.");
7669
    for (var chunk = doc; !chunk.lines;) {
7670
      for (var i = 0;; ++i) {
7671
        var child = chunk.children[i], sz = child.chunkSize();
7672
        if (n < sz) { chunk = child; break; }
7673
        n -= sz;
7674
      }
7675
    }
7676
    return chunk.lines[n];
7677
  }
7678
7679
  // Get the part of a document between two positions, as an array of
7680
  // strings.
7681
  function getBetween(doc, start, end) {
7682
    var out = [], n = start.line;
7683
    doc.iter(start.line, end.line + 1, function(line) {
7684
      var text = line.text;
7685
      if (n == end.line) text = text.slice(0, end.ch);
7686
      if (n == start.line) text = text.slice(start.ch);
7687
      out.push(text);
7688
      ++n;
7689
    });
7690
    return out;
7691
  }
7692
  // Get the lines between from and to, as array of strings.
7693
  function getLines(doc, from, to) {
7694
    var out = [];
7695
    doc.iter(from, to, function(line) { out.push(line.text); });
7696
    return out;
7697
  }
7698
7699
  // Update the height of a line, propagating the height change
7700
  // upwards to parent nodes.
7701
  function updateLineHeight(line, height) {
7702
    var diff = height - line.height;
7703
    if (diff) for (var n = line; n; n = n.parent) n.height += diff;
7704
  }
7705
7706
  // Given a line object, find its line number by walking up through
7707
  // its parent links.
7708
  function lineNo(line) {
7709
    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...
7710
    var cur = line.parent, no = indexOf(cur.lines, line);
7711
    for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
7712
      for (var i = 0;; ++i) {
7713
        if (chunk.children[i] == cur) break;
7714
        no += chunk.children[i].chunkSize();
7715
      }
7716
    }
7717
    return no + cur.first;
7718
  }
7719
7720
  // Find the line at the given vertical position, using the height
7721
  // information in the document tree.
7722
  function lineAtHeight(chunk, h) {
7723
    var n = chunk.first;
7724
    outer: do {
7725
      for (var i = 0; i < chunk.children.length; ++i) {
7726
        var child = chunk.children[i], ch = child.height;
7727
        if (h < ch) { chunk = child; continue outer; }
7728
        h -= ch;
7729
        n += child.chunkSize();
7730
      }
7731
      return n;
7732
    } while (!chunk.lines);
7733
    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 7725. 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...
7734
      var line = chunk.lines[i], lh = line.height;
7735
      if (h < lh) break;
7736
      h -= lh;
7737
    }
7738
    return n + i;
7739
  }
7740
7741
7742
  // Find the height above the given line.
7743
  function heightAtLine(lineObj) {
7744
    lineObj = visualLine(lineObj);
7745
7746
    var h = 0, chunk = lineObj.parent;
7747
    for (var i = 0; i < chunk.lines.length; ++i) {
7748
      var line = chunk.lines[i];
7749
      if (line == lineObj) break;
7750
      else h += line.height;
7751
    }
7752
    for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
7753
      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 7747. 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...
7754
        var cur = p.children[i];
7755
        if (cur == chunk) break;
7756
        else h += cur.height;
7757
      }
7758
    }
7759
    return h;
7760
  }
7761
7762
  // Get the bidi ordering for the given line (and cache it). Returns
7763
  // false for lines that are fully left-to-right, and an array of
7764
  // BidiSpan objects otherwise.
7765
  function getOrder(line) {
7766
    var order = line.order;
7767
    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...
7768
    return order;
7769
  }
7770
7771
  // HISTORY
7772
7773
  function History(startGen) {
7774
    // Arrays of change events and selections. Doing something adds an
7775
    // event to done and clears undo. Undoing moves events from done
7776
    // to undone, redoing moves them in the other direction.
7777
    this.done = []; this.undone = [];
7778
    this.undoDepth = Infinity;
7779
    // Used to track when changes can be merged into a single undo
7780
    // event
7781
    this.lastModTime = this.lastSelTime = 0;
7782
    this.lastOp = this.lastSelOp = null;
7783
    this.lastOrigin = this.lastSelOrigin = null;
7784
    // Used by the isClean() method
7785
    this.generation = this.maxGeneration = startGen || 1;
7786
  }
7787
7788
  // Create a history change event from an updateDoc-style change
7789
  // object.
7790
  function historyChangeFromChange(doc, change) {
7791
    var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
7792
    attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
7793
    linkedDocs(doc, function(doc) {attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);}, true);
7794
    return histChange;
7795
  }
7796
7797
  // Pop all selection events off the end of a history array. Stop at
7798
  // a change event.
7799
  function clearSelectionEvents(array) {
7800
    while (array.length) {
7801
      var last = lst(array);
7802
      if (last.ranges) array.pop();
7803
      else break;
7804
    }
7805
  }
7806
7807
  // Find the top change event in the history. Pop off selection
7808
  // events that are in the way.
7809
  function lastChangeEvent(hist, force) {
7810
    if (force) {
7811
      clearSelectionEvents(hist.done);
7812
      return lst(hist.done);
7813
    } else if (hist.done.length && !lst(hist.done).ranges) {
7814
      return lst(hist.done);
7815
    } 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...
7816
      hist.done.pop();
7817
      return lst(hist.done);
7818
    }
7819
  }
7820
7821
  // Register a change in the history. Merges changes that are within
7822
  // a single operation, ore are close together with an origin that
7823
  // allows merging (starting with "+") into a single event.
7824
  function addChangeToHistory(doc, change, selAfter, opId) {
7825
    var hist = doc.history;
7826
    hist.undone.length = 0;
7827
    var time = +new Date, cur;
7828
7829
    if ((hist.lastOp == opId ||
7830
         hist.lastOrigin == change.origin && change.origin &&
7831
         ((change.origin.charAt(0) == "+" && doc.cm && hist.lastModTime > time - doc.cm.options.historyEventDelay) ||
7832
          change.origin.charAt(0) == "*")) &&
7833
        (cur = lastChangeEvent(hist, hist.lastOp == opId))) {
7834
      // Merge this change into the last event
7835
      var last = lst(cur.changes);
7836
      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...
7837
        // Optimized case for simple insertion -- don't want to add
7838
        // new changesets for every character typed
7839
        last.to = changeEnd(change);
7840
      } else {
7841
        // Add new sub-event
7842
        cur.changes.push(historyChangeFromChange(doc, change));
7843
      }
7844
    } else {
7845
      // Can not be merged, start a new event.
7846
      var before = lst(hist.done);
7847
      if (!before || !before.ranges)
7848
        pushSelectionToHistory(doc.sel, hist.done);
7849
      cur = {changes: [historyChangeFromChange(doc, change)],
7850
             generation: hist.generation};
7851
      hist.done.push(cur);
7852
      while (hist.done.length > hist.undoDepth) {
7853
        hist.done.shift();
7854
        if (!hist.done[0].ranges) hist.done.shift();
7855
      }
7856
    }
7857
    hist.done.push(selAfter);
7858
    hist.generation = ++hist.maxGeneration;
7859
    hist.lastModTime = hist.lastSelTime = time;
7860
    hist.lastOp = hist.lastSelOp = opId;
7861
    hist.lastOrigin = hist.lastSelOrigin = change.origin;
7862
7863
    if (!last) signal(doc, "historyAdded");
7864
  }
7865
7866
  function selectionEventCanBeMerged(doc, origin, prev, sel) {
7867
    var ch = origin.charAt(0);
7868
    return ch == "*" ||
7869
      ch == "+" &&
7870
      prev.ranges.length == sel.ranges.length &&
7871
      prev.somethingSelected() == sel.somethingSelected() &&
7872
      new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500);
7873
  }
7874
7875
  // Called whenever the selection changes, sets the new selection as
7876
  // the pending selection in the history, and pushes the old pending
7877
  // selection into the 'done' array when it was significantly
7878
  // different (in number of selected ranges, emptiness, or time).
7879
  function addSelectionToHistory(doc, sel, opId, options) {
7880
    var hist = doc.history, origin = options && options.origin;
7881
7882
    // A new event is started when the previous origin does not match
7883
    // the current, or the origins don't allow matching. Origins
7884
    // starting with * are always merged, those starting with + are
7885
    // merged when similar and close together in time.
7886
    if (opId == hist.lastSelOp ||
7887
        (origin && hist.lastSelOrigin == origin &&
7888
         (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||
7889
          selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))
7890
      hist.done[hist.done.length - 1] = sel;
7891
    else
7892
      pushSelectionToHistory(sel, hist.done);
7893
7894
    hist.lastSelTime = +new Date;
7895
    hist.lastSelOrigin = origin;
7896
    hist.lastSelOp = opId;
7897
    if (options && options.clearRedo !== false)
7898
      clearSelectionEvents(hist.undone);
7899
  }
7900
7901
  function pushSelectionToHistory(sel, dest) {
7902
    var top = lst(dest);
7903
    if (!(top && top.ranges && top.equals(sel)))
7904
      dest.push(sel);
7905
  }
7906
7907
  // Used to store marked span information in the history.
7908
  function attachLocalSpans(doc, change, from, to) {
7909
    var existing = change["spans_" + doc.id], n = 0;
7910
    doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function(line) {
7911
      if (line.markedSpans)
7912
        (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans;
7913
      ++n;
7914
    });
7915
  }
7916
7917
  // When un/re-doing restores text containing marked spans, those
7918
  // that have been explicitly cleared should not be restored.
7919
  function removeClearedSpans(spans) {
7920
    if (!spans) return null;
7921
    for (var i = 0, out; i < spans.length; ++i) {
7922
      if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i); }
7923
      else if (out) out.push(spans[i]);
7924
    }
7925
    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...
7926
  }
7927
7928
  // Retrieve and filter the old marked spans stored in a change event.
7929
  function getOldSpans(doc, change) {
7930
    var found = change["spans_" + doc.id];
7931
    if (!found) return null;
7932
    for (var i = 0, nw = []; i < change.text.length; ++i)
7933
      nw.push(removeClearedSpans(found[i]));
7934
    return nw;
7935
  }
7936
7937
  // Used both to provide a JSON-safe object in .getHistory, and, when
7938
  // detaching a document, to split the history in two
7939
  function copyHistoryArray(events, newGroup, instantiateSel) {
7940
    for (var i = 0, copy = []; i < events.length; ++i) {
7941
      var event = events[i];
7942
      if (event.ranges) {
7943
        copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event);
7944
        continue;
7945
      }
7946
      var changes = event.changes, newChanges = [];
7947
      copy.push({changes: newChanges});
7948
      for (var j = 0; j < changes.length; ++j) {
7949
        var change = changes[j], m;
7950
        newChanges.push({from: change.from, to: change.to, text: change.text});
7951
        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...
7952
          if (indexOf(newGroup, Number(m[1])) > -1) {
7953
            lst(newChanges)[prop] = change[prop];
7954
            delete change[prop];
7955
          }
7956
        }
7957
      }
7958
    }
7959
    return copy;
7960
  }
7961
7962
  // Rebasing/resetting history to deal with externally-sourced changes
7963
7964
  function rebaseHistSelSingle(pos, from, to, diff) {
7965
    if (to < pos.line) {
7966
      pos.line += diff;
7967
    } else if (from < pos.line) {
7968
      pos.line = from;
7969
      pos.ch = 0;
7970
    }
7971
  }
7972
7973
  // Tries to rebase an array of history events given a change in the
7974
  // document. If the change touches the same lines as the event, the
7975
  // event, and everything 'behind' it, is discarded. If the change is
7976
  // before the event, the event's positions are updated. Uses a
7977
  // copy-on-write scheme for the positions, to avoid having to
7978
  // reallocate them all on every rebase, but also avoid problems with
7979
  // shared position objects being unsafely updated.
7980
  function rebaseHistArray(array, from, to, diff) {
7981
    for (var i = 0; i < array.length; ++i) {
7982
      var sub = array[i], ok = true;
7983
      if (sub.ranges) {
7984
        if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; }
7985
        for (var j = 0; j < sub.ranges.length; j++) {
7986
          rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff);
7987
          rebaseHistSelSingle(sub.ranges[j].head, from, to, diff);
7988
        }
7989
        continue;
7990
      }
7991
      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 7985. 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...
7992
        var cur = sub.changes[j];
7993
        if (to < cur.from.line) {
7994
          cur.from = Pos(cur.from.line + diff, cur.from.ch);
7995
          cur.to = Pos(cur.to.line + diff, cur.to.ch);
7996
        } else if (from <= cur.to.line) {
7997
          ok = false;
7998
          break;
7999
        }
8000
      }
8001
      if (!ok) {
8002
        array.splice(0, i + 1);
8003
        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...
8004
      }
8005
    }
8006
  }
8007
8008
  function rebaseHist(hist, change) {
8009
    var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1;
8010
    rebaseHistArray(hist.done, from, to, diff);
8011
    rebaseHistArray(hist.undone, from, to, diff);
8012
  }
8013
8014
  // EVENT UTILITIES
8015
8016
  // Due to the fact that we still support jurassic IE versions, some
8017
  // compatibility wrappers are needed.
8018
8019
  var e_preventDefault = CodeMirror.e_preventDefault = function(e) {
8020
    if (e.preventDefault) e.preventDefault();
8021
    else e.returnValue = false;
8022
  };
8023
  var e_stopPropagation = CodeMirror.e_stopPropagation = function(e) {
8024
    if (e.stopPropagation) e.stopPropagation();
8025
    else e.cancelBubble = true;
8026
  };
8027
  function e_defaultPrevented(e) {
8028
    return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false;
0 ignored issues
show
Best Practice introduced by
Comparing e.returnValue to false using the == operator is not safe. Consider using === instead.
Loading history...
Best Practice introduced by
Comparing e.defaultPrevented to null using the != operator is not safe. Consider using !== instead.
Loading history...
8029
  }
8030
  var e_stop = CodeMirror.e_stop = function(e) {e_preventDefault(e); e_stopPropagation(e);};
8031
8032
  function e_target(e) {return e.target || e.srcElement;}
8033
  function e_button(e) {
8034
    var b = e.which;
8035
    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...
8036
      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...
8037
      else if (e.button & 2) b = 3;
8038
      else if (e.button & 4) b = 2;
8039
    }
8040
    if (mac && e.ctrlKey && b == 1) b = 3;
8041
    return b;
8042
  }
8043
8044
  // EVENT HANDLING
8045
8046
  // Lightweight event framework. on/off also work on DOM nodes,
8047
  // registering native DOM handlers.
8048
8049
  var on = CodeMirror.on = function(emitter, type, f) {
8050
    if (emitter.addEventListener)
8051
      emitter.addEventListener(type, f, false);
8052
    else if (emitter.attachEvent)
8053
      emitter.attachEvent("on" + type, f);
8054
    else {
8055
      var map = emitter._handlers || (emitter._handlers = {});
8056
      var arr = map[type] || (map[type] = []);
8057
      arr.push(f);
8058
    }
8059
  };
8060
8061
  var off = CodeMirror.off = function(emitter, type, f) {
8062
    if (emitter.removeEventListener)
8063
      emitter.removeEventListener(type, f, false);
8064
    else if (emitter.detachEvent)
8065
      emitter.detachEvent("on" + type, f);
8066
    else {
8067
      var arr = emitter._handlers && emitter._handlers[type];
8068
      if (!arr) return;
8069
      for (var i = 0; i < arr.length; ++i)
8070
        if (arr[i] == f) { arr.splice(i, 1); break; }
8071
    }
8072
  };
8073
8074
  var signal = CodeMirror.signal = function(emitter, type /*, values...*/) {
8075
    var arr = emitter._handlers && emitter._handlers[type];
8076
    if (!arr) return;
8077
    var args = Array.prototype.slice.call(arguments, 2);
8078
    for (var i = 0; i < arr.length; ++i) arr[i].apply(null, args);
8079
  };
8080
8081
  var orphanDelayedCallbacks = null;
8082
8083
  // Often, we want to signal events at a point where we are in the
8084
  // middle of some work, but don't want the handler to start calling
8085
  // other methods on the editor, which might be in an inconsistent
8086
  // state or simply not expect any other events to happen.
8087
  // signalLater looks whether there are any handlers, and schedules
8088
  // them to be executed when the last operation ends, or, if no
8089
  // operation is active, when a timeout fires.
8090
  function signalLater(emitter, type /*, values...*/) {
8091
    var arr = emitter._handlers && emitter._handlers[type];
8092
    if (!arr) return;
8093
    var args = Array.prototype.slice.call(arguments, 2), list;
8094
    if (operationGroup) {
8095
      list = operationGroup.delayedCallbacks;
8096
    } else if (orphanDelayedCallbacks) {
8097
      list = orphanDelayedCallbacks;
8098
    } else {
8099
      list = orphanDelayedCallbacks = [];
8100
      setTimeout(fireOrphanDelayed, 0);
8101
    }
8102
    function bnd(f) {return function(){f.apply(null, args);};};
8103
    for (var i = 0; i < arr.length; ++i)
8104
      list.push(bnd(arr[i]));
8105
  }
8106
8107
  function fireOrphanDelayed() {
8108
    var delayed = orphanDelayedCallbacks;
8109
    orphanDelayedCallbacks = null;
8110
    for (var i = 0; i < delayed.length; ++i) delayed[i]();
8111
  }
8112
8113
  // The DOM events that CodeMirror handles can be overridden by
8114
  // registering a (non-DOM) handler on the editor for the event name,
8115
  // and preventDefault-ing the event in that handler.
8116
  function signalDOMEvent(cm, e, override) {
8117
    if (typeof e == "string")
8118
      e = {type: e, preventDefault: function() { this.defaultPrevented = true; }};
8119
    signal(cm, override || e.type, cm, e);
8120
    return e_defaultPrevented(e) || e.codemirrorIgnore;
8121
  }
8122
8123
  function signalCursorActivity(cm) {
8124
    var arr = cm._handlers && cm._handlers.cursorActivity;
8125
    if (!arr) return;
8126
    var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []);
8127
    for (var i = 0; i < arr.length; ++i) if (indexOf(set, arr[i]) == -1)
8128
      set.push(arr[i]);
8129
  }
8130
8131
  function hasHandler(emitter, type) {
8132
    var arr = emitter._handlers && emitter._handlers[type];
8133
    return arr && arr.length > 0;
8134
  }
8135
8136
  // Add on and off methods to a constructor's prototype, to make
8137
  // registering events on such objects more convenient.
8138
  function eventMixin(ctor) {
8139
    ctor.prototype.on = function(type, f) {on(this, type, f);};
8140
    ctor.prototype.off = function(type, f) {off(this, type, f);};
8141
  }
8142
8143
  // MISC UTILITIES
8144
8145
  // Number of pixels added to scroller and sizer to hide scrollbar
8146
  var scrollerGap = 30;
8147
8148
  // Returned or thrown by various protocols to signal 'I'm not
8149
  // handling this'.
8150
  var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}};
8151
8152
  // Reused option objects for setSelection & friends
8153
  var sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"};
8154
8155
  function Delayed() {this.id = null;}
8156
  Delayed.prototype.set = function(ms, f) {
8157
    clearTimeout(this.id);
8158
    this.id = setTimeout(f, ms);
8159
  };
8160
8161
  // Counts the column offset in a string, taking tabs into account.
8162
  // Used mostly to find indentation.
8163 View Code Duplication
  var countColumn = CodeMirror.countColumn = function(string, end, tabSize, startIndex, startValue) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
8164
    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...
8165
      end = string.search(/[^\s\u00a0]/);
8166
      if (end == -1) end = string.length;
8167
    }
8168
    for (var i = startIndex || 0, n = startValue || 0;;) {
8169
      var nextTab = string.indexOf("\t", i);
8170
      if (nextTab < 0 || nextTab >= end)
8171
        return n + (end - i);
8172
      n += nextTab - i;
8173
      n += tabSize - (n % tabSize);
8174
      i = nextTab + 1;
8175
    }
8176
  };
8177
8178
  // The inverse of countColumn -- find the offset that corresponds to
8179
  // a particular column.
8180
  function findColumn(string, goal, tabSize) {
8181
    for (var pos = 0, col = 0;;) {
8182
      var nextTab = string.indexOf("\t", pos);
8183
      if (nextTab == -1) nextTab = string.length;
8184
      var skipped = nextTab - pos;
8185
      if (nextTab == string.length || col + skipped >= goal)
8186
        return pos + Math.min(skipped, goal - col);
8187
      col += nextTab - pos;
8188
      col += tabSize - (col % tabSize);
8189
      pos = nextTab + 1;
8190
      if (col >= goal) return pos;
8191
    }
8192
  }
8193
8194
  var spaceStrs = [""];
8195
  function spaceStr(n) {
8196
    while (spaceStrs.length <= n)
8197
      spaceStrs.push(lst(spaceStrs) + " ");
8198
    return spaceStrs[n];
8199
  }
8200
8201
  function lst(arr) { return arr[arr.length-1]; }
8202
8203
  var selectInput = function(node) { node.select(); };
8204
  if (ios) // Mobile Safari apparently has a bug where select() is broken.
8205
    selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; };
8206
  else if (ie) // Suppress mysterious IE10 errors
8207
    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...
8208
8209
  function indexOf(array, elt) {
8210
    for (var i = 0; i < array.length; ++i)
8211
      if (array[i] == elt) return i;
8212
    return -1;
8213
  }
8214
  function map(array, f) {
8215
    var out = [];
8216
    for (var i = 0; i < array.length; i++) out[i] = f(array[i], i);
8217
    return out;
8218
  }
8219
8220
  function nothing() {}
8221
8222
  function createObj(base, props) {
8223
    var inst;
8224
    if (Object.create) {
8225
      inst = Object.create(base);
8226
    } else {
8227
      nothing.prototype = base;
8228
      inst = new nothing();
0 ignored issues
show
Coding Style Best Practice introduced by
By convention, constructors like nothing should be capitalized.
Loading history...
8229
    }
8230
    if (props) copyObj(props, inst);
8231
    return inst;
8232
  };
8233
8234
  function copyObj(obj, target, overwrite) {
8235
    if (!target) target = {};
8236
    for (var prop in obj)
8237
      if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
8238
        target[prop] = obj[prop];
8239
    return target;
8240
  }
8241
8242
  function bind(f) {
8243
    var args = Array.prototype.slice.call(arguments, 1);
8244
    return function(){return f.apply(null, args);};
8245
  }
8246
8247
  var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
8248
  var isWordCharBasic = CodeMirror.isWordChar = function(ch) {
8249
    return /\w/.test(ch) || ch > "\x80" &&
8250
      (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch));
8251
  };
8252
  function isWordChar(ch, helper) {
8253
    if (!helper) return isWordCharBasic(ch);
8254
    if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) return true;
8255
    return helper.test(ch);
8256
  }
8257
8258
  function isEmpty(obj) {
8259
    for (var n in obj) if (obj.hasOwnProperty(n) && obj[n]) return false;
8260
    return true;
8261
  }
8262
8263
  // Extending unicode characters. A series of a non-extending char +
8264
  // any number of extending chars is treated as a single unit as far
8265
  // as editing and measuring is concerned. This is not fully correct,
8266
  // since some scripts/fonts/browsers also treat other configurations
8267
  // of code points as a group.
8268
  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]/;
8269
  function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch); }
8270
8271
  // DOM UTILITIES
8272
8273
  function elt(tag, content, className, style) {
8274
    var e = document.createElement(tag);
8275
    if (className) e.className = className;
8276
    if (style) e.style.cssText = style;
8277
    if (typeof content == "string") e.appendChild(document.createTextNode(content));
8278
    else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]);
8279
    return e;
8280
  }
8281
8282
  var range;
8283
  if (document.createRange) range = function(node, start, end, endNode) {
8284
    var r = document.createRange();
8285
    r.setEnd(endNode || node, end);
8286
    r.setStart(node, start);
8287
    return r;
8288
  };
8289
  else range = function(node, start, end) {
8290
    var r = document.body.createTextRange();
8291
    try { r.moveToElementText(node.parentNode); }
8292
    catch(e) { return r; }
8293
    r.collapse(true);
8294
    r.moveEnd("character", end);
8295
    r.moveStart("character", start);
8296
    return r;
8297
  };
8298
8299
  function removeChildren(e) {
8300
    for (var count = e.childNodes.length; count > 0; --count)
8301
      e.removeChild(e.firstChild);
8302
    return e;
8303
  }
8304
8305
  function removeChildrenAndAdd(parent, e) {
8306
    return removeChildren(parent).appendChild(e);
8307
  }
8308
8309
  var contains = CodeMirror.contains = function(parent, child) {
8310
    if (child.nodeType == 3) // Android browser always returns false when child is a textnode
8311
      child = child.parentNode;
8312
    if (parent.contains)
8313
      return parent.contains(child);
8314
    do {
8315
      if (child.nodeType == 11) child = child.host;
8316
      if (child == parent) return true;
8317
    } 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...
8318
  };
8319
8320
  function activeElt() {
8321
    var activeElement = document.activeElement;
8322
    while (activeElement && activeElement.root && activeElement.root.activeElement)
8323
      activeElement = activeElement.root.activeElement;
8324
    return activeElement;
8325
  }
8326
  // Older versions of IE throws unspecified error when touching
8327
  // document.activeElement in some cases (during loading, in iframe)
8328
  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 8320 as a function. While this will work, it can be very confusing.
Loading history...
8329
    try { return document.activeElement; }
8330
    catch(e) { return document.body; }
8331
  };
8332
8333
  function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*"); }
8334
  var rmClass = CodeMirror.rmClass = function(node, cls) {
8335
    var current = node.className;
8336
    var match = classTest(cls).exec(current);
8337
    if (match) {
8338
      var after = current.slice(match.index + match[0].length);
8339
      node.className = current.slice(0, match.index) + (after ? match[1] + after : "");
8340
    }
8341
  };
8342
  var addClass = CodeMirror.addClass = function(node, cls) {
8343
    var current = node.className;
8344
    if (!classTest(cls).test(current)) node.className += (current ? " " : "") + cls;
8345
  };
8346
  function joinClasses(a, b) {
8347
    var as = a.split(" ");
8348
    for (var i = 0; i < as.length; i++)
8349
      if (as[i] && !classTest(as[i]).test(b)) b += " " + as[i];
8350
    return b;
8351
  }
8352
8353
  // WINDOW-WIDE EVENTS
8354
8355
  // These must be handled carefully, because naively registering a
8356
  // handler for each editor will cause the editors to never be
8357
  // garbage collected.
8358
8359
  function forEachCodeMirror(f) {
8360
    if (!document.body.getElementsByClassName) return;
8361
    var byClass = document.body.getElementsByClassName("CodeMirror");
8362
    for (var i = 0; i < byClass.length; i++) {
8363
      var cm = byClass[i].CodeMirror;
8364
      if (cm) f(cm);
8365
    }
8366
  }
8367
8368
  var globalsRegistered = false;
8369
  function ensureGlobalHandlers() {
8370
    if (globalsRegistered) return;
8371
    registerGlobalHandlers();
8372
    globalsRegistered = true;
8373
  }
8374
  function registerGlobalHandlers() {
8375
    // When the window resizes, we need to refresh active editors.
8376
    var resizeTimer;
8377
    on(window, "resize", function() {
8378
      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...
8379
        resizeTimer = null;
8380
        forEachCodeMirror(onResize);
8381
      }, 100);
8382
    });
8383
    // When the window loses focus, we want to show the editor as blurred
8384
    on(window, "blur", function() {
8385
      forEachCodeMirror(onBlur);
8386
    });
8387
  }
8388
8389
  // FEATURE DETECTION
8390
8391
  // Detect drag-and-drop
8392
  var dragAndDrop = function() {
8393
    // There is *some* kind of drag-and-drop support in IE6-8, but I
8394
    // couldn't get it to work yet.
8395
    if (ie && ie_version < 9) return false;
8396
    var div = elt('div');
8397
    return "draggable" in div || "dragDrop" in div;
8398
  }();
8399
8400
  var zwspSupported;
8401
  function zeroWidthElement(measure) {
8402
    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...
8403
      var test = elt("span", "\u200b");
8404
      removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]));
8405
      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...
8406
        zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8);
8407
    }
8408
    var node = zwspSupported ? elt("span", "\u200b") :
8409
      elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
8410
    node.setAttribute("cm-text", "");
8411
    return node;
8412
  }
8413
8414
  // Feature-detect IE's crummy client rect reporting for bidi text
8415
  var badBidiRects;
8416
  function hasBadBidiRects(measure) {
8417
    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...
8418
    var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA"));
8419
    var r0 = range(txt, 0, 1).getBoundingClientRect();
8420
    if (!r0 || r0.left == r0.right) return false; // Safari returns null in some cases (#2780)
8421
    var r1 = range(txt, 1, 2).getBoundingClientRect();
8422
    return badBidiRects = (r1.right - r0.right < 3);
8423
  }
8424
8425
  // See if "".split is the broken IE version, if so, provide an
8426
  // alternative way to split lines.
8427
  var splitLinesAuto = CodeMirror.splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) {
8428
    var pos = 0, result = [], l = string.length;
8429
    while (pos <= l) {
8430
      var nl = string.indexOf("\n", pos);
8431
      if (nl == -1) nl = string.length;
8432
      var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
8433
      var rt = line.indexOf("\r");
8434
      if (rt != -1) {
8435
        result.push(line.slice(0, rt));
8436
        pos += rt + 1;
8437
      } else {
8438
        result.push(line);
8439
        pos = nl + 1;
8440
      }
8441
    }
8442
    return result;
8443
  } : function(string){return string.split(/\r\n?|\n/);};
8444
8445
  var hasSelection = window.getSelection ? function(te) {
8446
    try { return te.selectionStart != te.selectionEnd; }
8447
    catch(e) { return false; }
8448
  } : function(te) {
8449
    try {var range = te.ownerDocument.selection.createRange();}
8450
    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...
8451
    if (!range || range.parentElement() != te) return false;
8452
    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...
8453
  };
8454
8455
  var hasCopyEvent = (function() {
8456
    var e = elt("div");
8457
    if ("oncopy" in e) return true;
8458
    e.setAttribute("oncopy", "return;");
8459
    return typeof e.oncopy == "function";
8460
  })();
8461
8462
  var badZoomedRects = null;
8463
  function hasBadZoomedRects(measure) {
8464
    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...
8465
    var node = removeChildrenAndAdd(measure, elt("span", "x"));
8466
    var normal = node.getBoundingClientRect();
8467
    var fromRange = range(node, 0, 1).getBoundingClientRect();
8468
    return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1;
8469
  }
8470
8471
  // KEY NAMES
8472
8473
  var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
8474
                  19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
8475
                  36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
8476
                  46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod", 107: "=", 109: "-", 127: "Delete",
8477
                  173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
8478
                  221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete",
8479
                  63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert"};
8480
  CodeMirror.keyNames = keyNames;
8481
  (function() {
8482
    // Number keys
8483
    for (var i = 0; i < 10; i++) keyNames[i + 48] = keyNames[i + 96] = String(i);
8484
    // Alphabetic keys
8485
    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 8483. 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...
8486
    // Function keys
8487
    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 8483. 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...
8488
  })();
8489
8490
  // BIDI HELPERS
8491
8492
  function iterateBidiSections(order, from, to, f) {
8493
    if (!order) return f(from, to, "ltr");
8494
    var found = false;
8495
    for (var i = 0; i < order.length; ++i) {
8496
      var part = order[i];
8497
      if (part.from < to && part.to > from || from == to && part.to == from) {
8498
        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...
8499
        found = true;
8500
      }
8501
    }
8502
    if (!found) f(from, to, "ltr");
0 ignored issues
show
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...
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...
8503
  }
8504
8505
  function bidiLeft(part) { return part.level % 2 ? part.to : part.from; }
8506
  function bidiRight(part) { return part.level % 2 ? part.from : part.to; }
8507
8508
  function lineLeft(line) { var order = getOrder(line); return order ? bidiLeft(order[0]) : 0; }
8509
  function lineRight(line) {
8510
    var order = getOrder(line);
8511
    if (!order) return line.text.length;
8512
    return bidiRight(lst(order));
8513
  }
8514
8515
  function lineStart(cm, lineN) {
8516
    var line = getLine(cm.doc, lineN);
8517
    var visual = visualLine(line);
8518
    if (visual != line) lineN = lineNo(visual);
8519
    var order = getOrder(visual);
8520
    var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual);
8521
    return Pos(lineN, ch);
8522
  }
8523
  function lineEnd(cm, lineN) {
8524
    var merged, line = getLine(cm.doc, lineN);
8525
    while (merged = collapsedSpanAtEnd(line)) {
8526
      line = merged.find(1, true).line;
8527
      lineN = null;
8528
    }
8529
    var order = getOrder(line);
8530
    var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line);
8531
    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...
8532
  }
8533
  function lineStartSmart(cm, pos) {
8534
    var start = lineStart(cm, pos.line);
8535
    var line = getLine(cm.doc, start.line);
8536
    var order = getOrder(line);
8537
    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...
8538
      var firstNonWS = Math.max(0, line.text.search(/\S/));
8539
      var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch;
8540
      return Pos(start.line, inWS ? 0 : firstNonWS);
8541
    }
8542
    return start;
8543
  }
8544
8545
  function compareBidiLevel(order, a, b) {
8546
    var linedir = order[0].level;
8547
    if (a == linedir) return true;
8548
    if (b == linedir) return false;
8549
    return a < b;
8550
  }
8551
  var bidiOther;
8552
  function getBidiPartAt(order, pos) {
8553
    bidiOther = null;
8554
    for (var i = 0, found; i < order.length; ++i) {
8555
      var cur = order[i];
8556
      if (cur.from < pos && cur.to > pos) return i;
8557
      if ((cur.from == pos || cur.to == pos)) {
8558
        if (found == null) {
0 ignored issues
show
Best Practice introduced by
Comparing found to null using the == operator is not safe. Consider using === instead.
Loading history...
Bug introduced by
The variable found seems to not be initialized for all possible execution paths.
Loading history...
8559
          found = i;
8560
        } else if (compareBidiLevel(order, cur.level, order[found].level)) {
8561
          if (cur.from != cur.to) bidiOther = found;
8562
          return i;
8563
        } else {
8564
          if (cur.from != cur.to) bidiOther = i;
8565
          return found;
8566
        }
8567
      }
8568
    }
8569
    return found;
8570
  }
8571
8572
  function moveInLine(line, pos, dir, byUnit) {
8573
    if (!byUnit) return pos + dir;
8574
    do pos += dir;
8575
    while (pos > 0 && isExtendingChar(line.text.charAt(pos)));
8576
    return pos;
8577
  }
8578
8579
  // This is needed in order to move 'visually' through bi-directional
8580
  // text -- i.e., pressing left should make the cursor go left, even
8581
  // when in RTL text. The tricky part is the 'jumps', where RTL and
8582
  // LTR text touch each other. This often requires the cursor offset
8583
  // to move more than one unit, in order to visually move one unit.
8584
  function moveVisually(line, start, dir, byUnit) {
8585
    var bidi = getOrder(line);
8586
    if (!bidi) return moveLogically(line, start, dir, byUnit);
8587
    var pos = getBidiPartAt(bidi, start), part = bidi[pos];
8588
    var target = moveInLine(line, start, part.level % 2 ? -dir : dir, byUnit);
8589
8590
    for (;;) {
8591
      if (target > part.from && target < part.to) return target;
8592
      if (target == part.from || target == part.to) {
8593
        if (getBidiPartAt(bidi, target) == pos) return target;
8594
        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...
8595
        return (dir > 0) == part.level % 2 ? part.to : part.from;
8596
      } 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...
8597
        part = bidi[pos += dir];
8598
        if (!part) return null;
8599
        if ((dir > 0) == part.level % 2)
8600
          target = moveInLine(line, part.to, -1, byUnit);
8601
        else
8602
          target = moveInLine(line, part.from, 1, byUnit);
8603
      }
8604
    }
8605
  }
8606
8607
  function moveLogically(line, start, dir, byUnit) {
8608
    var target = start + dir;
8609
    if (byUnit) while (target > 0 && isExtendingChar(line.text.charAt(target))) target += dir;
8610
    return target < 0 || target > line.text.length ? null : target;
8611
  }
8612
8613
  // Bidirectional ordering algorithm
8614
  // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
8615
  // that this (partially) implements.
8616
8617
  // One-char codes used for character types:
8618
  // L (L):   Left-to-Right
8619
  // R (R):   Right-to-Left
8620
  // r (AL):  Right-to-Left Arabic
8621
  // 1 (EN):  European Number
8622
  // + (ES):  European Number Separator
8623
  // % (ET):  European Number Terminator
8624
  // n (AN):  Arabic Number
8625
  // , (CS):  Common Number Separator
8626
  // m (NSM): Non-Spacing Mark
8627
  // b (BN):  Boundary Neutral
8628
  // s (B):   Paragraph Separator
8629
  // t (S):   Segment Separator
8630
  // w (WS):  Whitespace
8631
  // N (ON):  Other Neutrals
8632
8633
  // Returns null if characters are ordered as they appear
8634
  // (left-to-right), or an array of sections ({from, to, level}
8635
  // objects) in the order in which they occur visually.
8636
  var bidiOrdering = (function() {
8637
    // Character types for codepoints 0 to 0xff
8638
    var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN";
8639
    // Character types for codepoints 0x600 to 0x6ff
8640
    var arabicTypes = "rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmm";
8641
    function charType(code) {
8642
      if (code <= 0xf7) return lowTypes.charAt(code);
8643
      else if (0x590 <= code && code <= 0x5f4) return "R";
8644
      else if (0x600 <= code && code <= 0x6ed) return arabicTypes.charAt(code - 0x600);
8645
      else if (0x6ee <= code && code <= 0x8ac) return "r";
8646
      else if (0x2000 <= code && code <= 0x200b) return "w";
8647
      else if (code == 0x200c) return "b";
8648
      else return "L";
8649
    }
8650
8651
    var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
8652
    var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
8653
    // Browsers seem to always treat the boundaries of block elements as being L.
8654
    var outerType = "L";
8655
8656
    function BidiSpan(level, from, to) {
8657
      this.level = level;
8658
      this.from = from; this.to = to;
8659
    }
8660
8661
    return function(str) {
8662
      if (!bidiRE.test(str)) return false;
8663
      var len = str.length, types = [];
8664
      for (var i = 0, type; i < len; ++i)
8665
        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...
8666
8667
      // W1. Examine each non-spacing mark (NSM) in the level run, and
8668
      // change the type of the NSM to the type of the previous
8669
      // character. If the NSM is at the start of the level run, it will
8670
      // get the type of sor.
8671
      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 8664. 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...
8672
        var type = types[i];
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable type already seems to be declared on line 8664. 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...
8673
        if (type == "m") types[i] = prev;
8674
        else prev = type;
8675
      }
8676
8677
      // W2. Search backwards from each instance of a European number
8678
      // until the first strong type (R, L, AL, or sor) is found. If an
8679
      // AL is found, change the type of the European number to Arabic
8680
      // number.
8681
      // W3. Change all ALs to R.
8682
      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 8664. 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...
8683
        var type = types[i];
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable type already seems to be declared on line 8664. 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...
8684
        if (type == "1" && cur == "r") types[i] = "n";
8685
        else if (isStrong.test(type)) { cur = type; if (type == "r") types[i] = "R"; }
8686
      }
8687
8688
      // W4. A single European separator between two European numbers
8689
      // changes to a European number. A single common separator between
8690
      // two numbers of the same type changes to that type.
8691
      for (var i = 1, prev = types[0]; i < len - 1; ++i) {
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable prev already seems to be declared on line 8671. 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 8664. 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...
8692
        var type = types[i];
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable type already seems to be declared on line 8664. 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...
8693
        if (type == "+" && prev == "1" && types[i+1] == "1") types[i] = "1";
8694
        else if (type == "," && prev == types[i+1] &&
8695
                 (prev == "1" || prev == "n")) types[i] = prev;
8696
        prev = type;
8697
      }
8698
8699
      // W5. A sequence of European terminators adjacent to European
8700
      // numbers changes to all European numbers.
8701
      // W6. Otherwise, separators and terminators change to Other
8702
      // Neutral.
8703
      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 8664. 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...
8704
        var type = types[i];
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable type already seems to be declared on line 8664. 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...
8705
        if (type == ",") types[i] = "N";
8706
        else if (type == "%") {
8707
          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...
8708
          var replace = (i && types[i-1] == "!") || (end < len && types[end] == "1") ? "1" : "N";
8709
          for (var j = i; j < end; ++j) types[j] = replace;
8710
          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...
8711
        }
8712
      }
8713
8714
      // W7. Search backwards from each instance of a European number
8715
      // until the first strong type (R, L, or sor) is found. If an L is
8716
      // found, then change the type of the European number to L.
8717
      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 8664. 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 cur already seems to be declared on line 8682. 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...
8718
        var type = types[i];
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable type already seems to be declared on line 8664. 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...
8719
        if (cur == "L" && type == "1") types[i] = "L";
8720
        else if (isStrong.test(type)) cur = type;
8721
      }
8722
8723
      // N1. A sequence of neutrals takes the direction of the
8724
      // surrounding strong text if the text on both sides has the same
8725
      // direction. European and Arabic numbers act as if they were R in
8726
      // terms of their influence on neutrals. Start-of-level-run (sor)
8727
      // and end-of-level-run (eor) are used at level run boundaries.
8728
      // N2. Any remaining neutrals take the embedding direction.
8729
      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 8664. 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...
8730
        if (isNeutral.test(types[i])) {
8731
          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 8707. 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...
8732
          var before = (i ? types[i-1] : outerType) == "L";
8733
          var after = (end < len ? types[end] : outerType) == "L";
8734
          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 8708. 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...
8735
          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 8709. 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...
8736
          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...
8737
        }
8738
      }
8739
8740
      // Here we depart from the documented algorithm, in order to avoid
8741
      // building up an actual levels array. Since there are only three
8742
      // levels (0, 1, 2) in an implementation that doesn't take
8743
      // explicit embedding into account, we can build up the order on
8744
      // the fly, without following the level-based algorithm.
8745
      var order = [], m;
8746
      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 8664. 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...
8747
        if (countsAsLeft.test(types[i])) {
8748
          var start = i;
8749
          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...
8750
          order.push(new BidiSpan(0, start, i));
8751
        } else {
8752
          var pos = i, at = order.length;
8753
          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...
8754
          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 8709. 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...
8755
            if (countsAsNum.test(types[j])) {
8756
              if (pos < j) order.splice(at, 0, new BidiSpan(1, pos, j));
8757
              var nstart = j;
8758
              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...
8759
              order.splice(at, 0, new BidiSpan(2, nstart, j));
8760
              pos = j;
8761
            } 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...
8762
          }
8763
          if (pos < i) order.splice(at, 0, new BidiSpan(1, pos, i));
8764
        }
8765
      }
8766
      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...
8767
        order[0].from = m[0].length;
8768
        order.unshift(new BidiSpan(0, 0, m[0].length));
8769
      }
8770
      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...
8771
        lst(order).to -= m[0].length;
8772
        order.push(new BidiSpan(0, len - m[0].length, len));
8773
      }
8774
      if (order[0].level == 2)
8775
        order.unshift(new BidiSpan(1, order[0].to, order[0].to));
8776
      if (order[0].level != lst(order).level)
8777
        order.push(new BidiSpan(order[0].level, len, len));
8778
8779
      return order;
8780
    };
8781
  })();
8782
8783
  // THE END
8784
8785
  CodeMirror.version = "5.5.1";
8786
8787
  return CodeMirror;
8788
});
8789