Completed
Push — develop ( 48b424...bab30d )
by Daniel
15:53 queued 09:41
created

codemirror.js ➔ ... ➔ CodeMirror   F

Complexity

Conditions 19
Paths 1537

Size

Total Lines 69

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 69
rs 2.6059
c 0
b 0
f 0
cc 19
nc 1537
nop 2

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like codemirror.js ➔ ... ➔ CodeMirror 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 (global, factory) {
11
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
12
  typeof define === 'function' && define.amd ? define(factory) :
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...
13
  (global.CodeMirror = factory());
14
}(this, (function () { 'use strict';
15
16
// Kludges for bugs and behavior differences that can't be feature
17
// detected are enabled based on userAgent etc sniffing.
18
var userAgent = 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...
19
var platform = navigator.platform
20
21
var gecko = /gecko\/\d/i.test(userAgent)
22
var ie_upto10 = /MSIE \d/.test(userAgent)
23
var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent)
24
var ie = ie_upto10 || ie_11up
25
var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : ie_11up[1])
26
var webkit = /WebKit\//.test(userAgent)
27
var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent)
28
var chrome = /Chrome\//.test(userAgent)
29
var presto = /Opera\//.test(userAgent)
30
var safari = /Apple Computer/.test(navigator.vendor)
31
var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent)
32
var phantom = /PhantomJS/.test(userAgent)
33
34
var ios = /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent)
35
// This is woefully incomplete. Suggestions for alternative methods welcome.
36
var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent)
37
var mac = ios || /Mac/.test(platform)
38
var chromeOS = /\bCrOS\b/.test(userAgent)
39
var windows = /win/i.test(platform)
40
41
var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/)
42
if (presto_version) { presto_version = Number(presto_version[1]) }
43
if (presto_version && presto_version >= 15) { presto = false; webkit = true }
44
// Some browsers use the wrong event properties to signal cmd/ctrl on OS X
45
var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11))
46
var captureRightClick = gecko || (ie && ie_version >= 9)
47
48
function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") }
49
50
var rmClass = function(node, cls) {
51
  var current = node.className
52
  var match = classTest(cls).exec(current)
53
  if (match) {
54
    var after = current.slice(match.index + match[0].length)
55
    node.className = current.slice(0, match.index) + (after ? match[1] + after : "")
56
  }
57
}
58
59
function removeChildren(e) {
60
  for (var count = e.childNodes.length; count > 0; --count)
61
    { e.removeChild(e.firstChild) }
62
  return e
63
}
64
65
function removeChildrenAndAdd(parent, e) {
66
  return removeChildren(parent).appendChild(e)
67
}
68
69
function elt(tag, content, className, style) {
70
  var e = document.createElement(tag)
71
  if (className) { e.className = className }
72
  if (style) { e.style.cssText = style }
73
  if (typeof content == "string") { e.appendChild(document.createTextNode(content)) }
74
  else if (content) { for (var i = 0; i < content.length; ++i) { e.appendChild(content[i]) } }
75
  return e
76
}
77
78
var range
79
if (document.createRange) { range = function(node, start, end, endNode) {
80
  var r = document.createRange()
81
  r.setEnd(endNode || node, end)
82
  r.setStart(node, start)
83
  return r
84
} }
85
else { range = function(node, start, end) {
86
  var r = document.body.createTextRange()
87
  try { r.moveToElementText(node.parentNode) }
88
  catch(e) { return r }
89
  r.collapse(true)
90
  r.moveEnd("character", end)
91
  r.moveStart("character", start)
92
  return r
93
} }
94
95
function contains(parent, child) {
96
  if (child.nodeType == 3) // Android browser always returns false when child is a textnode
97
    { child = child.parentNode }
98
  if (parent.contains)
99
    { return parent.contains(child) }
100
  do {
101
    if (child.nodeType == 11) { child = child.host }
102
    if (child == parent) { return true }
103
  } 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...
104
}
105
106
function activeElt() {
107
  // IE and Edge may throw an "Unspecified Error" when accessing document.activeElement.
108
  // IE < 10 will throw when accessed while the page is loading or in an iframe.
109
  // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable.
110
  var activeElement
111
  try {
112
    activeElement = document.activeElement
113
  } catch(e) {
114
    activeElement = document.body || null
115
  }
116
  while (activeElement && activeElement.root && activeElement.root.activeElement)
117
    { activeElement = activeElement.root.activeElement }
118
  return activeElement
119
}
120
121
function addClass(node, cls) {
122
  var current = node.className
123
  if (!classTest(cls).test(current)) { node.className += (current ? " " : "") + cls }
124
}
125
function joinClasses(a, b) {
126
  var as = a.split(" ")
127
  for (var i = 0; i < as.length; i++)
128
    { if (as[i] && !classTest(as[i]).test(b)) { b += " " + as[i] } }
129
  return b
130
}
131
132
var selectInput = function(node) { node.select() }
133
if (ios) // Mobile Safari apparently has a bug where select() is broken.
134
  { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length } }
135
else if (ie) // Suppress mysterious IE10 errors
136
  { selectInput = function(node) { try { node.select() } catch(_e) {} } }
137
138
function bind(f) {
139
  var args = Array.prototype.slice.call(arguments, 1)
140
  return function(){return f.apply(null, args)}
141
}
142
143
function copyObj(obj, target, overwrite) {
144
  if (!target) { target = {} }
145
  for (var prop in obj)
146
    { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
147
      { target[prop] = obj[prop] } }
148
  return target
149
}
150
151
// Counts the column offset in a string, taking tabs into account.
152
// Used mostly to find indentation.
153
function countColumn(string, end, tabSize, startIndex, startValue) {
154
  if (end == null) {
155
    end = string.search(/[^\s\u00a0]/)
156
    if (end == -1) { end = string.length }
157
  }
158
  for (var i = startIndex || 0, n = startValue || 0;;) {
159
    var nextTab = string.indexOf("\t", i)
160
    if (nextTab < 0 || nextTab >= end)
161
      { return n + (end - i) }
162
    n += nextTab - i
163
    n += tabSize - (n % tabSize)
164
    i = nextTab + 1
165
  }
166
}
167
168
function Delayed() {this.id = null}
169
Delayed.prototype.set = function(ms, f) {
170
  clearTimeout(this.id)
171
  this.id = setTimeout(f, ms)
172
}
173
174
function indexOf(array, elt) {
175
  for (var i = 0; i < array.length; ++i)
176
    { if (array[i] == elt) { return i } }
177
  return -1
178
}
179
180
// Number of pixels added to scroller and sizer to hide scrollbar
181
var scrollerGap = 30
182
183
// Returned or thrown by various protocols to signal 'I'm not
184
// handling this'.
185
var Pass = {toString: function(){return "CodeMirror.Pass"}}
186
187
// Reused option objects for setSelection & friends
188
var sel_dontScroll = {scroll: false};
189
var sel_mouse = {origin: "*mouse"};
190
var sel_move = {origin: "+move"};
191
// The inverse of countColumn -- find the offset that corresponds to
192
// a particular column.
193
function findColumn(string, goal, tabSize) {
194
  for (var pos = 0, col = 0;;) {
195
    var nextTab = string.indexOf("\t", pos)
196
    if (nextTab == -1) { nextTab = string.length }
197
    var skipped = nextTab - pos
198
    if (nextTab == string.length || col + skipped >= goal)
199
      { return pos + Math.min(skipped, goal - col) }
200
    col += nextTab - pos
201
    col += tabSize - (col % tabSize)
202
    pos = nextTab + 1
203
    if (col >= goal) { return pos }
204
  }
205
}
206
207
var spaceStrs = [""]
208
function spaceStr(n) {
209
  while (spaceStrs.length <= n)
210
    { spaceStrs.push(lst(spaceStrs) + " ") }
211
  return spaceStrs[n]
212
}
213
214
function lst(arr) { return arr[arr.length-1] }
215
216
function map(array, f) {
217
  var out = []
218
  for (var i = 0; i < array.length; i++) { out[i] = f(array[i], i) }
219
  return out
220
}
221
222
function insertSorted(array, value, score) {
223
  var pos = 0, priority = score(value)
224
  while (pos < array.length && score(array[pos]) <= priority) { pos++ }
225
  array.splice(pos, 0, value)
226
}
227
228
function nothing() {}
229
230
function createObj(base, props) {
231
  var inst
232
  if (Object.create) {
233
    inst = Object.create(base)
234
  } else {
235
    nothing.prototype = base
236
    inst = new nothing()
0 ignored issues
show
Coding Style Best Practice introduced by
By convention, constructors like nothing should be capitalized.
Loading history...
237
  }
238
  if (props) { copyObj(props, inst) }
239
  return inst
240
}
241
242
var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/
243
function isWordCharBasic(ch) {
244
  return /\w/.test(ch) || ch > "\x80" &&
245
    (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch))
246
}
247
function isWordChar(ch, helper) {
248
  if (!helper) { return isWordCharBasic(ch) }
249
  if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) { return true }
250
  return helper.test(ch)
251
}
252
253
function isEmpty(obj) {
254
  for (var n in obj) { if (obj.hasOwnProperty(n) && obj[n]) { return false } }
255
  return true
256
}
257
258
// Extending unicode characters. A series of a non-extending char +
259
// any number of extending chars is treated as a single unit as far
260
// as editing and measuring is concerned. This is not fully correct,
261
// since some scripts/fonts/browsers also treat other configurations
262
// of code points as a group.
263
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]/
264
function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) }
265
266
// The display handles the DOM integration, both for input reading
267
// and content drawing. It holds references to DOM nodes and
268
// display-related state.
269
270
function Display(place, doc, input) {
271
  var d = this
272
  this.input = input
273
274
  // Covers bottom-right square when both scrollbars are present.
275
  d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler")
276
  d.scrollbarFiller.setAttribute("cm-not-content", "true")
277
  // Covers bottom of gutter when coverGutterNextToScrollbar is on
278
  // and h scrollbar is present.
279
  d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler")
280
  d.gutterFiller.setAttribute("cm-not-content", "true")
281
  // Will contain the actual code, positioned to cover the viewport.
282
  d.lineDiv = elt("div", null, "CodeMirror-code")
283
  // Elements are added to these to represent selection and cursors.
284
  d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1")
285
  d.cursorDiv = elt("div", null, "CodeMirror-cursors")
286
  // A visibility: hidden element used to find the size of things.
287
  d.measure = elt("div", null, "CodeMirror-measure")
288
  // When lines outside of the viewport are measured, they are drawn in this.
289
  d.lineMeasure = elt("div", null, "CodeMirror-measure")
290
  // Wraps everything that needs to exist inside the vertically-padded coordinate system
291
  d.lineSpace = elt("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],
292
                    null, "position: relative; outline: none")
293
  // Moved around its parent to cover visible view.
294
  d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null, "position: relative")
295
  // Set to the height of the document, allowing scrolling.
296
  d.sizer = elt("div", [d.mover], "CodeMirror-sizer")
297
  d.sizerWidth = null
298
  // Behavior of elts with overflow: auto and padding is
299
  // inconsistent across browsers. This is used to ensure the
300
  // scrollable area is big enough.
301
  d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;")
302
  // Will contain the gutters, if any.
303
  d.gutters = elt("div", null, "CodeMirror-gutters")
304
  d.lineGutter = null
305
  // Actual scrollable element.
306
  d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll")
307
  d.scroller.setAttribute("tabIndex", "-1")
308
  // The element in which the editor lives.
309
  d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror")
310
311
  // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)
312
  if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0 }
313
  if (!webkit && !(gecko && mobile)) { d.scroller.draggable = true }
314
315
  if (place) {
316
    if (place.appendChild) { place.appendChild(d.wrapper) }
317
    else { place(d.wrapper) }
318
  }
319
320
  // Current rendered range (may be bigger than the view window).
321
  d.viewFrom = d.viewTo = doc.first
322
  d.reportedViewFrom = d.reportedViewTo = doc.first
323
  // Information about the rendered lines.
324
  d.view = []
325
  d.renderedView = null
326
  // Holds info about a single rendered line when it was rendered
327
  // for measurement, while not in view.
328
  d.externalMeasured = null
329
  // Empty space (in pixels) above the view
330
  d.viewOffset = 0
331
  d.lastWrapHeight = d.lastWrapWidth = 0
332
  d.updateLineNumbers = null
333
334
  d.nativeBarWidth = d.barHeight = d.barWidth = 0
335
  d.scrollbarsClipped = false
336
337
  // Used to only resize the line number gutter when necessary (when
338
  // the amount of lines crosses a boundary that makes its width change)
339
  d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null
340
  // Set to true when a non-horizontal-scrolling line widget is
341
  // added. As an optimization, line widget aligning is skipped when
342
  // this is false.
343
  d.alignWidgets = false
344
345
  d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null
346
347
  // Tracks the maximum line length so that the horizontal scrollbar
348
  // can be kept static when scrolling.
349
  d.maxLine = null
350
  d.maxLineLength = 0
351
  d.maxLineChanged = false
352
353
  // Used for measuring wheel scrolling granularity
354
  d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null
355
356
  // True when shift is held down.
357
  d.shift = false
358
359
  // Used to track whether anything happened since the context menu
360
  // was opened.
361
  d.selForContextMenu = null
362
363
  d.activeTouch = null
364
365
  input.init(d)
366
}
367
368
// Find the line object corresponding to the given line number.
369
function getLine(doc, n) {
370
  n -= doc.first
371
  if (n < 0 || n >= doc.size) { throw new Error("There is no line " + (n + doc.first) + " in the document.") }
372
  var chunk = doc
373
  while (!chunk.lines) {
374
    for (var i = 0;; ++i) {
375
      var child = chunk.children[i], sz = child.chunkSize()
376
      if (n < sz) { chunk = child; break }
377
      n -= sz
378
    }
379
  }
380
  return chunk.lines[n]
381
}
382
383
// Get the part of a document between two positions, as an array of
384
// strings.
385
function getBetween(doc, start, end) {
386
  var out = [], n = start.line
387
  doc.iter(start.line, end.line + 1, function (line) {
388
    var text = line.text
389
    if (n == end.line) { text = text.slice(0, end.ch) }
390
    if (n == start.line) { text = text.slice(start.ch) }
391
    out.push(text)
392
    ++n
393
  })
394
  return out
395
}
396
// Get the lines between from and to, as array of strings.
397
function getLines(doc, from, to) {
398
  var out = []
399
  doc.iter(from, to, function (line) { out.push(line.text) }) // iter aborts when callback returns truthy value
400
  return out
401
}
402
403
// Update the height of a line, propagating the height change
404
// upwards to parent nodes.
405
function updateLineHeight(line, height) {
406
  var diff = height - line.height
407
  if (diff) { for (var n = line; n; n = n.parent) { n.height += diff } }
408
}
409
410
// Given a line object, find its line number by walking up through
411
// its parent links.
412
function lineNo(line) {
413
  if (line.parent == null) { return null }
414
  var cur = line.parent, no = indexOf(cur.lines, line)
415
  for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
416
    for (var i = 0;; ++i) {
417
      if (chunk.children[i] == cur) { break }
418
      no += chunk.children[i].chunkSize()
419
    }
420
  }
421
  return no + cur.first
422
}
423
424
// Find the line at the given vertical position, using the height
425
// information in the document tree.
426
function lineAtHeight(chunk, h) {
427
  var n = chunk.first
428
  outer: do {
429
    for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) {
430
      var child = chunk.children[i$1], ch = child.height
431
      if (h < ch) { chunk = child; continue outer }
432
      h -= ch
433
      n += child.chunkSize()
434
    }
435
    return n
436
  } while (!chunk.lines)
437
  var i = 0
438
  for (; i < chunk.lines.length; ++i) {
439
    var line = chunk.lines[i], lh = line.height
440
    if (h < lh) { break }
441
    h -= lh
442
  }
443
  return n + i
444
}
445
446
function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size}
447
448
function lineNumberFor(options, i) {
449
  return String(options.lineNumberFormatter(i + options.firstLineNumber))
450
}
451
452
// A Pos instance represents a position within the text.
453
function Pos (line, ch) {
454
  if (!(this instanceof Pos)) { return new Pos(line, ch) }
455
  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...
456
}
457
458
// Compare two positions, return 0 if they are the same, a negative
459
// number when a is less, and a positive number otherwise.
460
function cmp(a, b) { return a.line - b.line || a.ch - b.ch }
461
462
function copyPos(x) {return Pos(x.line, x.ch)}
463
function maxPos(a, b) { return cmp(a, b) < 0 ? b : a }
464
function minPos(a, b) { return cmp(a, b) < 0 ? a : b }
465
466
// Most of the external API clips given positions to make sure they
467
// actually exist within the document.
468
function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))}
469
function clipPos(doc, pos) {
470
  if (pos.line < doc.first) { return Pos(doc.first, 0) }
471
  var last = doc.first + doc.size - 1
472
  if (pos.line > last) { return Pos(last, getLine(doc, last).text.length) }
473
  return clipToLen(pos, getLine(doc, pos.line).text.length)
474
}
475
function clipToLen(pos, linelen) {
476
  var ch = pos.ch
477
  if (ch == null || ch > linelen) { return Pos(pos.line, linelen) }
478
  else if (ch < 0) { return Pos(pos.line, 0) }
479
  else { return pos }
480
}
481
function clipPosArray(doc, array) {
482
  var out = []
483
  for (var i = 0; i < array.length; i++) { out[i] = clipPos(doc, array[i]) }
484
  return out
485
}
486
487
// Optimize some code when these features are not used.
488
var sawReadOnlySpans = false;
489
var sawCollapsedSpans = false;
490
function seeReadOnlySpans() {
491
  sawReadOnlySpans = true
492
}
493
494
function seeCollapsedSpans() {
495
  sawCollapsedSpans = true
496
}
497
498
// TEXTMARKER SPANS
499
500
function MarkedSpan(marker, from, to) {
501
  this.marker = marker
502
  this.from = from; this.to = to
503
}
504
505
// Search an array of spans for a span matching the given marker.
506
function getMarkedSpanFor(spans, marker) {
507
  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...
508
    var span = spans[i]
509
    if (span.marker == marker) { return span }
510
  } }
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...
511
}
512
// Remove a span from an array, returning undefined if no spans are
513
// left (we don't store arrays for lines without spans).
514
function removeMarkedSpan(spans, span) {
515
  var r
516
  for (var i = 0; i < spans.length; ++i)
517
    { if (spans[i] != span) { (r || (r = [])).push(spans[i]) } }
518
  return r
0 ignored issues
show
Bug introduced by
The variable r seems to not be initialized for all possible execution paths.
Loading history...
519
}
520
// Add a span to a line.
521
function addMarkedSpan(line, span) {
522
  line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span]
523
  span.marker.attachLine(line)
524
}
525
526
// Used for the algorithm that adjusts markers for a change in the
527
// document. These functions cut an array of spans at a given
528
// character position, returning an array of remaining chunks (or
529
// undefined if nothing remains).
530 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...
531
  var nw
532
  if (old) { for (var i = 0; i < old.length; ++i) {
533
    var span = old[i], marker = span.marker
534
    var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh)
535
    if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) {
536
      var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh)
537
      ;(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to))
538
    }
539
  } }
540
  return nw
0 ignored issues
show
Bug introduced by
The variable nw seems to not be initialized for all possible execution paths.
Loading history...
541
}
542 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...
543
  var nw
544
  if (old) { for (var i = 0; i < old.length; ++i) {
545
    var span = old[i], marker = span.marker
546
    var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh)
547
    if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) {
548
      var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh)
549
      ;(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh,
550
                                            span.to == null ? null : span.to - endCh))
551
    }
552
  } }
553
  return nw
0 ignored issues
show
Bug introduced by
The variable nw seems to not be initialized for all possible execution paths.
Loading history...
554
}
555
556
// Given a change object, compute the new set of marker spans that
557
// cover the line in which the change took place. Removes spans
558
// entirely within the change, reconnects spans belonging to the
559
// same marker that appear on both sides of the change, and cuts off
560
// spans partially within the change. Returns an array of span
561
// arrays with one element for each line in (after) the change.
562
function stretchSpansOverChange(doc, change) {
563
  if (change.full) { return null }
564
  var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans
565
  var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans
566
  if (!oldFirst && !oldLast) { return null }
567
568
  var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0
569
  // Get the spans that 'stick out' on both sides
570
  var first = markedSpansBefore(oldFirst, startCh, isInsert)
571
  var last = markedSpansAfter(oldLast, endCh, isInsert)
572
573
  // Next, merge those two ends
574
  var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0)
575
  if (first) {
576
    // Fix up .to properties of first
577
    for (var i = 0; i < first.length; ++i) {
578
      var span = first[i]
579
      if (span.to == null) {
580
        var found = getMarkedSpanFor(last, span.marker)
581
        if (!found) { span.to = startCh }
582
        else if (sameLine) { span.to = found.to == null ? null : found.to + offset }
583
      }
584
    }
585
  }
586
  if (last) {
587
    // Fix up .from in last (or move them into first in case of sameLine)
588
    for (var i$1 = 0; i$1 < last.length; ++i$1) {
589
      var span$1 = last[i$1]
590
      if (span$1.to != null) { span$1.to += offset }
591
      if (span$1.from == null) {
592
        var found$1 = getMarkedSpanFor(first, span$1.marker)
593
        if (!found$1) {
594
          span$1.from = offset
595
          if (sameLine) { (first || (first = [])).push(span$1) }
596
        }
597
      } else {
598
        span$1.from += offset
599
        if (sameLine) { (first || (first = [])).push(span$1) }
600
      }
601
    }
602
  }
603
  // Make sure we didn't create any zero-length spans
604
  if (first) { first = clearEmptySpans(first) }
605
  if (last && last != first) { last = clearEmptySpans(last) }
606
607
  var newMarkers = [first]
608
  if (!sameLine) {
609
    // Fill gap with whole-line-spans
610
    var gap = change.text.length - 2, gapMarkers
611
    if (gap > 0 && first)
612
      { for (var i$2 = 0; i$2 < first.length; ++i$2)
613
        { if (first[i$2].to == null)
614
          { (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i$2].marker, null, null)) } } }
615
    for (var i$3 = 0; i$3 < gap; ++i$3)
616
      { 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...
617
    newMarkers.push(last)
618
  }
619
  return newMarkers
620
}
621
622
// Remove spans that are empty and don't have a clearWhenEmpty
623
// option of false.
624
function clearEmptySpans(spans) {
625
  for (var i = 0; i < spans.length; ++i) {
626
    var span = spans[i]
627
    if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)
628
      { spans.splice(i--, 1) }
629
  }
630
  if (!spans.length) { return null }
631
  return spans
632
}
633
634
// Used to 'clip' out readOnly ranges when making a change.
635
function removeReadOnlyRanges(doc, from, to) {
636
  var markers = null
637
  doc.iter(from.line, to.line + 1, function (line) {
638
    if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
639
      var mark = line.markedSpans[i].marker
640
      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 641. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
641
        { (markers || (markers = [])).push(mark) }
642
    } }
643
  })
644
  if (!markers) { return null }
645
  var parts = [{from: from, to: to}]
646
  for (var i = 0; i < markers.length; ++i) {
647
    var mk = markers[i], m = mk.find(0)
648
    for (var j = 0; j < parts.length; ++j) {
649
      var p = parts[j]
650
      if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) { continue }
651
      var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to)
652
      if (dfrom < 0 || !mk.inclusiveLeft && !dfrom)
653
        { newParts.push({from: p.from, to: m.from}) }
654
      if (dto > 0 || !mk.inclusiveRight && !dto)
655
        { newParts.push({from: m.to, to: p.to}) }
656
      parts.splice.apply(parts, newParts)
657
      j += newParts.length - 1
658
    }
659
  }
660
  return parts
661
}
662
663
// Connect or disconnect spans from a line.
664
function detachMarkedSpans(line) {
665
  var spans = line.markedSpans
666
  if (!spans) { return }
667
  for (var i = 0; i < spans.length; ++i)
668
    { spans[i].marker.detachLine(line) }
669
  line.markedSpans = null
670
}
671
function attachMarkedSpans(line, spans) {
672
  if (!spans) { return }
673
  for (var i = 0; i < spans.length; ++i)
674
    { spans[i].marker.attachLine(line) }
675
  line.markedSpans = spans
676
}
677
678
// Helpers used when computing which overlapping collapsed span
679
// counts as the larger one.
680
function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 }
681
function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 }
682
683
// Returns a number indicating which of two overlapping collapsed
684
// spans is larger (and thus includes the other). Falls back to
685
// comparing ids when the spans cover exactly the same range.
686
function compareCollapsedMarkers(a, b) {
687
  var lenDiff = a.lines.length - b.lines.length
688
  if (lenDiff != 0) { return lenDiff }
689
  var aPos = a.find(), bPos = b.find()
690
  var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b)
691
  if (fromCmp) { return -fromCmp }
692
  var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b)
693
  if (toCmp) { return toCmp }
694
  return b.id - a.id
695
}
696
697
// Find out whether a line ends or starts in a collapsed span. If
698
// so, return the marker for that span.
699
function collapsedSpanAtSide(line, start) {
700
  var sps = sawCollapsedSpans && line.markedSpans, found
701
  if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
0 ignored issues
show
Unused Code introduced by
The assignment to variable sp seems to be never used. Consider removing it.
Loading history...
Coding Style introduced by
Consider using undefined instead of void(0). It is equivalent and more straightforward to read.
Loading history...
702
    sp = sps[i]
703
    if (sp.marker.collapsed && (start ? sp.from : sp.to) == null &&
704
        (!found || compareCollapsedMarkers(found, sp.marker) < 0))
705
      { found = sp.marker }
706
  } }
707
  return found
0 ignored issues
show
Bug introduced by
The variable found seems to not be initialized for all possible execution paths.
Loading history...
708
}
709
function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) }
710
function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) }
711
712
// Test whether there exists a collapsed span that partially
713
// overlaps (covers the start or end, but not both) of a new span.
714
// Such overlap is not allowed.
715
function conflictingCollapsedRange(doc, lineNo, from, to, marker) {
716
  var line = getLine(doc, lineNo)
717
  var sps = sawCollapsedSpans && line.markedSpans
718
  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...
719
    var sp = sps[i]
720
    if (!sp.marker.collapsed) { continue }
721
    var found = sp.marker.find(0)
722
    var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker)
723
    var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker)
724
    if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue }
725
    if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) ||
726
        fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0))
727
      { return true }
728
  } }
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...
729
}
730
731
// A visual line is a line as drawn on the screen. Folding, for
732
// example, can cause multiple logical lines to appear on the same
733
// visual line. This finds the start of the visual line that the
734
// given line is part of (usually that is the line itself).
735
function visualLine(line) {
736
  var merged
737
  while (merged = collapsedSpanAtStart(line))
738
    { line = merged.find(-1, true).line }
739
  return line
740
}
741
742
// Returns an array of logical lines that continue the visual line
743
// started by the argument, or undefined if there are no such lines.
744
function visualLineContinued(line) {
745
  var merged, lines
746
  while (merged = collapsedSpanAtEnd(line)) {
747
    line = merged.find(1, true).line
748
    ;(lines || (lines = [])).push(line)
749
  }
750
  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 746 is not entered. Are you sure this can never be the case?
Loading history...
751
}
752
753
// Get the line number of the start of the visual line that the
754
// given line number is part of.
755
function visualLineNo(doc, lineN) {
756
  var line = getLine(doc, lineN), vis = visualLine(line)
757
  if (line == vis) { return lineN }
758
  return lineNo(vis)
759
}
760
761
// Get the line number of the start of the next visual line after
762
// the given line.
763
function visualLineEndNo(doc, lineN) {
764
  if (lineN > doc.lastLine()) { return lineN }
765
  var line = getLine(doc, lineN), merged
766
  if (!lineIsHidden(doc, line)) { return lineN }
767
  while (merged = collapsedSpanAtEnd(line))
768
    { line = merged.find(1, true).line }
769
  return lineNo(line) + 1
770
}
771
772
// Compute whether a line is hidden. Lines count as hidden when they
773
// are part of a visual line that starts with another line, or when
774
// they are entirely covered by collapsed, non-widget span.
775
function lineIsHidden(doc, line) {
776
  var sps = sawCollapsedSpans && line.markedSpans
777
  if (sps) { for (var sp = (void 0), 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...
Unused Code introduced by
The assignment to variable sp seems to be never used. Consider removing it.
Loading history...
Coding Style introduced by
Consider using undefined instead of void(0). It is equivalent and more straightforward to read.
Loading history...
778
    sp = sps[i]
779
    if (!sp.marker.collapsed) { continue }
780
    if (sp.from == null) { return true }
781
    if (sp.marker.widgetNode) { continue }
782
    if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
783
      { return true }
784
  } }
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...
785
}
786
function lineIsHiddenInner(doc, line, span) {
787
  if (span.to == null) {
788
    var end = span.marker.find(1, true)
789
    return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker))
790
  }
791
  if (span.marker.inclusiveRight && span.to == line.text.length)
792
    { return true }
793
  for (var sp = (void 0), i = 0; i < line.markedSpans.length; ++i) {
0 ignored issues
show
Coding Style introduced by
Consider using undefined instead of void(0). It is equivalent and more straightforward to read.
Loading history...
Unused Code introduced by
The assignment to variable sp seems to be never used. Consider removing it.
Loading history...
794
    sp = line.markedSpans[i]
795
    if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to &&
796
        (sp.to == null || sp.to != span.from) &&
797
        (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
798
        lineIsHiddenInner(doc, line, sp)) { return true }
799
  }
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...
800
}
801
802
// Find the height above the given line.
803
function heightAtLine(lineObj) {
804
  lineObj = visualLine(lineObj)
805
806
  var h = 0, chunk = lineObj.parent
807
  for (var i = 0; i < chunk.lines.length; ++i) {
808
    var line = chunk.lines[i]
809
    if (line == lineObj) { break }
810
    else { h += line.height }
811
  }
812
  for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
813
    for (var i$1 = 0; i$1 < p.children.length; ++i$1) {
814
      var cur = p.children[i$1]
815
      if (cur == chunk) { break }
816
      else { h += cur.height }
817
    }
818
  }
819
  return h
820
}
821
822
// Compute the character length of a line, taking into account
823
// collapsed ranges (see markText) that might hide parts, and join
824
// other lines onto it.
825
function lineLength(line) {
826
  if (line.height == 0) { return 0 }
827
  var len = line.text.length, merged, cur = line
828
  while (merged = collapsedSpanAtStart(cur)) {
829
    var found = merged.find(0, true)
830
    cur = found.from.line
831
    len += found.from.ch - found.to.ch
832
  }
833
  cur = line
834
  while (merged = collapsedSpanAtEnd(cur)) {
835
    var found$1 = merged.find(0, true)
836
    len -= cur.text.length - found$1.from.ch
837
    cur = found$1.to.line
838
    len += cur.text.length - found$1.to.ch
839
  }
840
  return len
841
}
842
843
// Find the longest line in the document.
844
function findMaxLine(cm) {
845
  var d = cm.display, doc = cm.doc
846
  d.maxLine = getLine(doc, doc.first)
847
  d.maxLineLength = lineLength(d.maxLine)
848
  d.maxLineChanged = true
849
  doc.iter(function (line) {
850
    var len = lineLength(line)
851
    if (len > d.maxLineLength) {
852
      d.maxLineLength = len
853
      d.maxLine = line
854
    }
855
  })
856
}
857
858
// BIDI HELPERS
859
860
function iterateBidiSections(order, from, to, f) {
861
  if (!order) { return f(from, to, "ltr") }
862
  var found = false
863
  for (var i = 0; i < order.length; ++i) {
864
    var part = order[i]
865
    if (part.from < to && part.to > from || from == to && part.to == from) {
866
      f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr")
867
      found = true
868
    }
869
  }
870
  if (!found) { f(from, to, "ltr") }
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
Complexity Best Practice introduced by
There is no return statement if !found is false. Are you sure this is correct? If so, consider adding return; explicitly.

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

Consider this little piece of code

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

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

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

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

Loading history...
871
}
872
873
function bidiLeft(part) { return part.level % 2 ? part.to : part.from }
874
function bidiRight(part) { return part.level % 2 ? part.from : part.to }
875
876
function lineLeft(line) { var order = getOrder(line); return order ? bidiLeft(order[0]) : 0 }
877
function lineRight(line) {
878
  var order = getOrder(line)
879
  if (!order) { return line.text.length }
880
  return bidiRight(lst(order))
881
}
882
883
function compareBidiLevel(order, a, b) {
884
  var linedir = order[0].level
885
  if (a == linedir) { return true }
886
  if (b == linedir) { return false }
887
  return a < b
888
}
889
890
var bidiOther = null
891
function getBidiPartAt(order, pos) {
892
  var found
893
  bidiOther = null
894
  for (var i = 0; i < order.length; ++i) {
895
    var cur = order[i]
896
    if (cur.from < pos && cur.to > pos) { return i }
897
    if ((cur.from == pos || cur.to == pos)) {
898
      if (found == null) {
0 ignored issues
show
Bug introduced by
The variable found seems to not be initialized for all possible execution paths.
Loading history...
899
        found = i
900
      } else if (compareBidiLevel(order, cur.level, order[found].level)) {
901
        if (cur.from != cur.to) { bidiOther = found }
902
        return i
903
      } else {
904
        if (cur.from != cur.to) { bidiOther = i }
905
        return found
906
      }
907
    }
908
  }
909
  return found
910
}
911
912
function moveInLine(line, pos, dir, byUnit) {
913
  if (!byUnit) { return pos + dir }
914
  do { pos += dir }
915
  while (pos > 0 && isExtendingChar(line.text.charAt(pos)))
916
  return pos
917
}
918
919
// This is needed in order to move 'visually' through bi-directional
920
// text -- i.e., pressing left should make the cursor go left, even
921
// when in RTL text. The tricky part is the 'jumps', where RTL and
922
// LTR text touch each other. This often requires the cursor offset
923
// to move more than one unit, in order to visually move one unit.
924
function moveVisually(line, start, dir, byUnit) {
925
  var bidi = getOrder(line)
926
  if (!bidi) { return moveLogically(line, start, dir, byUnit) }
927
  var pos = getBidiPartAt(bidi, start), part = bidi[pos]
928
  var target = moveInLine(line, start, part.level % 2 ? -dir : dir, byUnit)
929
930
  for (;;) {
931
    if (target > part.from && target < part.to) { return target }
932
    if (target == part.from || target == part.to) {
933
      if (getBidiPartAt(bidi, target) == pos) { return target }
934
      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...
935
      return (dir > 0) == part.level % 2 ? part.to : part.from
936
    } else {
937
      part = bidi[pos += dir]
938
      if (!part) { return null }
939
      if ((dir > 0) == part.level % 2)
940
        { target = moveInLine(line, part.to, -1, byUnit) }
941
      else
942
        { target = moveInLine(line, part.from, 1, byUnit) }
943
    }
944
  }
945
}
946
947
function moveLogically(line, start, dir, byUnit) {
948
  var target = start + dir
949
  if (byUnit) { while (target > 0 && isExtendingChar(line.text.charAt(target))) { target += dir } }
950
  return target < 0 || target > line.text.length ? null : target
951
}
952
953
// Bidirectional ordering algorithm
954
// See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
955
// that this (partially) implements.
956
957
// One-char codes used for character types:
958
// L (L):   Left-to-Right
959
// R (R):   Right-to-Left
960
// r (AL):  Right-to-Left Arabic
961
// 1 (EN):  European Number
962
// + (ES):  European Number Separator
963
// % (ET):  European Number Terminator
964
// n (AN):  Arabic Number
965
// , (CS):  Common Number Separator
966
// m (NSM): Non-Spacing Mark
967
// b (BN):  Boundary Neutral
968
// s (B):   Paragraph Separator
969
// t (S):   Segment Separator
970
// w (WS):  Whitespace
971
// N (ON):  Other Neutrals
972
973
// Returns null if characters are ordered as they appear
974
// (left-to-right), or an array of sections ({from, to, level}
975
// objects) in the order in which they occur visually.
976
var bidiOrdering = (function() {
977
  // Character types for codepoints 0 to 0xff
978
  var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN"
979
  // Character types for codepoints 0x600 to 0x6f9
980
  var arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111"
981
  function charType(code) {
982
    if (code <= 0xf7) { return lowTypes.charAt(code) }
983
    else if (0x590 <= code && code <= 0x5f4) { return "R" }
984
    else if (0x600 <= code && code <= 0x6f9) { return arabicTypes.charAt(code - 0x600) }
985
    else if (0x6ee <= code && code <= 0x8ac) { return "r" }
986
    else if (0x2000 <= code && code <= 0x200b) { return "w" }
987
    else if (code == 0x200c) { return "b" }
988
    else { return "L" }
989
  }
990
991
  var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/
992
  var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/
993
  // Browsers seem to always treat the boundaries of block elements as being L.
994
  var outerType = "L"
995
996
  function BidiSpan(level, from, to) {
997
    this.level = level
998
    this.from = from; this.to = to
999
  }
1000
1001
  return function(str) {
1002
    if (!bidiRE.test(str)) { return false }
1003
    var len = str.length, types = []
1004
    for (var i = 0; i < len; ++i)
1005
      { types.push(charType(str.charCodeAt(i))) }
1006
1007
    // W1. Examine each non-spacing mark (NSM) in the level run, and
1008
    // change the type of the NSM to the type of the previous
1009
    // character. If the NSM is at the start of the level run, it will
1010
    // get the type of sor.
1011
    for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) {
1012
      var type = types[i$1]
1013
      if (type == "m") { types[i$1] = prev }
1014
      else { prev = type }
1015
    }
1016
1017
    // W2. Search backwards from each instance of a European number
1018
    // until the first strong type (R, L, AL, or sor) is found. If an
1019
    // AL is found, change the type of the European number to Arabic
1020
    // number.
1021
    // W3. Change all ALs to R.
1022
    for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) {
1023
      var type$1 = types[i$2]
1024
      if (type$1 == "1" && cur == "r") { types[i$2] = "n" }
1025
      else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == "r") { types[i$2] = "R" } }
1026
    }
1027
1028
    // W4. A single European separator between two European numbers
1029
    // changes to a European number. A single common separator between
1030
    // two numbers of the same type changes to that type.
1031
    for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) {
1032
      var type$2 = types[i$3]
1033
      if (type$2 == "+" && prev$1 == "1" && types[i$3+1] == "1") { types[i$3] = "1" }
1034
      else if (type$2 == "," && prev$1 == types[i$3+1] &&
1035
               (prev$1 == "1" || prev$1 == "n")) { types[i$3] = prev$1 }
1036
      prev$1 = type$2
1037
    }
1038
1039
    // W5. A sequence of European terminators adjacent to European
1040
    // numbers changes to all European numbers.
1041
    // W6. Otherwise, separators and terminators change to Other
1042
    // Neutral.
1043
    for (var i$4 = 0; i$4 < len; ++i$4) {
1044
      var type$3 = types[i$4]
1045
      if (type$3 == ",") { types[i$4] = "N" }
1046
      else if (type$3 == "%") {
1047
        var end = (void 0)
0 ignored issues
show
Coding Style introduced by
Consider using undefined instead of void(0). It is equivalent and more straightforward to read.
Loading history...
Unused Code introduced by
The assignment to variable end seems to be never used. Consider removing it.
Loading history...
1048
        for (end = i$4 + 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...
1049
        var replace = (i$4 && types[i$4-1] == "!") || (end < len && types[end] == "1") ? "1" : "N"
1050
        for (var j = i$4; j < end; ++j) { types[j] = replace }
1051
        i$4 = end - 1
1052
      }
1053
    }
1054
1055
    // W7. Search backwards from each instance of a European number
1056
    // until the first strong type (R, L, or sor) is found. If an L is
1057
    // found, then change the type of the European number to L.
1058
    for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) {
1059
      var type$4 = types[i$5]
1060
      if (cur$1 == "L" && type$4 == "1") { types[i$5] = "L" }
1061
      else if (isStrong.test(type$4)) { cur$1 = type$4 }
1062
    }
1063
1064
    // N1. A sequence of neutrals takes the direction of the
1065
    // surrounding strong text if the text on both sides has the same
1066
    // direction. European and Arabic numbers act as if they were R in
1067
    // terms of their influence on neutrals. Start-of-level-run (sor)
1068
    // and end-of-level-run (eor) are used at level run boundaries.
1069
    // N2. Any remaining neutrals take the embedding direction.
1070
    for (var i$6 = 0; i$6 < len; ++i$6) {
1071
      if (isNeutral.test(types[i$6])) {
1072
        var end$1 = (void 0)
0 ignored issues
show
Unused Code introduced by
The assignment to variable end$1 seems to be never used. Consider removing it.
Loading history...
Coding Style introduced by
Consider using undefined instead of void(0). It is equivalent and more straightforward to read.
Loading history...
1073
        for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {}
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...
1074
        var before = (i$6 ? types[i$6-1] : outerType) == "L"
1075
        var after = (end$1 < len ? types[end$1] : outerType) == "L"
1076
        var replace$1 = before || after ? "L" : "R"
1077
        for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1 }
1078
        i$6 = end$1 - 1
1079
      }
1080
    }
1081
1082
    // Here we depart from the documented algorithm, in order to avoid
1083
    // building up an actual levels array. Since there are only three
1084
    // levels (0, 1, 2) in an implementation that doesn't take
1085
    // explicit embedding into account, we can build up the order on
1086
    // the fly, without following the level-based algorithm.
1087
    var order = [], m
1088
    for (var i$7 = 0; i$7 < len;) {
1089
      if (countsAsLeft.test(types[i$7])) {
1090
        var start = i$7
1091
        for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {}
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...
1092
        order.push(new BidiSpan(0, start, i$7))
1093
      } else {
1094
        var pos = i$7, at = order.length
1095
        for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {}
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...
1096
        for (var j$2 = pos; j$2 < i$7;) {
1097
          if (countsAsNum.test(types[j$2])) {
1098
            if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)) }
1099
            var nstart = j$2
1100
            for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {}
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...
1101
            order.splice(at, 0, new BidiSpan(2, nstart, j$2))
1102
            pos = j$2
1103
          } else { ++j$2 }
1104
        }
1105
        if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)) }
1106
      }
1107
    }
1108
    if (order[0].level == 1 && (m = str.match(/^\s+/))) {
1109
      order[0].from = m[0].length
1110
      order.unshift(new BidiSpan(0, 0, m[0].length))
1111
    }
1112
    if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
1113
      lst(order).to -= m[0].length
1114
      order.push(new BidiSpan(0, len - m[0].length, len))
1115
    }
1116
    if (order[0].level == 2)
1117
      { order.unshift(new BidiSpan(1, order[0].to, order[0].to)) }
1118
    if (order[0].level != lst(order).level)
1119
      { order.push(new BidiSpan(order[0].level, len, len)) }
1120
1121
    return order
1122
  }
1123
})()
1124
1125
// Get the bidi ordering for the given line (and cache it). Returns
1126
// false for lines that are fully left-to-right, and an array of
1127
// BidiSpan objects otherwise.
1128
function getOrder(line) {
1129
  var order = line.order
1130
  if (order == null) { order = line.order = bidiOrdering(line.text) }
1131
  return order
1132
}
1133
1134
// EVENT HANDLING
1135
1136
// Lightweight event framework. on/off also work on DOM nodes,
1137
// registering native DOM handlers.
1138
1139
var noHandlers = []
1140
1141
var on = function(emitter, type, f) {
1142
  if (emitter.addEventListener) {
1143
    emitter.addEventListener(type, f, false)
1144
  } else if (emitter.attachEvent) {
1145
    emitter.attachEvent("on" + type, f)
1146
  } else {
1147
    var map = emitter._handlers || (emitter._handlers = {})
1148
    map[type] = (map[type] || noHandlers).concat(f)
1149
  }
1150
}
1151
1152
function getHandlers(emitter, type) {
1153
  return emitter._handlers && emitter._handlers[type] || noHandlers
1154
}
1155
1156
function off(emitter, type, f) {
1157
  if (emitter.removeEventListener) {
1158
    emitter.removeEventListener(type, f, false)
1159
  } else if (emitter.detachEvent) {
1160
    emitter.detachEvent("on" + type, f)
1161
  } else {
1162
    var map = emitter._handlers, arr = map && map[type]
1163
    if (arr) {
1164
      var index = indexOf(arr, f)
1165
      if (index > -1)
1166
        { map[type] = arr.slice(0, index).concat(arr.slice(index + 1)) }
1167
    }
1168
  }
1169
}
1170
1171
function signal(emitter, type /*, values...*/) {
1172
  var handlers = getHandlers(emitter, type)
1173
  if (!handlers.length) { return }
1174
  var args = Array.prototype.slice.call(arguments, 2)
1175
  for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args) }
1176
}
1177
1178
// The DOM events that CodeMirror handles can be overridden by
1179
// registering a (non-DOM) handler on the editor for the event name,
1180
// and preventDefault-ing the event in that handler.
1181
function signalDOMEvent(cm, e, override) {
1182
  if (typeof e == "string")
1183
    { e = {type: e, preventDefault: function() { this.defaultPrevented = true }} }
1184
  signal(cm, override || e.type, cm, e)
1185
  return e_defaultPrevented(e) || e.codemirrorIgnore
1186
}
1187
1188
function signalCursorActivity(cm) {
1189
  var arr = cm._handlers && cm._handlers.cursorActivity
1190
  if (!arr) { return }
1191
  var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = [])
1192
  for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1)
1193
    { set.push(arr[i]) } }
1194
}
1195
1196
function hasHandler(emitter, type) {
1197
  return getHandlers(emitter, type).length > 0
1198
}
1199
1200
// Add on and off methods to a constructor's prototype, to make
1201
// registering events on such objects more convenient.
1202
function eventMixin(ctor) {
1203
  ctor.prototype.on = function(type, f) {on(this, type, f)}
1204
  ctor.prototype.off = function(type, f) {off(this, type, f)}
1205
}
1206
1207
// Due to the fact that we still support jurassic IE versions, some
1208
// compatibility wrappers are needed.
1209
1210
function e_preventDefault(e) {
1211
  if (e.preventDefault) { e.preventDefault() }
1212
  else { e.returnValue = false }
1213
}
1214
function e_stopPropagation(e) {
1215
  if (e.stopPropagation) { e.stopPropagation() }
1216
  else { e.cancelBubble = true }
1217
}
1218
function e_defaultPrevented(e) {
1219
  return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false
1220
}
1221
function e_stop(e) {e_preventDefault(e); e_stopPropagation(e)}
1222
1223
function e_target(e) {return e.target || e.srcElement}
1224
function e_button(e) {
1225
  var b = e.which
1226
  if (b == null) {
1227
    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...
1228
    else if (e.button & 2) { b = 3 }
1229
    else if (e.button & 4) { b = 2 }
1230
  }
1231
  if (mac && e.ctrlKey && b == 1) { b = 3 }
1232
  return b
1233
}
1234
1235
// Detect drag-and-drop
1236
var dragAndDrop = function() {
1237
  // There is *some* kind of drag-and-drop support in IE6-8, but I
1238
  // couldn't get it to work yet.
1239
  if (ie && ie_version < 9) { return false }
1240
  var div = elt('div')
1241
  return "draggable" in div || "dragDrop" in div
1242
}()
1243
1244
var zwspSupported
1245
function zeroWidthElement(measure) {
1246
  if (zwspSupported == null) {
1247
    var test = elt("span", "\u200b")
1248
    removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]))
1249
    if (measure.firstChild.offsetHeight != 0)
1250
      { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8) }
1251
  }
1252
  var node = zwspSupported ? elt("span", "\u200b") :
1253
    elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px")
1254
  node.setAttribute("cm-text", "")
1255
  return node
1256
}
1257
1258
// Feature-detect IE's crummy client rect reporting for bidi text
1259
var badBidiRects
1260
function hasBadBidiRects(measure) {
1261
  if (badBidiRects != null) { return badBidiRects }
1262
  var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA"))
1263
  var r0 = range(txt, 0, 1).getBoundingClientRect()
1264
  var r1 = range(txt, 1, 2).getBoundingClientRect()
1265
  removeChildren(measure)
1266
  if (!r0 || r0.left == r0.right) { return false } // Safari returns null in some cases (#2780)
1267
  return badBidiRects = (r1.right - r0.right < 3)
1268
}
1269
1270
// See if "".split is the broken IE version, if so, provide an
1271
// alternative way to split lines.
1272
var splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function (string) {
1273
  var pos = 0, result = [], l = string.length
1274
  while (pos <= l) {
1275
    var nl = string.indexOf("\n", pos)
1276
    if (nl == -1) { nl = string.length }
1277
    var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl)
1278
    var rt = line.indexOf("\r")
1279
    if (rt != -1) {
1280
      result.push(line.slice(0, rt))
1281
      pos += rt + 1
1282
    } else {
1283
      result.push(line)
1284
      pos = nl + 1
1285
    }
1286
  }
1287
  return result
1288
} : function (string) { return string.split(/\r\n?|\n/); }
1289
1290
var hasSelection = window.getSelection ? function (te) {
1291
  try { return te.selectionStart != te.selectionEnd }
1292
  catch(e) { return false }
1293
} : function (te) {
1294
  var range
1295
  try {range = te.ownerDocument.selection.createRange()}
1296
  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...
1297
  if (!range || range.parentElement() != te) { return false }
1298
  return range.compareEndPoints("StartToEnd", range) != 0
1299
}
1300
1301
var hasCopyEvent = (function () {
1302
  var e = elt("div")
1303
  if ("oncopy" in e) { return true }
1304
  e.setAttribute("oncopy", "return;")
1305
  return typeof e.oncopy == "function"
1306
})()
1307
1308
var badZoomedRects = null
1309
function hasBadZoomedRects(measure) {
1310
  if (badZoomedRects != null) { return badZoomedRects }
1311
  var node = removeChildrenAndAdd(measure, elt("span", "x"))
1312
  var normal = node.getBoundingClientRect()
1313
  var fromRange = range(node, 0, 1).getBoundingClientRect()
1314
  return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1
1315
}
1316
1317
var modes = {};
1318
var mimeModes = {};
1319
// Extra arguments are stored as the mode's dependencies, which is
1320
// used by (legacy) mechanisms like loadmode.js to automatically
1321
// load a mode. (Preferred mechanism is the require/define calls.)
1322
function defineMode(name, mode) {
1323
  if (arguments.length > 2)
1324
    { mode.dependencies = Array.prototype.slice.call(arguments, 2) }
1325
  modes[name] = mode
1326
}
1327
1328
function defineMIME(mime, spec) {
1329
  mimeModes[mime] = spec
1330
}
1331
1332
// Given a MIME type, a {name, ...options} config object, or a name
1333
// string, return a mode config object.
1334
function resolveMode(spec) {
1335
  if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
1336
    spec = mimeModes[spec]
1337
  } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
1338
    var found = mimeModes[spec.name]
1339
    if (typeof found == "string") { found = {name: found} }
1340
    spec = createObj(found, spec)
1341
    spec.name = found.name
1342
  } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
1343
    return resolveMode("application/xml")
1344
  } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) {
1345
    return resolveMode("application/json")
1346
  }
1347
  if (typeof spec == "string") { return {name: spec} }
1348
  else { return spec || {name: "null"} }
1349
}
1350
1351
// Given a mode spec (anything that resolveMode accepts), find and
1352
// initialize an actual mode object.
1353
function getMode(options, spec) {
1354
  spec = resolveMode(spec)
1355
  var mfactory = modes[spec.name]
1356
  if (!mfactory) { return getMode(options, "text/plain") }
1357
  var modeObj = mfactory(options, spec)
1358
  if (modeExtensions.hasOwnProperty(spec.name)) {
1359
    var exts = modeExtensions[spec.name]
1360
    for (var prop in exts) {
1361
      if (!exts.hasOwnProperty(prop)) { continue }
1362
      if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop] }
1363
      modeObj[prop] = exts[prop]
1364
    }
1365
  }
1366
  modeObj.name = spec.name
1367
  if (spec.helperType) { modeObj.helperType = spec.helperType }
1368
  if (spec.modeProps) { for (var prop$1 in spec.modeProps)
0 ignored issues
show
Complexity introduced by
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

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

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

    doSomethingWith(key);
}
Loading history...
1369
    { modeObj[prop$1] = spec.modeProps[prop$1] } }
1370
1371
  return modeObj
1372
}
1373
1374
// This can be used to attach properties to mode objects from
1375
// outside the actual mode definition.
1376
var modeExtensions = {}
1377
function extendMode(mode, properties) {
1378
  var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {})
1379
  copyObj(properties, exts)
1380
}
1381
1382
function copyState(mode, state) {
1383
  if (state === true) { return state }
1384
  if (mode.copyState) { return mode.copyState(state) }
1385
  var nstate = {}
1386
  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...
1387
    var val = state[n]
1388
    if (val instanceof Array) { val = val.concat([]) }
1389
    nstate[n] = val
1390
  }
1391
  return nstate
1392
}
1393
1394
// Given a mode and a state (for that mode), find the inner mode and
1395
// state at the position that the state refers to.
1396
function innerMode(mode, state) {
1397
  var info
1398
  while (mode.innerMode) {
1399
    info = mode.innerMode(state)
1400
    if (!info || info.mode == mode) { break }
1401
    state = info.state
1402
    mode = info.mode
1403
  }
1404
  return info || {mode: mode, state: state}
1405
}
1406
1407
function startState(mode, a1, a2) {
1408
  return mode.startState ? mode.startState(a1, a2) : true
1409
}
1410
1411
// STRING STREAM
1412
1413
// Fed to the mode parsers, provides helper functions to make
1414
// parsers more succinct.
1415
1416
var StringStream = function(string, tabSize) {
1417
  this.pos = this.start = 0
1418
  this.string = string
1419
  this.tabSize = tabSize || 8
1420
  this.lastColumnPos = this.lastColumnValue = 0
1421
  this.lineStart = 0
1422
}
1423
1424
StringStream.prototype = {
1425
  eol: function() {return this.pos >= this.string.length},
1426
  sol: function() {return this.pos == this.lineStart},
1427
  peek: function() {return this.string.charAt(this.pos) || undefined},
1428
  next: function() {
1429
    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...
1430
      { return this.string.charAt(this.pos++) }
1431
  },
1432
  eat: function(match) {
1433
    var ch = this.string.charAt(this.pos)
1434
    var ok
1435
    if (typeof match == "string") { ok = ch == match }
1436
    else { ok = ch && (match.test ? match.test(ch) : match(ch)) }
1437
    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...
1438
  },
1439
  eatWhile: function(match) {
1440
    var start = this.pos
1441
    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...
1442
    return this.pos > start
1443
  },
1444
  eatSpace: function() {
1445
    var this$1 = this;
1446
1447
    var start = this.pos
1448
    while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this$1.pos }
1449
    return this.pos > start
1450
  },
1451
  skipToEnd: function() {this.pos = this.string.length},
1452
  skipTo: function(ch) {
1453
    var found = this.string.indexOf(ch, this.pos)
1454
    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...
1455
  },
1456
  backUp: function(n) {this.pos -= n},
1457
  column: function() {
1458
    if (this.lastColumnPos < this.start) {
1459
      this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue)
1460
      this.lastColumnPos = this.start
1461
    }
1462
    return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
1463
  },
1464
  indentation: function() {
1465
    return countColumn(this.string, null, this.tabSize) -
1466
      (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
1467
  },
1468
  match: function(pattern, consume, caseInsensitive) {
1469
    if (typeof pattern == "string") {
1470
      var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; }
1471
      var substr = this.string.substr(this.pos, pattern.length)
1472
      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...
1473
        if (consume !== false) { this.pos += pattern.length }
1474
        return true
1475
      }
1476
    } else {
1477
      var match = this.string.slice(this.pos).match(pattern)
1478
      if (match && match.index > 0) { return null }
1479
      if (match && consume !== false) { this.pos += match[0].length }
1480
      return match
1481
    }
1482
  },
1483
  current: function(){return this.string.slice(this.start, this.pos)},
1484
  hideFirstChars: function(n, inner) {
1485
    this.lineStart += n
1486
    try { return inner() }
1487
    finally { this.lineStart -= n }
1488
  }
1489
}
1490
1491
// Compute a style array (an array starting with a mode generation
1492
// -- for invalidation -- followed by pairs of end positions and
1493
// style strings), which is used to highlight the tokens on the
1494
// line.
1495
function highlightLine(cm, line, state, forceToEnd) {
1496
  // A styles array always starts with a number identifying the
1497
  // mode/overlays that it is based on (for easy invalidation).
1498
  var st = [cm.state.modeGen], lineClasses = {}
1499
  // Compute the base array of styles
1500
  runMode(cm, line.text, cm.doc.mode, state, function (end, style) { return st.push(end, style); },
1501
    lineClasses, forceToEnd)
1502
1503
  // Run overlays, adjust style array.
1504
  var loop = function ( o ) {
1505
    var overlay = cm.state.overlays[o], i = 1, at = 0
1506
    runMode(cm, line.text, overlay.mode, true, function (end, style) {
1507
      var start = i
1508
      // Ensure there's a token end at the current position, and that i points at it
1509
      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 1514. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
1510
        var i_end = st[i]
0 ignored issues
show
Bug introduced by
The variable i is changed as part of the while loop for example by 2 on line 1513. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
1511
        if (i_end > end)
1512
          { st.splice(i, 1, end, st[i+1], i_end) }
1513
        i += 2
1514
        at = Math.min(end, i_end)
1515
      }
1516
      if (!style) { return }
1517
      if (overlay.opaque) {
1518
        st.splice(start, i - start, end, "overlay " + style)
1519
        i = start + 2
1520
      } else {
1521
        for (; start < i; start += 2) {
1522
          var cur = st[start+1]
1523
          st[start+1] = (cur ? cur + " " : "") + "overlay " + style
1524
        }
1525
      }
1526
    }, lineClasses)
1527
  };
1528
1529
  for (var o = 0; o < cm.state.overlays.length; ++o) loop( o );
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

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

Consider:

if (a > 0)
    b = 42;

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

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

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

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

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

Loading history...
1530
1531
  return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null}
1532
}
1533
1534
function getLineStyles(cm, line, updateFrontier) {
1535
  if (!line.styles || line.styles[0] != cm.state.modeGen) {
1536
    var state = getStateBefore(cm, lineNo(line))
1537
    var result = highlightLine(cm, line, line.text.length > cm.options.maxHighlightLength ? copyState(cm.doc.mode, state) : state)
1538
    line.stateAfter = state
1539
    line.styles = result.styles
1540
    if (result.classes) { line.styleClasses = result.classes }
1541
    else if (line.styleClasses) { line.styleClasses = null }
1542
    if (updateFrontier === cm.doc.frontier) { cm.doc.frontier++ }
1543
  }
1544
  return line.styles
1545
}
1546
1547
function getStateBefore(cm, n, precise) {
1548
  var doc = cm.doc, display = cm.display
1549
  if (!doc.mode.startState) { return true }
1550
  var pos = findStartLine(cm, n, precise), state = pos > doc.first && getLine(doc, pos-1).stateAfter
1551
  if (!state) { state = startState(doc.mode) }
1552
  else { state = copyState(doc.mode, state) }
1553
  doc.iter(pos, n, function (line) {
1554
    processLine(cm, line.text, state)
1555
    var save = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo
1556
    line.stateAfter = save ? copyState(doc.mode, state) : null
1557
    ++pos
1558
  })
1559
  if (precise) { doc.frontier = pos }
1560
  return state
1561
}
1562
1563
// Lightweight form of highlight -- proceed over this line and
1564
// update state, but don't save a style array. Used for lines that
1565
// aren't currently visible.
1566
function processLine(cm, text, state, startAt) {
1567
  var mode = cm.doc.mode
1568
  var stream = new StringStream(text, cm.options.tabSize)
1569
  stream.start = stream.pos = startAt || 0
1570
  if (text == "") { callBlankLine(mode, state) }
1571
  while (!stream.eol()) {
1572
    readToken(mode, stream, state)
1573
    stream.start = stream.pos
1574
  }
1575
}
1576
1577
function callBlankLine(mode, state) {
1578
  if (mode.blankLine) { return mode.blankLine(state) }
1579
  if (!mode.innerMode) { return }
1580
  var inner = innerMode(mode, state)
1581
  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...
1582
}
1583
1584
function readToken(mode, stream, state, inner) {
1585
  for (var i = 0; i < 10; i++) {
1586
    if (inner) { inner[0] = innerMode(mode, state).mode }
1587
    var style = mode.token(stream, state)
1588
    if (stream.pos > stream.start) { return style }
1589
  }
1590
  throw new Error("Mode " + mode.name + " failed to advance stream.")
1591
}
1592
1593
// Utility for getTokenAt and getLineTokens
1594
function takeToken(cm, pos, precise, asArray) {
1595
  var getObj = function (copy) { return ({
1596
    start: stream.start, end: stream.pos,
1597
    string: stream.current(),
1598
    type: style || null,
1599
    state: copy ? copyState(doc.mode, state) : state
1600
  }); }
1601
1602
  var doc = cm.doc, mode = doc.mode, style
1603
  pos = clipPos(doc, pos)
1604
  var line = getLine(doc, pos.line), state = getStateBefore(cm, pos.line, precise)
1605
  var stream = new StringStream(line.text, cm.options.tabSize), tokens
1606
  if (asArray) { tokens = [] }
1607
  while ((asArray || stream.pos < pos.ch) && !stream.eol()) {
1608
    stream.start = stream.pos
1609
    style = readToken(mode, stream, state)
1610
    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 1606 is false. Are you sure this can never be the case?
Loading history...
1611
  }
1612
  return asArray ? tokens : getObj()
1613
}
1614
1615
function extractLineClasses(type, output) {
1616
  if (type) { for (;;) {
1617
    var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/)
1618
    if (!lineClass) { break }
1619
    type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length)
1620
    var prop = lineClass[1] ? "bgClass" : "textClass"
1621
    if (output[prop] == null)
1622
      { output[prop] = lineClass[2] }
1623
    else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop]))
1624
      { output[prop] += " " + lineClass[2] }
1625
  } }
1626
  return type
1627
}
1628
1629
// Run the given mode's parser over a line, calling f for each token.
1630
function runMode(cm, text, mode, state, f, lineClasses, forceToEnd) {
1631
  var flattenSpans = mode.flattenSpans
1632
  if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans }
1633
  var curStart = 0, curStyle = null
1634
  var stream = new StringStream(text, cm.options.tabSize), style
1635
  var inner = cm.options.addModeClass && [null]
1636
  if (text == "") { extractLineClasses(callBlankLine(mode, state), lineClasses) }
1637
  while (!stream.eol()) {
1638
    if (stream.pos > cm.options.maxHighlightLength) {
1639
      flattenSpans = false
1640
      if (forceToEnd) { processLine(cm, text, state, stream.pos) }
1641
      stream.pos = text.length
1642
      style = null
1643
    } else {
1644
      style = extractLineClasses(readToken(mode, stream, state, inner), lineClasses)
1645
    }
1646
    if (inner) {
1647
      var mName = inner[0].name
1648
      if (mName) { style = "m-" + (style ? mName + " " + style : mName) }
1649
    }
1650
    if (!flattenSpans || curStyle != style) {
1651
      while (curStart < stream.start) {
1652
        curStart = Math.min(stream.start, curStart + 5000)
1653
        f(curStart, curStyle)
1654
      }
1655
      curStyle = style
1656
    }
1657
    stream.start = stream.pos
1658
  }
1659
  while (curStart < stream.pos) {
1660
    // Webkit seems to refuse to render text nodes longer than 57444
1661
    // characters, and returns inaccurate measurements in nodes
1662
    // starting around 5000 chars.
1663
    var pos = Math.min(stream.pos, curStart + 5000)
1664
    f(pos, curStyle)
1665
    curStart = pos
1666
  }
1667
}
1668
1669
// Finds the line to start with when starting a parse. Tries to
1670
// find a line with a stateAfter, so that it can start with a
1671
// valid state. If that fails, it returns the line with the
1672
// smallest indentation, which tends to need the least context to
1673
// parse correctly.
1674
function findStartLine(cm, n, precise) {
1675
  var minindent, minline, doc = cm.doc
1676
  var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100)
1677
  for (var search = n; search > lim; --search) {
1678
    if (search <= doc.first) { return doc.first }
1679
    var line = getLine(doc, search - 1)
1680
    if (line.stateAfter && (!precise || search <= doc.frontier)) { return search }
1681
    var indented = countColumn(line.text, null, cm.options.tabSize)
1682
    if (minline == null || minindent > indented) {
0 ignored issues
show
Bug introduced by
The variable minline seems to not be initialized for all possible execution paths.
Loading history...
Bug introduced by
The variable minindent seems to not be initialized for all possible execution paths.
Loading history...
1683
      minline = search - 1
1684
      minindent = indented
1685
    }
1686
  }
1687
  return minline
1688
}
1689
1690
// LINE DATA STRUCTURE
1691
1692
// Line objects. These hold state related to a line, including
1693
// highlighting info (the styles array).
1694
function Line(text, markedSpans, estimateHeight) {
1695
  this.text = text
1696
  attachMarkedSpans(this, markedSpans)
1697
  this.height = estimateHeight ? estimateHeight(this) : 1
1698
}
1699
eventMixin(Line)
1700
Line.prototype.lineNo = function() { return lineNo(this) }
1701
1702
// Change the content (text, markers) of a line. Automatically
1703
// invalidates cached information and tries to re-estimate the
1704
// line's height.
1705
function updateLine(line, text, markedSpans, estimateHeight) {
1706
  line.text = text
1707
  if (line.stateAfter) { line.stateAfter = null }
1708
  if (line.styles) { line.styles = null }
1709
  if (line.order != null) { line.order = null }
1710
  detachMarkedSpans(line)
1711
  attachMarkedSpans(line, markedSpans)
1712
  var estHeight = estimateHeight ? estimateHeight(line) : 1
1713
  if (estHeight != line.height) { updateLineHeight(line, estHeight) }
1714
}
1715
1716
// Detach a line from the document tree and its markers.
1717
function cleanUpLine(line) {
1718
  line.parent = null
1719
  detachMarkedSpans(line)
1720
}
1721
1722
// Convert a style as returned by a mode (either null, or a string
1723
// containing one or more styles) to a CSS style. This is cached,
1724
// and also looks for line-wide styles.
1725
var styleToClassCache = {};
1726
var styleToClassCacheWithMode = {};
1727
function interpretTokenStyle(style, options) {
1728
  if (!style || /^\s*$/.test(style)) { return null }
1729
  var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache
1730
  return cache[style] ||
1731
    (cache[style] = style.replace(/\S+/g, "cm-$&"))
1732
}
1733
1734
// Render the DOM representation of the text of a line. Also builds
1735
// up a 'line map', which points at the DOM nodes that represent
1736
// specific stretches of text, and is used by the measuring code.
1737
// The returned object contains the DOM node, this map, and
1738
// information about line-wide styles that were set by the mode.
1739
function buildLineContent(cm, lineView) {
1740
  // The padding-right forces the element to have a 'border', which
1741
  // is needed on Webkit to be able to get line-level bounding
1742
  // rectangles for it (in measureChar).
1743
  var content = elt("span", null, null, webkit ? "padding-right: .1px" : null)
1744
  var builder = {pre: elt("pre", [content], "CodeMirror-line"), content: content,
1745
                 col: 0, pos: 0, cm: cm,
1746
                 trailingSpace: false,
1747
                 splitSpaces: (ie || webkit) && cm.getOption("lineWrapping")}
1748
  lineView.measure = {}
1749
1750
  // Iterate over the logical lines that make up this visual line.
1751
  for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {
1752
    var line = i ? lineView.rest[i - 1] : lineView.line, order = (void 0)
0 ignored issues
show
Coding Style introduced by
Consider using undefined instead of void(0). It is equivalent and more straightforward to read.
Loading history...
Unused Code introduced by
The assignment to variable order seems to be never used. Consider removing it.
Loading history...
1753
    builder.pos = 0
1754
    builder.addToken = buildToken
1755
    // Optionally wire in some hacks into the token-rendering
1756
    // algorithm, to deal with browser quirks.
1757
    if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line)))
1758
      { builder.addToken = buildTokenBadBidi(builder.addToken, order) }
1759
    builder.map = []
1760
    var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line)
1761
    insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate))
1762
    if (line.styleClasses) {
1763
      if (line.styleClasses.bgClass)
1764
        { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || "") }
1765
      if (line.styleClasses.textClass)
1766
        { builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || "") }
1767
    }
1768
1769
    // Ensure at least a single node is present, for measuring.
1770
    if (builder.map.length == 0)
1771
      { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))) }
1772
1773
    // Store the map and a cache object for the current logical line
1774
    if (i == 0) {
1775
      lineView.measure.map = builder.map
1776
      lineView.measure.cache = {}
1777
    } else {
1778
      ;(lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map)
1779
      ;(lineView.measure.caches || (lineView.measure.caches = [])).push({})
1780
    }
1781
  }
1782
1783
  // See issue #2901
1784
  if (webkit) {
1785
    var last = builder.content.lastChild
1786
    if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab")))
1787
      { builder.content.className = "cm-tab-wrap-hack" }
1788
  }
1789
1790
  signal(cm, "renderLine", cm, lineView.line, builder.pre)
1791
  if (builder.pre.className)
1792
    { builder.textClass = joinClasses(builder.pre.className, builder.textClass || "") }
1793
1794
  return builder
1795
}
1796
1797
function defaultSpecialCharPlaceholder(ch) {
1798
  var token = elt("span", "\u2022", "cm-invalidchar")
1799
  token.title = "\\u" + ch.charCodeAt(0).toString(16)
1800
  token.setAttribute("aria-label", token.title)
1801
  return token
1802
}
1803
1804
// Build up the DOM representation for a single token, and add it to
1805
// the line map. Takes care to render special characters separately.
1806
function buildToken(builder, text, style, startStyle, endStyle, title, css) {
1807
  if (!text) { return }
1808
  var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text
1809
  var special = builder.cm.state.specialChars, mustWrap = false
1810
  var content
1811
  if (!special.test(text)) {
1812
    builder.col += text.length
1813
    content = document.createTextNode(displayText)
1814
    builder.map.push(builder.pos, builder.pos + text.length, content)
1815
    if (ie && ie_version < 9) { mustWrap = true }
1816
    builder.pos += text.length
1817
  } else {
1818
    content = document.createDocumentFragment()
1819
    var pos = 0
1820
    while (true) {
1821
      special.lastIndex = pos
1822
      var m = special.exec(text)
1823
      var skipped = m ? m.index - pos : text.length - pos
1824
      if (skipped) {
1825
        var txt = document.createTextNode(displayText.slice(pos, pos + skipped))
1826
        if (ie && ie_version < 9) { content.appendChild(elt("span", [txt])) }
1827
        else { content.appendChild(txt) }
1828
        builder.map.push(builder.pos, builder.pos + skipped, txt)
1829
        builder.col += skipped
1830
        builder.pos += skipped
1831
      }
1832
      if (!m) { break }
1833
      pos += skipped + 1
1834
      var txt$1 = (void 0)
0 ignored issues
show
Unused Code introduced by
The assignment to variable txt$1 seems to be never used. Consider removing it.
Loading history...
Coding Style introduced by
Consider using undefined instead of void(0). It is equivalent and more straightforward to read.
Loading history...
1835
      if (m[0] == "\t") {
1836
        var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize
1837
        txt$1 = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"))
1838
        txt$1.setAttribute("role", "presentation")
1839
        txt$1.setAttribute("cm-text", "\t")
1840
        builder.col += tabWidth
1841
      } else if (m[0] == "\r" || m[0] == "\n") {
1842
        txt$1 = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar"))
1843
        txt$1.setAttribute("cm-text", m[0])
1844
        builder.col += 1
1845
      } else {
1846
        txt$1 = builder.cm.options.specialCharPlaceholder(m[0])
1847
        txt$1.setAttribute("cm-text", m[0])
1848
        if (ie && ie_version < 9) { content.appendChild(elt("span", [txt$1])) }
1849
        else { content.appendChild(txt$1) }
1850
        builder.col += 1
1851
      }
1852
      builder.map.push(builder.pos, builder.pos + 1, txt$1)
1853
      builder.pos++
1854
    }
1855
  }
1856
  builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32
1857
  if (style || startStyle || endStyle || mustWrap || css) {
1858
    var fullStyle = style || ""
1859
    if (startStyle) { fullStyle += startStyle }
1860
    if (endStyle) { fullStyle += endStyle }
1861
    var token = elt("span", [content], fullStyle, css)
1862
    if (title) { token.title = title }
1863
    return builder.content.appendChild(token)
1864
  }
1865
  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...
1866
}
1867
1868
function splitSpaces(text, trailingBefore) {
1869
  if (text.length > 1 && !/  /.test(text)) { return text }
1870
  var spaceBefore = trailingBefore, result = ""
1871
  for (var i = 0; i < text.length; i++) {
1872
    var ch = text.charAt(i)
1873
    if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32))
1874
      { ch = "\u00a0" }
1875
    result += ch
1876
    spaceBefore = ch == " "
1877
  }
1878
  return result
1879
}
1880
1881
// Work around nonsense dimensions being reported for stretches of
1882
// right-to-left text.
1883
function buildTokenBadBidi(inner, order) {
1884
  return function (builder, text, style, startStyle, endStyle, title, css) {
1885
    style = style ? style + " cm-force-border" : "cm-force-border"
1886
    var start = builder.pos, end = start + text.length
1887
    for (;;) {
1888
      // Find the part that overlaps with the start of this text
1889
      var part = (void 0)
0 ignored issues
show
Coding Style introduced by
Consider using undefined instead of void(0). It is equivalent and more straightforward to read.
Loading history...
1890
      for (var i = 0; i < order.length; i++) {
1891
        part = order[i]
1892
        if (part.to > start && part.from <= start) { break }
1893
      }
1894
      if (part.to >= end) { return inner(builder, text, style, startStyle, endStyle, title, css) }
1895
      inner(builder, text.slice(0, part.to - start), style, startStyle, null, title, css)
1896
      startStyle = null
1897
      text = text.slice(part.to - start)
1898
      start = part.to
1899
    }
1900
  }
1901
}
1902
1903
function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
1904
  var widget = !ignoreWidget && marker.widgetNode
1905
  if (widget) { builder.map.push(builder.pos, builder.pos + size, widget) }
1906
  if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {
1907
    if (!widget)
1908
      { widget = builder.content.appendChild(document.createElement("span")) }
1909
    widget.setAttribute("cm-marker", marker.id)
1910
  }
1911
  if (widget) {
1912
    builder.cm.display.input.setUneditable(widget)
1913
    builder.content.appendChild(widget)
1914
  }
1915
  builder.pos += size
1916
  builder.trailingSpace = false
1917
}
1918
1919
// Outputs a number of spans to make up a line, taking highlighting
1920
// and marked text into account.
1921
function insertLineContent(line, builder, styles) {
1922
  var spans = line.markedSpans, allText = line.text, at = 0
1923
  if (!spans) {
1924
    for (var i$1 = 1; i$1 < styles.length; i$1+=2)
1925
      { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpretTokenStyle(styles[i$1+1], builder.cm.options)) }
1926
    return
1927
  }
1928
1929
  var len = allText.length, pos = 0, i = 1, text = "", style, css
1930
  var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed
1931
  for (;;) {
1932
    if (nextChange == pos) { // Update current marker set
1933
      spanStyle = spanEndStyle = spanStartStyle = title = css = ""
1934
      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...
1935
      var foundBookmarks = [], endStyles = (void 0)
0 ignored issues
show
Coding Style introduced by
Consider using undefined instead of void(0). It is equivalent and more straightforward to read.
Loading history...
1936
      for (var j = 0; j < spans.length; ++j) {
1937
        var sp = spans[j], m = sp.marker
1938
        if (m.type == "bookmark" && sp.from == pos && m.widgetNode) {
1939
          foundBookmarks.push(m)
1940
        } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) {
1941
          if (sp.to != null && sp.to != pos && nextChange > sp.to) {
1942
            nextChange = sp.to
1943
            spanEndStyle = ""
1944
          }
1945
          if (m.className) { spanStyle += " " + m.className }
1946
          if (m.css) { css = (css ? css + ";" : "") + m.css }
1947
          if (m.startStyle && sp.from == pos) { spanStartStyle += " " + m.startStyle }
1948
          if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [])).push(m.endStyle, sp.to) }
1949
          if (m.title && !title) { title = m.title }
1950
          if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))
1951
            { collapsed = sp }
1952
        } else if (sp.from > pos && nextChange > sp.from) {
1953
          nextChange = sp.from
1954
        }
1955
      }
1956
      if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2)
1957
        { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += " " + endStyles[j$1] } } }
1958
1959
      if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2)
1960
        { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]) } }
1961
      if (collapsed && (collapsed.from || 0) == pos) {
1962
        buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos,
1963
                           collapsed.marker, collapsed.from == null)
1964
        if (collapsed.to == null) { return }
1965
        if (collapsed.to == pos) { collapsed = false }
1966
      }
1967
    }
1968
    if (pos >= len) { break }
1969
1970
    var upto = Math.min(len, nextChange)
1971
    while (true) {
1972
      if (text) {
1973
        var end = pos + text.length
1974
        if (!collapsed) {
1975
          var tokenText = end > upto ? text.slice(0, upto - pos) : text
1976
          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...
1977
                           spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", title, css)
0 ignored issues
show
Bug introduced by
The variable spanStartStyle seems to not be initialized for all possible execution paths. Are you sure addToken handles undefined variables?
Loading history...
Bug introduced by
The variable css seems to not be initialized for all possible execution paths. Are you sure addToken handles undefined variables?
Loading history...
Bug introduced by
The variable title seems to not be initialized for all possible execution paths. Are you sure addToken handles undefined variables?
Loading history...
Bug introduced by
The variable spanEndStyle seems to not be initialized for all possible execution paths.
Loading history...
1978
        }
1979
        if (end >= upto) {text = text.slice(upto - pos); pos = upto; break}
1980
        pos = end
1981
        spanStartStyle = ""
1982
      }
1983
      text = allText.slice(at, at = styles[i++])
1984
      style = interpretTokenStyle(styles[i++], builder.cm.options)
1985
    }
1986
  }
1987
}
1988
1989
1990
// These objects are used to represent the visible (currently drawn)
1991
// part of the document. A LineView may correspond to multiple
1992
// logical lines, if those are connected by collapsed ranges.
1993
function LineView(doc, line, lineN) {
1994
  // The starting line
1995
  this.line = line
1996
  // Continuing lines, if any
1997
  this.rest = visualLineContinued(line)
1998
  // Number of logical lines in this visual line
1999
  this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1
2000
  this.node = this.text = null
2001
  this.hidden = lineIsHidden(doc, line)
2002
}
2003
2004
// Create a range of LineView objects for the given lines.
2005
function buildViewArray(cm, from, to) {
2006
  var array = [], nextPos
2007
  for (var pos = from; pos < to; pos = nextPos) {
2008
    var view = new LineView(cm.doc, getLine(cm.doc, pos), pos)
2009
    nextPos = pos + view.size
2010
    array.push(view)
2011
  }
2012
  return array
2013
}
2014
2015
var operationGroup = null
2016
2017
function pushOperation(op) {
2018
  if (operationGroup) {
2019
    operationGroup.ops.push(op)
2020
  } else {
2021
    op.ownsGroup = operationGroup = {
2022
      ops: [op],
2023
      delayedCallbacks: []
2024
    }
2025
  }
2026
}
2027
2028
function fireCallbacksForOps(group) {
2029
  // Calls delayed callbacks and cursorActivity handlers until no
2030
  // new ones appear
2031
  var callbacks = group.delayedCallbacks, i = 0
2032
  do {
2033
    for (; i < callbacks.length; i++)
2034
      { callbacks[i].call(null) }
2035
    for (var j = 0; j < group.ops.length; j++) {
2036
      var op = group.ops[j]
2037
      if (op.cursorActivityHandlers)
2038
        { while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
2039
          { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm) } }
2040
    }
2041
  } while (i < callbacks.length)
2042
}
2043
2044
function finishOperation(op, endCb) {
2045
  var group = op.ownsGroup
2046
  if (!group) { return }
2047
2048
  try { fireCallbacksForOps(group) }
2049
  finally {
2050
    operationGroup = null
2051
    endCb(group)
2052
  }
2053
}
2054
2055
var orphanDelayedCallbacks = null
2056
2057
// Often, we want to signal events at a point where we are in the
2058
// middle of some work, but don't want the handler to start calling
2059
// other methods on the editor, which might be in an inconsistent
2060
// state or simply not expect any other events to happen.
2061
// signalLater looks whether there are any handlers, and schedules
2062
// them to be executed when the last operation ends, or, if no
2063
// operation is active, when a timeout fires.
2064
function signalLater(emitter, type /*, values...*/) {
2065
  var arr = getHandlers(emitter, type)
2066
  if (!arr.length) { return }
2067
  var args = Array.prototype.slice.call(arguments, 2), list
2068
  if (operationGroup) {
2069
    list = operationGroup.delayedCallbacks
2070
  } else if (orphanDelayedCallbacks) {
2071
    list = orphanDelayedCallbacks
2072
  } else {
2073
    list = orphanDelayedCallbacks = []
2074
    setTimeout(fireOrphanDelayed, 0)
2075
  }
2076
  var loop = function ( i ) {
2077
    list.push(function () { return arr[i].apply(null, args); })
2078
  };
2079
2080
  for (var i = 0; i < arr.length; ++i)
2081
    loop( i );
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

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

Consider:

if (a > 0)
    b = 42;

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

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

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

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

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

Loading history...
2082
}
2083
2084
function fireOrphanDelayed() {
2085
  var delayed = orphanDelayedCallbacks
2086
  orphanDelayedCallbacks = null
2087
  for (var i = 0; i < delayed.length; ++i) { delayed[i]() }
2088
}
2089
2090
// When an aspect of a line changes, a string is added to
2091
// lineView.changes. This updates the relevant part of the line's
2092
// DOM structure.
2093
function updateLineForChanges(cm, lineView, lineN, dims) {
2094
  for (var j = 0; j < lineView.changes.length; j++) {
2095
    var type = lineView.changes[j]
2096
    if (type == "text") { updateLineText(cm, lineView) }
2097
    else if (type == "gutter") { updateLineGutter(cm, lineView, lineN, dims) }
2098
    else if (type == "class") { updateLineClasses(lineView) }
2099
    else if (type == "widget") { updateLineWidgets(cm, lineView, dims) }
2100
  }
2101
  lineView.changes = null
2102
}
2103
2104
// Lines with gutter elements, widgets or a background class need to
2105
// be wrapped, and have the extra elements added to the wrapper div
2106
function ensureLineWrapped(lineView) {
2107
  if (lineView.node == lineView.text) {
2108
    lineView.node = elt("div", null, null, "position: relative")
2109
    if (lineView.text.parentNode)
2110
      { lineView.text.parentNode.replaceChild(lineView.node, lineView.text) }
2111
    lineView.node.appendChild(lineView.text)
2112
    if (ie && ie_version < 8) { lineView.node.style.zIndex = 2 }
2113
  }
2114
  return lineView.node
2115
}
2116
2117
function updateLineBackground(lineView) {
2118
  var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass
2119
  if (cls) { cls += " CodeMirror-linebackground" }
2120
  if (lineView.background) {
2121
    if (cls) { lineView.background.className = cls }
2122
    else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null }
2123
  } else if (cls) {
2124
    var wrap = ensureLineWrapped(lineView)
2125
    lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild)
2126
  }
2127
}
2128
2129
// Wrapper around buildLineContent which will reuse the structure
2130
// in display.externalMeasured when possible.
2131
function getLineContent(cm, lineView) {
2132
  var ext = cm.display.externalMeasured
2133
  if (ext && ext.line == lineView.line) {
2134
    cm.display.externalMeasured = null
2135
    lineView.measure = ext.measure
2136
    return ext.built
2137
  }
2138
  return buildLineContent(cm, lineView)
2139
}
2140
2141
// Redraw the line's text. Interacts with the background and text
2142
// classes because the mode may output tokens that influence these
2143
// classes.
2144
function updateLineText(cm, lineView) {
2145
  var cls = lineView.text.className
2146
  var built = getLineContent(cm, lineView)
2147
  if (lineView.text == lineView.node) { lineView.node = built.pre }
2148
  lineView.text.parentNode.replaceChild(built.pre, lineView.text)
2149
  lineView.text = built.pre
2150
  if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {
2151
    lineView.bgClass = built.bgClass
2152
    lineView.textClass = built.textClass
2153
    updateLineClasses(lineView)
2154
  } else if (cls) {
2155
    lineView.text.className = cls
2156
  }
2157
}
2158
2159
function updateLineClasses(lineView) {
2160
  updateLineBackground(lineView)
2161
  if (lineView.line.wrapClass)
2162
    { ensureLineWrapped(lineView).className = lineView.line.wrapClass }
2163
  else if (lineView.node != lineView.text)
2164
    { lineView.node.className = "" }
2165
  var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass
2166
  lineView.text.className = textClass || ""
2167
}
2168
2169
function updateLineGutter(cm, lineView, lineN, dims) {
2170
  if (lineView.gutter) {
2171
    lineView.node.removeChild(lineView.gutter)
2172
    lineView.gutter = null
2173
  }
2174
  if (lineView.gutterBackground) {
2175
    lineView.node.removeChild(lineView.gutterBackground)
2176
    lineView.gutterBackground = null
2177
  }
2178
  if (lineView.line.gutterClass) {
2179
    var wrap = ensureLineWrapped(lineView)
2180
    lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass,
2181
                                    ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px; width: " + (dims.gutterTotalWidth) + "px"))
2182
    wrap.insertBefore(lineView.gutterBackground, lineView.text)
2183
  }
2184
  var markers = lineView.line.gutterMarkers
2185
  if (cm.options.lineNumbers || markers) {
2186
    var wrap$1 = ensureLineWrapped(lineView)
2187
    var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"))
2188
    cm.display.input.setUneditable(gutterWrap)
2189
    wrap$1.insertBefore(gutterWrap, lineView.text)
2190
    if (lineView.line.gutterClass)
2191
      { gutterWrap.className += " " + lineView.line.gutterClass }
2192
    if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
2193
      { lineView.lineNumber = gutterWrap.appendChild(
2194
        elt("div", lineNumberFor(cm.options, lineN),
2195
            "CodeMirror-linenumber CodeMirror-gutter-elt",
2196
            ("left: " + (dims.gutterLeft["CodeMirror-linenumbers"]) + "px; width: " + (cm.display.lineNumInnerWidth) + "px"))) }
2197
    if (markers) { for (var k = 0; k < cm.options.gutters.length; ++k) {
2198
      var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id]
2199
      if (found)
2200
        { gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt",
2201
                                   ("left: " + (dims.gutterLeft[id]) + "px; width: " + (dims.gutterWidth[id]) + "px"))) }
2202
    } }
2203
  }
2204
}
2205
2206
function updateLineWidgets(cm, lineView, dims) {
2207
  if (lineView.alignable) { lineView.alignable = null }
2208
  for (var node = lineView.node.firstChild, next = (void 0); node; node = next) {
0 ignored issues
show
Coding Style introduced by
Consider using undefined instead of void(0). It is equivalent and more straightforward to read.
Loading history...
Unused Code introduced by
The assignment to variable next seems to be never used. Consider removing it.
Loading history...
2209
    next = node.nextSibling
2210
    if (node.className == "CodeMirror-linewidget")
2211
      { lineView.node.removeChild(node) }
2212
  }
2213
  insertLineWidgets(cm, lineView, dims)
2214
}
2215
2216
// Build a line's DOM representation from scratch
2217
function buildLineElement(cm, lineView, lineN, dims) {
2218
  var built = getLineContent(cm, lineView)
2219
  lineView.text = lineView.node = built.pre
2220
  if (built.bgClass) { lineView.bgClass = built.bgClass }
2221
  if (built.textClass) { lineView.textClass = built.textClass }
2222
2223
  updateLineClasses(lineView)
2224
  updateLineGutter(cm, lineView, lineN, dims)
2225
  insertLineWidgets(cm, lineView, dims)
2226
  return lineView.node
2227
}
2228
2229
// A lineView may contain multiple logical lines (when merged by
2230
// collapsed spans). The widgets for all of them need to be drawn.
2231
function insertLineWidgets(cm, lineView, dims) {
2232
  insertLineWidgetsFor(cm, lineView.line, lineView, dims, true)
2233
  if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
2234
    { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false) } }
2235
}
2236
2237
function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {
2238
  if (!line.widgets) { return }
2239
  var wrap = ensureLineWrapped(lineView)
2240
  for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
2241
    var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget")
2242
    if (!widget.handleMouseEvents) { node.setAttribute("cm-ignore-events", "true") }
2243
    positionLineWidget(widget, node, lineView, dims)
2244
    cm.display.input.setUneditable(node)
2245
    if (allowAbove && widget.above)
2246
      { wrap.insertBefore(node, lineView.gutter || lineView.text) }
2247
    else
2248
      { wrap.appendChild(node) }
2249
    signalLater(widget, "redraw")
2250
  }
2251
}
2252
2253
function positionLineWidget(widget, node, lineView, dims) {
2254
  if (widget.noHScroll) {
2255
    ;(lineView.alignable || (lineView.alignable = [])).push(node)
2256
    var width = dims.wrapperWidth
2257
    node.style.left = dims.fixedPos + "px"
2258
    if (!widget.coverGutter) {
2259
      width -= dims.gutterTotalWidth
2260
      node.style.paddingLeft = dims.gutterTotalWidth + "px"
2261
    }
2262
    node.style.width = width + "px"
2263
  }
2264
  if (widget.coverGutter) {
2265
    node.style.zIndex = 5
2266
    node.style.position = "relative"
2267
    if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + "px" }
2268
  }
2269
}
2270
2271
function widgetHeight(widget) {
2272
  if (widget.height != null) { return widget.height }
2273
  var cm = widget.doc.cm
2274
  if (!cm) { return 0 }
2275
  if (!contains(document.body, widget.node)) {
2276
    var parentStyle = "position: relative;"
2277
    if (widget.coverGutter)
2278
      { parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;" }
2279
    if (widget.noHScroll)
2280
      { parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;" }
2281
    removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle))
2282
  }
2283
  return widget.height = widget.node.parentNode.offsetHeight
2284
}
2285
2286
// Return true when the given mouse event happened in a widget
2287
function eventInWidget(display, e) {
2288
  for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
2289
    if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") ||
2290
        (n.parentNode == display.sizer && n != display.mover))
2291
      { return true }
2292
  }
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...
2293
}
2294
2295
// POSITION MEASUREMENT
2296
2297
function paddingTop(display) {return display.lineSpace.offsetTop}
2298
function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight}
2299
function paddingH(display) {
2300
  if (display.cachedPaddingH) { return display.cachedPaddingH }
2301
  var e = removeChildrenAndAdd(display.measure, elt("pre", "x"))
2302
  var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle
2303
  var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)}
2304
  if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data }
2305
  return data
2306
}
2307
2308
function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth }
2309
function displayWidth(cm) {
2310
  return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth
2311
}
2312
function displayHeight(cm) {
2313
  return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight
2314
}
2315
2316
// Ensure the lineView.wrapping.heights array is populated. This is
2317
// an array of bottom offsets for the lines that make up a drawn
2318
// line. When lineWrapping is on, there might be more than one
2319
// height.
2320
function ensureLineHeights(cm, lineView, rect) {
2321
  var wrapping = cm.options.lineWrapping
2322
  var curWidth = wrapping && displayWidth(cm)
2323
  if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {
2324
    var heights = lineView.measure.heights = []
2325
    if (wrapping) {
2326
      lineView.measure.width = curWidth
2327
      var rects = lineView.text.firstChild.getClientRects()
2328
      for (var i = 0; i < rects.length - 1; i++) {
2329
        var cur = rects[i], next = rects[i + 1]
2330
        if (Math.abs(cur.bottom - next.bottom) > 2)
2331
          { heights.push((cur.bottom + next.top) / 2 - rect.top) }
2332
      }
2333
    }
2334
    heights.push(rect.bottom - rect.top)
2335
  }
2336
}
2337
2338
// Find a line map (mapping character offsets to text nodes) and a
2339
// measurement cache for the given line number. (A line view might
2340
// contain multiple lines when collapsed ranges are present.)
2341
function mapFromLineView(lineView, line, lineN) {
2342
  if (lineView.line == line)
2343
    { return {map: lineView.measure.map, cache: lineView.measure.cache} }
2344
  for (var i = 0; i < lineView.rest.length; i++)
2345
    { if (lineView.rest[i] == line)
2346
      { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } }
2347
  for (var i$1 = 0; i$1 < lineView.rest.length; i$1++)
2348
    { if (lineNo(lineView.rest[i$1]) > lineN)
2349
      { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], 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...
2350
}
2351
2352
// Render a line into the hidden node display.externalMeasured. Used
2353
// when measurement is needed for a line that's not in the viewport.
2354
function updateExternalMeasurement(cm, line) {
2355
  line = visualLine(line)
2356
  var lineN = lineNo(line)
2357
  var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN)
2358
  view.lineN = lineN
2359
  var built = view.built = buildLineContent(cm, view)
2360
  view.text = built.pre
2361
  removeChildrenAndAdd(cm.display.lineMeasure, built.pre)
2362
  return view
2363
}
2364
2365
// Get a {top, bottom, left, right} box (in line-local coordinates)
2366
// for a given character.
2367
function measureChar(cm, line, ch, bias) {
2368
  return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias)
2369
}
2370
2371
// Find a line view that corresponds to the given line number.
2372
function findViewForLine(cm, lineN) {
2373
  if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo)
2374
    { return cm.display.view[findViewIndex(cm, lineN)] }
2375
  var ext = cm.display.externalMeasured
2376
  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...
2377
    { return ext }
2378
}
2379
2380
// Measurement can be split in two steps, the set-up work that
2381
// applies to the whole line, and the measurement of the actual
2382
// character. Functions like coordsChar, that need to do a lot of
2383
// measurements in a row, can thus ensure that the set-up work is
2384
// only done once.
2385
function prepareMeasureForLine(cm, line) {
2386
  var lineN = lineNo(line)
2387
  var view = findViewForLine(cm, lineN)
2388
  if (view && !view.text) {
2389
    view = null
2390
  } else if (view && view.changes) {
2391
    updateLineForChanges(cm, view, lineN, getDimensions(cm))
2392
    cm.curOp.forceUpdate = true
2393
  }
2394
  if (!view)
2395
    { view = updateExternalMeasurement(cm, line) }
2396
2397
  var info = mapFromLineView(view, line, lineN)
2398
  return {
2399
    line: line, view: view, rect: null,
2400
    map: info.map, cache: info.cache, before: info.before,
2401
    hasHeights: false
2402
  }
2403
}
2404
2405
// Given a prepared measurement object, measures the position of an
2406
// actual character (or fetches it from the cache).
2407
function measureCharPrepared(cm, prepared, ch, bias, varHeight) {
2408
  if (prepared.before) { ch = -1 }
2409
  var key = ch + (bias || ""), found
2410
  if (prepared.cache.hasOwnProperty(key)) {
2411
    found = prepared.cache[key]
2412
  } else {
2413
    if (!prepared.rect)
2414
      { prepared.rect = prepared.view.text.getBoundingClientRect() }
2415
    if (!prepared.hasHeights) {
2416
      ensureLineHeights(cm, prepared.view, prepared.rect)
2417
      prepared.hasHeights = true
2418
    }
2419
    found = measureCharInner(cm, prepared, ch, bias)
2420
    if (!found.bogus) { prepared.cache[key] = found }
2421
  }
2422
  return {left: found.left, right: found.right,
2423
          top: varHeight ? found.rtop : found.top,
2424
          bottom: varHeight ? found.rbottom : found.bottom}
2425
}
2426
2427
var nullRect = {left: 0, right: 0, top: 0, bottom: 0}
2428
2429
function nodeAndOffsetInLineMap(map, ch, bias) {
2430
  var node, start, end, collapse, mStart, mEnd
2431
  // First, search the line map for the text node corresponding to,
2432
  // or closest to, the target character.
2433
  for (var i = 0; i < map.length; i += 3) {
2434
    mStart = map[i]
2435
    mEnd = map[i + 1]
2436
    if (ch < mStart) {
2437
      start = 0; end = 1
2438
      collapse = "left"
2439
    } else if (ch < mEnd) {
2440
      start = ch - mStart
2441
      end = start + 1
2442
    } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) {
2443
      end = mEnd - mStart
2444
      start = end - 1
2445
      if (ch >= mEnd) { collapse = "right" }
2446
    }
2447
    if (start != null) {
0 ignored issues
show
Bug introduced by
The variable start seems to not be initialized for all possible execution paths.
Loading history...
2448
      node = map[i + 2]
2449
      if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right"))
2450
        { collapse = bias }
2451
      if (bias == "left" && start == 0)
2452
        { while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) {
2453
          node = map[(i -= 3) + 2]
2454
          collapse = "left"
2455
        } }
2456
      if (bias == "right" && start == mEnd - mStart)
2457
        { while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].insertLeft) {
2458
          node = map[(i += 3) + 2]
2459
          collapse = "right"
2460
        } }
2461
      break
2462
    }
2463
  }
2464
  return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd}
0 ignored issues
show
Bug introduced by
The variable end seems to not be initialized for all possible execution paths.
Loading history...
Bug introduced by
The variable mEnd seems to not be initialized for all possible execution paths.
Loading history...
Bug introduced by
The variable node seems to not be initialized for all possible execution paths.
Loading history...
Bug introduced by
The variable collapse seems to not be initialized for all possible execution paths.
Loading history...
Bug introduced by
The variable mStart seems to not be initialized for all possible execution paths.
Loading history...
2465
}
2466
2467
function getUsefulRect(rects, bias) {
2468
  var rect = nullRect
2469
  if (bias == "left") { for (var i = 0; i < rects.length; i++) {
2470
    if ((rect = rects[i]).left != rect.right) { break }
2471
  } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) {
2472
    if ((rect = rects[i$1]).left != rect.right) { break }
2473
  } }
2474
  return rect
2475
}
2476
2477
function measureCharInner(cm, prepared, ch, bias) {
2478
  var place = nodeAndOffsetInLineMap(prepared.map, ch, bias)
2479
  var node = place.node, start = place.start, end = place.end, collapse = place.collapse
2480
2481
  var rect
2482
  if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.
2483
    for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonsense rectangles are returned
2484
      while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start }
2485
      while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) { ++end }
2486
      if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart)
2487
        { rect = node.parentNode.getBoundingClientRect() }
2488
      else
2489
        { rect = getUsefulRect(range(node, start, end).getClientRects(), bias) }
2490
      if (rect.left || rect.right || start == 0) { break }
2491
      end = start
2492
      start = start - 1
2493
      collapse = "right"
2494
    }
2495
    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...
2496
  } else { // If it is a widget, simply get the box for the whole widget.
2497
    if (start > 0) { collapse = bias = "right" }
2498
    var rects
2499
    if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)
2500
      { rect = rects[bias == "right" ? rects.length - 1 : 0] }
2501
    else
2502
      { rect = node.getBoundingClientRect() }
2503
  }
2504
  if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {
2505
    var rSpan = node.parentNode.getClientRects()[0]
2506
    if (rSpan)
2507
      { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom} }
2508
    else
2509
      { rect = nullRect }
2510
  }
2511
2512
  var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top
2513
  var mid = (rtop + rbot) / 2
2514
  var heights = prepared.view.measure.heights
2515
  var i = 0
2516
  for (; i < heights.length - 1; i++)
2517
    { if (mid < heights[i]) { break } }
2518
  var top = i ? heights[i - 1] : 0, bot = heights[i]
2519
  var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left,
2520
                right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left,
2521
                top: top, bottom: bot}
2522
  if (!rect.left && !rect.right) { result.bogus = true }
2523
  if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot }
2524
2525
  return result
2526
}
2527
2528
// Work around problem with bounding client rects on ranges being
2529
// returned incorrectly when zoomed on IE10 and below.
2530
function maybeUpdateRectForZooming(measure, rect) {
2531
  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...
2532
      screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))
2533
    { return rect }
2534
  var scaleX = screen.logicalXDPI / screen.deviceXDPI
2535
  var scaleY = screen.logicalYDPI / screen.deviceYDPI
2536
  return {left: rect.left * scaleX, right: rect.right * scaleX,
2537
          top: rect.top * scaleY, bottom: rect.bottom * scaleY}
2538
}
2539
2540
function clearLineMeasurementCacheFor(lineView) {
2541
  if (lineView.measure) {
2542
    lineView.measure.cache = {}
2543
    lineView.measure.heights = null
2544
    if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
2545
      { lineView.measure.caches[i] = {} } }
2546
  }
2547
}
2548
2549
function clearLineMeasurementCache(cm) {
2550
  cm.display.externalMeasure = null
2551
  removeChildren(cm.display.lineMeasure)
2552
  for (var i = 0; i < cm.display.view.length; i++)
2553
    { clearLineMeasurementCacheFor(cm.display.view[i]) }
2554
}
2555
2556
function clearCaches(cm) {
2557
  clearLineMeasurementCache(cm)
2558
  cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null
2559
  if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true }
2560
  cm.display.lineNumChars = null
2561
}
2562
2563
function pageScrollX() { return window.pageXOffset || (document.documentElement || document.body).scrollLeft }
2564
function pageScrollY() { return window.pageYOffset || (document.documentElement || document.body).scrollTop }
2565
2566
// Converts a {top, bottom, left, right} box from line-local
2567
// coordinates into another coordinate system. Context may be one of
2568
// "line", "div" (display.lineDiv), "local"./null (editor), "window",
2569
// or "page".
2570
function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) {
2571
  if (!includeWidgets && lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above) {
2572
    var size = widgetHeight(lineObj.widgets[i])
2573
    rect.top += size; rect.bottom += size
2574
  } } }
2575
  if (context == "line") { return rect }
2576
  if (!context) { context = "local" }
2577
  var yOff = heightAtLine(lineObj)
2578
  if (context == "local") { yOff += paddingTop(cm.display) }
2579
  else { yOff -= cm.display.viewOffset }
2580
  if (context == "page" || context == "window") {
2581
    var lOff = cm.display.lineSpace.getBoundingClientRect()
2582
    yOff += lOff.top + (context == "window" ? 0 : pageScrollY())
2583
    var xOff = lOff.left + (context == "window" ? 0 : pageScrollX())
2584
    rect.left += xOff; rect.right += xOff
2585
  }
2586
  rect.top += yOff; rect.bottom += yOff
2587
  return rect
2588
}
2589
2590
// Coverts a box from "div" coords to another coordinate system.
2591
// Context may be "window", "page", "div", or "local"./null.
2592
function fromCoordSystem(cm, coords, context) {
2593
  if (context == "div") { return coords }
2594
  var left = coords.left, top = coords.top
2595
  // First move into "page" coordinate system
2596
  if (context == "page") {
2597
    left -= pageScrollX()
2598
    top -= pageScrollY()
2599
  } else if (context == "local" || !context) {
2600
    var localBox = cm.display.sizer.getBoundingClientRect()
2601
    left += localBox.left
2602
    top += localBox.top
2603
  }
2604
2605
  var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect()
2606
  return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top}
2607
}
2608
2609
function charCoords(cm, pos, context, lineObj, bias) {
2610
  if (!lineObj) { lineObj = getLine(cm.doc, pos.line) }
2611
  return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context)
2612
}
2613
2614
// Returns a box for a given cursor position, which may have an
2615
// 'other' property containing the position of the secondary cursor
2616
// on a bidi boundary.
2617
function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
2618
  lineObj = lineObj || getLine(cm.doc, pos.line)
2619
  if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj) }
2620
  function get(ch, right) {
2621
    var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight)
2622
    if (right) { m.left = m.right; } else { m.right = m.left }
2623
    return intoCoordSystem(cm, lineObj, m, context)
2624
  }
2625
  function getBidi(ch, partPos) {
2626
    var part = order[partPos], right = part.level % 2
2627
    if (ch == bidiLeft(part) && partPos && part.level < order[partPos - 1].level) {
2628
      part = order[--partPos]
2629
      ch = bidiRight(part) - (part.level % 2 ? 0 : 1)
2630
      right = true
2631
    } else if (ch == bidiRight(part) && partPos < order.length - 1 && part.level < order[partPos + 1].level) {
2632
      part = order[++partPos]
2633
      ch = bidiLeft(part) - part.level % 2
2634
      right = false
2635
    }
2636
    if (right && ch == part.to && ch > part.from) { return get(ch - 1) }
2637
    return get(ch, right)
2638
  }
2639
  var order = getOrder(lineObj), ch = pos.ch
2640
  if (!order) { return get(ch) }
2641
  var partPos = getBidiPartAt(order, ch)
2642
  var val = getBidi(ch, partPos)
2643
  if (bidiOther != null) { val.other = getBidi(ch, bidiOther) }
2644
  return val
2645
}
2646
2647
// Used to cheaply estimate the coordinates for a position. Used for
2648
// intermediate scroll updates.
2649
function estimateCoords(cm, pos) {
2650
  var left = 0
2651
  pos = clipPos(cm.doc, pos)
2652
  if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch }
2653
  var lineObj = getLine(cm.doc, pos.line)
2654
  var top = heightAtLine(lineObj) + paddingTop(cm.display)
2655
  return {left: left, right: left, top: top, bottom: top + lineObj.height}
2656
}
2657
2658
// Positions returned by coordsChar contain some extra information.
2659
// xRel is the relative x position of the input coordinates compared
2660
// to the found position (so xRel > 0 means the coordinates are to
2661
// the right of the character position, for example). When outside
2662
// is true, that means the coordinates lie outside the line's
2663
// vertical range.
2664
function PosWithInfo(line, ch, outside, xRel) {
2665
  var pos = Pos(line, ch)
2666
  pos.xRel = xRel
2667
  if (outside) { pos.outside = true }
2668
  return pos
2669
}
2670
2671
// Compute the character position closest to the given coordinates.
2672
// Input must be lineSpace-local ("div" coordinate system).
2673
function coordsChar(cm, x, y) {
2674
  var doc = cm.doc
2675
  y += cm.display.viewOffset
2676
  if (y < 0) { return PosWithInfo(doc.first, 0, true, -1) }
2677
  var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1
2678
  if (lineN > last)
2679
    { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, true, 1) }
2680
  if (x < 0) { x = 0 }
2681
2682
  var lineObj = getLine(doc, lineN)
2683
  for (;;) {
2684
    var found = coordsCharInner(cm, lineObj, lineN, x, y)
2685
    var merged = collapsedSpanAtEnd(lineObj)
2686
    var mergedPos = merged && merged.find(0, true)
2687
    if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0))
2688
      { lineN = lineNo(lineObj = mergedPos.to.line) }
2689
    else
2690
      { return found }
2691
  }
2692
}
2693
2694
function coordsCharInner(cm, lineObj, lineNo, x, y) {
2695
  var innerOff = y - heightAtLine(lineObj)
2696
  var wrongLine = false, adjust = 2 * cm.display.wrapper.clientWidth
2697
  var preparedMeasure = prepareMeasureForLine(cm, lineObj)
2698
2699
  function getX(ch) {
2700
    var sp = cursorCoords(cm, Pos(lineNo, ch), "line", lineObj, preparedMeasure)
2701
    wrongLine = true
2702
    if (innerOff > sp.bottom) { return sp.left - adjust }
2703
    else if (innerOff < sp.top) { return sp.left + adjust }
2704
    else { wrongLine = false }
2705
    return sp.left
2706
  }
2707
2708
  var bidi = getOrder(lineObj), dist = lineObj.text.length
2709
  var from = lineLeft(lineObj), to = lineRight(lineObj)
2710
  var fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine
2711
2712
  if (x > toX) { return PosWithInfo(lineNo, to, toOutside, 1) }
2713
  // Do a binary search between these bounds.
2714
  for (;;) {
2715
    if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
2716
      var ch = x < fromX || x - fromX <= toX - x ? from : to
2717
      var outside = ch == from ? fromOutside : toOutside
2718
      var xDiff = x - (ch == from ? fromX : toX)
2719
      // This is a kludge to handle the case where the coordinates
2720
      // are after a line-wrapped line. We should replace it with a
2721
      // more general handling of cursor positions around line
2722
      // breaks. (Issue #4078)
2723
      if (toOutside && !bidi && !/\s/.test(lineObj.text.charAt(ch)) && xDiff > 0 &&
2724
          ch < lineObj.text.length && preparedMeasure.view.measure.heights.length > 1) {
2725
        var charSize = measureCharPrepared(cm, preparedMeasure, ch, "right")
2726
        if (innerOff <= charSize.bottom && innerOff >= charSize.top && Math.abs(x - charSize.right) < xDiff) {
2727
          outside = false
2728
          ch++
2729
          xDiff = x - charSize.right
2730
        }
2731
      }
2732
      while (isExtendingChar(lineObj.text.charAt(ch))) { ++ch }
2733
      var pos = PosWithInfo(lineNo, ch, outside, xDiff < -1 ? -1 : xDiff > 1 ? 1 : 0)
2734
      return pos
2735
    }
2736
    var step = Math.ceil(dist / 2), middle = from + step
2737
    if (bidi) {
2738
      middle = from
2739
      for (var i = 0; i < step; ++i) { middle = moveVisually(lineObj, middle, 1) }
2740
    }
2741
    var middleX = getX(middle)
2742
    if (middleX > x) {to = middle; toX = middleX; if (toOutside = wrongLine) { toX += 1000; } dist = step}
2743
    else {from = middle; fromX = middleX; fromOutside = wrongLine; dist -= step}
2744
  }
2745
}
2746
2747
var measureText
2748
// Compute the default text height.
2749
function textHeight(display) {
2750
  if (display.cachedTextHeight != null) { return display.cachedTextHeight }
2751
  if (measureText == null) {
2752
    measureText = elt("pre")
2753
    // Measure a bunch of lines, for browsers that compute
2754
    // fractional heights.
2755
    for (var i = 0; i < 49; ++i) {
2756
      measureText.appendChild(document.createTextNode("x"))
2757
      measureText.appendChild(elt("br"))
2758
    }
2759
    measureText.appendChild(document.createTextNode("x"))
2760
  }
2761
  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 2751 is false. Are you sure the function removeChildrenAndAdd handles undefined variables?
Loading history...
2762
  var height = measureText.offsetHeight / 50
2763
  if (height > 3) { display.cachedTextHeight = height }
2764
  removeChildren(display.measure)
2765
  return height || 1
2766
}
2767
2768
// Compute the default character width.
2769
function charWidth(display) {
2770
  if (display.cachedCharWidth != null) { return display.cachedCharWidth }
2771
  var anchor = elt("span", "xxxxxxxxxx")
2772
  var pre = elt("pre", [anchor])
2773
  removeChildrenAndAdd(display.measure, pre)
2774
  var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10
2775
  if (width > 2) { display.cachedCharWidth = width }
2776
  return width || 10
2777
}
2778
2779
// Do a bulk-read of the DOM positions and sizes needed to draw the
2780
// view, so that we don't interleave reading and writing to the DOM.
2781
function getDimensions(cm) {
2782
  var d = cm.display, left = {}, width = {}
2783
  var gutterLeft = d.gutters.clientLeft
2784
  for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
2785
    left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft
2786
    width[cm.options.gutters[i]] = n.clientWidth
2787
  }
2788
  return {fixedPos: compensateForHScroll(d),
2789
          gutterTotalWidth: d.gutters.offsetWidth,
2790
          gutterLeft: left,
2791
          gutterWidth: width,
2792
          wrapperWidth: d.wrapper.clientWidth}
2793
}
2794
2795
// Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
2796
// but using getBoundingClientRect to get a sub-pixel-accurate
2797
// result.
2798
function compensateForHScroll(display) {
2799
  return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left
2800
}
2801
2802
// Returns a function that estimates the height of a line, to use as
2803
// first approximation until the line becomes visible (and is thus
2804
// properly measurable).
2805
function estimateHeight(cm) {
2806
  var th = textHeight(cm.display), wrapping = cm.options.lineWrapping
2807
  var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3)
2808
  return function (line) {
2809
    if (lineIsHidden(cm.doc, line)) { return 0 }
2810
2811
    var widgetsHeight = 0
2812
    if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) {
2813
      if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height }
2814
    } }
2815
2816
    if (wrapping)
2817
      { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th }
2818
    else
2819
      { return widgetsHeight + th }
2820
  }
2821
}
2822
2823
function estimateLineHeights(cm) {
2824
  var doc = cm.doc, est = estimateHeight(cm)
2825
  doc.iter(function (line) {
2826
    var estHeight = est(line)
2827
    if (estHeight != line.height) { updateLineHeight(line, estHeight) }
2828
  })
2829
}
2830
2831
// Given a mouse event, find the corresponding position. If liberal
2832
// is false, it checks whether a gutter or scrollbar was clicked,
2833
// and returns null if it was. forRect is used by rectangular
2834
// selections, and tries to estimate a character position even for
2835
// coordinates beyond the right of the text.
2836
function posFromMouse(cm, e, liberal, forRect) {
2837
  var display = cm.display
2838
  if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") { return null }
2839
2840
  var x, y, space = display.lineSpace.getBoundingClientRect()
2841
  // Fails unpredictably on IE[67] when mouse is dragged around quickly.
2842
  try { x = e.clientX - space.left; y = e.clientY - space.top }
2843
  catch (e) { return null }
2844
  var coords = coordsChar(cm, x, y), line
2845
  if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) {
2846
    var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length
2847
    coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff))
2848
  }
2849
  return coords
2850
}
2851
2852
// Find the view element corresponding to a given line. Return null
2853
// when the line isn't visible.
2854
function findViewIndex(cm, n) {
2855
  if (n >= cm.display.viewTo) { return null }
2856
  n -= cm.display.viewFrom
2857
  if (n < 0) { return null }
2858
  var view = cm.display.view
2859
  for (var i = 0; i < view.length; i++) {
2860
    n -= view[i].size
2861
    if (n < 0) { return i }
2862
  }
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...
2863
}
2864
2865
function updateSelection(cm) {
2866
  cm.display.input.showSelection(cm.display.input.prepareSelection())
2867
}
2868
2869
function prepareSelection(cm, primary) {
2870
  var doc = cm.doc, result = {}
2871
  var curFragment = result.cursors = document.createDocumentFragment()
2872
  var selFragment = result.selection = document.createDocumentFragment()
2873
2874
  for (var i = 0; i < doc.sel.ranges.length; i++) {
2875
    if (primary === false && i == doc.sel.primIndex) { continue }
2876
    var range = doc.sel.ranges[i]
2877
    if (range.from().line >= cm.display.viewTo || range.to().line < cm.display.viewFrom) { continue }
2878
    var collapsed = range.empty()
2879
    if (collapsed || cm.options.showCursorWhenSelecting)
2880
      { drawSelectionCursor(cm, range.head, curFragment) }
2881
    if (!collapsed)
2882
      { drawSelectionRange(cm, range, selFragment) }
2883
  }
2884
  return result
2885
}
2886
2887
// Draws a cursor for the given range
2888
function drawSelectionCursor(cm, head, output) {
2889
  var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine)
2890
2891
  var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor"))
2892
  cursor.style.left = pos.left + "px"
2893
  cursor.style.top = pos.top + "px"
2894
  cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px"
2895
2896
  if (pos.other) {
2897
    // Secondary cursor, shown when on a 'jump' in bi-directional text
2898
    var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"))
2899
    otherCursor.style.display = ""
2900
    otherCursor.style.left = pos.other.left + "px"
2901
    otherCursor.style.top = pos.other.top + "px"
2902
    otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px"
2903
  }
2904
}
2905
2906
// Draws the given range as a highlighted selection
2907
function drawSelectionRange(cm, range, output) {
2908
  var display = cm.display, doc = cm.doc
2909
  var fragment = document.createDocumentFragment()
2910
  var padding = paddingH(cm.display), leftSide = padding.left
2911
  var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right
2912
2913
  function add(left, top, width, bottom) {
2914
    if (top < 0) { top = 0 }
2915
    top = Math.round(top)
2916
    bottom = Math.round(bottom)
2917
    fragment.appendChild(elt("div", null, "CodeMirror-selected", ("position: absolute; left: " + left + "px;\n                             top: " + top + "px; width: " + (width == null ? rightSide - left : width) + "px;\n                             height: " + (bottom - top) + "px")))
2918
  }
2919
2920
  function drawForLine(line, fromArg, toArg) {
2921
    var lineObj = getLine(doc, line)
2922
    var lineLen = lineObj.text.length
2923
    var start, end
2924
    function coords(ch, bias) {
2925
      return charCoords(cm, Pos(line, ch), "div", lineObj, bias)
2926
    }
2927
2928
    iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir) {
2929
      var leftPos = coords(from, "left"), rightPos, left, right
2930
      if (from == to) {
2931
        rightPos = leftPos
2932
        left = right = leftPos.left
2933
      } else {
2934
        rightPos = coords(to - 1, "right")
2935
        if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp }
2936
        left = leftPos.left
2937
        right = rightPos.right
2938
      }
2939
      if (fromArg == null && from == 0) { left = leftSide }
2940
      if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
2941
        add(left, leftPos.top, null, leftPos.bottom)
2942
        left = leftSide
2943
        if (leftPos.bottom < rightPos.top) { add(left, leftPos.bottom, null, rightPos.top) }
2944
      }
2945
      if (toArg == null && to == lineLen) { right = rightSide }
2946
      if (!start || leftPos.top < start.top || leftPos.top == start.top && leftPos.left < start.left)
2947
        { start = leftPos }
2948
      if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.bottom && rightPos.right > end.right)
2949
        { end = rightPos }
2950
      if (left < leftSide + 1) { left = leftSide }
2951
      add(left, rightPos.top, right - left, rightPos.bottom)
2952
    })
2953
    return {start: start, end: end}
2954
  }
2955
2956
  var sFrom = range.from(), sTo = range.to()
2957
  if (sFrom.line == sTo.line) {
2958
    drawForLine(sFrom.line, sFrom.ch, sTo.ch)
2959
  } else {
2960
    var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line)
2961
    var singleVLine = visualLine(fromLine) == visualLine(toLine)
2962
    var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end
2963
    var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start
2964
    if (singleVLine) {
2965
      if (leftEnd.top < rightStart.top - 2) {
2966
        add(leftEnd.right, leftEnd.top, null, leftEnd.bottom)
2967
        add(leftSide, rightStart.top, rightStart.left, rightStart.bottom)
2968
      } else {
2969
        add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom)
2970
      }
2971
    }
2972
    if (leftEnd.bottom < rightStart.top)
2973
      { add(leftSide, leftEnd.bottom, null, rightStart.top) }
2974
  }
2975
2976
  output.appendChild(fragment)
2977
}
2978
2979
// Cursor-blinking
2980
function restartBlink(cm) {
2981
  if (!cm.state.focused) { return }
2982
  var display = cm.display
2983
  clearInterval(display.blinker)
2984
  var on = true
2985
  display.cursorDiv.style.visibility = ""
2986
  if (cm.options.cursorBlinkRate > 0)
2987
    { display.blinker = setInterval(function () { return display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; },
2988
      cm.options.cursorBlinkRate) }
2989
  else if (cm.options.cursorBlinkRate < 0)
2990
    { display.cursorDiv.style.visibility = "hidden" }
2991
}
2992
2993
function ensureFocus(cm) {
2994
  if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm) }
2995
}
2996
2997
function delayBlurEvent(cm) {
2998
  cm.state.delayingBlurEvent = true
2999
  setTimeout(function () { if (cm.state.delayingBlurEvent) {
3000
    cm.state.delayingBlurEvent = false
3001
    onBlur(cm)
3002
  } }, 100)
3003
}
3004
3005
function onFocus(cm, e) {
3006
  if (cm.state.delayingBlurEvent) { cm.state.delayingBlurEvent = false }
3007
3008
  if (cm.options.readOnly == "nocursor") { return }
3009
  if (!cm.state.focused) {
3010
    signal(cm, "focus", cm, e)
3011
    cm.state.focused = true
3012
    addClass(cm.display.wrapper, "CodeMirror-focused")
3013
    // This test prevents this from firing when a context
3014
    // menu is closed (since the input reset would kill the
3015
    // select-all detection hack)
3016
    if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {
3017
      cm.display.input.reset()
3018
      if (webkit) { setTimeout(function () { return cm.display.input.reset(true); }, 20) } // Issue #1730
3019
    }
3020
    cm.display.input.receivedFocus()
3021
  }
3022
  restartBlink(cm)
3023
}
3024
function onBlur(cm, e) {
3025
  if (cm.state.delayingBlurEvent) { return }
3026
3027
  if (cm.state.focused) {
3028
    signal(cm, "blur", cm, e)
3029
    cm.state.focused = false
3030
    rmClass(cm.display.wrapper, "CodeMirror-focused")
3031
  }
3032
  clearInterval(cm.display.blinker)
3033
  setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false } }, 150)
3034
}
3035
3036
// Re-align line numbers and gutter marks to compensate for
3037
// horizontal scrolling.
3038
function alignHorizontally(cm) {
3039
  var display = cm.display, view = display.view
3040
  if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) { return }
3041
  var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft
3042
  var gutterW = display.gutters.offsetWidth, left = comp + "px"
3043
  for (var i = 0; i < view.length; i++) { if (!view[i].hidden) {
3044
    if (cm.options.fixedGutter) {
3045
      if (view[i].gutter)
3046
        { view[i].gutter.style.left = left }
3047
      if (view[i].gutterBackground)
3048
        { view[i].gutterBackground.style.left = left }
3049
    }
3050
    var align = view[i].alignable
3051
    if (align) { for (var j = 0; j < align.length; j++)
3052
      { align[j].style.left = left } }
3053
  } }
3054
  if (cm.options.fixedGutter)
3055
    { display.gutters.style.left = (comp + gutterW) + "px" }
3056
}
3057
3058
// Used to ensure that the line number gutter is still the right
3059
// size for the current document size. Returns true when an update
3060
// is needed.
3061
function maybeUpdateLineNumberWidth(cm) {
3062
  if (!cm.options.lineNumbers) { return false }
3063
  var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display
3064
  if (last.length != display.lineNumChars) {
3065
    var test = display.measure.appendChild(elt("div", [elt("div", last)],
3066
                                               "CodeMirror-linenumber CodeMirror-gutter-elt"))
3067
    var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW
3068
    display.lineGutter.style.width = ""
3069
    display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1
3070
    display.lineNumWidth = display.lineNumInnerWidth + padding
3071
    display.lineNumChars = display.lineNumInnerWidth ? last.length : -1
3072
    display.lineGutter.style.width = display.lineNumWidth + "px"
3073
    updateGutterSpace(cm)
3074
    return true
3075
  }
3076
  return false
3077
}
3078
3079
// Read the actual heights of the rendered lines, and update their
3080
// stored heights to match.
3081
function updateHeightsInViewport(cm) {
3082
  var display = cm.display
3083
  var prevBottom = display.lineDiv.offsetTop
3084
  for (var i = 0; i < display.view.length; i++) {
3085
    var cur = display.view[i], height = (void 0)
0 ignored issues
show
Unused Code introduced by
The assignment to variable height seems to be never used. Consider removing it.
Loading history...
Coding Style introduced by
Consider using undefined instead of void(0). It is equivalent and more straightforward to read.
Loading history...
3086
    if (cur.hidden) { continue }
3087
    if (ie && ie_version < 8) {
3088
      var bot = cur.node.offsetTop + cur.node.offsetHeight
3089
      height = bot - prevBottom
3090
      prevBottom = bot
3091
    } else {
3092
      var box = cur.node.getBoundingClientRect()
3093
      height = box.bottom - box.top
3094
    }
3095
    var diff = cur.line.height - height
3096
    if (height < 2) { height = textHeight(display) }
3097
    if (diff > .001 || diff < -.001) {
3098
      updateLineHeight(cur.line, height)
3099
      updateWidgetHeight(cur.line)
3100
      if (cur.rest) { for (var j = 0; j < cur.rest.length; j++)
3101
        { updateWidgetHeight(cur.rest[j]) } }
3102
    }
3103
  }
3104
}
3105
3106
// Read and store the height of line widgets associated with the
3107
// given line.
3108
function updateWidgetHeight(line) {
3109
  if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i)
3110
    { line.widgets[i].height = line.widgets[i].node.parentNode.offsetHeight } }
3111
}
3112
3113
// Compute the lines that are visible in a given viewport (defaults
3114
// the the current scroll position). viewport may contain top,
3115
// height, and ensure (see op.scrollToPos) properties.
3116
function visibleLines(display, doc, viewport) {
3117
  var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop
3118
  top = Math.floor(top - paddingTop(display))
3119
  var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight
3120
3121
  var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom)
3122
  // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
3123
  // forces those lines into the viewport (if possible).
3124
  if (viewport && viewport.ensure) {
3125
    var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line
3126
    if (ensureFrom < from) {
3127
      from = ensureFrom
3128
      to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight)
3129
    } else if (Math.min(ensureTo, doc.lastLine()) >= to) {
3130
      from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight)
3131
      to = ensureTo
3132
    }
3133
  }
3134
  return {from: from, to: Math.max(to, from + 1)}
3135
}
3136
3137
// Sync the scrollable area and scrollbars, ensure the viewport
3138
// covers the visible area.
3139
function setScrollTop(cm, val) {
3140
  if (Math.abs(cm.doc.scrollTop - val) < 2) { return }
3141
  cm.doc.scrollTop = val
3142
  if (!gecko) { updateDisplaySimple(cm, {top: val}) }
3143
  if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = val }
3144
  cm.display.scrollbars.setScrollTop(val)
3145
  if (gecko) { updateDisplaySimple(cm) }
3146
  startWorker(cm, 100)
3147
}
3148
// Sync scroller and scrollbar, ensure the gutter elements are
3149
// aligned.
3150
function setScrollLeft(cm, val, isScroller) {
3151
  if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) { return }
3152
  val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth)
3153
  cm.doc.scrollLeft = val
3154
  alignHorizontally(cm)
3155
  if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val }
3156
  cm.display.scrollbars.setScrollLeft(val)
3157
}
3158
3159
// Since the delta values reported on mouse wheel events are
3160
// unstandardized between browsers and even browser versions, and
3161
// generally horribly unpredictable, this code starts by measuring
3162
// the scroll effect that the first few mouse wheel events have,
3163
// and, from that, detects the way it can convert deltas to pixel
3164
// offsets afterwards.
3165
//
3166
// The reason we want to know the amount a wheel event will scroll
3167
// is that it gives us a chance to update the display before the
3168
// actual scrolling happens, reducing flickering.
3169
3170
var wheelSamples = 0;
3171
var wheelPixelsPerUnit = null;
3172
// Fill in a browser-detected starting value on browsers where we
3173
// know one. These don't have to be accurate -- the result of them
3174
// being wrong would just be a slight flicker on the first wheel
3175
// scroll (if it is large enough).
3176
if (ie) { wheelPixelsPerUnit = -.53 }
3177
else if (gecko) { wheelPixelsPerUnit = 15 }
3178
else if (chrome) { wheelPixelsPerUnit = -.7 }
3179
else if (safari) { wheelPixelsPerUnit = -1/3 }
3180
3181
function wheelEventDelta(e) {
3182
  var dx = e.wheelDeltaX, dy = e.wheelDeltaY
3183
  if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail }
3184
  if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail }
3185
  else if (dy == null) { dy = e.wheelDelta }
3186
  return {x: dx, y: dy}
3187
}
3188
function wheelEventPixels(e) {
3189
  var delta = wheelEventDelta(e)
3190
  delta.x *= wheelPixelsPerUnit
3191
  delta.y *= wheelPixelsPerUnit
3192
  return delta
3193
}
3194
3195
function onScrollWheel(cm, e) {
3196
  var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y
3197
3198
  var display = cm.display, scroll = display.scroller
3199
  // Quit if there's nothing to scroll here
3200
  var canScrollX = scroll.scrollWidth > scroll.clientWidth
3201
  var canScrollY = scroll.scrollHeight > scroll.clientHeight
3202
  if (!(dx && canScrollX || dy && canScrollY)) { return }
3203
3204
  // Webkit browsers on OS X abort momentum scrolls when the target
3205
  // of the scroll event is removed from the scrollable element.
3206
  // This hack (see related code in patchDisplay) makes sure the
3207
  // element is kept around.
3208
  if (dy && mac && webkit) {
3209
    outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {
3210
      for (var i = 0; i < view.length; i++) {
3211
        if (view[i].node == cur) {
3212
          cm.display.currentWheelTarget = cur
3213
          break outer
3214
        }
3215
      }
3216
    }
3217
  }
3218
3219
  // On some browsers, horizontal scrolling will cause redraws to
3220
  // happen before the gutter has been realigned, causing it to
3221
  // wriggle around in a most unseemly way. When we have an
3222
  // estimated pixels/delta value, we just handle horizontal
3223
  // scrolling entirely here. It'll be slightly off from native, but
3224
  // better than glitching out.
3225
  if (dx && !gecko && !presto && wheelPixelsPerUnit != null) {
3226
    if (dy && canScrollY)
3227
      { setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight))) }
3228
    setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth)))
3229
    // Only prevent default scrolling if vertical scrolling is
3230
    // actually possible. Otherwise, it causes vertical scroll
3231
    // jitter on OSX trackpads when deltaX is small and deltaY
3232
    // is large (issue #3579)
3233
    if (!dy || (dy && canScrollY))
3234
      { e_preventDefault(e) }
3235
    display.wheelStartX = null // Abort measurement, if in progress
3236
    return
3237
  }
3238
3239
  // 'Project' the visible viewport to cover the area that is being
3240
  // scrolled into view (if we know enough to estimate it).
3241
  if (dy && wheelPixelsPerUnit != null) {
3242
    var pixels = dy * wheelPixelsPerUnit
3243
    var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight
3244
    if (pixels < 0) { top = Math.max(0, top + pixels - 50) }
3245
    else { bot = Math.min(cm.doc.height, bot + pixels + 50) }
3246
    updateDisplaySimple(cm, {top: top, bottom: bot})
3247
  }
3248
3249
  if (wheelSamples < 20) {
3250
    if (display.wheelStartX == null) {
3251
      display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop
3252
      display.wheelDX = dx; display.wheelDY = dy
3253
      setTimeout(function () {
3254
        if (display.wheelStartX == null) { return }
3255
        var movedX = scroll.scrollLeft - display.wheelStartX
3256
        var movedY = scroll.scrollTop - display.wheelStartY
3257
        var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
3258
          (movedX && display.wheelDX && movedX / display.wheelDX)
3259
        display.wheelStartX = display.wheelStartY = null
3260
        if (!sample) { return }
3261
        wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1)
3262
        ++wheelSamples
3263
      }, 200)
3264
    } else {
3265
      display.wheelDX += dx; display.wheelDY += dy
3266
    }
3267
  }
3268
}
3269
3270
// SCROLLBARS
3271
3272
// Prepare DOM reads needed to update the scrollbars. Done in one
3273
// shot to minimize update/measure roundtrips.
3274
function measureForScrollbars(cm) {
3275
  var d = cm.display, gutterW = d.gutters.offsetWidth
3276
  var docH = Math.round(cm.doc.height + paddingVert(cm.display))
3277
  return {
3278
    clientHeight: d.scroller.clientHeight,
3279
    viewHeight: d.wrapper.clientHeight,
3280
    scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,
3281
    viewWidth: d.wrapper.clientWidth,
3282
    barLeft: cm.options.fixedGutter ? gutterW : 0,
3283
    docHeight: docH,
3284
    scrollHeight: docH + scrollGap(cm) + d.barHeight,
3285
    nativeBarWidth: d.nativeBarWidth,
3286
    gutterWidth: gutterW
3287
  }
3288
}
3289
3290
var NativeScrollbars = function(place, scroll, cm) {
3291
  this.cm = cm
3292
  var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar")
3293
  var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar")
3294
  place(vert); place(horiz)
3295
3296
  on(vert, "scroll", function () {
3297
    if (vert.clientHeight) { scroll(vert.scrollTop, "vertical") }
3298
  })
3299
  on(horiz, "scroll", function () {
3300
    if (horiz.clientWidth) { scroll(horiz.scrollLeft, "horizontal") }
3301
  })
3302
3303
  this.checkedZeroWidth = false
3304
  // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
3305
  if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = "18px" }
3306
};
3307
3308
NativeScrollbars.prototype.update = function (measure) {
3309
  var needsH = measure.scrollWidth > measure.clientWidth + 1
3310
  var needsV = measure.scrollHeight > measure.clientHeight + 1
3311
  var sWidth = measure.nativeBarWidth
3312
3313
  if (needsV) {
3314
    this.vert.style.display = "block"
3315
    this.vert.style.bottom = needsH ? sWidth + "px" : "0"
3316
    var totalHeight = measure.viewHeight - (needsH ? sWidth : 0)
3317
    // A bug in IE8 can cause this value to be negative, so guard it.
3318
    this.vert.firstChild.style.height =
3319
      Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px"
3320
  } else {
3321
    this.vert.style.display = ""
3322
    this.vert.firstChild.style.height = "0"
3323
  }
3324
3325
  if (needsH) {
3326
    this.horiz.style.display = "block"
3327
    this.horiz.style.right = needsV ? sWidth + "px" : "0"
3328
    this.horiz.style.left = measure.barLeft + "px"
3329
    var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0)
3330
    this.horiz.firstChild.style.width =
3331
      (measure.scrollWidth - measure.clientWidth + totalWidth) + "px"
3332
  } else {
3333
    this.horiz.style.display = ""
3334
    this.horiz.firstChild.style.width = "0"
3335
  }
3336
3337
  if (!this.checkedZeroWidth && measure.clientHeight > 0) {
3338
    if (sWidth == 0) { this.zeroWidthHack() }
3339
    this.checkedZeroWidth = true
3340
  }
3341
3342
  return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0}
3343
};
3344
3345
NativeScrollbars.prototype.setScrollLeft = function (pos) {
3346
  if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos }
3347
  if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz) }
3348
};
3349
3350
NativeScrollbars.prototype.setScrollTop = function (pos) {
3351
  if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos }
3352
  if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert) }
3353
};
3354
3355
NativeScrollbars.prototype.zeroWidthHack = function () {
3356
  var w = mac && !mac_geMountainLion ? "12px" : "18px"
3357
  this.horiz.style.height = this.vert.style.width = w
3358
  this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none"
3359
  this.disableHoriz = new Delayed
3360
  this.disableVert = new Delayed
3361
};
3362
3363
NativeScrollbars.prototype.enableZeroWidthBar = function (bar, delay) {
3364
  bar.style.pointerEvents = "auto"
3365
  function maybeDisable() {
3366
    // To find out whether the scrollbar is still visible, we
3367
    // check whether the element under the pixel in the bottom
3368
    // left corner of the scrollbar box is the scrollbar box
3369
    // itself (when the bar is still visible) or its filler child
3370
    // (when the bar is hidden). If it is still visible, we keep
3371
    // it enabled, if it's hidden, we disable pointer events.
3372
    var box = bar.getBoundingClientRect()
3373
    var elt = document.elementFromPoint(box.left + 1, box.bottom - 1)
3374
    if (elt != bar) { bar.style.pointerEvents = "none" }
3375
    else { delay.set(1000, maybeDisable) }
3376
  }
3377
  delay.set(1000, maybeDisable)
3378
};
3379
3380
NativeScrollbars.prototype.clear = function () {
3381
  var parent = this.horiz.parentNode
3382
  parent.removeChild(this.horiz)
3383
  parent.removeChild(this.vert)
3384
};
3385
3386
var NullScrollbars = function () {};
3387
3388
NullScrollbars.prototype.update = function () { return {bottom: 0, right: 0} };
3389
NullScrollbars.prototype.setScrollLeft = function () {};
3390
NullScrollbars.prototype.setScrollTop = function () {};
3391
NullScrollbars.prototype.clear = function () {};
3392
3393
function updateScrollbars(cm, measure) {
3394
  if (!measure) { measure = measureForScrollbars(cm) }
3395
  var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight
3396
  updateScrollbarsInner(cm, measure)
3397
  for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) {
3398
    if (startWidth != cm.display.barWidth && cm.options.lineWrapping)
3399
      { updateHeightsInViewport(cm) }
3400
    updateScrollbarsInner(cm, measureForScrollbars(cm))
3401
    startWidth = cm.display.barWidth; startHeight = cm.display.barHeight
3402
  }
3403
}
3404
3405
// Re-synchronize the fake scrollbars with the actual size of the
3406
// content.
3407
function updateScrollbarsInner(cm, measure) {
3408
  var d = cm.display
3409
  var sizes = d.scrollbars.update(measure)
3410
3411
  d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px"
3412
  d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px"
3413
  d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent"
3414
3415
  if (sizes.right && sizes.bottom) {
3416
    d.scrollbarFiller.style.display = "block"
3417
    d.scrollbarFiller.style.height = sizes.bottom + "px"
3418
    d.scrollbarFiller.style.width = sizes.right + "px"
3419
  } else { d.scrollbarFiller.style.display = "" }
3420
  if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
3421
    d.gutterFiller.style.display = "block"
3422
    d.gutterFiller.style.height = sizes.bottom + "px"
3423
    d.gutterFiller.style.width = measure.gutterWidth + "px"
3424
  } else { d.gutterFiller.style.display = "" }
3425
}
3426
3427
var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars}
3428
3429
function initScrollbars(cm) {
3430
  if (cm.display.scrollbars) {
3431
    cm.display.scrollbars.clear()
3432
    if (cm.display.scrollbars.addClass)
3433
      { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass) }
3434
  }
3435
3436
  cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) {
3437
    cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller)
3438
    // Prevent clicks in the scrollbars from killing focus
3439
    on(node, "mousedown", function () {
3440
      if (cm.state.focused) { setTimeout(function () { return cm.display.input.focus(); }, 0) }
3441
    })
3442
    node.setAttribute("cm-not-content", "true")
3443
  }, function (pos, axis) {
3444
    if (axis == "horizontal") { setScrollLeft(cm, pos) }
3445
    else { setScrollTop(cm, pos) }
3446
  }, cm)
3447
  if (cm.display.scrollbars.addClass)
3448
    { addClass(cm.display.wrapper, cm.display.scrollbars.addClass) }
3449
}
3450
3451
// SCROLLING THINGS INTO VIEW
3452
3453
// If an editor sits on the top or bottom of the window, partially
3454
// scrolled out of view, this ensures that the cursor is visible.
3455
function maybeScrollWindow(cm, coords) {
3456
  if (signalDOMEvent(cm, "scrollCursorIntoView")) { return }
3457
3458
  var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null
3459
  if (coords.top + box.top < 0) { doScroll = true }
3460
  else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) { doScroll = false }
3461
  if (doScroll != null && !phantom) {
3462
    var scrollNode = elt("div", "\u200b", null, ("position: absolute;\n                         top: " + (coords.top - display.viewOffset - paddingTop(cm.display)) + "px;\n                         height: " + (coords.bottom - coords.top + scrollGap(cm) + display.barHeight) + "px;\n                         left: " + (coords.left) + "px; width: 2px;"))
3463
    cm.display.lineSpace.appendChild(scrollNode)
3464
    scrollNode.scrollIntoView(doScroll)
3465
    cm.display.lineSpace.removeChild(scrollNode)
3466
  }
3467
}
3468
3469
// Scroll a given position into view (immediately), verifying that
3470
// it actually became visible (as line heights are accurately
3471
// measured, the position of something may 'drift' during drawing).
3472
function scrollPosIntoView(cm, pos, end, margin) {
3473
  if (margin == null) { margin = 0 }
3474
  var coords
3475
  for (var limit = 0; limit < 5; limit++) {
3476
    var changed = false
3477
    coords = cursorCoords(cm, pos)
3478
    var endCoords = !end || end == pos ? coords : cursorCoords(cm, end)
3479
    var scrollPos = calculateScrollPos(cm, Math.min(coords.left, endCoords.left),
3480
                                       Math.min(coords.top, endCoords.top) - margin,
3481
                                       Math.max(coords.left, endCoords.left),
3482
                                       Math.max(coords.bottom, endCoords.bottom) + margin)
3483
    var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft
3484
    if (scrollPos.scrollTop != null) {
3485
      setScrollTop(cm, scrollPos.scrollTop)
3486
      if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true }
3487
    }
3488
    if (scrollPos.scrollLeft != null) {
3489
      setScrollLeft(cm, scrollPos.scrollLeft)
3490
      if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true }
3491
    }
3492
    if (!changed) { break }
3493
  }
3494
  return coords
0 ignored issues
show
Bug introduced by
The variable coords seems to not be initialized for all possible execution paths.
Loading history...
3495
}
3496
3497
// Scroll a given set of coordinates into view (immediately).
3498
function scrollIntoView(cm, x1, y1, x2, y2) {
3499
  var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2)
3500
  if (scrollPos.scrollTop != null) { setScrollTop(cm, scrollPos.scrollTop) }
3501
  if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft) }
3502
}
3503
3504
// Calculate a new scroll position needed to scroll the given
3505
// rectangle into view. Returns an object with scrollTop and
3506
// scrollLeft properties. When these are undefined, the
3507
// vertical/horizontal position does not need to be adjusted.
3508
function calculateScrollPos(cm, x1, y1, x2, y2) {
3509
  var display = cm.display, snapMargin = textHeight(cm.display)
3510
  if (y1 < 0) { y1 = 0 }
3511
  var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop
3512
  var screen = displayHeight(cm), result = {}
3513
  if (y2 - y1 > screen) { y2 = y1 + screen }
3514
  var docBottom = cm.doc.height + paddingVert(display)
3515
  var atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin
3516
  if (y1 < screentop) {
3517
    result.scrollTop = atTop ? 0 : y1
3518
  } else if (y2 > screentop + screen) {
3519
    var newTop = Math.min(y1, (atBottom ? docBottom : y2) - screen)
3520
    if (newTop != screentop) { result.scrollTop = newTop }
3521
  }
3522
3523
  var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft
3524
  var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0)
3525
  var tooWide = x2 - x1 > screenw
3526
  if (tooWide) { x2 = x1 + screenw }
3527
  if (x1 < 10)
3528
    { result.scrollLeft = 0 }
3529
  else if (x1 < screenleft)
3530
    { result.scrollLeft = Math.max(0, x1 - (tooWide ? 0 : 10)) }
3531
  else if (x2 > screenw + screenleft - 3)
3532
    { result.scrollLeft = x2 + (tooWide ? 0 : 10) - screenw }
3533
  return result
3534
}
3535
3536
// Store a relative adjustment to the scroll position in the current
3537
// operation (to be applied when the operation finishes).
3538
function addToScrollPos(cm, left, top) {
3539
  if (left != null || top != null) { resolveScrollToPos(cm) }
3540
  if (left != null)
3541
    { cm.curOp.scrollLeft = (cm.curOp.scrollLeft == null ? cm.doc.scrollLeft : cm.curOp.scrollLeft) + left }
3542
  if (top != null)
3543
    { cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top }
3544
}
3545
3546
// Make sure that at the end of the operation the current cursor is
3547
// shown.
3548
function ensureCursorVisible(cm) {
3549
  resolveScrollToPos(cm)
3550
  var cur = cm.getCursor(), from = cur, to = cur
3551
  if (!cm.options.lineWrapping) {
3552
    from = cur.ch ? Pos(cur.line, cur.ch - 1) : cur
3553
    to = Pos(cur.line, cur.ch + 1)
3554
  }
3555
  cm.curOp.scrollToPos = {from: from, to: to, margin: cm.options.cursorScrollMargin, isCursor: true}
3556
}
3557
3558
// When an operation has its scrollToPos property set, and another
3559
// scroll action is applied before the end of the operation, this
3560
// 'simulates' scrolling that position into view in a cheap way, so
3561
// that the effect of intermediate scroll commands is not ignored.
3562
function resolveScrollToPos(cm) {
3563
  var range = cm.curOp.scrollToPos
3564
  if (range) {
3565
    cm.curOp.scrollToPos = null
3566
    var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to)
3567
    var sPos = calculateScrollPos(cm, Math.min(from.left, to.left),
3568
                                  Math.min(from.top, to.top) - range.margin,
3569
                                  Math.max(from.right, to.right),
3570
                                  Math.max(from.bottom, to.bottom) + range.margin)
3571
    cm.scrollTo(sPos.scrollLeft, sPos.scrollTop)
3572
  }
3573
}
3574
3575
// Operations are used to wrap a series of changes to the editor
3576
// state in such a way that each change won't have to update the
3577
// cursor and display (which would be awkward, slow, and
3578
// error-prone). Instead, display updates are batched and then all
3579
// combined and executed at once.
3580
3581
var nextOpId = 0
3582
// Start a new operation.
3583
function startOperation(cm) {
3584
  cm.curOp = {
3585
    cm: cm,
3586
    viewChanged: false,      // Flag that indicates that lines might need to be redrawn
3587
    startHeight: cm.doc.height, // Used to detect need to update scrollbar
3588
    forceUpdate: false,      // Used to force a redraw
3589
    updateInput: null,       // Whether to reset the input textarea
3590
    typing: false,           // Whether this reset should be careful to leave existing text (for compositing)
3591
    changeObjs: null,        // Accumulated changes, for firing change events
3592
    cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on
3593
    cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
3594
    selectionChanged: false, // Whether the selection needs to be redrawn
3595
    updateMaxLine: false,    // Set when the widest line needs to be determined anew
3596
    scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
3597
    scrollToPos: null,       // Used to scroll to a specific position
3598
    focus: false,
3599
    id: ++nextOpId           // Unique ID
3600
  }
3601
  pushOperation(cm.curOp)
3602
}
3603
3604
// Finish an operation, updating the display and signalling delayed events
3605
function endOperation(cm) {
3606
  var op = cm.curOp
3607
  finishOperation(op, function (group) {
3608
    for (var i = 0; i < group.ops.length; i++)
3609
      { group.ops[i].cm.curOp = null }
3610
    endOperations(group)
3611
  })
3612
}
3613
3614
// The DOM updates done when an operation finishes are batched so
3615
// that the minimum number of relayouts are required.
3616
function endOperations(group) {
3617
  var ops = group.ops
3618
  for (var i = 0; i < ops.length; i++) // Read DOM
3619
    { endOperation_R1(ops[i]) }
3620
  for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe)
3621
    { endOperation_W1(ops[i$1]) }
3622
  for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM
3623
    { endOperation_R2(ops[i$2]) }
3624
  for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe)
3625
    { endOperation_W2(ops[i$3]) }
3626
  for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM
3627
    { endOperation_finish(ops[i$4]) }
3628
}
3629
3630
function endOperation_R1(op) {
3631
  var cm = op.cm, display = cm.display
3632
  maybeClipScrollbars(cm)
3633
  if (op.updateMaxLine) { findMaxLine(cm) }
3634
3635
  op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||
3636
    op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
3637
                       op.scrollToPos.to.line >= display.viewTo) ||
3638
    display.maxLineChanged && cm.options.lineWrapping
3639
  op.update = op.mustUpdate &&
3640
    new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate)
3641
}
3642
3643
function endOperation_W1(op) {
3644
  op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update)
3645
}
3646
3647
function endOperation_R2(op) {
3648
  var cm = op.cm, display = cm.display
3649
  if (op.updatedDisplay) { updateHeightsInViewport(cm) }
3650
3651
  op.barMeasure = measureForScrollbars(cm)
3652
3653
  // If the max line changed since it was last measured, measure it,
3654
  // and ensure the document's width matches it.
3655
  // updateDisplay_W2 will use these properties to do the actual resizing
3656
  if (display.maxLineChanged && !cm.options.lineWrapping) {
3657
    op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3
3658
    cm.display.sizerWidth = op.adjustWidthTo
3659
    op.barMeasure.scrollWidth =
3660
      Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth)
3661
    op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm))
3662
  }
3663
3664
  if (op.updatedDisplay || op.selectionChanged)
3665
    { op.preparedSelection = display.input.prepareSelection(op.focus) }
3666
}
3667
3668
function endOperation_W2(op) {
3669
  var cm = op.cm
3670
3671
  if (op.adjustWidthTo != null) {
3672
    cm.display.sizer.style.minWidth = op.adjustWidthTo + "px"
3673
    if (op.maxScrollLeft < cm.doc.scrollLeft)
3674
      { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true) }
3675
    cm.display.maxLineChanged = false
3676
  }
3677
3678
  var takeFocus = op.focus && op.focus == activeElt() && (!document.hasFocus || document.hasFocus())
3679
  if (op.preparedSelection)
3680
    { cm.display.input.showSelection(op.preparedSelection, takeFocus) }
3681
  if (op.updatedDisplay || op.startHeight != cm.doc.height)
3682
    { updateScrollbars(cm, op.barMeasure) }
3683
  if (op.updatedDisplay)
3684
    { setDocumentHeight(cm, op.barMeasure) }
3685
3686
  if (op.selectionChanged) { restartBlink(cm) }
3687
3688
  if (cm.state.focused && op.updateInput)
3689
    { cm.display.input.reset(op.typing) }
3690
  if (takeFocus) { ensureFocus(op.cm) }
3691
}
3692
3693
function endOperation_finish(op) {
3694
  var cm = op.cm, display = cm.display, doc = cm.doc
3695
3696
  if (op.updatedDisplay) { postUpdateDisplay(cm, op.update) }
3697
3698
  // Abort mouse wheel delta measurement, when scrolling explicitly
3699
  if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))
3700
    { display.wheelStartX = display.wheelStartY = null }
3701
3702
  // Propagate the scroll position to the actual DOM scroller
3703
  if (op.scrollTop != null && (display.scroller.scrollTop != op.scrollTop || op.forceScroll)) {
3704
    doc.scrollTop = Math.max(0, Math.min(display.scroller.scrollHeight - display.scroller.clientHeight, op.scrollTop))
3705
    display.scrollbars.setScrollTop(doc.scrollTop)
3706
    display.scroller.scrollTop = doc.scrollTop
3707
  }
3708
  if (op.scrollLeft != null && (display.scroller.scrollLeft != op.scrollLeft || op.forceScroll)) {
3709
    doc.scrollLeft = Math.max(0, Math.min(display.scroller.scrollWidth - display.scroller.clientWidth, op.scrollLeft))
3710
    display.scrollbars.setScrollLeft(doc.scrollLeft)
3711
    display.scroller.scrollLeft = doc.scrollLeft
3712
    alignHorizontally(cm)
3713
  }
3714
  // If we need to scroll a specific position into view, do so.
3715
  if (op.scrollToPos) {
3716
    var coords = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
3717
                                   clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin)
3718
    if (op.scrollToPos.isCursor && cm.state.focused) { maybeScrollWindow(cm, coords) }
3719
  }
3720
3721
  // Fire events for markers that are hidden/unidden by editing or
3722
  // undoing
3723
  var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers
3724
  if (hidden) { for (var i = 0; i < hidden.length; ++i)
3725
    { if (!hidden[i].lines.length) { signal(hidden[i], "hide") } } }
3726
  if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1)
3727
    { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], "unhide") } } }
3728
3729
  if (display.wrapper.offsetHeight)
3730
    { doc.scrollTop = cm.display.scroller.scrollTop }
3731
3732
  // Fire change events, and delayed event handlers
3733
  if (op.changeObjs)
3734
    { signal(cm, "changes", cm, op.changeObjs) }
3735
  if (op.update)
3736
    { op.update.finish() }
3737
}
3738
3739
// Run the given function in an operation
3740
function runInOp(cm, f) {
3741
  if (cm.curOp) { return f() }
3742
  startOperation(cm)
3743
  try { return f() }
3744
  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...
3745
}
3746
// Wraps a function in an operation. Returns the wrapped function.
3747
function operation(cm, f) {
3748
  return function() {
3749
    if (cm.curOp) { return f.apply(cm, arguments) }
3750
    startOperation(cm)
3751
    try { return f.apply(cm, arguments) }
3752
    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...
3753
  }
3754
}
3755
// Used to add methods to editor and doc instances, wrapping them in
3756
// operations.
3757
function methodOp(f) {
3758
  return function() {
3759
    if (this.curOp) { return f.apply(this, arguments) }
3760
    startOperation(this)
3761
    try { return f.apply(this, arguments) }
3762
    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...
3763
  }
3764
}
3765
function docMethodOp(f) {
3766
  return function() {
3767
    var cm = this.cm
3768
    if (!cm || cm.curOp) { return f.apply(this, arguments) }
3769
    startOperation(cm)
3770
    try { return f.apply(this, arguments) }
3771
    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...
3772
  }
3773
}
3774
3775
// Updates the display.view data structure for a given change to the
3776
// document. From and to are in pre-change coordinates. Lendiff is
3777
// the amount of lines added or subtracted by the change. This is
3778
// used for changes that span multiple lines, or change the way
3779
// lines are divided into visual lines. regLineChange (below)
3780
// registers single-line changes.
3781
function regChange(cm, from, to, lendiff) {
3782
  if (from == null) { from = cm.doc.first }
3783
  if (to == null) { to = cm.doc.first + cm.doc.size }
3784
  if (!lendiff) { lendiff = 0 }
3785
3786
  var display = cm.display
3787
  if (lendiff && to < display.viewTo &&
3788
      (display.updateLineNumbers == null || display.updateLineNumbers > from))
3789
    { display.updateLineNumbers = from }
3790
3791
  cm.curOp.viewChanged = true
3792
3793
  if (from >= display.viewTo) { // Change after
3794
    if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)
3795
      { resetView(cm) }
3796
  } else if (to <= display.viewFrom) { // Change before
3797
    if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {
3798
      resetView(cm)
3799
    } else {
3800
      display.viewFrom += lendiff
3801
      display.viewTo += lendiff
3802
    }
3803
  } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap
3804
    resetView(cm)
3805
  } else if (from <= display.viewFrom) { // Top overlap
3806
    var cut = viewCuttingPoint(cm, to, to + lendiff, 1)
3807
    if (cut) {
3808
      display.view = display.view.slice(cut.index)
3809
      display.viewFrom = cut.lineN
3810
      display.viewTo += lendiff
3811
    } else {
3812
      resetView(cm)
3813
    }
3814
  } else if (to >= display.viewTo) { // Bottom overlap
3815
    var cut$1 = viewCuttingPoint(cm, from, from, -1)
3816
    if (cut$1) {
3817
      display.view = display.view.slice(0, cut$1.index)
3818
      display.viewTo = cut$1.lineN
3819
    } else {
3820
      resetView(cm)
3821
    }
3822
  } else { // Gap in the middle
3823
    var cutTop = viewCuttingPoint(cm, from, from, -1)
3824
    var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1)
3825
    if (cutTop && cutBot) {
3826
      display.view = display.view.slice(0, cutTop.index)
3827
        .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))
3828
        .concat(display.view.slice(cutBot.index))
3829
      display.viewTo += lendiff
3830
    } else {
3831
      resetView(cm)
3832
    }
3833
  }
3834
3835
  var ext = display.externalMeasured
3836
  if (ext) {
3837
    if (to < ext.lineN)
3838
      { ext.lineN += lendiff }
3839
    else if (from < ext.lineN + ext.size)
3840
      { display.externalMeasured = null }
3841
  }
3842
}
3843
3844
// Register a change to a single line. Type must be one of "text",
3845
// "gutter", "class", "widget"
3846
function regLineChange(cm, line, type) {
3847
  cm.curOp.viewChanged = true
3848
  var display = cm.display, ext = cm.display.externalMeasured
3849
  if (ext && line >= ext.lineN && line < ext.lineN + ext.size)
3850
    { display.externalMeasured = null }
3851
3852
  if (line < display.viewFrom || line >= display.viewTo) { return }
3853
  var lineView = display.view[findViewIndex(cm, line)]
3854
  if (lineView.node == null) { return }
3855
  var arr = lineView.changes || (lineView.changes = [])
3856
  if (indexOf(arr, type) == -1) { arr.push(type) }
3857
}
3858
3859
// Clear the view.
3860
function resetView(cm) {
3861
  cm.display.viewFrom = cm.display.viewTo = cm.doc.first
3862
  cm.display.view = []
3863
  cm.display.viewOffset = 0
3864
}
3865
3866
function viewCuttingPoint(cm, oldN, newN, dir) {
3867
  var index = findViewIndex(cm, oldN), diff, view = cm.display.view
3868
  if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)
3869
    { return {index: index, lineN: newN} }
3870
  var n = cm.display.viewFrom
3871
  for (var i = 0; i < index; i++)
3872
    { n += view[i].size }
3873
  if (n != oldN) {
3874
    if (dir > 0) {
3875
      if (index == view.length - 1) { return null }
3876
      diff = (n + view[index].size) - oldN
3877
      index++
3878
    } else {
3879
      diff = n - oldN
3880
    }
3881
    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...
3882
  }
3883
  while (visualLineNo(cm.doc, newN) != newN) {
3884
    if (index == (dir < 0 ? 0 : view.length - 1)) { return null }
3885
    newN += dir * view[index - (dir < 0 ? 1 : 0)].size
3886
    index += dir
3887
  }
3888
  return {index: index, lineN: newN}
3889
}
3890
3891
// Force the view to cover a given range, adding empty view element
3892
// or clipping off existing ones as needed.
3893
function adjustView(cm, from, to) {
3894
  var display = cm.display, view = display.view
3895
  if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {
3896
    display.view = buildViewArray(cm, from, to)
3897
    display.viewFrom = from
3898
  } else {
3899
    if (display.viewFrom > from)
3900
      { display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view) }
3901
    else if (display.viewFrom < from)
3902
      { display.view = display.view.slice(findViewIndex(cm, from)) }
3903
    display.viewFrom = from
3904
    if (display.viewTo < to)
3905
      { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)) }
3906
    else if (display.viewTo > to)
3907
      { display.view = display.view.slice(0, findViewIndex(cm, to)) }
3908
  }
3909
  display.viewTo = to
3910
}
3911
3912
// Count the number of lines in the view whose DOM representation is
3913
// out of date (or nonexistent).
3914
function countDirtyView(cm) {
3915
  var view = cm.display.view, dirty = 0
3916
  for (var i = 0; i < view.length; i++) {
3917
    var lineView = view[i]
3918
    if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty }
3919
  }
3920
  return dirty
3921
}
3922
3923
// HIGHLIGHT WORKER
3924
3925
function startWorker(cm, time) {
3926
  if (cm.doc.mode.startState && cm.doc.frontier < cm.display.viewTo)
3927
    { cm.state.highlight.set(time, bind(highlightWorker, cm)) }
3928
}
3929
3930
function highlightWorker(cm) {
3931
  var doc = cm.doc
3932
  if (doc.frontier < doc.first) { doc.frontier = doc.first }
3933
  if (doc.frontier >= cm.display.viewTo) { return }
3934
  var end = +new Date + cm.options.workTime
3935
  var state = copyState(doc.mode, getStateBefore(cm, doc.frontier))
3936
  var changedLines = []
3937
3938
  doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function (line) {
3939
    if (doc.frontier >= cm.display.viewFrom) { // Visible
3940
      var oldStyles = line.styles, tooLong = line.text.length > cm.options.maxHighlightLength
3941
      var highlighted = highlightLine(cm, line, tooLong ? copyState(doc.mode, state) : state, true)
3942
      line.styles = highlighted.styles
3943
      var oldCls = line.styleClasses, newCls = highlighted.classes
3944
      if (newCls) { line.styleClasses = newCls }
3945
      else if (oldCls) { line.styleClasses = null }
3946
      var ischange = !oldStyles || oldStyles.length != line.styles.length ||
3947
        oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass)
3948
      for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldStyles[i] != line.styles[i] }
3949
      if (ischange) { changedLines.push(doc.frontier) }
3950
      line.stateAfter = tooLong ? state : copyState(doc.mode, state)
3951
    } else {
3952
      if (line.text.length <= cm.options.maxHighlightLength)
3953
        { processLine(cm, line.text, state) }
3954
      line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null
3955
    }
3956
    ++doc.frontier
3957
    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...
3958
      startWorker(cm, cm.options.workDelay)
3959
      return true
3960
    }
3961
  })
3962
  if (changedLines.length) { runInOp(cm, function () {
3963
    for (var i = 0; i < changedLines.length; i++)
3964
      { regLineChange(cm, changedLines[i], "text") }
3965
  }) }
3966
}
3967
3968
// DISPLAY DRAWING
3969
3970
var DisplayUpdate = function(cm, viewport, force) {
3971
  var display = cm.display
3972
3973
  this.viewport = viewport
3974
  // Store some values that we'll need later (but don't want to force a relayout for)
3975
  this.visible = visibleLines(display, cm.doc, viewport)
3976
  this.editorIsHidden = !display.wrapper.offsetWidth
3977
  this.wrapperHeight = display.wrapper.clientHeight
3978
  this.wrapperWidth = display.wrapper.clientWidth
3979
  this.oldDisplayWidth = displayWidth(cm)
3980
  this.force = force
3981
  this.dims = getDimensions(cm)
3982
  this.events = []
3983
};
3984
3985
DisplayUpdate.prototype.signal = function (emitter, type) {
3986
  if (hasHandler(emitter, type))
3987
    { this.events.push(arguments) }
3988
};
3989
DisplayUpdate.prototype.finish = function () {
3990
    var this$1 = this;
3991
3992
  for (var i = 0; i < this.events.length; i++)
3993
    { signal.apply(null, this$1.events[i]) }
3994
};
3995
3996
function maybeClipScrollbars(cm) {
3997
  var display = cm.display
3998
  if (!display.scrollbarsClipped && display.scroller.offsetWidth) {
3999
    display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth
4000
    display.heightForcer.style.height = scrollGap(cm) + "px"
4001
    display.sizer.style.marginBottom = -display.nativeBarWidth + "px"
4002
    display.sizer.style.borderRightWidth = scrollGap(cm) + "px"
4003
    display.scrollbarsClipped = true
4004
  }
4005
}
4006
4007
// Does the actual updating of the line display. Bails out
4008
// (returning false) when there is nothing to be done and forced is
4009
// false.
4010
function updateDisplayIfNeeded(cm, update) {
4011
  var display = cm.display, doc = cm.doc
4012
4013
  if (update.editorIsHidden) {
4014
    resetView(cm)
4015
    return false
4016
  }
4017
4018
  // Bail out if the visible area is already rendered and nothing changed.
4019
  if (!update.force &&
4020
      update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&
4021
      (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&
4022
      display.renderedView == display.view && countDirtyView(cm) == 0)
4023
    { return false }
4024
4025
  if (maybeUpdateLineNumberWidth(cm)) {
4026
    resetView(cm)
4027
    update.dims = getDimensions(cm)
4028
  }
4029
4030
  // Compute a suitable new viewport (from & to)
4031
  var end = doc.first + doc.size
4032
  var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first)
4033
  var to = Math.min(end, update.visible.to + cm.options.viewportMargin)
4034
  if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max(doc.first, display.viewFrom) }
4035
  if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, display.viewTo) }
4036
  if (sawCollapsedSpans) {
4037
    from = visualLineNo(cm.doc, from)
4038
    to = visualLineEndNo(cm.doc, to)
4039
  }
4040
4041
  var different = from != display.viewFrom || to != display.viewTo ||
4042
    display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth
4043
  adjustView(cm, from, to)
4044
4045
  display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom))
4046
  // Position the mover div to align with the current scroll position
4047
  cm.display.mover.style.top = display.viewOffset + "px"
4048
4049
  var toUpdate = countDirtyView(cm)
4050
  if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view &&
4051
      (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))
4052
    { return false }
4053
4054
  // For big changes, we hide the enclosing element during the
4055
  // update, since that speeds up the operations on most browsers.
4056
  var focused = activeElt()
4057
  if (toUpdate > 4) { display.lineDiv.style.display = "none" }
4058
  patchDisplay(cm, display.updateLineNumbers, update.dims)
4059
  if (toUpdate > 4) { display.lineDiv.style.display = "" }
4060
  display.renderedView = display.view
4061
  // There might have been a widget with a focused element that got
4062
  // hidden or updated, if so re-focus it.
4063
  if (focused && activeElt() != focused && focused.offsetHeight) { focused.focus() }
4064
4065
  // Prevent selection and cursors from interfering with the scroll
4066
  // width and height.
4067
  removeChildren(display.cursorDiv)
4068
  removeChildren(display.selectionDiv)
4069
  display.gutters.style.height = display.sizer.style.minHeight = 0
4070
4071
  if (different) {
4072
    display.lastWrapHeight = update.wrapperHeight
4073
    display.lastWrapWidth = update.wrapperWidth
4074
    startWorker(cm, 400)
4075
  }
4076
4077
  display.updateLineNumbers = null
4078
4079
  return true
4080
}
4081
4082
function postUpdateDisplay(cm, update) {
4083
  var viewport = update.viewport
4084
4085
  for (var first = true;; first = false) {
4086
    if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) {
4087
      // Clip forced viewport to actual scrollable area.
4088
      if (viewport && viewport.top != null)
4089
        { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)} }
4090
      // Updated line heights might result in the drawn area not
4091
      // actually covering the viewport. Keep looping until it does.
4092
      update.visible = visibleLines(cm.display, cm.doc, viewport)
4093
      if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)
4094
        { break }
4095
    }
4096
    if (!updateDisplayIfNeeded(cm, update)) { break }
4097
    updateHeightsInViewport(cm)
4098
    var barMeasure = measureForScrollbars(cm)
4099
    updateSelection(cm)
4100
    updateScrollbars(cm, barMeasure)
4101
    setDocumentHeight(cm, barMeasure)
4102
  }
4103
4104
  update.signal(cm, "update", cm)
4105
  if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {
4106
    update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo)
4107
    cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo
4108
  }
4109
}
4110
4111
function updateDisplaySimple(cm, viewport) {
4112
  var update = new DisplayUpdate(cm, viewport)
4113
  if (updateDisplayIfNeeded(cm, update)) {
4114
    updateHeightsInViewport(cm)
4115
    postUpdateDisplay(cm, update)
4116
    var barMeasure = measureForScrollbars(cm)
4117
    updateSelection(cm)
4118
    updateScrollbars(cm, barMeasure)
4119
    setDocumentHeight(cm, barMeasure)
4120
    update.finish()
4121
  }
4122
}
4123
4124
// Sync the actual display DOM structure with display.view, removing
4125
// nodes for lines that are no longer in view, and creating the ones
4126
// that are not there yet, and updating the ones that are out of
4127
// date.
4128
function patchDisplay(cm, updateNumbersFrom, dims) {
4129
  var display = cm.display, lineNumbers = cm.options.lineNumbers
4130
  var container = display.lineDiv, cur = container.firstChild
4131
4132
  function rm(node) {
4133
    var next = node.nextSibling
4134
    // Works around a throw-scroll bug in OS X Webkit
4135
    if (webkit && mac && cm.display.currentWheelTarget == node)
4136
      { node.style.display = "none" }
4137
    else
4138
      { node.parentNode.removeChild(node) }
4139
    return next
4140
  }
4141
4142
  var view = display.view, lineN = display.viewFrom
4143
  // Loop over the elements in the view, syncing cur (the DOM nodes
4144
  // in display.lineDiv) with the view as we go.
4145
  for (var i = 0; i < view.length; i++) {
4146
    var lineView = view[i]
4147
    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...
4148
    } else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet
4149
      var node = buildLineElement(cm, lineView, lineN, dims)
4150
      container.insertBefore(node, cur)
4151
    } else { // Already drawn
4152
      while (cur != lineView.node) { cur = rm(cur) }
4153
      var updateNumber = lineNumbers && updateNumbersFrom != null &&
4154
        updateNumbersFrom <= lineN && lineView.lineNumber
4155
      if (lineView.changes) {
4156
        if (indexOf(lineView.changes, "gutter") > -1) { updateNumber = false }
4157
        updateLineForChanges(cm, lineView, lineN, dims)
4158
      }
4159
      if (updateNumber) {
4160
        removeChildren(lineView.lineNumber)
4161
        lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN)))
4162
      }
4163
      cur = lineView.node.nextSibling
4164
    }
4165
    lineN += lineView.size
4166
  }
4167
  while (cur) { cur = rm(cur) }
4168
}
4169
4170
function updateGutterSpace(cm) {
4171
  var width = cm.display.gutters.offsetWidth
4172
  cm.display.sizer.style.marginLeft = width + "px"
4173
}
4174
4175
function setDocumentHeight(cm, measure) {
4176
  cm.display.sizer.style.minHeight = measure.docHeight + "px"
4177
  cm.display.heightForcer.style.top = measure.docHeight + "px"
4178
  cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px"
4179
}
4180
4181
// Rebuild the gutter elements, ensure the margin to the left of the
4182
// code matches their width.
4183
function updateGutters(cm) {
4184
  var gutters = cm.display.gutters, specs = cm.options.gutters
4185
  removeChildren(gutters)
4186
  var i = 0
4187
  for (; i < specs.length; ++i) {
4188
    var gutterClass = specs[i]
4189
    var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass))
4190
    if (gutterClass == "CodeMirror-linenumbers") {
4191
      cm.display.lineGutter = gElt
4192
      gElt.style.width = (cm.display.lineNumWidth || 1) + "px"
4193
    }
4194
  }
4195
  gutters.style.display = i ? "" : "none"
4196
  updateGutterSpace(cm)
4197
}
4198
4199
// Make sure the gutters options contains the element
4200
// "CodeMirror-linenumbers" when the lineNumbers option is true.
4201
function setGuttersForLineNumbers(options) {
4202
  var found = indexOf(options.gutters, "CodeMirror-linenumbers")
4203
  if (found == -1 && options.lineNumbers) {
4204
    options.gutters = options.gutters.concat(["CodeMirror-linenumbers"])
4205
  } else if (found > -1 && !options.lineNumbers) {
4206
    options.gutters = options.gutters.slice(0)
4207
    options.gutters.splice(found, 1)
4208
  }
4209
}
4210
4211
// Selection objects are immutable. A new one is created every time
4212
// the selection changes. A selection is one or more non-overlapping
4213
// (and non-touching) ranges, sorted, and an integer that indicates
4214
// which one is the primary selection (the one that's scrolled into
4215
// view, that getCursor returns, etc).
4216
function Selection(ranges, primIndex) {
4217
  this.ranges = ranges
4218
  this.primIndex = primIndex
4219
}
4220
4221
Selection.prototype = {
4222
  primary: function() { return this.ranges[this.primIndex] },
4223
  equals: function(other) {
4224
    var this$1 = this;
4225
4226
    if (other == this) { return true }
4227
    if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false }
4228
    for (var i = 0; i < this.ranges.length; i++) {
4229
      var here = this$1.ranges[i], there = other.ranges[i]
4230
      if (cmp(here.anchor, there.anchor) != 0 || cmp(here.head, there.head) != 0) { return false }
4231
    }
4232
    return true
4233
  },
4234
  deepCopy: function() {
4235
    var this$1 = this;
4236
4237
    var out = []
4238
    for (var i = 0; i < this.ranges.length; i++)
4239
      { out[i] = new Range(copyPos(this$1.ranges[i].anchor), copyPos(this$1.ranges[i].head)) }
4240
    return new Selection(out, this.primIndex)
4241
  },
4242
  somethingSelected: function() {
4243
    var this$1 = this;
4244
4245
    for (var i = 0; i < this.ranges.length; i++)
4246
      { if (!this$1.ranges[i].empty()) { return true } }
4247
    return false
4248
  },
4249
  contains: function(pos, end) {
4250
    var this$1 = this;
4251
4252
    if (!end) { end = pos }
4253
    for (var i = 0; i < this.ranges.length; i++) {
4254
      var range = this$1.ranges[i]
4255
      if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)
4256
        { return i }
4257
    }
4258
    return -1
4259
  }
4260
}
4261
4262
function Range(anchor, head) {
4263
  this.anchor = anchor; this.head = head
4264
}
4265
4266
Range.prototype = {
4267
  from: function() { return minPos(this.anchor, this.head) },
4268
  to: function() { return maxPos(this.anchor, this.head) },
4269
  empty: function() {
4270
    return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch
4271
  }
4272
}
4273
4274
// Take an unsorted, potentially overlapping set of ranges, and
4275
// build a selection out of it. 'Consumes' ranges array (modifying
4276
// it).
4277
function normalizeSelection(ranges, primIndex) {
4278
  var prim = ranges[primIndex]
4279
  ranges.sort(function (a, b) { return cmp(a.from(), b.from()); })
4280
  primIndex = indexOf(ranges, prim)
4281
  for (var i = 1; i < ranges.length; i++) {
4282
    var cur = ranges[i], prev = ranges[i - 1]
4283
    if (cmp(prev.to(), cur.from()) >= 0) {
4284
      var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to())
4285
      var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head
4286
      if (i <= primIndex) { --primIndex }
4287
      ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to))
4288
    }
4289
  }
4290
  return new Selection(ranges, primIndex)
4291
}
4292
4293
function simpleSelection(anchor, head) {
4294
  return new Selection([new Range(anchor, head || anchor)], 0)
4295
}
4296
4297
// Compute the position of the end of a change (its 'to' property
4298
// refers to the pre-change end).
4299
function changeEnd(change) {
4300
  if (!change.text) { return change.to }
4301
  return Pos(change.from.line + change.text.length - 1,
4302
             lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0))
4303
}
4304
4305
// Adjust a position to refer to the post-change position of the
4306
// same text, or the end of the change if the change covers it.
4307
function adjustForChange(pos, change) {
4308
  if (cmp(pos, change.from) < 0) { return pos }
4309
  if (cmp(pos, change.to) <= 0) { return changeEnd(change) }
4310
4311
  var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch
4312
  if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch }
4313
  return Pos(line, ch)
4314
}
4315
4316
function computeSelAfterChange(doc, change) {
4317
  var out = []
4318
  for (var i = 0; i < doc.sel.ranges.length; i++) {
4319
    var range = doc.sel.ranges[i]
4320
    out.push(new Range(adjustForChange(range.anchor, change),
4321
                       adjustForChange(range.head, change)))
4322
  }
4323
  return normalizeSelection(out, doc.sel.primIndex)
4324
}
4325
4326
function offsetPos(pos, old, nw) {
4327
  if (pos.line == old.line)
4328
    { return Pos(nw.line, pos.ch - old.ch + nw.ch) }
4329
  else
4330
    { return Pos(nw.line + (pos.line - old.line), pos.ch) }
4331
}
4332
4333
// Used by replaceSelections to allow moving the selection to the
4334
// start or around the replaced test. Hint may be "start" or "around".
4335
function computeReplacedSel(doc, changes, hint) {
4336
  var out = []
4337
  var oldPrev = Pos(doc.first, 0), newPrev = oldPrev
4338
  for (var i = 0; i < changes.length; i++) {
4339
    var change = changes[i]
4340
    var from = offsetPos(change.from, oldPrev, newPrev)
4341
    var to = offsetPos(changeEnd(change), oldPrev, newPrev)
4342
    oldPrev = change.to
4343
    newPrev = to
4344
    if (hint == "around") {
4345
      var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0
4346
      out[i] = new Range(inv ? to : from, inv ? from : to)
4347
    } else {
4348
      out[i] = new Range(from, from)
4349
    }
4350
  }
4351
  return new Selection(out, doc.sel.primIndex)
4352
}
4353
4354
// Used to get the editor into a consistent state again when options change.
4355
4356
function loadMode(cm) {
4357
  cm.doc.mode = getMode(cm.options, cm.doc.modeOption)
4358
  resetModeState(cm)
4359
}
4360
4361
function resetModeState(cm) {
4362
  cm.doc.iter(function (line) {
4363
    if (line.stateAfter) { line.stateAfter = null }
4364
    if (line.styles) { line.styles = null }
4365
  })
4366
  cm.doc.frontier = cm.doc.first
4367
  startWorker(cm, 100)
4368
  cm.state.modeGen++
4369
  if (cm.curOp) { regChange(cm) }
4370
}
4371
4372
// DOCUMENT DATA STRUCTURE
4373
4374
// By default, updates that start and end at the beginning of a line
4375
// are treated specially, in order to make the association of line
4376
// widgets and marker elements with the text behave more intuitive.
4377
function isWholeLineUpdate(doc, change) {
4378
  return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" &&
4379
    (!doc.cm || doc.cm.options.wholeLineUpdateBefore)
4380
}
4381
4382
// Perform a change on the document data structure.
4383
function updateDoc(doc, change, markedSpans, estimateHeight) {
4384
  function spansFor(n) {return markedSpans ? markedSpans[n] : null}
4385
  function update(line, text, spans) {
4386
    updateLine(line, text, spans, estimateHeight)
4387
    signalLater(line, "change", line, change)
4388
  }
4389
  function linesFor(start, end) {
4390
    var result = []
4391
    for (var i = start; i < end; ++i)
4392
      { result.push(new Line(text[i], spansFor(i), estimateHeight)) }
4393
    return result
4394
  }
4395
4396
  var from = change.from, to = change.to, text = change.text
4397
  var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line)
4398
  var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line
4399
4400
  // Adjust the line structure
4401
  if (change.full) {
4402
    doc.insert(0, linesFor(0, text.length))
4403
    doc.remove(text.length, doc.size - text.length)
4404
  } else if (isWholeLineUpdate(doc, change)) {
4405
    // This is a whole-line replace. Treated specially to make
4406
    // sure line objects move the way they are supposed to.
4407
    var added = linesFor(0, text.length - 1)
4408
    update(lastLine, lastLine.text, lastSpans)
4409
    if (nlines) { doc.remove(from.line, nlines) }
4410
    if (added.length) { doc.insert(from.line, added) }
4411
  } else if (firstLine == lastLine) {
4412
    if (text.length == 1) {
4413
      update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans)
4414
    } else {
4415
      var added$1 = linesFor(1, text.length - 1)
4416
      added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight))
4417
      update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0))
4418
      doc.insert(from.line + 1, added$1)
4419
    }
4420
  } else if (text.length == 1) {
4421
    update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0))
4422
    doc.remove(from.line + 1, nlines)
4423
  } else {
4424
    update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0))
4425
    update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans)
4426
    var added$2 = linesFor(1, text.length - 1)
4427
    if (nlines > 1) { doc.remove(from.line + 1, nlines - 1) }
4428
    doc.insert(from.line + 1, added$2)
4429
  }
4430
4431
  signalLater(doc, "change", doc, change)
4432
}
4433
4434
// Call f for all linked documents.
4435
function linkedDocs(doc, f, sharedHistOnly) {
4436
  function propagate(doc, skip, sharedHist) {
4437
    if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) {
4438
      var rel = doc.linked[i]
4439
      if (rel.doc == skip) { continue }
4440
      var shared = sharedHist && rel.sharedHist
4441
      if (sharedHistOnly && !shared) { continue }
4442
      f(rel.doc, shared)
4443
      propagate(rel.doc, doc, shared)
4444
    } }
4445
  }
4446
  propagate(doc, null, true)
4447
}
4448
4449
// Attach a document to an editor.
4450
function attachDoc(cm, doc) {
4451
  if (doc.cm) { throw new Error("This document is already in use.") }
4452
  cm.doc = doc
4453
  doc.cm = cm
4454
  estimateLineHeights(cm)
4455
  loadMode(cm)
4456
  if (!cm.options.lineWrapping) { findMaxLine(cm) }
4457
  cm.options.mode = doc.modeOption
4458
  regChange(cm)
4459
}
4460
4461
function History(startGen) {
4462
  // Arrays of change events and selections. Doing something adds an
4463
  // event to done and clears undo. Undoing moves events from done
4464
  // to undone, redoing moves them in the other direction.
4465
  this.done = []; this.undone = []
4466
  this.undoDepth = Infinity
4467
  // Used to track when changes can be merged into a single undo
4468
  // event
4469
  this.lastModTime = this.lastSelTime = 0
4470
  this.lastOp = this.lastSelOp = null
4471
  this.lastOrigin = this.lastSelOrigin = null
4472
  // Used by the isClean() method
4473
  this.generation = this.maxGeneration = startGen || 1
4474
}
4475
4476
// Create a history change event from an updateDoc-style change
4477
// object.
4478
function historyChangeFromChange(doc, change) {
4479
  var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)}
4480
  attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1)
4481
  linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); }, true)
4482
  return histChange
4483
}
4484
4485
// Pop all selection events off the end of a history array. Stop at
4486
// a change event.
4487
function clearSelectionEvents(array) {
4488
  while (array.length) {
4489
    var last = lst(array)
4490
    if (last.ranges) { array.pop() }
4491
    else { break }
4492
  }
4493
}
4494
4495
// Find the top change event in the history. Pop off selection
4496
// events that are in the way.
4497
function lastChangeEvent(hist, force) {
4498
  if (force) {
4499
    clearSelectionEvents(hist.done)
4500
    return lst(hist.done)
4501
  } else if (hist.done.length && !lst(hist.done).ranges) {
4502
    return lst(hist.done)
4503
  } 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...
4504
    hist.done.pop()
4505
    return lst(hist.done)
4506
  }
4507
}
4508
4509
// Register a change in the history. Merges changes that are within
4510
// a single operation, or are close together with an origin that
4511
// allows merging (starting with "+") into a single event.
4512
function addChangeToHistory(doc, change, selAfter, opId) {
4513
  var hist = doc.history
4514
  hist.undone.length = 0
4515
  var time = +new Date, cur
4516
  var last
4517
4518
  if ((hist.lastOp == opId ||
4519
       hist.lastOrigin == change.origin && change.origin &&
4520
       ((change.origin.charAt(0) == "+" && doc.cm && hist.lastModTime > time - doc.cm.options.historyEventDelay) ||
4521
        change.origin.charAt(0) == "*")) &&
4522
      (cur = lastChangeEvent(hist, hist.lastOp == opId))) {
4523
    // Merge this change into the last event
4524
    last = lst(cur.changes)
4525
    if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) {
4526
      // Optimized case for simple insertion -- don't want to add
4527
      // new changesets for every character typed
4528
      last.to = changeEnd(change)
4529
    } else {
4530
      // Add new sub-event
4531
      cur.changes.push(historyChangeFromChange(doc, change))
4532
    }
4533
  } else {
4534
    // Can not be merged, start a new event.
4535
    var before = lst(hist.done)
4536
    if (!before || !before.ranges)
4537
      { pushSelectionToHistory(doc.sel, hist.done) }
4538
    cur = {changes: [historyChangeFromChange(doc, change)],
4539
           generation: hist.generation}
4540
    hist.done.push(cur)
4541
    while (hist.done.length > hist.undoDepth) {
4542
      hist.done.shift()
4543
      if (!hist.done[0].ranges) { hist.done.shift() }
4544
    }
4545
  }
4546
  hist.done.push(selAfter)
4547
  hist.generation = ++hist.maxGeneration
4548
  hist.lastModTime = hist.lastSelTime = time
4549
  hist.lastOp = hist.lastSelOp = opId
4550
  hist.lastOrigin = hist.lastSelOrigin = change.origin
4551
4552
  if (!last) { signal(doc, "historyAdded") }
4553
}
4554
4555
function selectionEventCanBeMerged(doc, origin, prev, sel) {
4556
  var ch = origin.charAt(0)
4557
  return ch == "*" ||
4558
    ch == "+" &&
4559
    prev.ranges.length == sel.ranges.length &&
4560
    prev.somethingSelected() == sel.somethingSelected() &&
4561
    new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500)
4562
}
4563
4564
// Called whenever the selection changes, sets the new selection as
4565
// the pending selection in the history, and pushes the old pending
4566
// selection into the 'done' array when it was significantly
4567
// different (in number of selected ranges, emptiness, or time).
4568
function addSelectionToHistory(doc, sel, opId, options) {
4569
  var hist = doc.history, origin = options && options.origin
4570
4571
  // A new event is started when the previous origin does not match
4572
  // the current, or the origins don't allow matching. Origins
4573
  // starting with * are always merged, those starting with + are
4574
  // merged when similar and close together in time.
4575
  if (opId == hist.lastSelOp ||
4576
      (origin && hist.lastSelOrigin == origin &&
4577
       (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||
4578
        selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))
4579
    { hist.done[hist.done.length - 1] = sel }
4580
  else
4581
    { pushSelectionToHistory(sel, hist.done) }
4582
4583
  hist.lastSelTime = +new Date
4584
  hist.lastSelOrigin = origin
4585
  hist.lastSelOp = opId
4586
  if (options && options.clearRedo !== false)
4587
    { clearSelectionEvents(hist.undone) }
4588
}
4589
4590
function pushSelectionToHistory(sel, dest) {
4591
  var top = lst(dest)
4592
  if (!(top && top.ranges && top.equals(sel)))
4593
    { dest.push(sel) }
4594
}
4595
4596
// Used to store marked span information in the history.
4597
function attachLocalSpans(doc, change, from, to) {
4598
  var existing = change["spans_" + doc.id], n = 0
4599
  doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function (line) {
4600
    if (line.markedSpans)
4601
      { (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans }
4602
    ++n
4603
  })
4604
}
4605
4606
// When un/re-doing restores text containing marked spans, those
4607
// that have been explicitly cleared should not be restored.
4608
function removeClearedSpans(spans) {
4609
  if (!spans) { return null }
4610
  var out
4611
  for (var i = 0; i < spans.length; ++i) {
4612
    if (spans[i].marker.explicitlyCleared) { if (!out) { out = spans.slice(0, i) } }
4613
    else if (out) { out.push(spans[i]) }
4614
  }
4615
  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...
4616
}
4617
4618
// Retrieve and filter the old marked spans stored in a change event.
4619
function getOldSpans(doc, change) {
4620
  var found = change["spans_" + doc.id]
4621
  if (!found) { return null }
4622
  var nw = []
4623
  for (var i = 0; i < change.text.length; ++i)
4624
    { nw.push(removeClearedSpans(found[i])) }
4625
  return nw
4626
}
4627
4628
// Used for un/re-doing changes from the history. Combines the
4629
// result of computing the existing spans with the set of spans that
4630
// existed in the history (so that deleting around a span and then
4631
// undoing brings back the span).
4632
function mergeOldSpans(doc, change) {
4633
  var old = getOldSpans(doc, change)
4634
  var stretched = stretchSpansOverChange(doc, change)
4635
  if (!old) { return stretched }
4636
  if (!stretched) { return old }
4637
4638
  for (var i = 0; i < old.length; ++i) {
4639
    var oldCur = old[i], stretchCur = stretched[i]
4640
    if (oldCur && stretchCur) {
4641
      spans: for (var j = 0; j < stretchCur.length; ++j) {
4642
        var span = stretchCur[j]
4643
        for (var k = 0; k < oldCur.length; ++k)
4644
          { if (oldCur[k].marker == span.marker) { continue spans } }
4645
        oldCur.push(span)
4646
      }
4647
    } else if (stretchCur) {
4648
      old[i] = stretchCur
4649
    }
4650
  }
4651
  return old
4652
}
4653
4654
// Used both to provide a JSON-safe object in .getHistory, and, when
4655
// detaching a document, to split the history in two
4656
function copyHistoryArray(events, newGroup, instantiateSel) {
4657
  var copy = []
4658
  for (var i = 0; i < events.length; ++i) {
4659
    var event = events[i]
4660
    if (event.ranges) {
4661
      copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event)
4662
      continue
4663
    }
4664
    var changes = event.changes, newChanges = []
4665
    copy.push({changes: newChanges})
4666
    for (var j = 0; j < changes.length; ++j) {
4667
      var change = changes[j], m = (void 0)
0 ignored issues
show
Unused Code introduced by
The assignment to variable m seems to be never used. Consider removing it.
Loading history...
Coding Style introduced by
Consider using undefined instead of void(0). It is equivalent and more straightforward to read.
Loading history...
4668
      newChanges.push({from: change.from, to: change.to, text: change.text})
4669
      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...
4670
        if (indexOf(newGroup, Number(m[1])) > -1) {
4671
          lst(newChanges)[prop] = change[prop]
4672
          delete change[prop]
4673
        }
4674
      } } }
4675
    }
4676
  }
4677
  return copy
4678
}
4679
4680
// The 'scroll' parameter given to many of these indicated whether
4681
// the new cursor position should be scrolled into view after
4682
// modifying the selection.
4683
4684
// If shift is held or the extend flag is set, extends a range to
4685
// include a given position (and optionally a second position).
4686
// Otherwise, simply returns the range between the given positions.
4687
// Used for cursor motion and such.
4688
function extendRange(doc, range, head, other) {
4689
  if (doc.cm && doc.cm.display.shift || doc.extend) {
4690
    var anchor = range.anchor
4691
    if (other) {
4692
      var posBefore = cmp(head, anchor) < 0
4693
      if (posBefore != (cmp(other, anchor) < 0)) {
4694
        anchor = head
4695
        head = other
4696
      } else if (posBefore != (cmp(head, other) < 0)) {
4697
        head = other
4698
      }
4699
    }
4700
    return new Range(anchor, head)
4701
  } else {
4702
    return new Range(other || head, head)
4703
  }
4704
}
4705
4706
// Extend the primary selection range, discard the rest.
4707
function extendSelection(doc, head, other, options) {
4708
  setSelection(doc, new Selection([extendRange(doc, doc.sel.primary(), head, other)], 0), options)
4709
}
4710
4711
// Extend all selections (pos is an array of selections with length
4712
// equal the number of selections)
4713
function extendSelections(doc, heads, options) {
4714
  var out = []
4715
  for (var i = 0; i < doc.sel.ranges.length; i++)
4716
    { out[i] = extendRange(doc, doc.sel.ranges[i], heads[i], null) }
4717
  var newSel = normalizeSelection(out, doc.sel.primIndex)
4718
  setSelection(doc, newSel, options)
4719
}
4720
4721
// Updates a single range in the selection.
4722
function replaceOneSelection(doc, i, range, options) {
4723
  var ranges = doc.sel.ranges.slice(0)
4724
  ranges[i] = range
4725
  setSelection(doc, normalizeSelection(ranges, doc.sel.primIndex), options)
4726
}
4727
4728
// Reset the selection to a single range.
4729
function setSimpleSelection(doc, anchor, head, options) {
4730
  setSelection(doc, simpleSelection(anchor, head), options)
4731
}
4732
4733
// Give beforeSelectionChange handlers a change to influence a
4734
// selection update.
4735
function filterSelectionChange(doc, sel, options) {
4736
  var obj = {
4737
    ranges: sel.ranges,
4738
    update: function(ranges) {
4739
      var this$1 = this;
4740
4741
      this.ranges = []
4742
      for (var i = 0; i < ranges.length; i++)
4743
        { this$1.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),
4744
                                   clipPos(doc, ranges[i].head)) }
4745
    },
4746
    origin: options && options.origin
4747
  }
4748
  signal(doc, "beforeSelectionChange", doc, obj)
4749
  if (doc.cm) { signal(doc.cm, "beforeSelectionChange", doc.cm, obj) }
4750
  if (obj.ranges != sel.ranges) { return normalizeSelection(obj.ranges, obj.ranges.length - 1) }
4751
  else { return sel }
4752
}
4753
4754
function setSelectionReplaceHistory(doc, sel, options) {
4755
  var done = doc.history.done, last = lst(done)
4756
  if (last && last.ranges) {
4757
    done[done.length - 1] = sel
4758
    setSelectionNoUndo(doc, sel, options)
4759
  } else {
4760
    setSelection(doc, sel, options)
4761
  }
4762
}
4763
4764
// Set a new selection.
4765
function setSelection(doc, sel, options) {
4766
  setSelectionNoUndo(doc, sel, options)
4767
  addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options)
4768
}
4769
4770
function setSelectionNoUndo(doc, sel, options) {
4771
  if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange"))
4772
    { sel = filterSelectionChange(doc, sel, options) }
4773
4774
  var bias = options && options.bias ||
4775
    (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1)
4776
  setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true))
4777
4778
  if (!(options && options.scroll === false) && doc.cm)
4779
    { ensureCursorVisible(doc.cm) }
4780
}
4781
4782
function setSelectionInner(doc, sel) {
4783
  if (sel.equals(doc.sel)) { return }
4784
4785
  doc.sel = sel
4786
4787
  if (doc.cm) {
4788
    doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true
4789
    signalCursorActivity(doc.cm)
4790
  }
4791
  signalLater(doc, "cursorActivity", doc)
4792
}
4793
4794
// Verify that the selection does not partially select any atomic
4795
// marked ranges.
4796
function reCheckSelection(doc) {
4797
  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...
4798
}
4799
4800
// Return a selection that does not partially select any atomic
4801
// ranges.
4802
function skipAtomicInSelection(doc, sel, bias, mayClear) {
4803
  var out
4804
  for (var i = 0; i < sel.ranges.length; i++) {
4805
    var range = sel.ranges[i]
4806
    var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i]
4807
    var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear)
4808
    var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear)
4809
    if (out || newAnchor != range.anchor || newHead != range.head) {
4810
      if (!out) { out = sel.ranges.slice(0, i) }
4811
      out[i] = new Range(newAnchor, newHead)
4812
    }
4813
  }
4814
  return out ? normalizeSelection(out, sel.primIndex) : sel
4815
}
4816
4817
function skipAtomicInner(doc, pos, oldPos, dir, mayClear) {
4818
  var line = getLine(doc, pos.line)
4819
  if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
4820
    var sp = line.markedSpans[i], m = sp.marker
4821
    if ((sp.from == null || (m.inclusiveLeft ? sp.from <= pos.ch : sp.from < pos.ch)) &&
4822
        (sp.to == null || (m.inclusiveRight ? sp.to >= pos.ch : sp.to > pos.ch))) {
4823
      if (mayClear) {
4824
        signal(m, "beforeCursorEnter")
4825
        if (m.explicitlyCleared) {
4826
          if (!line.markedSpans) { break }
4827
          else {--i; continue}
4828
        }
4829
      }
4830
      if (!m.atomic) { continue }
4831
4832
      if (oldPos) {
4833
        var near = m.find(dir < 0 ? 1 : -1), diff = (void 0)
0 ignored issues
show
Unused Code introduced by
The assignment to variable diff seems to be never used. Consider removing it.
Loading history...
Coding Style introduced by
Consider using undefined instead of void(0). It is equivalent and more straightforward to read.
Loading history...
4834
        if (dir < 0 ? m.inclusiveRight : m.inclusiveLeft)
4835
          { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null) }
4836
        if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0))
4837
          { return skipAtomicInner(doc, near, pos, dir, mayClear) }
4838
      }
4839
4840
      var far = m.find(dir < 0 ? -1 : 1)
4841
      if (dir < 0 ? m.inclusiveLeft : m.inclusiveRight)
4842
        { far = movePos(doc, far, dir, far.line == pos.line ? line : null) }
4843
      return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null
4844
    }
4845
  } }
4846
  return pos
4847
}
4848
4849
// Ensure a given position is not inside an atomic range.
4850
function skipAtomic(doc, pos, oldPos, bias, mayClear) {
4851
  var dir = bias || 1
4852
  var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) ||
4853
      (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) ||
4854
      skipAtomicInner(doc, pos, oldPos, -dir, mayClear) ||
4855
      (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true))
4856
  if (!found) {
4857
    doc.cantEdit = true
4858
    return Pos(doc.first, 0)
4859
  }
4860
  return found
4861
}
4862
4863
function movePos(doc, pos, dir, line) {
4864
  if (dir < 0 && pos.ch == 0) {
4865
    if (pos.line > doc.first) { return clipPos(doc, Pos(pos.line - 1)) }
4866
    else { return null }
4867
  } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) {
4868
    if (pos.line < doc.first + doc.size - 1) { return Pos(pos.line + 1, 0) }
4869
    else { return null }
4870
  } else {
4871
    return new Pos(pos.line, pos.ch + dir)
4872
  }
4873
}
4874
4875
function selectAll(cm) {
4876
  cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll)
4877
}
4878
4879
// UPDATING
4880
4881
// Allow "beforeChange" event handlers to influence a change
4882
function filterChange(doc, change, update) {
4883
  var obj = {
4884
    canceled: false,
4885
    from: change.from,
4886
    to: change.to,
4887
    text: change.text,
4888
    origin: change.origin,
4889
    cancel: function () { return obj.canceled = true; }
4890
  }
4891
  if (update) { obj.update = function (from, to, text, origin) {
4892
    if (from) { obj.from = clipPos(doc, from) }
4893
    if (to) { obj.to = clipPos(doc, to) }
4894
    if (text) { obj.text = text }
4895
    if (origin !== undefined) { obj.origin = origin }
4896
  } }
4897
  signal(doc, "beforeChange", doc, obj)
4898
  if (doc.cm) { signal(doc.cm, "beforeChange", doc.cm, obj) }
4899
4900
  if (obj.canceled) { return null }
4901
  return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin}
4902
}
4903
4904
// Apply a change to a document, and add it to the document's
4905
// history, and propagating it to all linked documents.
4906
function makeChange(doc, change, ignoreReadOnly) {
4907
  if (doc.cm) {
4908
    if (!doc.cm.curOp) { return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) }
4909
    if (doc.cm.state.suppressEdits) { return }
4910
  }
4911
4912
  if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
4913
    change = filterChange(doc, change, true)
4914
    if (!change) { return }
4915
  }
4916
4917
  // Possibly split or suppress the update based on the presence
4918
  // of read-only spans in its range.
4919
  var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to)
4920
  if (split) {
4921
    for (var i = split.length - 1; i >= 0; --i)
4922
      { 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...
4923
  } else {
4924
    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...
4925
  }
4926
}
4927
4928
function makeChangeInner(doc, change) {
4929
  if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) { return }
4930
  var selAfter = computeSelAfterChange(doc, change)
4931
  addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN)
4932
4933
  makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change))
4934
  var rebased = []
4935
4936
  linkedDocs(doc, function (doc, sharedHist) {
4937
    if (!sharedHist && indexOf(rebased, doc.history) == -1) {
4938
      rebaseHist(doc.history, change)
4939
      rebased.push(doc.history)
4940
    }
4941
    makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change))
4942
  })
4943
}
4944
4945
// Revert a change stored in a document's history.
4946
function makeChangeFromHistory(doc, type, allowSelectionOnly) {
4947
  if (doc.cm && doc.cm.state.suppressEdits && !allowSelectionOnly) { return }
4948
4949
  var hist = doc.history, event, selAfter = doc.sel
4950
  var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done
4951
4952
  // Verify that there is a useable event (so that ctrl-z won't
4953
  // needlessly clear selection events)
4954
  var i = 0
4955
  for (; i < source.length; i++) {
4956
    event = source[i]
4957
    if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges)
4958
      { break }
4959
  }
4960
  if (i == source.length) { return }
4961
  hist.lastOrigin = hist.lastSelOrigin = null
4962
4963
  for (;;) {
4964
    event = source.pop()
4965
    if (event.ranges) {
4966
      pushSelectionToHistory(event, dest)
4967
      if (allowSelectionOnly && !event.equals(doc.sel)) {
4968
        setSelection(doc, event, {clearRedo: false})
4969
        return
4970
      }
4971
      selAfter = event
4972
    }
4973
    else { break }
4974
  }
4975
4976
  // Build up a reverse change object to add to the opposite history
4977
  // stack (redo when undoing, and vice versa).
4978
  var antiChanges = []
4979
  pushSelectionToHistory(selAfter, dest)
4980
  dest.push({changes: antiChanges, generation: hist.generation})
4981
  hist.generation = event.generation || ++hist.maxGeneration
4982
4983
  var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")
4984
4985
  var loop = function ( i ) {
4986
    var change = event.changes[i]
4987
    change.origin = type
4988
    if (filter && !filterChange(doc, change, false)) {
4989
      source.length = 0
4990
      return {}
4991
    }
4992
4993
    antiChanges.push(historyChangeFromChange(doc, change))
4994
4995
    var after = i ? computeSelAfterChange(doc, change) : lst(source)
4996
    makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change))
4997
    if (!i && doc.cm) { doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}) }
4998
    var rebased = []
4999
5000
    // Propagate to the linked documents
5001
    linkedDocs(doc, function (doc, sharedHist) {
5002
      if (!sharedHist && indexOf(rebased, doc.history) == -1) {
5003
        rebaseHist(doc.history, change)
5004
        rebased.push(doc.history)
5005
      }
5006
      makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change))
5007
    })
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...
5008
  };
5009
5010
  for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) {
5011
    var returned = loop( i$1 );
5012
5013
    if ( returned ) return returned.v;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

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

Consider:

if (a > 0)
    b = 42;

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

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

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

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

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

Loading history...
5014
  }
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...
5015
}
5016
5017
// Sub-views need their line numbers shifted when text is added
5018
// above or below them in the parent document.
5019
function shiftDoc(doc, distance) {
5020
  if (distance == 0) { return }
5021
  doc.first += distance
5022
  doc.sel = new Selection(map(doc.sel.ranges, function (range) { return new Range(
5023
    Pos(range.anchor.line + distance, range.anchor.ch),
5024
    Pos(range.head.line + distance, range.head.ch)
5025
  ); }), doc.sel.primIndex)
5026
  if (doc.cm) {
5027
    regChange(doc.cm, doc.first, doc.first - distance, distance)
5028
    for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++)
5029
      { regLineChange(doc.cm, l, "gutter") }
5030
  }
5031
}
5032
5033
// More lower-level change function, handling only a single document
5034
// (not linked ones).
5035
function makeChangeSingleDoc(doc, change, selAfter, spans) {
5036
  if (doc.cm && !doc.cm.curOp)
5037
    { return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) }
5038
5039
  if (change.to.line < doc.first) {
5040
    shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line))
5041
    return
5042
  }
5043
  if (change.from.line > doc.lastLine()) { return }
5044
5045
  // Clip the change to the size of this doc
5046
  if (change.from.line < doc.first) {
5047
    var shift = change.text.length - 1 - (doc.first - change.from.line)
5048
    shiftDoc(doc, shift)
5049
    change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
5050
              text: [lst(change.text)], origin: change.origin}
5051
  }
5052
  var last = doc.lastLine()
5053
  if (change.to.line > last) {
5054
    change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
5055
              text: [change.text[0]], origin: change.origin}
5056
  }
5057
5058
  change.removed = getBetween(doc, change.from, change.to)
5059
5060
  if (!selAfter) { selAfter = computeSelAfterChange(doc, change) }
5061
  if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans) }
5062
  else { updateDoc(doc, change, spans) }
5063
  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...
5064
}
5065
5066
// Handle the interaction of a change to a document with the editor
5067
// that this document is part of.
5068
function makeChangeSingleDocInEditor(cm, change, spans) {
5069
  var doc = cm.doc, display = cm.display, from = change.from, to = change.to
5070
5071
  var recomputeMaxLength = false, checkWidthStart = from.line
5072
  if (!cm.options.lineWrapping) {
5073
    checkWidthStart = lineNo(visualLine(getLine(doc, from.line)))
5074
    doc.iter(checkWidthStart, to.line + 1, function (line) {
5075
      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...
5076
        recomputeMaxLength = true
5077
        return true
5078
      }
5079
    })
5080
  }
5081
5082
  if (doc.sel.contains(change.from, change.to) > -1)
5083
    { signalCursorActivity(cm) }
5084
5085
  updateDoc(doc, change, spans, estimateHeight(cm))
5086
5087
  if (!cm.options.lineWrapping) {
5088
    doc.iter(checkWidthStart, from.line + change.text.length, function (line) {
5089
      var len = lineLength(line)
5090
      if (len > display.maxLineLength) {
5091
        display.maxLine = line
5092
        display.maxLineLength = len
5093
        display.maxLineChanged = true
5094
        recomputeMaxLength = false
5095
      }
5096
    })
5097
    if (recomputeMaxLength) { cm.curOp.updateMaxLine = true }
5098
  }
5099
5100
  // Adjust frontier, schedule worker
5101
  doc.frontier = Math.min(doc.frontier, from.line)
5102
  startWorker(cm, 400)
5103
5104
  var lendiff = change.text.length - (to.line - from.line) - 1
5105
  // Remember that these lines changed, for updating the display
5106
  if (change.full)
5107
    { regChange(cm) }
5108
  else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change))
5109
    { regLineChange(cm, from.line, "text") }
5110
  else
5111
    { regChange(cm, from.line, to.line + 1, lendiff) }
5112
5113
  var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change")
5114
  if (changeHandler || changesHandler) {
5115
    var obj = {
5116
      from: from, to: to,
5117
      text: change.text,
5118
      removed: change.removed,
5119
      origin: change.origin
5120
    }
5121
    if (changeHandler) { signalLater(cm, "change", cm, obj) }
5122
    if (changesHandler) { (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj) }
5123
  }
5124
  cm.display.selForContextMenu = null
5125
}
5126
5127
function replaceRange(doc, code, from, to, origin) {
5128
  if (!to) { to = from }
5129
  if (cmp(to, from) < 0) { var tmp = to; to = from; from = tmp }
5130
  if (typeof code == "string") { code = doc.splitLines(code) }
5131
  makeChange(doc, {from: from, to: to, text: code, origin: origin})
5132
}
5133
5134
// Rebasing/resetting history to deal with externally-sourced changes
5135
5136
function rebaseHistSelSingle(pos, from, to, diff) {
5137
  if (to < pos.line) {
5138
    pos.line += diff
5139
  } else if (from < pos.line) {
5140
    pos.line = from
5141
    pos.ch = 0
5142
  }
5143
}
5144
5145
// Tries to rebase an array of history events given a change in the
5146
// document. If the change touches the same lines as the event, the
5147
// event, and everything 'behind' it, is discarded. If the change is
5148
// before the event, the event's positions are updated. Uses a
5149
// copy-on-write scheme for the positions, to avoid having to
5150
// reallocate them all on every rebase, but also avoid problems with
5151
// shared position objects being unsafely updated.
5152
function rebaseHistArray(array, from, to, diff) {
5153
  for (var i = 0; i < array.length; ++i) {
5154
    var sub = array[i], ok = true
5155
    if (sub.ranges) {
5156
      if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true }
5157
      for (var j = 0; j < sub.ranges.length; j++) {
5158
        rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff)
5159
        rebaseHistSelSingle(sub.ranges[j].head, from, to, diff)
5160
      }
5161
      continue
5162
    }
5163
    for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) {
5164
      var cur = sub.changes[j$1]
5165
      if (to < cur.from.line) {
5166
        cur.from = Pos(cur.from.line + diff, cur.from.ch)
5167
        cur.to = Pos(cur.to.line + diff, cur.to.ch)
5168
      } else if (from <= cur.to.line) {
5169
        ok = false
5170
        break
5171
      }
5172
    }
5173
    if (!ok) {
5174
      array.splice(0, i + 1)
5175
      i = 0
5176
    }
5177
  }
5178
}
5179
5180
function rebaseHist(hist, change) {
5181
  var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1
5182
  rebaseHistArray(hist.done, from, to, diff)
5183
  rebaseHistArray(hist.undone, from, to, diff)
5184
}
5185
5186
// Utility for applying a change to a line by handle or number,
5187
// returning the number and optionally registering the line as
5188
// changed.
5189
function changeLine(doc, handle, changeType, op) {
5190
  var no = handle, line = handle
5191
  if (typeof handle == "number") { line = getLine(doc, clipLine(doc, handle)) }
5192
  else { no = lineNo(handle) }
5193
  if (no == null) { return null }
5194
  if (op(line, no) && doc.cm) { regLineChange(doc.cm, no, changeType) }
5195
  return line
5196
}
5197
5198
// The document is represented as a BTree consisting of leaves, with
5199
// chunk of lines in them, and branches, with up to ten leaves or
5200
// other branch nodes below them. The top node is always a branch
5201
// node, and is the document object itself (meaning it has
5202
// additional methods and properties).
5203
//
5204
// All nodes have parent links. The tree is used both to go from
5205
// line numbers to line objects, and to go from objects to numbers.
5206
// It also indexes by height, and is used to convert between height
5207
// and line object, and to find the total height of the document.
5208
//
5209
// See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html
5210
5211
function LeafChunk(lines) {
5212
  var this$1 = this;
5213
5214
  this.lines = lines
5215
  this.parent = null
5216
  var height = 0
5217
  for (var i = 0; i < lines.length; ++i) {
5218
    lines[i].parent = this$1
5219
    height += lines[i].height
5220
  }
5221
  this.height = height
5222
}
5223
5224
LeafChunk.prototype = {
5225
  chunkSize: function() { return this.lines.length },
5226
  // Remove the n lines at offset 'at'.
5227
  removeInner: function(at, n) {
5228
    var this$1 = this;
5229
5230
    for (var i = at, e = at + n; i < e; ++i) {
5231
      var line = this$1.lines[i]
5232
      this$1.height -= line.height
5233
      cleanUpLine(line)
5234
      signalLater(line, "delete")
5235
    }
5236
    this.lines.splice(at, n)
5237
  },
5238
  // Helper used to collapse a small branch into a single leaf.
5239
  collapse: function(lines) {
5240
    lines.push.apply(lines, this.lines)
5241
  },
5242
  // Insert the given array of lines at offset 'at', count them as
5243
  // having the given height.
5244
  insertInner: function(at, lines, height) {
5245
    var this$1 = this;
5246
5247
    this.height += height
5248
    this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at))
5249
    for (var i = 0; i < lines.length; ++i) { lines[i].parent = this$1 }
5250
  },
5251
  // Used to iterate over a part of the tree.
5252
  iterN: function(at, n, op) {
5253
    var this$1 = this;
5254
5255
    for (var e = at + n; at < e; ++at)
5256
      { if (op(this$1.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...
5257
  }
5258
}
5259
5260
function BranchChunk(children) {
5261
  var this$1 = this;
5262
5263
  this.children = children
5264
  var size = 0, height = 0
5265
  for (var i = 0; i < children.length; ++i) {
5266
    var ch = children[i]
5267
    size += ch.chunkSize(); height += ch.height
5268
    ch.parent = this$1
5269
  }
5270
  this.size = size
5271
  this.height = height
5272
  this.parent = null
5273
}
5274
5275
BranchChunk.prototype = {
5276
  chunkSize: function() { return this.size },
5277
  removeInner: function(at, n) {
5278
    var this$1 = this;
5279
5280
    this.size -= n
5281
    for (var i = 0; i < this.children.length; ++i) {
5282
      var child = this$1.children[i], sz = child.chunkSize()
5283
      if (at < sz) {
5284
        var rm = Math.min(n, sz - at), oldHeight = child.height
5285
        child.removeInner(at, rm)
5286
        this$1.height -= oldHeight - child.height
5287
        if (sz == rm) { this$1.children.splice(i--, 1); child.parent = null }
5288
        if ((n -= rm) == 0) { break }
5289
        at = 0
5290
      } else { at -= sz }
5291
    }
5292
    // If the result is smaller than 25 lines, ensure that it is a
5293
    // single leaf node.
5294
    if (this.size - n < 25 &&
5295
        (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) {
5296
      var lines = []
5297
      this.collapse(lines)
5298
      this.children = [new LeafChunk(lines)]
5299
      this.children[0].parent = this
5300
    }
5301
  },
5302
  collapse: function(lines) {
5303
    var this$1 = this;
5304
5305
    for (var i = 0; i < this.children.length; ++i) { this$1.children[i].collapse(lines) }
5306
  },
5307
  insertInner: function(at, lines, height) {
5308
    var this$1 = this;
5309
5310
    this.size += lines.length
5311
    this.height += height
5312
    for (var i = 0; i < this.children.length; ++i) {
5313
      var child = this$1.children[i], sz = child.chunkSize()
5314
      if (at <= sz) {
5315
        child.insertInner(at, lines, height)
5316
        if (child.lines && child.lines.length > 50) {
5317
          // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced.
5318
          // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest.
5319
          var remaining = child.lines.length % 25 + 25
5320
          for (var pos = remaining; pos < child.lines.length;) {
5321
            var leaf = new LeafChunk(child.lines.slice(pos, pos += 25))
5322
            child.height -= leaf.height
5323
            this$1.children.splice(++i, 0, leaf)
5324
            leaf.parent = this$1
5325
          }
5326
          child.lines = child.lines.slice(0, remaining)
5327
          this$1.maybeSpill()
5328
        }
5329
        break
5330
      }
5331
      at -= sz
5332
    }
5333
  },
5334
  // When a node has grown, check whether it should be split.
5335
  maybeSpill: function() {
5336
    if (this.children.length <= 10) { return }
5337
    var me = this
5338
    do {
5339
      var spilled = me.children.splice(me.children.length - 5, 5)
5340
      var sibling = new BranchChunk(spilled)
5341
      if (!me.parent) { // Become the parent node
5342
        var copy = new BranchChunk(me.children)
5343
        copy.parent = me
5344
        me.children = [copy, sibling]
5345
        me = copy
5346
     } else {
5347
        me.size -= sibling.size
5348
        me.height -= sibling.height
5349
        var myIndex = indexOf(me.parent.children, me)
5350
        me.parent.children.splice(myIndex + 1, 0, sibling)
5351
      }
5352
      sibling.parent = me.parent
5353
    } while (me.children.length > 10)
5354
    me.parent.maybeSpill()
5355
  },
5356
  iterN: function(at, n, op) {
5357
    var this$1 = this;
5358
5359
    for (var i = 0; i < this.children.length; ++i) {
5360
      var child = this$1.children[i], sz = child.chunkSize()
5361
      if (at < sz) {
5362
        var used = Math.min(n, sz - at)
5363
        if (child.iterN(at, used, op)) { return true }
5364
        if ((n -= used) == 0) { 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...
5365
        at = 0
5366
      } else { at -= sz }
5367
    }
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...
5368
  }
5369
}
5370
5371
// Line widgets are block elements displayed above or below a line.
5372
5373
function LineWidget(doc, node, options) {
5374
  var this$1 = this;
5375
5376
  if (options) { for (var opt in options) { if (options.hasOwnProperty(opt))
5377
    { this$1[opt] = options[opt] } } }
5378
  this.doc = doc
5379
  this.node = node
5380
}
5381
eventMixin(LineWidget)
5382
5383
function adjustScrollWhenAboveVisible(cm, line, diff) {
5384
  if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))
5385
    { addToScrollPos(cm, null, diff) }
5386
}
5387
5388
LineWidget.prototype.clear = function() {
5389
  var this$1 = this;
5390
5391
  var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line)
5392
  if (no == null || !ws) { return }
5393
  for (var i = 0; i < ws.length; ++i) { if (ws[i] == this$1) { ws.splice(i--, 1) } }
5394
  if (!ws.length) { line.widgets = null }
5395
  var height = widgetHeight(this)
5396
  updateLineHeight(line, Math.max(0, line.height - height))
5397
  if (cm) { runInOp(cm, function () {
5398
    adjustScrollWhenAboveVisible(cm, line, -height)
5399
    regLineChange(cm, no, "widget")
5400
  }) }
5401
}
5402
LineWidget.prototype.changed = function() {
5403
  var oldH = this.height, cm = this.doc.cm, line = this.line
5404
  this.height = null
5405
  var diff = widgetHeight(this) - oldH
5406
  if (!diff) { return }
5407
  updateLineHeight(line, line.height + diff)
5408
  if (cm) { runInOp(cm, function () {
5409
    cm.curOp.forceUpdate = true
5410
    adjustScrollWhenAboveVisible(cm, line, diff)
5411
  }) }
5412
}
5413
5414
function addLineWidget(doc, handle, node, options) {
5415
  var widget = new LineWidget(doc, node, options)
5416
  var cm = doc.cm
5417
  if (cm && widget.noHScroll) { cm.display.alignWidgets = true }
5418
  changeLine(doc, handle, "widget", function (line) {
5419
    var widgets = line.widgets || (line.widgets = [])
5420
    if (widget.insertAt == null) { widgets.push(widget) }
5421
    else { widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget) }
5422
    widget.line = line
5423
    if (cm && !lineIsHidden(doc, line)) {
5424
      var aboveVisible = heightAtLine(line) < doc.scrollTop
5425
      updateLineHeight(line, line.height + widgetHeight(widget))
5426
      if (aboveVisible) { addToScrollPos(cm, null, widget.height) }
5427
      cm.curOp.forceUpdate = true
5428
    }
5429
    return true
5430
  })
5431
  return widget
5432
}
5433
5434
// TEXTMARKERS
5435
5436
// Created with markText and setBookmark methods. A TextMarker is a
5437
// handle that can be used to clear or find a marked position in the
5438
// document. Line objects hold arrays (markedSpans) containing
5439
// {from, to, marker} object pointing to such marker objects, and
5440
// indicating that such a marker is present on that line. Multiple
5441
// lines may point to the same marker when it spans across lines.
5442
// The spans will have null for their from/to properties when the
5443
// marker continues beyond the start/end of the line. Markers have
5444
// links back to the lines they currently touch.
5445
5446
// Collapsed markers have unique ids, in order to be able to order
5447
// them, which is needed for uniquely determining an outer marker
5448
// when they overlap (they may nest, but not partially overlap).
5449
var nextMarkerId = 0
5450
5451
function TextMarker(doc, type) {
5452
  this.lines = []
5453
  this.type = type
5454
  this.doc = doc
5455
  this.id = ++nextMarkerId
5456
}
5457
eventMixin(TextMarker)
5458
5459
// Clear the marker.
5460
TextMarker.prototype.clear = function() {
5461
  var this$1 = this;
5462
5463
  if (this.explicitlyCleared) { return }
5464
  var cm = this.doc.cm, withOp = cm && !cm.curOp
5465
  if (withOp) { startOperation(cm) }
5466
  if (hasHandler(this, "clear")) {
5467
    var found = this.find()
5468
    if (found) { signalLater(this, "clear", found.from, found.to) }
5469
  }
5470
  var min = null, max = null
5471
  for (var i = 0; i < this.lines.length; ++i) {
5472
    var line = this$1.lines[i]
5473
    var span = getMarkedSpanFor(line.markedSpans, this$1)
5474
    if (cm && !this$1.collapsed) { regLineChange(cm, lineNo(line), "text") }
5475
    else if (cm) {
5476
      if (span.to != null) { max = lineNo(line) }
5477
      if (span.from != null) { min = lineNo(line) }
5478
    }
5479
    line.markedSpans = removeMarkedSpan(line.markedSpans, span)
5480
    if (span.from == null && this$1.collapsed && !lineIsHidden(this$1.doc, line) && cm)
5481
      { updateLineHeight(line, textHeight(cm.display)) }
5482
  }
5483
  if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) {
5484
    var visual = visualLine(this$1.lines[i$1]), len = lineLength(visual)
5485
    if (len > cm.display.maxLineLength) {
5486
      cm.display.maxLine = visual
5487
      cm.display.maxLineLength = len
5488
      cm.display.maxLineChanged = true
5489
    }
5490
  } }
5491
5492
  if (min != null && cm && this.collapsed) { regChange(cm, min, max + 1) }
5493
  this.lines.length = 0
5494
  this.explicitlyCleared = true
5495
  if (this.atomic && this.doc.cantEdit) {
5496
    this.doc.cantEdit = false
5497
    if (cm) { reCheckSelection(cm.doc) }
5498
  }
5499
  if (cm) { signalLater(cm, "markerCleared", cm, this) }
5500
  if (withOp) { endOperation(cm) }
5501
  if (this.parent) { this.parent.clear() }
5502
}
5503
5504
// Find the position of the marker in the document. Returns a {from,
5505
// to} object by default. Side can be passed to get a specific side
5506
// -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the
5507
// Pos objects returned contain a line object, rather than a line
5508
// number (used to prevent looking up the same line twice).
5509
TextMarker.prototype.find = function(side, lineObj) {
5510
  var this$1 = this;
5511
5512
  if (side == null && this.type == "bookmark") { side = 1 }
5513
  var from, to
5514
  for (var i = 0; i < this.lines.length; ++i) {
5515
    var line = this$1.lines[i]
5516
    var span = getMarkedSpanFor(line.markedSpans, this$1)
5517
    if (span.from != null) {
5518
      from = Pos(lineObj ? line : lineNo(line), span.from)
5519
      if (side == -1) { return from }
5520
    }
5521
    if (span.to != null) {
5522
      to = Pos(lineObj ? line : lineNo(line), span.to)
5523
      if (side == 1) { return to }
5524
    }
5525
  }
5526
  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...
5527
}
5528
5529
// Signals that the marker's widget changed, and surrounding layout
5530
// should be recomputed.
5531
TextMarker.prototype.changed = function() {
5532
  var pos = this.find(-1, true), widget = this, cm = this.doc.cm
5533
  if (!pos || !cm) { return }
5534
  runInOp(cm, function () {
5535
    var line = pos.line, lineN = lineNo(pos.line)
5536
    var view = findViewForLine(cm, lineN)
5537
    if (view) {
5538
      clearLineMeasurementCacheFor(view)
5539
      cm.curOp.selectionChanged = cm.curOp.forceUpdate = true
5540
    }
5541
    cm.curOp.updateMaxLine = true
5542
    if (!lineIsHidden(widget.doc, line) && widget.height != null) {
5543
      var oldHeight = widget.height
5544
      widget.height = null
5545
      var dHeight = widgetHeight(widget) - oldHeight
5546
      if (dHeight)
5547
        { updateLineHeight(line, line.height + dHeight) }
5548
    }
5549
  })
5550
}
5551
5552
TextMarker.prototype.attachLine = function(line) {
5553
  if (!this.lines.length && this.doc.cm) {
5554
    var op = this.doc.cm.curOp
5555
    if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
5556
      { (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this) }
5557
  }
5558
  this.lines.push(line)
5559
}
5560
TextMarker.prototype.detachLine = function(line) {
5561
  this.lines.splice(indexOf(this.lines, line), 1)
5562
  if (!this.lines.length && this.doc.cm) {
5563
    var op = this.doc.cm.curOp
5564
    ;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this)
5565
  }
5566
}
5567
5568
// Create a marker, wire it up to the right lines, and
5569
function markText(doc, from, to, options, type) {
5570
  // Shared markers (across linked documents) are handled separately
5571
  // (markTextShared will call out to this again, once per
5572
  // document).
5573
  if (options && options.shared) { return markTextShared(doc, from, to, options, type) }
5574
  // Ensure we are in an operation.
5575
  if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, markText)(doc, from, to, options, type) }
5576
5577
  var marker = new TextMarker(doc, type), diff = cmp(from, to)
5578
  if (options) { copyObj(options, marker, false) }
5579
  // Don't connect empty markers unless clearWhenEmpty is false
5580
  if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false)
5581
    { return marker }
5582
  if (marker.replacedWith) {
5583
    // Showing up as a widget implies collapsed (widget replaces text)
5584
    marker.collapsed = true
5585
    marker.widgetNode = elt("span", [marker.replacedWith], "CodeMirror-widget")
5586
    if (!options.handleMouseEvents) { marker.widgetNode.setAttribute("cm-ignore-events", "true") }
5587
    if (options.insertLeft) { marker.widgetNode.insertLeft = true }
5588
  }
5589
  if (marker.collapsed) {
5590
    if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||
5591
        from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker))
5592
      { throw new Error("Inserting collapsed marker partially overlapping an existing one") }
5593
    seeCollapsedSpans()
5594
  }
5595
5596
  if (marker.addToHistory)
5597
    { addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN) }
5598
5599
  var curLine = from.line, cm = doc.cm, updateMaxLine
5600
  doc.iter(curLine, to.line + 1, function (line) {
5601
    if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine)
5602
      { updateMaxLine = true }
5603
    if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0) }
5604
    addMarkedSpan(line, new MarkedSpan(marker,
5605
                                       curLine == from.line ? from.ch : null,
5606
                                       curLine == to.line ? to.ch : null))
5607
    ++curLine
5608
  })
5609
  // lineIsHidden depends on the presence of the spans, so needs a second pass
5610
  if (marker.collapsed) { doc.iter(from.line, to.line + 1, function (line) {
5611
    if (lineIsHidden(doc, line)) { updateLineHeight(line, 0) }
5612
  }) }
5613
5614
  if (marker.clearOnEnter) { on(marker, "beforeCursorEnter", function () { return marker.clear(); }) }
5615
5616
  if (marker.readOnly) {
5617
    seeReadOnlySpans()
5618
    if (doc.history.done.length || doc.history.undone.length)
5619
      { doc.clearHistory() }
5620
  }
5621
  if (marker.collapsed) {
5622
    marker.id = ++nextMarkerId
5623
    marker.atomic = true
5624
  }
5625
  if (cm) {
5626
    // Sync editor state
5627
    if (updateMaxLine) { cm.curOp.updateMaxLine = true }
5628
    if (marker.collapsed)
5629
      { regChange(cm, from.line, to.line + 1) }
5630
    else if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.css)
5631
      { for (var i = from.line; i <= to.line; i++) { regLineChange(cm, i, "text") } }
5632
    if (marker.atomic) { reCheckSelection(cm.doc) }
5633
    signalLater(cm, "markerAdded", cm, marker)
5634
  }
5635
  return marker
5636
}
5637
5638
// SHARED TEXTMARKERS
5639
5640
// A shared marker spans multiple linked documents. It is
5641
// implemented as a meta-marker-object controlling multiple normal
5642
// markers.
5643
function SharedTextMarker(markers, primary) {
5644
  var this$1 = this;
5645
5646
  this.markers = markers
5647
  this.primary = primary
5648
  for (var i = 0; i < markers.length; ++i)
5649
    { markers[i].parent = this$1 }
5650
}
5651
eventMixin(SharedTextMarker)
5652
5653
SharedTextMarker.prototype.clear = function() {
5654
  var this$1 = this;
5655
5656
  if (this.explicitlyCleared) { return }
5657
  this.explicitlyCleared = true
5658
  for (var i = 0; i < this.markers.length; ++i)
5659
    { this$1.markers[i].clear() }
5660
  signalLater(this, "clear")
5661
}
5662
SharedTextMarker.prototype.find = function(side, lineObj) {
5663
  return this.primary.find(side, lineObj)
5664
}
5665
5666
function markTextShared(doc, from, to, options, type) {
5667
  options = copyObj(options)
5668
  options.shared = false
5669
  var markers = [markText(doc, from, to, options, type)], primary = markers[0]
5670
  var widget = options.widgetNode
5671
  linkedDocs(doc, function (doc) {
5672
    if (widget) { options.widgetNode = widget.cloneNode(true) }
5673
    markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type))
5674
    for (var i = 0; i < doc.linked.length; ++i)
5675
      { if (doc.linked[i].isParent) { return } }
5676
    primary = lst(markers)
5677
  })
5678
  return new SharedTextMarker(markers, primary)
5679
}
5680
5681
function findSharedMarkers(doc) {
5682
  return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function (m) { return m.parent; })
5683
}
5684
5685
function copySharedMarkers(doc, markers) {
5686
  for (var i = 0; i < markers.length; i++) {
5687
    var marker = markers[i], pos = marker.find()
5688
    var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to)
5689
    if (cmp(mFrom, mTo)) {
5690
      var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type)
5691
      marker.markers.push(subMark)
5692
      subMark.parent = marker
5693
    }
5694
  }
5695
}
5696
5697
function detachSharedMarkers(markers) {
5698
  var loop = function ( i ) {
5699
    var marker = markers[i], linked = [marker.primary.doc]
5700
    linkedDocs(marker.primary.doc, function (d) { return linked.push(d); })
5701
    for (var j = 0; j < marker.markers.length; j++) {
5702
      var subMarker = marker.markers[j]
5703
      if (indexOf(linked, subMarker.doc) == -1) {
5704
        subMarker.parent = null
5705
        marker.markers.splice(j--, 1)
5706
      }
5707
    }
5708
  };
5709
5710
  for (var i = 0; i < markers.length; i++) loop( i );
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

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

Consider:

if (a > 0)
    b = 42;

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

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

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

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

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

Loading history...
5711
}
5712
5713
var nextDocId = 0
5714
var Doc = function(text, mode, firstLine, lineSep) {
5715
  if (!(this instanceof Doc)) { return new Doc(text, mode, firstLine, lineSep) }
5716
  if (firstLine == null) { firstLine = 0 }
5717
5718
  BranchChunk.call(this, [new LeafChunk([new Line("", null)])])
5719
  this.first = firstLine
5720
  this.scrollTop = this.scrollLeft = 0
5721
  this.cantEdit = false
5722
  this.cleanGeneration = 1
5723
  this.frontier = firstLine
5724
  var start = Pos(firstLine, 0)
5725
  this.sel = simpleSelection(start)
5726
  this.history = new History(null)
5727
  this.id = ++nextDocId
5728
  this.modeOption = mode
5729
  this.lineSep = lineSep
5730
  this.extend = false
5731
5732
  if (typeof text == "string") { text = this.splitLines(text) }
5733
  updateDoc(this, {from: start, to: start, text: text})
5734
  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...
5735
}
5736
5737
Doc.prototype = createObj(BranchChunk.prototype, {
5738
  constructor: Doc,
5739
  // Iterate over the document. Supports two forms -- with only one
5740
  // argument, it calls that for each line in the document. With
5741
  // three, it iterates over the range given by the first two (with
5742
  // the second being non-inclusive).
5743
  iter: function(from, to, op) {
5744
    if (op) { this.iterN(from - this.first, to - from, op) }
5745
    else { this.iterN(this.first, this.first + this.size, from) }
5746
  },
5747
5748
  // Non-public interface for adding and removing lines.
5749
  insert: function(at, lines) {
5750
    var height = 0
5751
    for (var i = 0; i < lines.length; ++i) { height += lines[i].height }
5752
    this.insertInner(at - this.first, lines, height)
5753
  },
5754
  remove: function(at, n) { this.removeInner(at - this.first, n) },
5755
5756
  // From here, the methods are part of the public interface. Most
5757
  // are also available from CodeMirror (editor) instances.
5758
5759
  getValue: function(lineSep) {
5760
    var lines = getLines(this, this.first, this.first + this.size)
5761
    if (lineSep === false) { return lines }
5762
    return lines.join(lineSep || this.lineSeparator())
5763
  },
5764
  setValue: docMethodOp(function(code) {
5765
    var top = Pos(this.first, 0), last = this.first + this.size - 1
5766
    makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
5767
                      text: this.splitLines(code), origin: "setValue", full: true}, true)
5768
    setSelection(this, simpleSelection(top))
5769
  }),
5770
  replaceRange: function(code, from, to, origin) {
5771
    from = clipPos(this, from)
5772
    to = to ? clipPos(this, to) : from
5773
    replaceRange(this, code, from, to, origin)
5774
  },
5775
  getRange: function(from, to, lineSep) {
5776
    var lines = getBetween(this, clipPos(this, from), clipPos(this, to))
5777
    if (lineSep === false) { return lines }
5778
    return lines.join(lineSep || this.lineSeparator())
5779
  },
5780
5781
  getLine: function(line) {var l = this.getLineHandle(line); return l && l.text},
5782
5783
  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...
5784
  getLineNumber: function(line) {return lineNo(line)},
5785
5786
  getLineHandleVisualStart: function(line) {
5787
    if (typeof line == "number") { line = getLine(this, line) }
5788
    return visualLine(line)
5789
  },
5790
5791
  lineCount: function() {return this.size},
5792
  firstLine: function() {return this.first},
5793
  lastLine: function() {return this.first + this.size - 1},
5794
5795
  clipPos: function(pos) {return clipPos(this, pos)},
5796
5797
  getCursor: function(start) {
5798
    var range = this.sel.primary(), pos
5799
    if (start == null || start == "head") { pos = range.head }
5800
    else if (start == "anchor") { pos = range.anchor }
5801
    else if (start == "end" || start == "to" || start === false) { pos = range.to() }
5802
    else { pos = range.from() }
5803
    return pos
5804
  },
5805
  listSelections: function() { return this.sel.ranges },
5806
  somethingSelected: function() {return this.sel.somethingSelected()},
5807
5808
  setCursor: docMethodOp(function(line, ch, options) {
5809
    setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options)
5810
  }),
5811
  setSelection: docMethodOp(function(anchor, head, options) {
5812
    setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options)
5813
  }),
5814
  extendSelection: docMethodOp(function(head, other, options) {
5815
    extendSelection(this, clipPos(this, head), other && clipPos(this, other), options)
5816
  }),
5817
  extendSelections: docMethodOp(function(heads, options) {
5818
    extendSelections(this, clipPosArray(this, heads), options)
5819
  }),
5820
  extendSelectionsBy: docMethodOp(function(f, options) {
5821
    var heads = map(this.sel.ranges, f)
5822
    extendSelections(this, clipPosArray(this, heads), options)
5823
  }),
5824
  setSelections: docMethodOp(function(ranges, primary, options) {
5825
    var this$1 = this;
5826
5827
    if (!ranges.length) { return }
5828
    var out = []
5829
    for (var i = 0; i < ranges.length; i++)
5830
      { out[i] = new Range(clipPos(this$1, ranges[i].anchor),
5831
                         clipPos(this$1, ranges[i].head)) }
5832
    if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex) }
5833
    setSelection(this, normalizeSelection(out, primary), options)
5834
  }),
5835
  addSelection: docMethodOp(function(anchor, head, options) {
5836
    var ranges = this.sel.ranges.slice(0)
5837
    ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)))
5838
    setSelection(this, normalizeSelection(ranges, ranges.length - 1), options)
5839
  }),
5840
5841
  getSelection: function(lineSep) {
5842
    var this$1 = this;
5843
5844
    var ranges = this.sel.ranges, lines
5845
    for (var i = 0; i < ranges.length; i++) {
5846
      var sel = getBetween(this$1, ranges[i].from(), ranges[i].to())
5847
      lines = lines ? lines.concat(sel) : sel
5848
    }
5849
    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...
5850
    else { return lines.join(lineSep || this.lineSeparator()) }
5851
  },
5852
  getSelections: function(lineSep) {
5853
    var this$1 = this;
5854
5855
    var parts = [], ranges = this.sel.ranges
5856
    for (var i = 0; i < ranges.length; i++) {
5857
      var sel = getBetween(this$1, ranges[i].from(), ranges[i].to())
5858
      if (lineSep !== false) { sel = sel.join(lineSep || this$1.lineSeparator()) }
5859
      parts[i] = sel
5860
    }
5861
    return parts
5862
  },
5863
  replaceSelection: function(code, collapse, origin) {
5864
    var dup = []
5865
    for (var i = 0; i < this.sel.ranges.length; i++)
5866
      { dup[i] = code }
5867
    this.replaceSelections(dup, collapse, origin || "+input")
5868
  },
5869
  replaceSelections: docMethodOp(function(code, collapse, origin) {
5870
    var this$1 = this;
5871
5872
    var changes = [], sel = this.sel
5873
    for (var i = 0; i < sel.ranges.length; i++) {
5874
      var range = sel.ranges[i]
5875
      changes[i] = {from: range.from(), to: range.to(), text: this$1.splitLines(code[i]), origin: origin}
5876
    }
5877
    var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse)
5878
    for (var i$1 = changes.length - 1; i$1 >= 0; i$1--)
5879
      { makeChange(this$1, changes[i$1]) }
5880
    if (newSel) { setSelectionReplaceHistory(this, newSel) }
5881
    else if (this.cm) { ensureCursorVisible(this.cm) }
5882
  }),
5883
  undo: docMethodOp(function() {makeChangeFromHistory(this, "undo")}),
5884
  redo: docMethodOp(function() {makeChangeFromHistory(this, "redo")}),
5885
  undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true)}),
5886
  redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true)}),
5887
5888
  setExtending: function(val) {this.extend = val},
5889
  getExtending: function() {return this.extend},
5890
5891
  historySize: function() {
5892
    var hist = this.history, done = 0, undone = 0
5893
    for (var i = 0; i < hist.done.length; i++) { if (!hist.done[i].ranges) { ++done } }
5894
    for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].ranges) { ++undone } }
5895
    return {undo: done, redo: undone}
5896
  },
5897
  clearHistory: function() {this.history = new History(this.history.maxGeneration)},
5898
5899
  markClean: function() {
5900
    this.cleanGeneration = this.changeGeneration(true)
5901
  },
5902
  changeGeneration: function(forceSplit) {
5903
    if (forceSplit)
5904
      { this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null }
5905
    return this.history.generation
5906
  },
5907
  isClean: function (gen) {
5908
    return this.history.generation == (gen || this.cleanGeneration)
5909
  },
5910
5911
  getHistory: function() {
5912
    return {done: copyHistoryArray(this.history.done),
5913
            undone: copyHistoryArray(this.history.undone)}
5914
  },
5915
  setHistory: function(histData) {
5916
    var hist = this.history = new History(this.history.maxGeneration)
5917
    hist.done = copyHistoryArray(histData.done.slice(0), null, true)
5918
    hist.undone = copyHistoryArray(histData.undone.slice(0), null, true)
5919
  },
5920
5921
  setGutterMarker: docMethodOp(function(line, gutterID, value) {
5922
    return changeLine(this, line, "gutter", function (line) {
5923
      var markers = line.gutterMarkers || (line.gutterMarkers = {})
5924
      markers[gutterID] = value
5925
      if (!value && isEmpty(markers)) { line.gutterMarkers = null }
5926
      return true
5927
    })
5928
  }),
5929
5930
  clearGutter: docMethodOp(function(gutterID) {
5931
    var this$1 = this;
5932
5933
    var i = this.first
5934
    this.iter(function (line) {
5935
      if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
5936
        changeLine(this$1, line, "gutter", function () {
5937
          line.gutterMarkers[gutterID] = null
5938
          if (isEmpty(line.gutterMarkers)) { line.gutterMarkers = null }
5939
          return true
5940
        })
5941
      }
5942
      ++i
5943
    })
5944
  }),
5945
5946
  lineInfo: function(line) {
5947
    var n
5948
    if (typeof line == "number") {
5949
      if (!isLine(this, line)) { return null }
5950
      n = line
5951
      line = getLine(this, line)
5952
      if (!line) { return null }
5953
    } else {
5954
      n = lineNo(line)
5955
      if (n == null) { return null }
5956
    }
5957
    return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
5958
            textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
5959
            widgets: line.widgets}
5960
  },
5961
5962
  addLineClass: docMethodOp(function(handle, where, cls) {
5963
    return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
5964
      var prop = where == "text" ? "textClass"
5965
               : where == "background" ? "bgClass"
5966
               : where == "gutter" ? "gutterClass" : "wrapClass"
5967
      if (!line[prop]) { line[prop] = cls }
5968
      else if (classTest(cls).test(line[prop])) { return false }
5969
      else { line[prop] += " " + cls }
5970
      return true
5971
    })
5972
  }),
5973
  removeLineClass: docMethodOp(function(handle, where, cls) {
5974
    return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
5975
      var prop = where == "text" ? "textClass"
5976
               : where == "background" ? "bgClass"
5977
               : where == "gutter" ? "gutterClass" : "wrapClass"
5978
      var cur = line[prop]
5979
      if (!cur) { return false }
5980
      else if (cls == null) { line[prop] = null }
5981
      else {
5982
        var found = cur.match(classTest(cls))
5983
        if (!found) { return false }
5984
        var end = found.index + found[0].length
5985
        line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null
5986
      }
5987
      return true
5988
    })
5989
  }),
5990
5991
  addLineWidget: docMethodOp(function(handle, node, options) {
5992
    return addLineWidget(this, handle, node, options)
5993
  }),
5994
  removeLineWidget: function(widget) { widget.clear() },
5995
5996
  markText: function(from, to, options) {
5997
    return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range")
5998
  },
5999
  setBookmark: function(pos, options) {
6000
    var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
6001
                    insertLeft: options && options.insertLeft,
6002
                    clearWhenEmpty: false, shared: options && options.shared,
6003
                    handleMouseEvents: options && options.handleMouseEvents}
6004
    pos = clipPos(this, pos)
6005
    return markText(this, pos, pos, realOpts, "bookmark")
6006
  },
6007
  findMarksAt: function(pos) {
6008
    pos = clipPos(this, pos)
6009
    var markers = [], spans = getLine(this, pos.line).markedSpans
6010
    if (spans) { for (var i = 0; i < spans.length; ++i) {
6011
      var span = spans[i]
6012
      if ((span.from == null || span.from <= pos.ch) &&
6013
          (span.to == null || span.to >= pos.ch))
6014
        { markers.push(span.marker.parent || span.marker) }
6015
    } }
6016
    return markers
6017
  },
6018
  findMarks: function(from, to, filter) {
6019
    from = clipPos(this, from); to = clipPos(this, to)
6020
    var found = [], lineNo = from.line
6021
    this.iter(from.line, to.line + 1, function (line) {
6022
      var spans = line.markedSpans
6023
      if (spans) { for (var i = 0; i < spans.length; i++) {
6024
        var span = spans[i]
6025
        if (!(span.to != null && lineNo == from.line && from.ch >= span.to ||
6026
              span.from == null && lineNo != from.line ||
6027
              span.from != null && lineNo == to.line && span.from >= to.ch) &&
6028
            (!filter || filter(span.marker)))
6029
          { found.push(span.marker.parent || span.marker) }
6030
      } }
6031
      ++lineNo
6032
    })
6033
    return found
6034
  },
6035
  getAllMarks: function() {
6036
    var markers = []
6037
    this.iter(function (line) {
6038
      var sps = line.markedSpans
6039
      if (sps) { for (var i = 0; i < sps.length; ++i)
6040
        { if (sps[i].from != null) { markers.push(sps[i].marker) } } }
6041
    })
6042
    return markers
6043
  },
6044
6045
  posFromIndex: function(off) {
6046
    var ch, lineNo = this.first, sepSize = this.lineSeparator().length
6047
    this.iter(function (line) {
6048
      var sz = line.text.length + sepSize
6049
      if (sz > off) { ch = off; return true }
6050
      off -= sz
6051
      ++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...
6052
    })
6053
    return clipPos(this, Pos(lineNo, ch))
6054
  },
6055
  indexFromPos: function (coords) {
6056
    coords = clipPos(this, coords)
6057
    var index = coords.ch
6058
    if (coords.line < this.first || coords.ch < 0) { return 0 }
6059
    var sepSize = this.lineSeparator().length
6060
    this.iter(this.first, coords.line, function (line) { // iter aborts when callback returns a truthy value
6061
      index += line.text.length + sepSize
6062
    })
6063
    return index
6064
  },
6065
6066
  copy: function(copyHistory) {
6067
    var doc = new Doc(getLines(this, this.first, this.first + this.size),
6068
                      this.modeOption, this.first, this.lineSep)
6069
    doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft
6070
    doc.sel = this.sel
6071
    doc.extend = false
6072
    if (copyHistory) {
6073
      doc.history.undoDepth = this.history.undoDepth
6074
      doc.setHistory(this.getHistory())
6075
    }
6076
    return doc
6077
  },
6078
6079
  linkedDoc: function(options) {
6080
    if (!options) { options = {} }
6081
    var from = this.first, to = this.first + this.size
6082
    if (options.from != null && options.from > from) { from = options.from }
6083
    if (options.to != null && options.to < to) { to = options.to }
6084
    var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep)
6085
    if (options.sharedHist) { copy.history = this.history
6086
    ; }(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist})
6087
    copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}]
6088
    copySharedMarkers(copy, findSharedMarkers(this))
6089
    return copy
6090
  },
6091
  unlinkDoc: function(other) {
6092
    var this$1 = this;
6093
6094
    if (other instanceof CodeMirror) { other = other.doc }
6095
    if (this.linked) { for (var i = 0; i < this.linked.length; ++i) {
6096
      var link = this$1.linked[i]
6097
      if (link.doc != other) { continue }
6098
      this$1.linked.splice(i, 1)
6099
      other.unlinkDoc(this$1)
6100
      detachSharedMarkers(findSharedMarkers(this$1))
6101
      break
6102
    } }
6103
    // If the histories were shared, split them again
6104
    if (other.history == this.history) {
6105
      var splitIds = [other.id]
6106
      linkedDocs(other, function (doc) { return splitIds.push(doc.id); }, true)
6107
      other.history = new History(null)
6108
      other.history.done = copyHistoryArray(this.history.done, splitIds)
6109
      other.history.undone = copyHistoryArray(this.history.undone, splitIds)
6110
    }
6111
  },
6112
  iterLinkedDocs: function(f) {linkedDocs(this, f)},
6113
6114
  getMode: function() {return this.mode},
6115
  getEditor: function() {return this.cm},
6116
6117
  splitLines: function(str) {
6118
    if (this.lineSep) { return str.split(this.lineSep) }
6119
    return splitLinesAuto(str)
6120
  },
6121
  lineSeparator: function() { return this.lineSep || "\n" }
6122
})
6123
6124
// Public alias.
6125
Doc.prototype.eachLine = Doc.prototype.iter
6126
6127
// Kludge to work around strange IE behavior where it'll sometimes
6128
// re-fire a series of drag-related events right after the drop (#1551)
6129
var lastDrop = 0
6130
6131
function onDrop(e) {
6132
  var cm = this
6133
  clearDragCursor(cm)
6134
  if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))
6135
    { return }
6136
  e_preventDefault(e)
6137
  if (ie) { lastDrop = +new Date }
6138
  var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files
6139
  if (!pos || cm.isReadOnly()) { return }
6140
  // Might be a file drop, in which case we simply extract the text
6141
  // and insert it.
6142
  if (files && files.length && window.FileReader && window.File) {
6143
    var n = files.length, text = Array(n), read = 0
6144
    var loadFile = function (file, i) {
6145
      if (cm.options.allowDropFileTypes &&
6146
          indexOf(cm.options.allowDropFileTypes, file.type) == -1)
6147
        { return }
6148
6149
      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...
6150
      reader.onload = operation(cm, function () {
6151
        var content = reader.result
6152
        if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) { content = "" }
6153
        text[i] = content
6154
        if (++read == n) {
6155
          pos = clipPos(cm.doc, pos)
6156
          var change = {from: pos, to: pos,
6157
                        text: cm.doc.splitLines(text.join(cm.doc.lineSeparator())),
6158
                        origin: "paste"}
6159
          makeChange(cm.doc, change)
6160
          setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change)))
6161
        }
6162
      })
6163
      reader.readAsText(file)
6164
    }
6165
    for (var i = 0; i < n; ++i) { loadFile(files[i], i) }
6166
  } else { // Normal drop
6167
    // Don't do a replace if the drop happened inside of the selected text.
6168
    if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {
6169
      cm.state.draggingText(e)
6170
      // Ensure the editor is re-focused
6171
      setTimeout(function () { return cm.display.input.focus(); }, 20)
6172
      return
6173
    }
6174
    try {
6175
      var text$1 = e.dataTransfer.getData("Text")
6176
      if (text$1) {
6177
        var selected
6178
        if (cm.state.draggingText && !cm.state.draggingText.copy)
6179
          { selected = cm.listSelections() }
6180
        setSelectionNoUndo(cm.doc, simpleSelection(pos, pos))
6181
        if (selected) { for (var i$1 = 0; i$1 < selected.length; ++i$1)
6182
          { replaceRange(cm.doc, "", selected[i$1].anchor, selected[i$1].head, "drag") } }
6183
        cm.replaceSelection(text$1, "around", "paste")
6184
        cm.display.input.focus()
6185
      }
6186
    }
6187
    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...
6188
  }
6189
}
6190
6191
function onDragStart(cm, e) {
6192
  if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return }
6193
  if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return }
6194
6195
  e.dataTransfer.setData("Text", cm.getSelection())
6196
  e.dataTransfer.effectAllowed = "copyMove"
6197
6198
  // Use dummy image instead of default browsers image.
6199
  // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
6200
  if (e.dataTransfer.setDragImage && !safari) {
6201
    var img = elt("img", null, null, "position: fixed; left: 0; top: 0;")
6202
    img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
6203
    if (presto) {
6204
      img.width = img.height = 1
6205
      cm.display.wrapper.appendChild(img)
6206
      // Force a relayout, or Opera won't use our image for some obscure reason
6207
      img._top = img.offsetTop
6208
    }
6209
    e.dataTransfer.setDragImage(img, 0, 0)
6210
    if (presto) { img.parentNode.removeChild(img) }
6211
  }
6212
}
6213
6214
function onDragOver(cm, e) {
6215
  var pos = posFromMouse(cm, e)
6216
  if (!pos) { return }
6217
  var frag = document.createDocumentFragment()
6218
  drawSelectionCursor(cm, pos, frag)
6219
  if (!cm.display.dragCursor) {
6220
    cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors")
6221
    cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv)
6222
  }
6223
  removeChildrenAndAdd(cm.display.dragCursor, frag)
6224
}
6225
6226
function clearDragCursor(cm) {
6227
  if (cm.display.dragCursor) {
6228
    cm.display.lineSpace.removeChild(cm.display.dragCursor)
6229
    cm.display.dragCursor = null
6230
  }
6231
}
6232
6233
// These must be handled carefully, because naively registering a
6234
// handler for each editor will cause the editors to never be
6235
// garbage collected.
6236
6237
function forEachCodeMirror(f) {
6238
  if (!document.body.getElementsByClassName) { return }
6239
  var byClass = document.body.getElementsByClassName("CodeMirror")
6240
  for (var i = 0; i < byClass.length; i++) {
6241
    var cm = byClass[i].CodeMirror
6242
    if (cm) { f(cm) }
6243
  }
6244
}
6245
6246
var globalsRegistered = false
6247
function ensureGlobalHandlers() {
6248
  if (globalsRegistered) { return }
6249
  registerGlobalHandlers()
6250
  globalsRegistered = true
6251
}
6252
function registerGlobalHandlers() {
6253
  // When the window resizes, we need to refresh active editors.
6254
  var resizeTimer
6255
  on(window, "resize", function () {
6256
    if (resizeTimer == null) { resizeTimer = setTimeout(function () {
6257
      resizeTimer = null
6258
      forEachCodeMirror(onResize)
6259
    }, 100) }
6260
  })
6261
  // When the window loses focus, we want to show the editor as blurred
6262
  on(window, "blur", function () { return forEachCodeMirror(onBlur); })
6263
}
6264
// Called when the window resizes
6265
function onResize(cm) {
6266
  var d = cm.display
6267
  if (d.lastWrapHeight == d.wrapper.clientHeight && d.lastWrapWidth == d.wrapper.clientWidth)
6268
    { return }
6269
  // Might be a text scaling operation, clear size caches.
6270
  d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null
6271
  d.scrollbarsClipped = false
6272
  cm.setSize()
6273
}
6274
6275
var keyNames = {
6276
  3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
6277
  19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
6278
  36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
6279
  46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod",
6280
  106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 127: "Delete",
6281
  173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
6282
  221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete",
6283
  63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert"
6284
}
6285
6286
// Number keys
6287
for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i) }
6288
// Alphabetic keys
6289
for (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1) }
6290
// Function keys
6291
for (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235] = "F" + i$2 }
6292
6293
var keyMap = {}
6294
6295
keyMap.basic = {
6296
  "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
6297
  "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
6298
  "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore",
6299
  "Tab": "defaultTab", "Shift-Tab": "indentAuto",
6300
  "Enter": "newlineAndIndent", "Insert": "toggleOverwrite",
6301
  "Esc": "singleSelection"
6302
}
6303
// Note that the save and find-related commands aren't defined by
6304
// default. User code or addons can define them. Unknown commands
6305
// are simply ignored.
6306
keyMap.pcDefault = {
6307
  "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
6308
  "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown",
6309
  "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
6310
  "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
6311
  "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
6312
  "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
6313
  "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection",
6314
  fallthrough: "basic"
6315
}
6316
// Very basic readline/emacs-style bindings, which are standard on Mac.
6317
keyMap.emacsy = {
6318
  "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
6319
  "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
6320
  "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
6321
  "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars",
6322
  "Ctrl-O": "openLine"
6323
}
6324
keyMap.macDefault = {
6325
  "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
6326
  "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
6327
  "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore",
6328
  "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
6329
  "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
6330
  "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight",
6331
  "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd",
6332
  fallthrough: ["basic", "emacsy"]
6333
}
6334
keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault
6335
6336
// KEYMAP DISPATCH
6337
6338
function normalizeKeyName(name) {
6339
  var parts = name.split(/-(?!$)/)
6340
  name = parts[parts.length - 1]
6341
  var alt, ctrl, shift, cmd
6342
  for (var i = 0; i < parts.length - 1; i++) {
6343
    var mod = parts[i]
6344
    if (/^(cmd|meta|m)$/i.test(mod)) { cmd = true }
6345
    else if (/^a(lt)?$/i.test(mod)) { alt = true }
6346
    else if (/^(c|ctrl|control)$/i.test(mod)) { ctrl = true }
6347
    else if (/^s(hift)?$/i.test(mod)) { shift = true }
6348
    else { throw new Error("Unrecognized modifier name: " + mod) }
6349
  }
6350
  if (alt) { name = "Alt-" + name }
6351
  if (ctrl) { name = "Ctrl-" + name }
6352
  if (cmd) { name = "Cmd-" + name }
6353
  if (shift) { name = "Shift-" + name }
6354
  return name
6355
}
6356
6357
// This is a kludge to keep keymaps mostly working as raw objects
6358
// (backwards compatibility) while at the same time support features
6359
// like normalization and multi-stroke key bindings. It compiles a
6360
// new normalized keymap, and then updates the old object to reflect
6361
// this.
6362
function normalizeKeyMap(keymap) {
6363
  var copy = {}
6364
  for (var keyname in keymap) { if (keymap.hasOwnProperty(keyname)) {
6365
    var value = keymap[keyname]
6366
    if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) { continue }
6367
    if (value == "...") { delete keymap[keyname]; continue }
6368
6369
    var keys = map(keyname.split(" "), normalizeKeyName)
6370
    for (var i = 0; i < keys.length; i++) {
6371
      var val = (void 0), name = (void 0)
0 ignored issues
show
Unused Code introduced by
The assignment to variable val seems to be never used. Consider removing it.
Loading history...
Coding Style introduced by
Consider using undefined instead of void(0). It is equivalent and more straightforward to read.
Loading history...
Unused Code introduced by
The assignment to variable name seems to be never used. Consider removing it.
Loading history...
6372
      if (i == keys.length - 1) {
6373
        name = keys.join(" ")
6374
        val = value
6375
      } else {
6376
        name = keys.slice(0, i + 1).join(" ")
6377
        val = "..."
6378
      }
6379
      var prev = copy[name]
6380
      if (!prev) { copy[name] = val }
6381
      else if (prev != val) { throw new Error("Inconsistent bindings for " + name) }
6382
    }
6383
    delete keymap[keyname]
6384
  } }
6385
  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...
6386
  return keymap
6387
}
6388
6389
function lookupKey(key, map, handle, context) {
6390
  map = getKeyMap(map)
6391
  var found = map.call ? map.call(key, context) : map[key]
6392
  if (found === false) { return "nothing" }
6393
  if (found === "...") { return "multi" }
6394
  if (found != null && handle(found)) { return "handled" }
6395
6396
  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...
6397
    if (Object.prototype.toString.call(map.fallthrough) != "[object Array]")
6398
      { return lookupKey(key, map.fallthrough, handle, context) }
6399
    for (var i = 0; i < map.fallthrough.length; i++) {
6400
      var result = lookupKey(key, map.fallthrough[i], handle, context)
6401
      if (result) { return result }
6402
    }
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...
6403
  }
6404
}
6405
6406
// Modifier key presses don't count as 'real' key presses for the
6407
// purpose of keymap fallthrough.
6408
function isModifierKey(value) {
6409
  var name = typeof value == "string" ? value : keyNames[value.keyCode]
6410
  return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod"
6411
}
6412
6413
// Look up the name of a key as indicated by an event object.
6414
function keyName(event, noShift) {
6415
  if (presto && event.keyCode == 34 && event["char"]) { return false }
6416
  var base = keyNames[event.keyCode], name = base
6417
  if (name == null || event.altGraphKey) { return false }
6418
  if (event.altKey && base != "Alt") { name = "Alt-" + name }
6419
  if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") { name = "Ctrl-" + name }
6420
  if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") { name = "Cmd-" + name }
6421
  if (!noShift && event.shiftKey && base != "Shift") { name = "Shift-" + name }
6422
  return name
6423
}
6424
6425
function getKeyMap(val) {
6426
  return typeof val == "string" ? keyMap[val] : val
6427
}
6428
6429
// Helper for deleting text near the selection(s), used to implement
6430
// backspace, delete, and similar functionality.
6431
function deleteNearSelection(cm, compute) {
6432
  var ranges = cm.doc.sel.ranges, kill = []
6433
  // Build up a set of ranges to kill first, merging overlapping
6434
  // ranges.
6435
  for (var i = 0; i < ranges.length; i++) {
6436
    var toKill = compute(ranges[i])
6437
    while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {
6438
      var replaced = kill.pop()
6439
      if (cmp(replaced.from, toKill.from) < 0) {
6440
        toKill.from = replaced.from
6441
        break
6442
      }
6443
    }
6444
    kill.push(toKill)
6445
  }
6446
  // Next, remove those actual ranges.
6447
  runInOp(cm, function () {
6448
    for (var i = kill.length - 1; i >= 0; i--)
6449
      { replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete") }
6450
    ensureCursorVisible(cm)
6451
  })
6452
}
6453
6454
// Commands are parameter-less actions that can be performed on an
6455
// editor, mostly used for keybindings.
6456
var commands = {
6457
  selectAll: selectAll,
6458
  singleSelection: function (cm) { return cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll); },
6459
  killLine: function (cm) { return deleteNearSelection(cm, function (range) {
6460
    if (range.empty()) {
6461
      var len = getLine(cm.doc, range.head.line).text.length
6462
      if (range.head.ch == len && range.head.line < cm.lastLine())
6463
        { return {from: range.head, to: Pos(range.head.line + 1, 0)} }
6464
      else
6465
        { return {from: range.head, to: Pos(range.head.line, len)} }
6466
    } else {
6467
      return {from: range.from(), to: range.to()}
6468
    }
6469
  }); },
6470
  deleteLine: function (cm) { return deleteNearSelection(cm, function (range) { return ({
6471
    from: Pos(range.from().line, 0),
6472
    to: clipPos(cm.doc, Pos(range.to().line + 1, 0))
6473
  }); }); },
6474
  delLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { return ({
6475
    from: Pos(range.from().line, 0), to: range.from()
6476
  }); }); },
6477
  delWrappedLineLeft: function (cm) { return deleteNearSelection(cm, function (range) {
6478
    var top = cm.charCoords(range.head, "div").top + 5
6479
    var leftPos = cm.coordsChar({left: 0, top: top}, "div")
6480
    return {from: leftPos, to: range.from()}
6481
  }); },
6482
  delWrappedLineRight: function (cm) { return deleteNearSelection(cm, function (range) {
6483
    var top = cm.charCoords(range.head, "div").top + 5
6484
    var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div")
6485
    return {from: range.from(), to: rightPos }
6486
  }); },
6487
  undo: function (cm) { return cm.undo(); },
6488
  redo: function (cm) { return cm.redo(); },
6489
  undoSelection: function (cm) { return cm.undoSelection(); },
6490
  redoSelection: function (cm) { return cm.redoSelection(); },
6491
  goDocStart: function (cm) { return cm.extendSelection(Pos(cm.firstLine(), 0)); },
6492
  goDocEnd: function (cm) { return cm.extendSelection(Pos(cm.lastLine())); },
6493
  goLineStart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStart(cm, range.head.line); },
6494
    {origin: "+move", bias: 1}
6495
  ); },
6496
  goLineStartSmart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStartSmart(cm, range.head); },
6497
    {origin: "+move", bias: 1}
6498
  ); },
6499
  goLineEnd: function (cm) { return cm.extendSelectionsBy(function (range) { return lineEnd(cm, range.head.line); },
6500
    {origin: "+move", bias: -1}
6501
  ); },
6502
  goLineRight: function (cm) { return cm.extendSelectionsBy(function (range) {
6503
    var top = cm.charCoords(range.head, "div").top + 5
6504
    return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div")
6505
  }, sel_move); },
6506
  goLineLeft: function (cm) { return cm.extendSelectionsBy(function (range) {
6507
    var top = cm.charCoords(range.head, "div").top + 5
6508
    return cm.coordsChar({left: 0, top: top}, "div")
6509
  }, sel_move); },
6510
  goLineLeftSmart: function (cm) { return cm.extendSelectionsBy(function (range) {
6511
    var top = cm.charCoords(range.head, "div").top + 5
6512
    var pos = cm.coordsChar({left: 0, top: top}, "div")
6513
    if (pos.ch < cm.getLine(pos.line).search(/\S/)) { return lineStartSmart(cm, range.head) }
6514
    return pos
6515
  }, sel_move); },
6516
  goLineUp: function (cm) { return cm.moveV(-1, "line"); },
6517
  goLineDown: function (cm) { return cm.moveV(1, "line"); },
6518
  goPageUp: function (cm) { return cm.moveV(-1, "page"); },
6519
  goPageDown: function (cm) { return cm.moveV(1, "page"); },
6520
  goCharLeft: function (cm) { return cm.moveH(-1, "char"); },
6521
  goCharRight: function (cm) { return cm.moveH(1, "char"); },
6522
  goColumnLeft: function (cm) { return cm.moveH(-1, "column"); },
6523
  goColumnRight: function (cm) { return cm.moveH(1, "column"); },
6524
  goWordLeft: function (cm) { return cm.moveH(-1, "word"); },
6525
  goGroupRight: function (cm) { return cm.moveH(1, "group"); },
6526
  goGroupLeft: function (cm) { return cm.moveH(-1, "group"); },
6527
  goWordRight: function (cm) { return cm.moveH(1, "word"); },
6528
  delCharBefore: function (cm) { return cm.deleteH(-1, "char"); },
6529
  delCharAfter: function (cm) { return cm.deleteH(1, "char"); },
6530
  delWordBefore: function (cm) { return cm.deleteH(-1, "word"); },
6531
  delWordAfter: function (cm) { return cm.deleteH(1, "word"); },
6532
  delGroupBefore: function (cm) { return cm.deleteH(-1, "group"); },
6533
  delGroupAfter: function (cm) { return cm.deleteH(1, "group"); },
6534
  indentAuto: function (cm) { return cm.indentSelection("smart"); },
6535
  indentMore: function (cm) { return cm.indentSelection("add"); },
6536
  indentLess: function (cm) { return cm.indentSelection("subtract"); },
6537
  insertTab: function (cm) { return cm.replaceSelection("\t"); },
6538
  insertSoftTab: function (cm) {
6539
    var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize
6540
    for (var i = 0; i < ranges.length; i++) {
6541
      var pos = ranges[i].from()
6542
      var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize)
6543
      spaces.push(spaceStr(tabSize - col % tabSize))
6544
    }
6545
    cm.replaceSelections(spaces)
6546
  },
6547
  defaultTab: function (cm) {
6548
    if (cm.somethingSelected()) { cm.indentSelection("add") }
6549
    else { cm.execCommand("insertTab") }
6550
  },
6551
  // Swap the two chars left and right of each selection's head.
6552
  // Move cursor behind the two swapped characters afterwards.
6553
  //
6554
  // Doesn't consider line feeds a character.
6555
  // Doesn't scan more than one line above to find a character.
6556
  // Doesn't do anything on an empty line.
6557
  // Doesn't do anything with non-empty selections.
6558
  transposeChars: function (cm) { return runInOp(cm, function () {
6559
    var ranges = cm.listSelections(), newSel = []
6560
    for (var i = 0; i < ranges.length; i++) {
6561
      if (!ranges[i].empty()) { continue }
6562
      var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text
6563
      if (line) {
6564
        if (cur.ch == line.length) { cur = new Pos(cur.line, cur.ch - 1) }
6565
        if (cur.ch > 0) {
6566
          cur = new Pos(cur.line, cur.ch + 1)
6567
          cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),
6568
                          Pos(cur.line, cur.ch - 2), cur, "+transpose")
6569
        } else if (cur.line > cm.doc.first) {
6570
          var prev = getLine(cm.doc, cur.line - 1).text
6571
          if (prev) {
6572
            cur = new Pos(cur.line, 1)
6573
            cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() +
6574
                            prev.charAt(prev.length - 1),
6575
                            Pos(cur.line - 1, prev.length - 1), cur, "+transpose")
6576
          }
6577
        }
6578
      }
6579
      newSel.push(new Range(cur, cur))
6580
    }
6581
    cm.setSelections(newSel)
6582
  }); },
6583
  newlineAndIndent: function (cm) { return runInOp(cm, function () {
6584
    var sels = cm.listSelections()
6585
    for (var i = sels.length - 1; i >= 0; i--)
6586
      { cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input") }
6587
    sels = cm.listSelections()
6588
    for (var i$1 = 0; i$1 < sels.length; i$1++)
6589
      { cm.indentLine(sels[i$1].from().line, null, true) }
6590
    ensureCursorVisible(cm)
6591
  }); },
6592
  openLine: function (cm) { return cm.replaceSelection("\n", "start"); },
6593
  toggleOverwrite: function (cm) { return cm.toggleOverwrite(); }
6594
}
6595
6596
6597
function lineStart(cm, lineN) {
6598
  var line = getLine(cm.doc, lineN)
6599
  var visual = visualLine(line)
6600
  if (visual != line) { lineN = lineNo(visual) }
6601
  var order = getOrder(visual)
6602
  var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual)
6603
  return Pos(lineN, ch)
6604
}
6605
function lineEnd(cm, lineN) {
6606
  var merged, line = getLine(cm.doc, lineN)
6607
  while (merged = collapsedSpanAtEnd(line)) {
6608
    line = merged.find(1, true).line
6609
    lineN = null
6610
  }
6611
  var order = getOrder(line)
6612
  var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line)
6613
  return Pos(lineN == null ? lineNo(line) : lineN, ch)
6614
}
6615
function lineStartSmart(cm, pos) {
6616
  var start = lineStart(cm, pos.line)
6617
  var line = getLine(cm.doc, start.line)
6618
  var order = getOrder(line)
6619
  if (!order || order[0].level == 0) {
6620
    var firstNonWS = Math.max(0, line.text.search(/\S/))
6621
    var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch
6622
    return Pos(start.line, inWS ? 0 : firstNonWS)
6623
  }
6624
  return start
6625
}
6626
6627
// Run a handler that was bound to a key.
6628
function doHandleBinding(cm, bound, dropShift) {
6629
  if (typeof bound == "string") {
6630
    bound = commands[bound]
6631
    if (!bound) { return false }
6632
  }
6633
  // Ensure previous input has been read, so that the handler sees a
6634
  // consistent view of the document
6635
  cm.display.input.ensurePolled()
6636
  var prevShift = cm.display.shift, done = false
6637
  try {
6638
    if (cm.isReadOnly()) { cm.state.suppressEdits = true }
6639
    if (dropShift) { cm.display.shift = false }
6640
    done = bound(cm) != Pass
6641
  } finally {
6642
    cm.display.shift = prevShift
6643
    cm.state.suppressEdits = false
6644
  }
6645
  return done
6646
}
6647
6648
function lookupKeyForEditor(cm, name, handle) {
6649
  for (var i = 0; i < cm.state.keyMaps.length; i++) {
6650
    var result = lookupKey(name, cm.state.keyMaps[i], handle, cm)
6651
    if (result) { return result }
6652
  }
6653
  return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm))
6654
    || lookupKey(name, cm.options.keyMap, handle, cm)
6655
}
6656
6657
var stopSeq = new Delayed
6658
function dispatchKey(cm, name, e, handle) {
6659
  var seq = cm.state.keySeq
6660
  if (seq) {
6661
    if (isModifierKey(name)) { return "handled" }
6662
    stopSeq.set(50, function () {
6663
      if (cm.state.keySeq == seq) {
6664
        cm.state.keySeq = null
6665
        cm.display.input.reset()
6666
      }
6667
    })
6668
    name = seq + " " + name
6669
  }
6670
  var result = lookupKeyForEditor(cm, name, handle)
6671
6672
  if (result == "multi")
6673
    { cm.state.keySeq = name }
6674
  if (result == "handled")
6675
    { signalLater(cm, "keyHandled", cm, name, e) }
6676
6677
  if (result == "handled" || result == "multi") {
6678
    e_preventDefault(e)
6679
    restartBlink(cm)
6680
  }
6681
6682
  if (seq && !result && /\'$/.test(name)) {
6683
    e_preventDefault(e)
6684
    return true
6685
  }
6686
  return !!result
6687
}
6688
6689
// Handle a key from the keydown event.
6690
function handleKeyBinding(cm, e) {
6691
  var name = keyName(e, true)
6692
  if (!name) { return false }
6693
6694
  if (e.shiftKey && !cm.state.keySeq) {
6695
    // First try to resolve full name (including 'Shift-'). Failing
6696
    // that, see if there is a cursor-motion command (starting with
6697
    // 'go') bound to the keyname without 'Shift-'.
6698
    return dispatchKey(cm, "Shift-" + name, e, function (b) { return doHandleBinding(cm, b, true); })
6699
        || dispatchKey(cm, name, e, function (b) {
6700
             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...
6701
               { return doHandleBinding(cm, b) }
6702
           })
6703
  } else {
6704
    return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b); })
6705
  }
6706
}
6707
6708
// Handle a key from the keypress event
6709
function handleCharBinding(cm, e, ch) {
6710
  return dispatchKey(cm, "'" + ch + "'", e, function (b) { return doHandleBinding(cm, b, true); })
6711
}
6712
6713
var lastStoppedKey = null
6714
function onKeyDown(e) {
6715
  var cm = this
6716
  cm.curOp.focus = activeElt()
6717
  if (signalDOMEvent(cm, e)) { return }
6718
  // IE does strange things with escape.
6719
  if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false }
6720
  var code = e.keyCode
6721
  cm.display.shift = code == 16 || e.shiftKey
6722
  var handled = handleKeyBinding(cm, e)
6723
  if (presto) {
6724
    lastStoppedKey = handled ? code : null
6725
    // Opera has no cut event... we try to at least catch the key combo
6726
    if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
6727
      { cm.replaceSelection("", null, "cut") }
6728
  }
6729
6730
  // Turn mouse into crosshair when Alt is held on Mac.
6731
  if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className))
6732
    { showCrossHair(cm) }
6733
}
6734
6735
function showCrossHair(cm) {
6736
  var lineDiv = cm.display.lineDiv
6737
  addClass(lineDiv, "CodeMirror-crosshair")
6738
6739
  function up(e) {
6740
    if (e.keyCode == 18 || !e.altKey) {
6741
      rmClass(lineDiv, "CodeMirror-crosshair")
6742
      off(document, "keyup", up)
6743
      off(document, "mouseover", up)
6744
    }
6745
  }
6746
  on(document, "keyup", up)
6747
  on(document, "mouseover", up)
6748
}
6749
6750
function onKeyUp(e) {
6751
  if (e.keyCode == 16) { this.doc.sel.shift = false }
6752
  signalDOMEvent(this, e)
6753
}
6754
6755
function onKeyPress(e) {
6756
  var cm = this
6757
  if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return }
6758
  var keyCode = e.keyCode, charCode = e.charCode
6759
  if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return}
6760
  if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) { return }
6761
  var ch = String.fromCharCode(charCode == null ? keyCode : charCode)
6762
  // Some browsers fire keypress events for backspace
6763
  if (ch == "\x08") { return }
6764
  if (handleCharBinding(cm, e, ch)) { return }
6765
  cm.display.input.onKeyPress(e)
6766
}
6767
6768
// A mouse down can be a single click, double click, triple click,
6769
// start of selection drag, start of text drag, new cursor
6770
// (ctrl-click), rectangle drag (alt-drag), or xwin
6771
// middle-click-paste. Or it might be a click on something we should
6772
// not interfere with, such as a scrollbar or widget.
6773
function onMouseDown(e) {
6774
  var cm = this, display = cm.display
6775
  if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) { return }
6776
  display.input.ensurePolled()
6777
  display.shift = e.shiftKey
6778
6779
  if (eventInWidget(display, e)) {
6780
    if (!webkit) {
6781
      // Briefly turn off draggability, to allow widgets to do
6782
      // normal dragging things.
6783
      display.scroller.draggable = false
6784
      setTimeout(function () { return display.scroller.draggable = true; }, 100)
6785
    }
6786
    return
6787
  }
6788
  if (clickInGutter(cm, e)) { return }
6789
  var start = posFromMouse(cm, e)
6790
  window.focus()
6791
6792
  switch (e_button(e)) {
0 ignored issues
show
Coding Style introduced by
As per coding-style, switch statements should have a default case.
Loading history...
6793
  case 1:
6794
    // #3261: make sure, that we're not starting a second selection
6795
    if (cm.state.selectingText)
6796
      { cm.state.selectingText(e) }
6797
    else if (start)
6798
      { leftButtonDown(cm, e, start) }
6799
    else if (e_target(e) == display.scroller)
6800
      { e_preventDefault(e) }
6801
    break
6802
  case 2:
6803
    if (webkit) { cm.state.lastMiddleDown = +new Date }
6804
    if (start) { extendSelection(cm.doc, start) }
6805
    setTimeout(function () { return display.input.focus(); }, 20)
6806
    e_preventDefault(e)
6807
    break
6808
  case 3:
6809
    if (captureRightClick) { onContextMenu(cm, e) }
6810
    else { delayBlurEvent(cm) }
6811
    break
6812
  }
6813
}
6814
6815
var lastClick;
6816
var lastDoubleClick;
6817
function leftButtonDown(cm, e, start) {
6818
  if (ie) { setTimeout(bind(ensureFocus, cm), 0) }
6819
  else { cm.curOp.focus = activeElt() }
6820
6821
  var now = +new Date, type
6822
  if (lastDoubleClick && lastDoubleClick.time > now - 400 && cmp(lastDoubleClick.pos, start) == 0) {
6823
    type = "triple"
6824
  } else if (lastClick && lastClick.time > now - 400 && cmp(lastClick.pos, start) == 0) {
6825
    type = "double"
6826
    lastDoubleClick = {time: now, pos: start}
6827
  } else {
6828
    type = "single"
6829
    lastClick = {time: now, pos: start}
6830
  }
6831
6832
  var sel = cm.doc.sel, modifier = mac ? e.metaKey : e.ctrlKey, contained
6833
  if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() &&
6834
      type == "single" && (contained = sel.contains(start)) > -1 &&
6835
      (cmp((contained = sel.ranges[contained]).from(), start) < 0 || start.xRel > 0) &&
6836
      (cmp(contained.to(), start) > 0 || start.xRel < 0))
6837
    { leftButtonStartDrag(cm, e, start, modifier) }
6838
  else
6839
    { leftButtonSelect(cm, e, start, type, modifier) }
6840
}
6841
6842
// Start a text drag. When it ends, see if any dragging actually
6843
// happen, and treat as a click if it didn't.
6844
function leftButtonStartDrag(cm, e, start, modifier) {
6845
  var display = cm.display, startTime = +new Date
6846
  var dragEnd = operation(cm, function (e2) {
6847
    if (webkit) { display.scroller.draggable = false }
6848
    cm.state.draggingText = false
6849
    off(document, "mouseup", dragEnd)
6850
    off(display.scroller, "drop", dragEnd)
6851
    if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
6852
      e_preventDefault(e2)
6853
      if (!modifier && +new Date - 200 < startTime)
6854
        { extendSelection(cm.doc, start) }
6855
      // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)
6856
      if (webkit || ie && ie_version == 9)
6857
        { setTimeout(function () {document.body.focus(); display.input.focus()}, 20) }
6858
      else
6859
        { display.input.focus() }
6860
    }
6861
  })
6862
  // Let the drag handler handle this.
6863
  if (webkit) { display.scroller.draggable = true }
6864
  cm.state.draggingText = dragEnd
6865
  dragEnd.copy = mac ? e.altKey : e.ctrlKey
6866
  // IE's approach to draggable
6867
  if (display.scroller.dragDrop) { display.scroller.dragDrop() }
6868
  on(document, "mouseup", dragEnd)
6869
  on(display.scroller, "drop", dragEnd)
6870
}
6871
6872
// Normal selection, as opposed to text dragging.
6873
function leftButtonSelect(cm, e, start, type, addNew) {
6874
  var display = cm.display, doc = cm.doc
6875
  e_preventDefault(e)
6876
6877
  var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges
6878
  if (addNew && !e.shiftKey) {
6879
    ourIndex = doc.sel.contains(start)
6880
    if (ourIndex > -1)
6881
      { ourRange = ranges[ourIndex] }
6882
    else
6883
      { ourRange = new Range(start, start) }
6884
  } else {
6885
    ourRange = doc.sel.primary()
6886
    ourIndex = doc.sel.primIndex
6887
  }
6888
6889
  if (chromeOS ? e.shiftKey && e.metaKey : e.altKey) {
6890
    type = "rect"
6891
    if (!addNew) { ourRange = new Range(start, start) }
6892
    start = posFromMouse(cm, e, true, true)
6893
    ourIndex = -1
6894
  } else if (type == "double") {
6895
    var word = cm.findWordAt(start)
6896
    if (cm.display.shift || doc.extend)
6897
      { ourRange = extendRange(doc, ourRange, word.anchor, word.head) }
6898
    else
6899
      { ourRange = word }
6900
  } else if (type == "triple") {
6901
    var line = new Range(Pos(start.line, 0), clipPos(doc, Pos(start.line + 1, 0)))
6902
    if (cm.display.shift || doc.extend)
6903
      { ourRange = extendRange(doc, ourRange, line.anchor, line.head) }
6904
    else
6905
      { ourRange = line }
6906
  } else {
6907
    ourRange = extendRange(doc, ourRange, start)
6908
  }
6909
6910
  if (!addNew) {
6911
    ourIndex = 0
6912
    setSelection(doc, new Selection([ourRange], 0), sel_mouse)
6913
    startSel = doc.sel
6914
  } else if (ourIndex == -1) {
6915
    ourIndex = ranges.length
6916
    setSelection(doc, normalizeSelection(ranges.concat([ourRange]), ourIndex),
6917
                 {scroll: false, origin: "*mouse"})
6918
  } else if (ranges.length > 1 && ranges[ourIndex].empty() && type == "single" && !e.shiftKey) {
6919
    setSelection(doc, normalizeSelection(ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0),
6920
                 {scroll: false, origin: "*mouse"})
6921
    startSel = doc.sel
6922
  } else {
6923
    replaceOneSelection(doc, ourIndex, ourRange, sel_mouse)
6924
  }
6925
6926
  var lastPos = start
6927
  function extendTo(pos) {
6928
    if (cmp(lastPos, pos) == 0) { return }
6929
    lastPos = pos
6930
6931
    if (type == "rect") {
6932
      var ranges = [], tabSize = cm.options.tabSize
6933
      var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize)
6934
      var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize)
6935
      var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol)
6936
      for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line));
6937
           line <= end; line++) {
6938
        var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize)
6939
        if (left == right)
6940
          { ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))) }
6941
        else if (text.length > leftPos)
6942
          { ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))) }
6943
      }
6944
      if (!ranges.length) { ranges.push(new Range(start, start)) }
6945
      setSelection(doc, normalizeSelection(startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex),
6946
                   {origin: "*mouse", scroll: false})
6947
      cm.scrollIntoView(pos)
6948
    } else {
6949
      var oldRange = ourRange
6950
      var anchor = oldRange.anchor, head = pos
6951
      if (type != "single") {
6952
        var range
6953
        if (type == "double")
6954
          { range = cm.findWordAt(pos) }
6955
        else
6956
          { range = new Range(Pos(pos.line, 0), clipPos(doc, Pos(pos.line + 1, 0))) }
6957
        if (cmp(range.anchor, anchor) > 0) {
6958
          head = range.head
6959
          anchor = minPos(oldRange.from(), range.anchor)
6960
        } else {
6961
          head = range.anchor
6962
          anchor = maxPos(oldRange.to(), range.head)
6963
        }
6964
      }
6965
      var ranges$1 = startSel.ranges.slice(0)
6966
      ranges$1[ourIndex] = new Range(clipPos(doc, anchor), head)
6967
      setSelection(doc, normalizeSelection(ranges$1, ourIndex), sel_mouse)
6968
    }
6969
  }
6970
6971
  var editorSize = display.wrapper.getBoundingClientRect()
6972
  // Used to ensure timeout re-tries don't fire when another extend
6973
  // happened in the meantime (clearTimeout isn't reliable -- at
6974
  // least on Chrome, the timeouts still happen even when cleared,
6975
  // if the clear happens after their scheduled firing time).
6976
  var counter = 0
6977
6978
  function extend(e) {
6979
    var curCount = ++counter
6980
    var cur = posFromMouse(cm, e, true, type == "rect")
6981
    if (!cur) { return }
6982
    if (cmp(cur, lastPos) != 0) {
6983
      cm.curOp.focus = activeElt()
6984
      extendTo(cur)
6985
      var visible = visibleLines(display, doc)
6986
      if (cur.line >= visible.to || cur.line < visible.from)
6987
        { setTimeout(operation(cm, function () {if (counter == curCount) { extend(e) }}), 150) }
6988
    } else {
6989
      var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0
6990
      if (outside) { setTimeout(operation(cm, function () {
6991
        if (counter != curCount) { return }
6992
        display.scroller.scrollTop += outside
6993
        extend(e)
6994
      }), 50) }
6995
    }
6996
  }
6997
6998
  function done(e) {
6999
    cm.state.selectingText = false
7000
    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...
7001
    e_preventDefault(e)
7002
    display.input.focus()
7003
    off(document, "mousemove", move)
7004
    off(document, "mouseup", up)
7005
    doc.history.lastSelOrigin = null
7006
  }
7007
7008
  var move = operation(cm, function (e) {
7009
    if (!e_button(e)) { done(e) }
7010
    else { extend(e) }
7011
  })
7012
  var up = operation(cm, done)
7013
  cm.state.selectingText = up
7014
  on(document, "mousemove", move)
7015
  on(document, "mouseup", up)
7016
}
7017
7018
7019
// Determines whether an event happened in the gutter, and fires the
7020
// handlers for the corresponding event.
7021
function gutterEvent(cm, e, type, prevent) {
7022
  var mX, mY
7023
  try { mX = e.clientX; mY = e.clientY }
7024
  catch(e) { return false }
7025
  if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false }
7026
  if (prevent) { e_preventDefault(e) }
7027
7028
  var display = cm.display
7029
  var lineBox = display.lineDiv.getBoundingClientRect()
7030
7031
  if (mY > lineBox.bottom || !hasHandler(cm, type)) { return e_defaultPrevented(e) }
7032
  mY -= lineBox.top - display.viewOffset
7033
7034
  for (var i = 0; i < cm.options.gutters.length; ++i) {
7035
    var g = display.gutters.childNodes[i]
7036
    if (g && g.getBoundingClientRect().right >= mX) {
7037
      var line = lineAtHeight(cm.doc, mY)
7038
      var gutter = cm.options.gutters[i]
7039
      signal(cm, type, cm, line, gutter, e)
7040
      return e_defaultPrevented(e)
7041
    }
7042
  }
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...
7043
}
7044
7045
function clickInGutter(cm, e) {
7046
  return gutterEvent(cm, e, "gutterClick", true)
7047
}
7048
7049
// CONTEXT MENU HANDLING
7050
7051
// To make the context menu work, we need to briefly unhide the
7052
// textarea (making it as unobtrusive as possible) to let the
7053
// right-click take effect on it.
7054
function onContextMenu(cm, e) {
7055
  if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) { return }
7056
  if (signalDOMEvent(cm, e, "contextmenu")) { return }
7057
  cm.display.input.onContextMenu(e)
7058
}
7059
7060
function contextMenuInGutter(cm, e) {
7061
  if (!hasHandler(cm, "gutterContextMenu")) { return false }
7062
  return gutterEvent(cm, e, "gutterContextMenu", false)
7063
}
7064
7065
function themeChanged(cm) {
7066
  cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
7067
    cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-")
7068
  clearCaches(cm)
7069
}
7070
7071
var Init = {toString: function(){return "CodeMirror.Init"}}
7072
7073
var defaults = {}
7074
var optionHandlers = {}
7075
7076
function defineOptions(CodeMirror) {
7077
  var optionHandlers = CodeMirror.optionHandlers
7078
7079
  function option(name, deflt, handle, notOnInit) {
7080
    CodeMirror.defaults[name] = deflt
7081
    if (handle) { optionHandlers[name] =
7082
      notOnInit ? function (cm, val, old) {if (old != Init) { handle(cm, val, old) }} : handle }
7083
  }
7084
7085
  CodeMirror.defineOption = option
7086
7087
  // Passed to option handlers when there is no old value.
7088
  CodeMirror.Init = Init
7089
7090
  // These two are, on init, called from the constructor because they
7091
  // have to be initialized before the editor can start at all.
7092
  option("value", "", function (cm, val) { return cm.setValue(val); }, true)
7093
  option("mode", null, function (cm, val) {
7094
    cm.doc.modeOption = val
7095
    loadMode(cm)
7096
  }, true)
7097
7098
  option("indentUnit", 2, loadMode, true)
7099
  option("indentWithTabs", false)
7100
  option("smartIndent", true)
7101
  option("tabSize", 4, function (cm) {
7102
    resetModeState(cm)
7103
    clearCaches(cm)
7104
    regChange(cm)
7105
  }, true)
7106
  option("lineSeparator", null, function (cm, val) {
7107
    cm.doc.lineSep = val
7108
    if (!val) { return }
7109
    var newBreaks = [], lineNo = cm.doc.first
7110
    cm.doc.iter(function (line) {
7111
      for (var pos = 0;;) {
7112
        var found = line.text.indexOf(val, pos)
7113
        if (found == -1) { break }
7114
        pos = found + val.length
7115
        newBreaks.push(Pos(lineNo, found))
7116
      }
7117
      lineNo++
7118
    })
7119
    for (var i = newBreaks.length - 1; i >= 0; i--)
7120
      { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)) }
7121
  })
7122
  option("specialChars", /[\u0000-\u001f\u007f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff]/g, function (cm, val, old) {
7123
    cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g")
7124
    if (old != Init) { cm.refresh() }
7125
  })
7126
  option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function (cm) { return cm.refresh(); }, true)
7127
  option("electricChars", true)
7128
  option("inputStyle", mobile ? "contenteditable" : "textarea", function () {
7129
    throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME
7130
  }, true)
7131
  option("spellcheck", false, function (cm, val) { return cm.getInputField().spellcheck = val; }, true)
7132
  option("rtlMoveVisually", !windows)
7133
  option("wholeLineUpdateBefore", true)
7134
7135
  option("theme", "default", function (cm) {
7136
    themeChanged(cm)
7137
    guttersChanged(cm)
7138
  }, true)
7139
  option("keyMap", "default", function (cm, val, old) {
7140
    var next = getKeyMap(val)
7141
    var prev = old != Init && getKeyMap(old)
7142
    if (prev && prev.detach) { prev.detach(cm, next) }
7143
    if (next.attach) { next.attach(cm, prev || null) }
7144
  })
7145
  option("extraKeys", null)
7146
7147
  option("lineWrapping", false, wrappingChanged, true)
7148
  option("gutters", [], function (cm) {
7149
    setGuttersForLineNumbers(cm.options)
7150
    guttersChanged(cm)
7151
  }, true)
7152
  option("fixedGutter", true, function (cm, val) {
7153
    cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0"
7154
    cm.refresh()
7155
  }, true)
7156
  option("coverGutterNextToScrollbar", false, function (cm) { return updateScrollbars(cm); }, true)
7157
  option("scrollbarStyle", "native", function (cm) {
7158
    initScrollbars(cm)
7159
    updateScrollbars(cm)
7160
    cm.display.scrollbars.setScrollTop(cm.doc.scrollTop)
7161
    cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft)
7162
  }, true)
7163
  option("lineNumbers", false, function (cm) {
7164
    setGuttersForLineNumbers(cm.options)
7165
    guttersChanged(cm)
7166
  }, true)
7167
  option("firstLineNumber", 1, guttersChanged, true)
7168
  option("lineNumberFormatter", function (integer) { return integer; }, guttersChanged, true)
7169
  option("showCursorWhenSelecting", false, updateSelection, true)
7170
7171
  option("resetSelectionOnContextMenu", true)
7172
  option("lineWiseCopyCut", true)
7173
7174
  option("readOnly", false, function (cm, val) {
7175
    if (val == "nocursor") {
7176
      onBlur(cm)
7177
      cm.display.input.blur()
7178
      cm.display.disabled = true
7179
    } else {
7180
      cm.display.disabled = false
7181
    }
7182
    cm.display.input.readOnlyChanged(val)
7183
  })
7184
  option("disableInput", false, function (cm, val) {if (!val) { cm.display.input.reset() }}, true)
7185
  option("dragDrop", true, dragDropChanged)
7186
  option("allowDropFileTypes", null)
7187
7188
  option("cursorBlinkRate", 530)
7189
  option("cursorScrollMargin", 0)
7190
  option("cursorHeight", 1, updateSelection, true)
7191
  option("singleCursorHeightPerLine", true, updateSelection, true)
7192
  option("workTime", 100)
7193
  option("workDelay", 100)
7194
  option("flattenSpans", true, resetModeState, true)
7195
  option("addModeClass", false, resetModeState, true)
7196
  option("pollInterval", 100)
7197
  option("undoDepth", 200, function (cm, val) { return cm.doc.history.undoDepth = val; })
7198
  option("historyEventDelay", 1250)
7199
  option("viewportMargin", 10, function (cm) { return cm.refresh(); }, true)
7200
  option("maxHighlightLength", 10000, resetModeState, true)
7201
  option("moveInputWithCursor", true, function (cm, val) {
7202
    if (!val) { cm.display.input.resetPosition() }
7203
  })
7204
7205
  option("tabindex", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || ""; })
7206
  option("autofocus", null)
7207
}
7208
7209
function guttersChanged(cm) {
7210
  updateGutters(cm)
7211
  regChange(cm)
7212
  alignHorizontally(cm)
7213
}
7214
7215
function dragDropChanged(cm, value, old) {
7216
  var wasOn = old && old != Init
7217
  if (!value != !wasOn) {
7218
    var funcs = cm.display.dragFunctions
7219
    var toggle = value ? on : off
7220
    toggle(cm.display.scroller, "dragstart", funcs.start)
7221
    toggle(cm.display.scroller, "dragenter", funcs.enter)
7222
    toggle(cm.display.scroller, "dragover", funcs.over)
7223
    toggle(cm.display.scroller, "dragleave", funcs.leave)
7224
    toggle(cm.display.scroller, "drop", funcs.drop)
7225
  }
7226
}
7227
7228
function wrappingChanged(cm) {
7229
  if (cm.options.lineWrapping) {
7230
    addClass(cm.display.wrapper, "CodeMirror-wrap")
7231
    cm.display.sizer.style.minWidth = ""
7232
    cm.display.sizerWidth = null
7233
  } else {
7234
    rmClass(cm.display.wrapper, "CodeMirror-wrap")
7235
    findMaxLine(cm)
7236
  }
7237
  estimateLineHeights(cm)
7238
  regChange(cm)
7239
  clearCaches(cm)
7240
  setTimeout(function () { return updateScrollbars(cm); }, 100)
7241
}
7242
7243
// A CodeMirror instance represents an editor. This is the object
7244
// that user code is usually dealing with.
7245
7246
function CodeMirror(place, options) {
7247
  var this$1 = this;
7248
7249
  if (!(this instanceof CodeMirror)) { return new CodeMirror(place, options) }
7250
7251
  this.options = options = options ? copyObj(options) : {}
7252
  // Determine effective options based on given values and defaults.
7253
  copyObj(defaults, options, false)
7254
  setGuttersForLineNumbers(options)
7255
7256
  var doc = options.value
7257
  if (typeof doc == "string") { doc = new Doc(doc, options.mode, null, options.lineSeparator) }
7258
  this.doc = doc
7259
7260
  var input = new CodeMirror.inputStyles[options.inputStyle](this)
7261
  var display = this.display = new Display(place, doc, input)
7262
  display.wrapper.CodeMirror = this
7263
  updateGutters(this)
7264
  themeChanged(this)
7265
  if (options.lineWrapping)
7266
    { this.display.wrapper.className += " CodeMirror-wrap" }
7267
  initScrollbars(this)
7268
7269
  this.state = {
7270
    keyMaps: [],  // stores maps added by addKeyMap
7271
    overlays: [], // highlighting overlays, as added by addOverlay
7272
    modeGen: 0,   // bumped when mode/overlay changes, used to invalidate highlighting info
7273
    overwrite: false,
7274
    delayingBlurEvent: false,
7275
    focused: false,
7276
    suppressEdits: false, // used to disable editing during key handlers when in readOnly mode
7277
    pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edits in input.poll
7278
    selectingText: false,
7279
    draggingText: false,
7280
    highlight: new Delayed(), // stores highlight worker timeout
7281
    keySeq: null,  // Unfinished key sequence
7282
    specialChars: null
7283
  }
7284
7285
  if (options.autofocus && !mobile) { display.input.focus() }
7286
7287
  // Override magic textarea content restore that IE sometimes does
7288
  // on our hidden textarea on reload
7289
  if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20) }
7290
7291
  registerEventHandlers(this)
7292
  ensureGlobalHandlers()
7293
7294
  startOperation(this)
7295
  this.curOp.forceUpdate = true
7296
  attachDoc(this, doc)
7297
7298
  if ((options.autofocus && !mobile) || this.hasFocus())
7299
    { setTimeout(bind(onFocus, this), 20) }
7300
  else
7301
    { onBlur(this) }
7302
7303
  for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt))
7304
    { optionHandlers[opt](this$1, options[opt], Init) } }
7305
  maybeUpdateLineNumberWidth(this)
7306
  if (options.finishInit) { options.finishInit(this) }
7307
  for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this$1) }
7308
  endOperation(this)
7309
  // Suppress optimizelegibility in Webkit, since it breaks text
7310
  // measuring on line wrapping boundaries.
7311
  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...
7312
      getComputedStyle(display.lineDiv).textRendering == "optimizelegibility")
7313
    { 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...
7314
}
7315
7316
// The default configuration options.
7317
CodeMirror.defaults = defaults
7318
// Functions to run when options are changed.
7319
CodeMirror.optionHandlers = optionHandlers
7320
7321
// Attach the necessary event handlers when initializing the editor
7322
function registerEventHandlers(cm) {
7323
  var d = cm.display
7324
  on(d.scroller, "mousedown", operation(cm, onMouseDown))
7325
  // Older IE's will not fire a second mousedown for a double click
7326
  if (ie && ie_version < 11)
7327
    { on(d.scroller, "dblclick", operation(cm, function (e) {
7328
      if (signalDOMEvent(cm, e)) { return }
7329
      var pos = posFromMouse(cm, e)
7330
      if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) { return }
7331
      e_preventDefault(e)
7332
      var word = cm.findWordAt(pos)
7333
      extendSelection(cm.doc, word.anchor, word.head)
7334
    })) }
7335
  else
7336
    { on(d.scroller, "dblclick", function (e) { return signalDOMEvent(cm, e) || e_preventDefault(e); }) }
7337
  // Some browsers fire contextmenu *after* opening the menu, at
7338
  // which point we can't mess with it anymore. Context menu is
7339
  // handled in onMouseDown for these browsers.
7340
  if (!captureRightClick) { on(d.scroller, "contextmenu", function (e) { return onContextMenu(cm, e); }) }
7341
7342
  // Used to suppress mouse event handling when a touch happens
7343
  var touchFinished, prevTouch = {end: 0}
7344
  function finishTouch() {
7345
    if (d.activeTouch) {
7346
      touchFinished = setTimeout(function () { return d.activeTouch = null; }, 1000)
7347
      prevTouch = d.activeTouch
7348
      prevTouch.end = +new Date
7349
    }
7350
  }
7351
  function isMouseLikeTouchEvent(e) {
7352
    if (e.touches.length != 1) { return false }
7353
    var touch = e.touches[0]
7354
    return touch.radiusX <= 1 && touch.radiusY <= 1
7355
  }
7356
  function farAway(touch, other) {
7357
    if (other.left == null) { return true }
7358
    var dx = other.left - touch.left, dy = other.top - touch.top
7359
    return dx * dx + dy * dy > 20 * 20
7360
  }
7361
  on(d.scroller, "touchstart", function (e) {
7362
    if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e)) {
7363
      d.input.ensurePolled()
7364
      clearTimeout(touchFinished)
7365
      var now = +new Date
7366
      d.activeTouch = {start: now, moved: false,
7367
                       prev: now - prevTouch.end <= 300 ? prevTouch : null}
7368
      if (e.touches.length == 1) {
7369
        d.activeTouch.left = e.touches[0].pageX
7370
        d.activeTouch.top = e.touches[0].pageY
7371
      }
7372
    }
7373
  })
7374
  on(d.scroller, "touchmove", function () {
7375
    if (d.activeTouch) { d.activeTouch.moved = true }
7376
  })
7377
  on(d.scroller, "touchend", function (e) {
7378
    var touch = d.activeTouch
7379
    if (touch && !eventInWidget(d, e) && touch.left != null &&
7380
        !touch.moved && new Date - touch.start < 300) {
7381
      var pos = cm.coordsChar(d.activeTouch, "page"), range
7382
      if (!touch.prev || farAway(touch, touch.prev)) // Single tap
7383
        { range = new Range(pos, pos) }
7384
      else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap
7385
        { range = cm.findWordAt(pos) }
7386
      else // Triple tap
7387
        { range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) }
7388
      cm.setSelection(range.anchor, range.head)
7389
      cm.focus()
7390
      e_preventDefault(e)
7391
    }
7392
    finishTouch()
7393
  })
7394
  on(d.scroller, "touchcancel", finishTouch)
7395
7396
  // Sync scrolling between fake scrollbars and real scrollable
7397
  // area, ensure viewport is updated when scrolling.
7398
  on(d.scroller, "scroll", function () {
7399
    if (d.scroller.clientHeight) {
7400
      setScrollTop(cm, d.scroller.scrollTop)
7401
      setScrollLeft(cm, d.scroller.scrollLeft, true)
7402
      signal(cm, "scroll", cm)
7403
    }
7404
  })
7405
7406
  // Listen to wheel events in order to try and update the viewport on time.
7407
  on(d.scroller, "mousewheel", function (e) { return onScrollWheel(cm, e); })
7408
  on(d.scroller, "DOMMouseScroll", function (e) { return onScrollWheel(cm, e); })
7409
7410
  // Prevent wrapper from ever scrolling
7411
  on(d.wrapper, "scroll", function () { return d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; })
7412
7413
  d.dragFunctions = {
7414
    enter: function (e) {if (!signalDOMEvent(cm, e)) { e_stop(e) }},
7415
    over: function (e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e) }},
7416
    start: function (e) { return onDragStart(cm, e); },
7417
    drop: operation(cm, onDrop),
7418
    leave: function (e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm) }}
7419
  }
7420
7421
  var inp = d.input.getField()
7422
  on(inp, "keyup", function (e) { return onKeyUp.call(cm, e); })
7423
  on(inp, "keydown", operation(cm, onKeyDown))
7424
  on(inp, "keypress", operation(cm, onKeyPress))
7425
  on(inp, "focus", function (e) { return onFocus(cm, e); })
7426
  on(inp, "blur", function (e) { return onBlur(cm, e); })
7427
}
7428
7429
var initHooks = []
7430
CodeMirror.defineInitHook = function (f) { return initHooks.push(f); }
7431
7432
// Indent the given line. The how parameter can be "smart",
7433
// "add"/null, "subtract", or "prev". When aggressive is false
7434
// (typically set to true for forced single-line indents), empty
7435
// lines are not indented, and places where the mode returns Pass
7436
// are left alone.
7437
function indentLine(cm, n, how, aggressive) {
7438
  var doc = cm.doc, state
7439
  if (how == null) { how = "add" }
7440
  if (how == "smart") {
7441
    // Fall back to "prev" when the mode doesn't have an indentation
7442
    // method.
7443
    if (!doc.mode.indent) { how = "prev" }
7444
    else { state = getStateBefore(cm, n) }
7445
  }
7446
7447
  var tabSize = cm.options.tabSize
7448
  var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize)
7449
  if (line.stateAfter) { line.stateAfter = null }
7450
  var curSpaceString = line.text.match(/^\s*/)[0], indentation
7451
  if (!aggressive && !/\S/.test(line.text)) {
7452
    indentation = 0
7453
    how = "not"
7454
  } else if (how == "smart") {
7455
    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...
7456
    if (indentation == Pass || indentation > 150) {
7457
      if (!aggressive) { return }
7458
      how = "prev"
7459
    }
7460
  }
7461
  if (how == "prev") {
7462
    if (n > doc.first) { indentation = countColumn(getLine(doc, n-1).text, null, tabSize) }
7463
    else { indentation = 0 }
7464
  } else if (how == "add") {
7465
    indentation = curSpace + cm.options.indentUnit
7466
  } else if (how == "subtract") {
7467
    indentation = curSpace - cm.options.indentUnit
7468
  } else if (typeof how == "number") {
7469
    indentation = curSpace + how
7470
  }
7471
  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 7454 is false. Are you sure the function max handles undefined variables?
Loading history...
7472
7473
  var indentString = "", pos = 0
7474
  if (cm.options.indentWithTabs)
7475
    { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t"} }
7476
  if (pos < indentation) { indentString += spaceStr(indentation - pos) }
7477
7478
  if (indentString != curSpaceString) {
7479
    replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input")
7480
    line.stateAfter = null
7481
    return true
7482
  } else {
7483
    // Ensure that, if the cursor was in the whitespace at the start
7484
    // of the line, it is moved to the end of that space.
7485
    for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) {
7486
      var range = doc.sel.ranges[i$1]
7487
      if (range.head.line == n && range.head.ch < curSpaceString.length) {
7488
        var pos$1 = Pos(n, curSpaceString.length)
7489
        replaceOneSelection(doc, i$1, new Range(pos$1, pos$1))
7490
        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...
7491
      }
7492
    }
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...
7493
  }
7494
}
7495
7496
// This will be set to a {lineWise: bool, text: [string]} object, so
7497
// that, when pasting, we know what kind of selections the copied
7498
// text was made out of.
7499
var lastCopied = null
7500
7501
function setLastCopied(newLastCopied) {
7502
  lastCopied = newLastCopied
7503
}
7504
7505
function applyTextInput(cm, inserted, deleted, sel, origin) {
7506
  var doc = cm.doc
7507
  cm.display.shift = false
7508
  if (!sel) { sel = doc.sel }
7509
7510
  var paste = cm.state.pasteIncoming || origin == "paste"
7511
  var textLines = splitLinesAuto(inserted), multiPaste = null
7512
  // When pasing N lines into N selections, insert one line per selection
7513
  if (paste && sel.ranges.length > 1) {
7514
    if (lastCopied && lastCopied.text.join("\n") == inserted) {
7515
      if (sel.ranges.length % lastCopied.text.length == 0) {
7516
        multiPaste = []
7517
        for (var i = 0; i < lastCopied.text.length; i++)
7518
          { multiPaste.push(doc.splitLines(lastCopied.text[i])) }
7519
      }
7520
    } else if (textLines.length == sel.ranges.length) {
7521
      multiPaste = map(textLines, function (l) { return [l]; })
7522
    }
7523
  }
7524
7525
  var updateInput
7526
  // Normal behavior is to insert the new text into every selection
7527
  for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) {
7528
    var range = sel.ranges[i$1]
7529
    var from = range.from(), to = range.to()
7530
    if (range.empty()) {
7531
      if (deleted && deleted > 0) // Handle deletion
7532
        { from = Pos(from.line, from.ch - deleted) }
7533
      else if (cm.state.overwrite && !paste) // Handle overwrite
7534
        { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)) }
7535
      else if (lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == inserted)
7536
        { from = to = Pos(from.line, 0) }
7537
    }
7538
    updateInput = cm.curOp.updateInput
7539
    var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines,
7540
                       origin: origin || (paste ? "paste" : cm.state.cutIncoming ? "cut" : "+input")}
7541
    makeChange(cm.doc, changeEvent)
7542
    signalLater(cm, "inputRead", cm, changeEvent)
7543
  }
7544
  if (inserted && !paste)
7545
    { triggerElectric(cm, inserted) }
7546
7547
  ensureCursorVisible(cm)
7548
  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...
7549
  cm.curOp.typing = true
7550
  cm.state.pasteIncoming = cm.state.cutIncoming = false
7551
}
7552
7553
function handlePaste(e, cm) {
7554
  var pasted = e.clipboardData && e.clipboardData.getData("Text")
7555
  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...
7556
    e.preventDefault()
7557
    if (!cm.isReadOnly() && !cm.options.disableInput)
7558
      { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, "paste"); }) }
7559
    return true
7560
  }
7561
}
7562
7563
function triggerElectric(cm, inserted) {
7564
  // When an 'electric' character is inserted, immediately trigger a reindent
7565
  if (!cm.options.electricChars || !cm.options.smartIndent) { return }
7566
  var sel = cm.doc.sel
7567
7568
  for (var i = sel.ranges.length - 1; i >= 0; i--) {
7569
    var range = sel.ranges[i]
7570
    if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head.line)) { continue }
7571
    var mode = cm.getModeAt(range.head)
7572
    var indented = false
7573
    if (mode.electricChars) {
7574
      for (var j = 0; j < mode.electricChars.length; j++)
7575
        { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
7576
          indented = indentLine(cm, range.head.line, "smart")
7577
          break
7578
        } }
7579
    } else if (mode.electricInput) {
7580
      if (mode.electricInput.test(getLine(cm.doc, range.head.line).text.slice(0, range.head.ch)))
7581
        { indented = indentLine(cm, range.head.line, "smart") }
7582
    }
7583
    if (indented) { signalLater(cm, "electricInput", cm, range.head.line) }
7584
  }
7585
}
7586
7587
function copyableRanges(cm) {
7588
  var text = [], ranges = []
7589
  for (var i = 0; i < cm.doc.sel.ranges.length; i++) {
7590
    var line = cm.doc.sel.ranges[i].head.line
7591
    var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)}
7592
    ranges.push(lineRange)
7593
    text.push(cm.getRange(lineRange.anchor, lineRange.head))
7594
  }
7595
  return {text: text, ranges: ranges}
7596
}
7597
7598
function disableBrowserMagic(field, spellcheck) {
7599
  field.setAttribute("autocorrect", "off")
7600
  field.setAttribute("autocapitalize", "off")
7601
  field.setAttribute("spellcheck", !!spellcheck)
7602
}
7603
7604
function hiddenTextarea() {
7605
  var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none")
7606
  var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;")
7607
  // The textarea is kept positioned near the cursor to prevent the
7608
  // fact that it'll be scrolled into view on input from scrolling
7609
  // our fake cursor out of view. On webkit, when wrap=off, paste is
7610
  // very slow. So make the area wide instead.
7611
  if (webkit) { te.style.width = "1000px" }
7612
  else { te.setAttribute("wrap", "off") }
7613
  // If border: 0; -- iOS fails to open keyboard (issue #1287)
7614
  if (ios) { te.style.border = "1px solid black" }
7615
  disableBrowserMagic(te)
7616
  return div
7617
}
7618
7619
// The publicly visible API. Note that methodOp(f) means
7620
// 'wrap f in an operation, performed on its `this` parameter'.
7621
7622
// This is not the complete set of editor methods. Most of the
7623
// methods defined on the Doc type are also injected into
7624
// CodeMirror.prototype, for backwards compatibility and
7625
// convenience.
7626
7627
function addEditorMethods(CodeMirror) {
7628
  var optionHandlers = CodeMirror.optionHandlers
7629
7630
  var helpers = CodeMirror.helpers = {}
7631
7632
  CodeMirror.prototype = {
7633
    constructor: CodeMirror,
7634
    focus: function(){window.focus(); this.display.input.focus()},
7635
7636
    setOption: function(option, value) {
7637
      var options = this.options, old = options[option]
7638
      if (options[option] == value && option != "mode") { return }
7639
      options[option] = value
7640
      if (optionHandlers.hasOwnProperty(option))
7641
        { operation(this, optionHandlers[option])(this, value, old) }
7642
      signal(this, "optionChange", this, option)
7643
    },
7644
7645
    getOption: function(option) {return this.options[option]},
7646
    getDoc: function() {return this.doc},
7647
7648
    addKeyMap: function(map, bottom) {
7649
      this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map))
7650
    },
7651
    removeKeyMap: function(map) {
7652
      var maps = this.state.keyMaps
7653
      for (var i = 0; i < maps.length; ++i)
7654
        { if (maps[i] == map || maps[i].name == map) {
7655
          maps.splice(i, 1)
7656
          return true
7657
        } }
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...
7658
    },
7659
7660
    addOverlay: methodOp(function(spec, options) {
7661
      var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec)
7662
      if (mode.startState) { throw new Error("Overlays may not be stateful.") }
7663
      insertSorted(this.state.overlays,
7664
                   {mode: mode, modeSpec: spec, opaque: options && options.opaque,
7665
                    priority: (options && options.priority) || 0},
7666
                   function (overlay) { return overlay.priority; })
7667
      this.state.modeGen++
7668
      regChange(this)
7669
    }),
7670
    removeOverlay: methodOp(function(spec) {
7671
      var this$1 = this;
7672
7673
      var overlays = this.state.overlays
7674
      for (var i = 0; i < overlays.length; ++i) {
7675
        var cur = overlays[i].modeSpec
7676
        if (cur == spec || typeof spec == "string" && cur.name == spec) {
7677
          overlays.splice(i, 1)
7678
          this$1.state.modeGen++
7679
          regChange(this$1)
7680
          return
7681
        }
7682
      }
7683
    }),
7684
7685
    indentLine: methodOp(function(n, dir, aggressive) {
7686
      if (typeof dir != "string" && typeof dir != "number") {
7687
        if (dir == null) { dir = this.options.smartIndent ? "smart" : "prev" }
7688
        else { dir = dir ? "add" : "subtract" }
7689
      }
7690
      if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive) }
7691
    }),
7692
    indentSelection: methodOp(function(how) {
7693
      var this$1 = this;
7694
7695
      var ranges = this.doc.sel.ranges, end = -1
7696
      for (var i = 0; i < ranges.length; i++) {
7697
        var range = ranges[i]
7698
        if (!range.empty()) {
7699
          var from = range.from(), to = range.to()
7700
          var start = Math.max(end, from.line)
7701
          end = Math.min(this$1.lastLine(), to.line - (to.ch ? 0 : 1)) + 1
7702
          for (var j = start; j < end; ++j)
7703
            { indentLine(this$1, j, how) }
7704
          var newRanges = this$1.doc.sel.ranges
7705
          if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0)
7706
            { replaceOneSelection(this$1.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll) }
7707
        } else if (range.head.line > end) {
7708
          indentLine(this$1, range.head.line, how, true)
7709
          end = range.head.line
7710
          if (i == this$1.doc.sel.primIndex) { ensureCursorVisible(this$1) }
7711
        }
7712
      }
7713
    }),
7714
7715
    // Fetch the parser token for a given character. Useful for hacks
7716
    // that want to inspect the mode state (say, for completion).
7717
    getTokenAt: function(pos, precise) {
7718
      return takeToken(this, pos, precise)
7719
    },
7720
7721
    getLineTokens: function(line, precise) {
7722
      return takeToken(this, Pos(line), precise, true)
7723
    },
7724
7725
    getTokenTypeAt: function(pos) {
7726
      pos = clipPos(this.doc, pos)
7727
      var styles = getLineStyles(this, getLine(this.doc, pos.line))
7728
      var before = 0, after = (styles.length - 1) / 2, ch = pos.ch
7729
      var type
7730
      if (ch == 0) { type = styles[2] }
7731
      else { for (;;) {
7732
        var mid = (before + after) >> 1
7733
        if ((mid ? styles[mid * 2 - 1] : 0) >= ch) { after = mid }
7734
        else if (styles[mid * 2 + 1] < ch) { before = mid + 1 }
7735
        else { type = styles[mid * 2 + 2]; break }
7736
      } }
7737
      var cut = type ? type.indexOf("overlay ") : -1
7738
      return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1)
7739
    },
7740
7741
    getModeAt: function(pos) {
7742
      var mode = this.doc.mode
7743
      if (!mode.innerMode) { return mode }
7744
      return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode
7745
    },
7746
7747
    getHelper: function(pos, type) {
7748
      return this.getHelpers(pos, type)[0]
7749
    },
7750
7751
    getHelpers: function(pos, type) {
7752
      var this$1 = this;
7753
7754
      var found = []
7755
      if (!helpers.hasOwnProperty(type)) { return found }
7756
      var help = helpers[type], mode = this.getModeAt(pos)
7757
      if (typeof mode[type] == "string") {
7758
        if (help[mode[type]]) { found.push(help[mode[type]]) }
7759
      } else if (mode[type]) {
7760
        for (var i = 0; i < mode[type].length; i++) {
7761
          var val = help[mode[type][i]]
7762
          if (val) { found.push(val) }
7763
        }
7764
      } else if (mode.helperType && help[mode.helperType]) {
7765
        found.push(help[mode.helperType])
7766
      } else if (help[mode.name]) {
7767
        found.push(help[mode.name])
7768
      }
7769
      for (var i$1 = 0; i$1 < help._global.length; i$1++) {
7770
        var cur = help._global[i$1]
7771
        if (cur.pred(mode, this$1) && indexOf(found, cur.val) == -1)
7772
          { found.push(cur.val) }
7773
      }
7774
      return found
7775
    },
7776
7777
    getStateAfter: function(line, precise) {
7778
      var doc = this.doc
7779
      line = clipLine(doc, line == null ? doc.first + doc.size - 1: line)
7780
      return getStateBefore(this, line + 1, precise)
7781
    },
7782
7783
    cursorCoords: function(start, mode) {
7784
      var pos, range = this.doc.sel.primary()
7785
      if (start == null) { pos = range.head }
7786
      else if (typeof start == "object") { pos = clipPos(this.doc, start) }
7787
      else { pos = start ? range.from() : range.to() }
7788
      return cursorCoords(this, pos, mode || "page")
7789
    },
7790
7791
    charCoords: function(pos, mode) {
7792
      return charCoords(this, clipPos(this.doc, pos), mode || "page")
7793
    },
7794
7795
    coordsChar: function(coords, mode) {
7796
      coords = fromCoordSystem(this, coords, mode || "page")
7797
      return coordsChar(this, coords.left, coords.top)
7798
    },
7799
7800
    lineAtHeight: function(height, mode) {
7801
      height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top
7802
      return lineAtHeight(this.doc, height + this.display.viewOffset)
7803
    },
7804
    heightAtLine: function(line, mode, includeWidgets) {
7805
      var end = false, lineObj
7806
      if (typeof line == "number") {
7807
        var last = this.doc.first + this.doc.size - 1
7808
        if (line < this.doc.first) { line = this.doc.first }
7809
        else if (line > last) { line = last; end = true }
7810
        lineObj = getLine(this.doc, line)
7811
      } else {
7812
        lineObj = line
7813
      }
7814
      return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page", includeWidgets).top +
7815
        (end ? this.doc.height - heightAtLine(lineObj) : 0)
7816
    },
7817
7818
    defaultTextHeight: function() { return textHeight(this.display) },
7819
    defaultCharWidth: function() { return charWidth(this.display) },
7820
7821
    getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}},
7822
7823
    addWidget: function(pos, node, scroll, vert, horiz) {
7824
      var display = this.display
7825
      pos = cursorCoords(this, clipPos(this.doc, pos))
7826
      var top = pos.bottom, left = pos.left
7827
      node.style.position = "absolute"
7828
      node.setAttribute("cm-ignore-events", "true")
7829
      this.display.input.setUneditable(node)
7830
      display.sizer.appendChild(node)
7831
      if (vert == "over") {
7832
        top = pos.top
7833
      } else if (vert == "above" || vert == "near") {
7834
        var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
7835
        hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth)
7836
        // Default to positioning above (if specified and possible); otherwise default to positioning below
7837
        if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
7838
          { top = pos.top - node.offsetHeight }
7839
        else if (pos.bottom + node.offsetHeight <= vspace)
7840
          { top = pos.bottom }
7841
        if (left + node.offsetWidth > hspace)
7842
          { left = hspace - node.offsetWidth }
7843
      }
7844
      node.style.top = top + "px"
7845
      node.style.left = node.style.right = ""
7846
      if (horiz == "right") {
7847
        left = display.sizer.clientWidth - node.offsetWidth
7848
        node.style.right = "0px"
7849
      } else {
7850
        if (horiz == "left") { left = 0 }
7851
        else if (horiz == "middle") { left = (display.sizer.clientWidth - node.offsetWidth) / 2 }
7852
        node.style.left = left + "px"
7853
      }
7854
      if (scroll)
7855
        { scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight) }
7856
    },
7857
7858
    triggerOnKeyDown: methodOp(onKeyDown),
7859
    triggerOnKeyPress: methodOp(onKeyPress),
7860
    triggerOnKeyUp: onKeyUp,
7861
7862
    execCommand: function(cmd) {
7863
      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...
7864
        { return commands[cmd].call(null, this) }
7865
    },
7866
7867
    triggerElectric: methodOp(function(text) { triggerElectric(this, text) }),
7868
7869
    findPosH: function(from, amount, unit, visually) {
7870
      var this$1 = this;
7871
7872
      var dir = 1
7873
      if (amount < 0) { dir = -1; amount = -amount }
7874
      var cur = clipPos(this.doc, from)
7875
      for (var i = 0; i < amount; ++i) {
7876
        cur = findPosH(this$1.doc, cur, dir, unit, visually)
7877
        if (cur.hitSide) { break }
7878
      }
7879
      return cur
7880
    },
7881
7882
    moveH: methodOp(function(dir, unit) {
7883
      var this$1 = this;
7884
7885
      this.extendSelectionsBy(function (range) {
7886
        if (this$1.display.shift || this$1.doc.extend || range.empty())
7887
          { return findPosH(this$1.doc, range.head, dir, unit, this$1.options.rtlMoveVisually) }
7888
        else
7889
          { return dir < 0 ? range.from() : range.to() }
7890
      }, sel_move)
7891
    }),
7892
7893
    deleteH: methodOp(function(dir, unit) {
7894
      var sel = this.doc.sel, doc = this.doc
7895
      if (sel.somethingSelected())
7896
        { doc.replaceSelection("", null, "+delete") }
7897
      else
7898
        { deleteNearSelection(this, function (range) {
7899
          var other = findPosH(doc, range.head, dir, unit, false)
7900
          return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other}
7901
        }) }
7902
    }),
7903
7904
    findPosV: function(from, amount, unit, goalColumn) {
7905
      var this$1 = this;
7906
7907
      var dir = 1, x = goalColumn
7908
      if (amount < 0) { dir = -1; amount = -amount }
7909
      var cur = clipPos(this.doc, from)
7910
      for (var i = 0; i < amount; ++i) {
7911
        var coords = cursorCoords(this$1, cur, "div")
7912
        if (x == null) { x = coords.left }
7913
        else { coords.left = x }
7914
        cur = findPosV(this$1, coords, dir, unit)
7915
        if (cur.hitSide) { break }
7916
      }
7917
      return cur
7918
    },
7919
7920
    moveV: methodOp(function(dir, unit) {
7921
      var this$1 = this;
7922
7923
      var doc = this.doc, goals = []
7924
      var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected()
7925
      doc.extendSelectionsBy(function (range) {
7926
        if (collapse)
7927
          { return dir < 0 ? range.from() : range.to() }
7928
        var headPos = cursorCoords(this$1, range.head, "div")
7929
        if (range.goalColumn != null) { headPos.left = range.goalColumn }
7930
        goals.push(headPos.left)
7931
        var pos = findPosV(this$1, headPos, dir, unit)
7932
        if (unit == "page" && range == doc.sel.primary())
7933
          { addToScrollPos(this$1, null, charCoords(this$1, pos, "div").top - headPos.top) }
7934
        return pos
7935
      }, sel_move)
7936
      if (goals.length) { for (var i = 0; i < doc.sel.ranges.length; i++)
7937
        { doc.sel.ranges[i].goalColumn = goals[i] } }
7938
    }),
7939
7940
    // Find the word at the given position (as returned by coordsChar).
7941
    findWordAt: function(pos) {
7942
      var doc = this.doc, line = getLine(doc, pos.line).text
7943
      var start = pos.ch, end = pos.ch
7944
      if (line) {
7945
        var helper = this.getHelper(pos, "wordChars")
7946
        if ((pos.xRel < 0 || end == line.length) && start) { --start; } else { ++end }
7947
        var startChar = line.charAt(start)
7948
        var check = isWordChar(startChar, helper)
7949
          ? function (ch) { return isWordChar(ch, helper); }
7950
          : /\s/.test(startChar) ? function (ch) { return /\s/.test(ch); }
7951
          : function (ch) { return (!/\s/.test(ch) && !isWordChar(ch)); }
7952
        while (start > 0 && check(line.charAt(start - 1))) { --start }
7953
        while (end < line.length && check(line.charAt(end))) { ++end }
7954
      }
7955
      return new Range(Pos(pos.line, start), Pos(pos.line, end))
7956
    },
7957
7958
    toggleOverwrite: function(value) {
7959
      if (value != null && value == this.state.overwrite) { return }
7960
      if (this.state.overwrite = !this.state.overwrite)
7961
        { addClass(this.display.cursorDiv, "CodeMirror-overwrite") }
7962
      else
7963
        { rmClass(this.display.cursorDiv, "CodeMirror-overwrite") }
7964
7965
      signal(this, "overwriteToggle", this, this.state.overwrite)
7966
    },
7967
    hasFocus: function() { return this.display.input.getField() == activeElt() },
7968
    isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) },
7969
7970
    scrollTo: methodOp(function(x, y) {
7971
      if (x != null || y != null) { resolveScrollToPos(this) }
7972
      if (x != null) { this.curOp.scrollLeft = x }
7973
      if (y != null) { this.curOp.scrollTop = y }
7974
    }),
7975
    getScrollInfo: function() {
7976
      var scroller = this.display.scroller
7977
      return {left: scroller.scrollLeft, top: scroller.scrollTop,
7978
              height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight,
7979
              width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth,
7980
              clientHeight: displayHeight(this), clientWidth: displayWidth(this)}
7981
    },
7982
7983
    scrollIntoView: methodOp(function(range, margin) {
7984
      if (range == null) {
7985
        range = {from: this.doc.sel.primary().head, to: null}
7986
        if (margin == null) { margin = this.options.cursorScrollMargin }
7987
      } else if (typeof range == "number") {
7988
        range = {from: Pos(range, 0), to: null}
7989
      } else if (range.from == null) {
7990
        range = {from: range, to: null}
7991
      }
7992
      if (!range.to) { range.to = range.from }
7993
      range.margin = margin || 0
7994
7995
      if (range.from.line != null) {
7996
        resolveScrollToPos(this)
7997
        this.curOp.scrollToPos = range
7998
      } else {
7999
        var sPos = calculateScrollPos(this, Math.min(range.from.left, range.to.left),
8000
                                      Math.min(range.from.top, range.to.top) - range.margin,
8001
                                      Math.max(range.from.right, range.to.right),
8002
                                      Math.max(range.from.bottom, range.to.bottom) + range.margin)
8003
        this.scrollTo(sPos.scrollLeft, sPos.scrollTop)
8004
      }
8005
    }),
8006
8007
    setSize: methodOp(function(width, height) {
8008
      var this$1 = this;
8009
8010
      var interpret = function (val) { return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; }
8011
      if (width != null) { this.display.wrapper.style.width = interpret(width) }
8012
      if (height != null) { this.display.wrapper.style.height = interpret(height) }
8013
      if (this.options.lineWrapping) { clearLineMeasurementCache(this) }
8014
      var lineNo = this.display.viewFrom
8015
      this.doc.iter(lineNo, this.display.viewTo, function (line) {
8016
        if (line.widgets) { for (var i = 0; i < line.widgets.length; i++)
8017
          { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo, "widget"); break } } }
8018
        ++lineNo
8019
      })
8020
      this.curOp.forceUpdate = true
8021
      signal(this, "refresh", this)
8022
    }),
8023
8024
    operation: function(f){return runInOp(this, f)},
8025
8026
    refresh: methodOp(function() {
8027
      var oldHeight = this.display.cachedTextHeight
8028
      regChange(this)
8029
      this.curOp.forceUpdate = true
8030
      clearCaches(this)
8031
      this.scrollTo(this.doc.scrollLeft, this.doc.scrollTop)
8032
      updateGutterSpace(this)
8033
      if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5)
8034
        { estimateLineHeights(this) }
8035
      signal(this, "refresh", this)
8036
    }),
8037
8038
    swapDoc: methodOp(function(doc) {
8039
      var old = this.doc
8040
      old.cm = null
8041
      attachDoc(this, doc)
8042
      clearCaches(this)
8043
      this.display.input.reset()
8044
      this.scrollTo(doc.scrollLeft, doc.scrollTop)
8045
      this.curOp.forceScroll = true
8046
      signalLater(this, "swapDoc", this, old)
8047
      return old
8048
    }),
8049
8050
    getInputField: function(){return this.display.input.getField()},
8051
    getWrapperElement: function(){return this.display.wrapper},
8052
    getScrollerElement: function(){return this.display.scroller},
8053
    getGutterElement: function(){return this.display.gutters}
8054
  }
8055
  eventMixin(CodeMirror)
8056
8057
  CodeMirror.registerHelper = function(type, name, value) {
8058
    if (!helpers.hasOwnProperty(type)) { helpers[type] = CodeMirror[type] = {_global: []} }
8059
    helpers[type][name] = value
8060
  }
8061
  CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {
8062
    CodeMirror.registerHelper(type, name, value)
8063
    helpers[type]._global.push({pred: predicate, val: value})
8064
  }
8065
}
8066
8067
// Used for horizontal relative motion. Dir is -1 or 1 (left or
8068
// right), unit can be "char", "column" (like char, but doesn't
8069
// cross line boundaries), "word" (across next word), or "group" (to
8070
// the start of next group of word or non-word-non-whitespace
8071
// chars). The visually param controls whether, in right-to-left
8072
// text, direction 1 means to move towards the next index in the
8073
// string, or towards the character to the right of the current
8074
// position. The resulting position will have a hitSide=true
8075
// property if it reached the end of the document.
8076
function findPosH(doc, pos, dir, unit, visually) {
8077
  var line = pos.line, ch = pos.ch, origDir = dir
8078
  var lineObj = getLine(doc, line)
8079
  function findNextLine() {
8080
    var l = line + dir
8081
    if (l < doc.first || l >= doc.first + doc.size) { return false }
8082
    line = l
8083
    return lineObj = getLine(doc, l)
8084
  }
8085
  function moveOnce(boundToLine) {
8086
    var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, true)
8087
    if (next == null) {
8088
      if (!boundToLine && findNextLine()) {
8089
        if (visually) { ch = (dir < 0 ? lineRight : lineLeft)(lineObj) }
8090
        else { ch = dir < 0 ? lineObj.text.length : 0 }
8091
      } else { return false }
8092
    } else { ch = next }
8093
    return true
8094
  }
8095
8096
  if (unit == "char") {
8097
    moveOnce()
8098
  } else if (unit == "column") {
8099
    moveOnce(true)
8100
  } else if (unit == "word" || unit == "group") {
8101
    var sawType = null, group = unit == "group"
8102
    var helper = doc.cm && doc.cm.getHelper(pos, "wordChars")
8103
    for (var first = true;; first = false) {
8104
      if (dir < 0 && !moveOnce(!first)) { break }
8105
      var cur = lineObj.text.charAt(ch) || "\n"
8106
      var type = isWordChar(cur, helper) ? "w"
8107
        : group && cur == "\n" ? "n"
8108
        : !group || /\s/.test(cur) ? null
8109
        : "p"
8110
      if (group && !first && !type) { type = "s" }
8111
      if (sawType && sawType != type) {
8112
        if (dir < 0) {dir = 1; moveOnce()}
8113
        break
8114
      }
8115
8116
      if (type) { sawType = type }
8117
      if (dir > 0 && !moveOnce(!first)) { break }
8118
    }
8119
  }
8120
  var result = skipAtomic(doc, Pos(line, ch), pos, origDir, true)
8121
  if (!cmp(pos, result)) { result.hitSide = true }
8122
  return result
8123
}
8124
8125
// For relative vertical movement. Dir may be -1 or 1. Unit can be
8126
// "page" or "line". The resulting position will have a hitSide=true
8127
// property if it reached the end of the document.
8128
function findPosV(cm, pos, dir, unit) {
8129
  var doc = cm.doc, x = pos.left, y
8130
  if (unit == "page") {
8131
    var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight)
8132
    var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3)
8133
    y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount
8134
8135
  } else if (unit == "line") {
8136
    y = dir > 0 ? pos.bottom + 3 : pos.top - 3
8137
  }
8138
  var target
8139
  for (;;) {
8140
    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 8135 is false. Are you sure the function coordsChar handles undefined variables?
Loading history...
8141
    if (!target.outside) { break }
8142
    if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break }
8143
    y += dir * 5
8144
  }
8145
  return target
8146
}
8147
8148
// CONTENTEDITABLE INPUT STYLE
8149
8150
var ContentEditableInput = function(cm) {
8151
  this.cm = cm
8152
  this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null
8153
  this.polling = new Delayed()
8154
  this.composing = null
8155
  this.gracePeriod = false
8156
  this.readDOMTimeout = null
8157
};
8158
8159
ContentEditableInput.prototype.init = function (display) {
8160
    var this$1 = this;
8161
8162
  var input = this, cm = input.cm
8163
  var div = input.div = display.lineDiv
8164
  disableBrowserMagic(div, cm.options.spellcheck)
8165
8166
  on(div, "paste", function (e) {
8167
    if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
8168
    // IE doesn't fire input events, so we schedule a read for the pasted content in this way
8169
    if (ie_version <= 11) { setTimeout(operation(cm, function () {
8170
      if (!input.pollContent()) { regChange(cm) }
8171
    }), 20) }
8172
  })
8173
8174
  on(div, "compositionstart", function (e) {
8175
    this$1.composing = {data: e.data, done: false}
8176
  })
8177
  on(div, "compositionupdate", function (e) {
8178
    if (!this$1.composing) { this$1.composing = {data: e.data, done: false} }
8179
  })
8180
  on(div, "compositionend", function (e) {
8181
    if (this$1.composing) {
8182
      if (e.data != this$1.composing.data) { this$1.readFromDOMSoon() }
8183
      this$1.composing.done = true
8184
    }
8185
  })
8186
8187
  on(div, "touchstart", function () { return input.forceCompositionEnd(); })
8188
8189
  on(div, "input", function () {
8190
    if (!this$1.composing) { this$1.readFromDOMSoon() }
8191
  })
8192
8193
  function onCopyCut(e) {
8194
    if (signalDOMEvent(cm, e)) { return }
8195
    if (cm.somethingSelected()) {
8196
      setLastCopied({lineWise: false, text: cm.getSelections()})
8197
      if (e.type == "cut") { cm.replaceSelection("", null, "cut") }
8198
    } else if (!cm.options.lineWiseCopyCut) {
8199
      return
8200
    } else {
8201
      var ranges = copyableRanges(cm)
8202
      setLastCopied({lineWise: true, text: ranges.text})
8203
      if (e.type == "cut") {
8204
        cm.operation(function () {
8205
          cm.setSelections(ranges.ranges, 0, sel_dontScroll)
8206
          cm.replaceSelection("", null, "cut")
8207
        })
8208
      }
8209
    }
8210
    if (e.clipboardData) {
8211
      e.clipboardData.clearData()
8212
      var content = lastCopied.text.join("\n")
8213
      // iOS exposes the clipboard API, but seems to discard content inserted into it
8214
      e.clipboardData.setData("Text", content)
8215
      if (e.clipboardData.getData("Text") == content) {
8216
        e.preventDefault()
8217
        return
8218
      }
8219
    }
8220
    // Old-fashioned briefly-focus-a-textarea hack
8221
    var kludge = hiddenTextarea(), te = kludge.firstChild
8222
    cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild)
8223
    te.value = lastCopied.text.join("\n")
8224
    var hadFocus = document.activeElement
8225
    selectInput(te)
8226
    setTimeout(function () {
8227
      cm.display.lineSpace.removeChild(kludge)
8228
      hadFocus.focus()
8229
      if (hadFocus == div) { input.showPrimarySelection() }
8230
    }, 50)
8231
  }
8232
  on(div, "copy", onCopyCut)
8233
  on(div, "cut", onCopyCut)
8234
};
8235
8236
ContentEditableInput.prototype.prepareSelection = function () {
8237
  var result = prepareSelection(this.cm, false)
8238
  result.focus = this.cm.state.focused
8239
  return result
8240
};
8241
8242
ContentEditableInput.prototype.showSelection = function (info, takeFocus) {
8243
  if (!info || !this.cm.display.view.length) { return }
8244
  if (info.focus || takeFocus) { this.showPrimarySelection() }
8245
  this.showMultipleSelections(info)
8246
};
8247
8248
ContentEditableInput.prototype.showPrimarySelection = function () {
8249
  var sel = window.getSelection(), prim = this.cm.doc.sel.primary()
8250
  var curAnchor = domToPos(this.cm, sel.anchorNode, sel.anchorOffset)
8251
  var curFocus = domToPos(this.cm, sel.focusNode, sel.focusOffset)
8252
  if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad &&
8253
      cmp(minPos(curAnchor, curFocus), prim.from()) == 0 &&
8254
      cmp(maxPos(curAnchor, curFocus), prim.to()) == 0)
8255
    { return }
8256
8257
  var start = posToDOM(this.cm, prim.from())
8258
  var end = posToDOM(this.cm, prim.to())
8259
  if (!start && !end) { return }
8260
8261
  var view = this.cm.display.view
8262
  var old = sel.rangeCount && sel.getRangeAt(0)
8263
  if (!start) {
8264
    start = {node: view[0].measure.map[2], offset: 0}
8265
  } else if (!end) { // FIXME dangerously hacky
8266
    var measure = view[view.length - 1].measure
8267
    var map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map
8268
    end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map.length - 3]}
8269
  }
8270
8271
  var rng
8272
  try { rng = range(start.node, start.offset, end.offset, end.node) }
0 ignored issues
show
Bug introduced by
The call to range seems to have too many arguments starting with end.node.
Loading history...
8273
  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...
8274
  if (rng) {
8275
    if (!gecko && this.cm.state.focused) {
8276
      sel.collapse(start.node, start.offset)
8277
      if (!rng.collapsed) {
8278
        sel.removeAllRanges()
8279
        sel.addRange(rng)
8280
      }
8281
    } else {
8282
      sel.removeAllRanges()
8283
      sel.addRange(rng)
8284
    }
8285
    if (old && sel.anchorNode == null) { sel.addRange(old) }
8286
    else if (gecko) { this.startGracePeriod() }
8287
  }
8288
  this.rememberSelection()
8289
};
8290
8291
ContentEditableInput.prototype.startGracePeriod = function () {
8292
    var this$1 = this;
8293
8294
  clearTimeout(this.gracePeriod)
8295
  this.gracePeriod = setTimeout(function () {
8296
    this$1.gracePeriod = false
8297
    if (this$1.selectionChanged())
8298
      { this$1.cm.operation(function () { return this$1.cm.curOp.selectionChanged = true; }) }
8299
  }, 20)
8300
};
8301
8302
ContentEditableInput.prototype.showMultipleSelections = function (info) {
8303
  removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors)
8304
  removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection)
8305
};
8306
8307
ContentEditableInput.prototype.rememberSelection = function () {
8308
  var sel = window.getSelection()
8309
  this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset
8310
  this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset
8311
};
8312
8313
ContentEditableInput.prototype.selectionInEditor = function () {
8314
  var sel = window.getSelection()
8315
  if (!sel.rangeCount) { return false }
8316
  var node = sel.getRangeAt(0).commonAncestorContainer
8317
  return contains(this.div, node)
8318
};
8319
8320
ContentEditableInput.prototype.focus = function () {
8321
  if (this.cm.options.readOnly != "nocursor") {
8322
    if (!this.selectionInEditor())
8323
      { this.showSelection(this.prepareSelection(), true) }
8324
    this.div.focus()
8325
  }
8326
};
8327
ContentEditableInput.prototype.blur = function () { this.div.blur() };
8328
ContentEditableInput.prototype.getField = function () { return this.div };
8329
8330
ContentEditableInput.prototype.supportsTouch = function () { return true };
8331
8332
ContentEditableInput.prototype.receivedFocus = function () {
8333
  var input = this
8334
  if (this.selectionInEditor())
8335
    { this.pollSelection() }
8336
  else
8337
    { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }) }
8338
8339
  function poll() {
8340
    if (input.cm.state.focused) {
8341
      input.pollSelection()
8342
      input.polling.set(input.cm.options.pollInterval, poll)
8343
    }
8344
  }
8345
  this.polling.set(this.cm.options.pollInterval, poll)
8346
};
8347
8348
ContentEditableInput.prototype.selectionChanged = function () {
8349
  var sel = window.getSelection()
8350
  return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||
8351
    sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset
8352
};
8353
8354
ContentEditableInput.prototype.pollSelection = function () {
8355
  if (!this.composing && this.readDOMTimeout == null && !this.gracePeriod && this.selectionChanged()) {
8356
    var sel = window.getSelection(), cm = this.cm
8357
    this.rememberSelection()
8358
    var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset)
8359
    var head = domToPos(cm, sel.focusNode, sel.focusOffset)
8360
    if (anchor && head) { runInOp(cm, function () {
8361
      setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll)
8362
      if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true }
8363
    }) }
8364
  }
8365
};
8366
8367
ContentEditableInput.prototype.pollContent = function () {
8368
  if (this.readDOMTimeout != null) {
8369
    clearTimeout(this.readDOMTimeout)
8370
    this.readDOMTimeout = null
8371
  }
8372
8373
  var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary()
8374
  var from = sel.from(), to = sel.to()
8375
  if (from.ch == 0 && from.line > cm.firstLine())
8376
    { from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length) }
8377
  if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine())
8378
    { to = Pos(to.line + 1, 0) }
8379
  if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return false }
8380
8381
  var fromIndex, fromLine, fromNode
8382
  if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) {
8383
    fromLine = lineNo(display.view[0].line)
8384
    fromNode = display.view[0].node
8385
  } else {
8386
    fromLine = lineNo(display.view[fromIndex].line)
8387
    fromNode = display.view[fromIndex - 1].node.nextSibling
8388
  }
8389
  var toIndex = findViewIndex(cm, to.line)
8390
  var toLine, toNode
8391
  if (toIndex == display.view.length - 1) {
8392
    toLine = display.viewTo - 1
8393
    toNode = display.lineDiv.lastChild
8394
  } else {
8395
    toLine = lineNo(display.view[toIndex + 1].line) - 1
8396
    toNode = display.view[toIndex + 1].node.previousSibling
8397
  }
8398
8399
  if (!fromNode) { return false }
8400
  var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine))
8401
  var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length))
8402
  while (newText.length > 1 && oldText.length > 1) {
8403
    if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine-- }
8404
    else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++ }
8405
    else { break }
8406
  }
8407
8408
  var cutFront = 0, cutEnd = 0
8409
  var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length)
8410
  while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront))
8411
    { ++cutFront }
8412
  var newBot = lst(newText), oldBot = lst(oldText)
8413
  var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0),
8414
                           oldBot.length - (oldText.length == 1 ? cutFront : 0))
8415
  while (cutEnd < maxCutEnd &&
8416
         newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1))
8417
    { ++cutEnd }
8418
8419
  newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, "")
8420
  newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, "")
8421
8422
  var chFrom = Pos(fromLine, cutFront)
8423
  var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0)
8424
  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...
8425
    replaceRange(cm.doc, newText, chFrom, chTo, "+input")
8426
    return true
8427
  }
8428
};
8429
8430
ContentEditableInput.prototype.ensurePolled = function () {
8431
  this.forceCompositionEnd()
8432
};
8433
ContentEditableInput.prototype.reset = function () {
8434
  this.forceCompositionEnd()
8435
};
8436
ContentEditableInput.prototype.forceCompositionEnd = function () {
8437
  if (!this.composing) { return }
8438
  clearTimeout(this.readDOMTimeout)
8439
  this.composing = null
8440
  if (!this.pollContent()) { regChange(this.cm) }
8441
  this.div.blur()
8442
  this.div.focus()
8443
};
8444
ContentEditableInput.prototype.readFromDOMSoon = function () {
8445
    var this$1 = this;
8446
8447
  if (this.readDOMTimeout != null) { return }
8448
  this.readDOMTimeout = setTimeout(function () {
8449
    this$1.readDOMTimeout = null
8450
    if (this$1.composing) {
8451
      if (this$1.composing.done) { this$1.composing = null }
8452
      else { return }
8453
    }
8454
    if (this$1.cm.isReadOnly() || !this$1.pollContent())
8455
      { runInOp(this$1.cm, function () { return regChange(this$1.cm); }) }
8456
  }, 80)
8457
};
8458
8459
ContentEditableInput.prototype.setUneditable = function (node) {
8460
  node.contentEditable = "false"
8461
};
8462
8463
ContentEditableInput.prototype.onKeyPress = function (e) {
8464
  e.preventDefault()
8465
  if (!this.cm.isReadOnly())
8466
    { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0) }
8467
};
8468
8469
ContentEditableInput.prototype.readOnlyChanged = function (val) {
8470
  this.div.contentEditable = String(val != "nocursor")
8471
};
8472
8473
ContentEditableInput.prototype.onContextMenu = function () {};
8474
ContentEditableInput.prototype.resetPosition = function () {};
8475
8476
ContentEditableInput.prototype.needsContentAttribute = true
8477
8478
function posToDOM(cm, pos) {
8479
  var view = findViewForLine(cm, pos.line)
8480
  if (!view || view.hidden) { return null }
8481
  var line = getLine(cm.doc, pos.line)
8482
  var info = mapFromLineView(view, line, pos.line)
8483
8484
  var order = getOrder(line), side = "left"
8485
  if (order) {
8486
    var partPos = getBidiPartAt(order, pos.ch)
8487
    side = partPos % 2 ? "right" : "left"
8488
  }
8489
  var result = nodeAndOffsetInLineMap(info.map, pos.ch, side)
8490
  result.offset = result.collapse == "right" ? result.end : result.start
8491
  return result
8492
}
8493
8494
function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos }
8495
8496
function domTextBetween(cm, from, to, fromLine, toLine) {
8497
  var text = "", closing = false, lineSep = cm.doc.lineSeparator()
8498
  function recognizeMarker(id) { return function (marker) { return marker.id == id; } }
8499
  function walk(node) {
8500
    if (node.nodeType == 1) {
8501
      var cmText = node.getAttribute("cm-text")
8502
      if (cmText != null) {
8503
        if (cmText == "") { text += node.textContent.replace(/\u200b/g, "") }
8504
        else { text += cmText }
8505
        return
8506
      }
8507
      var markerID = node.getAttribute("cm-marker"), range
8508
      if (markerID) {
8509
        var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID))
8510
        if (found.length && (range = found[0].find()))
8511
          { text += getBetween(cm.doc, range.from, range.to).join(lineSep) }
8512
        return
8513
      }
8514
      if (node.getAttribute("contenteditable") == "false") { return }
8515
      for (var i = 0; i < node.childNodes.length; i++)
8516
        { walk(node.childNodes[i]) }
8517
      if (/^(pre|div|p)$/i.test(node.nodeName))
8518
        { closing = true }
8519
    } else if (node.nodeType == 3) {
8520
      var val = node.nodeValue
8521
      if (!val) { return }
8522
      if (closing) {
8523
        text += lineSep
8524
        closing = false
8525
      }
8526
      text += val
8527
    }
8528
  }
8529
  for (;;) {
8530
    walk(from)
8531
    if (from == to) { break }
8532
    from = from.nextSibling
8533
  }
8534
  return text
8535
}
8536
8537
function domToPos(cm, node, offset) {
8538
  var lineNode
8539
  if (node == cm.display.lineDiv) {
8540
    lineNode = cm.display.lineDiv.childNodes[offset]
8541
    if (!lineNode) { return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) }
8542
    node = null; offset = 0
8543
  } else {
8544
    for (lineNode = node;; lineNode = lineNode.parentNode) {
8545
      if (!lineNode || lineNode == cm.display.lineDiv) { return null }
8546
      if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) { break }
8547
    }
8548
  }
8549
  for (var i = 0; i < cm.display.view.length; i++) {
8550
    var lineView = cm.display.view[i]
8551
    if (lineView.node == lineNode)
8552
      { return locateNodeInLineView(lineView, node, offset) }
8553
  }
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...
8554
}
8555
8556
function locateNodeInLineView(lineView, node, offset) {
8557
  var wrapper = lineView.text.firstChild, bad = false
8558
  if (!node || !contains(wrapper, node)) { return badPos(Pos(lineNo(lineView.line), 0), true) }
8559
  if (node == wrapper) {
8560
    bad = true
8561
    node = wrapper.childNodes[offset]
8562
    offset = 0
8563
    if (!node) {
8564
      var line = lineView.rest ? lst(lineView.rest) : lineView.line
8565
      return badPos(Pos(lineNo(line), line.text.length), bad)
8566
    }
8567
  }
8568
8569
  var textNode = node.nodeType == 3 ? node : null, topNode = node
8570
  if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {
8571
    textNode = node.firstChild
8572
    if (offset) { offset = textNode.nodeValue.length }
8573
  }
8574
  while (topNode.parentNode != wrapper) { topNode = topNode.parentNode }
8575
  var measure = lineView.measure, maps = measure.maps
8576
8577
  function find(textNode, topNode, offset) {
8578
    for (var i = -1; i < (maps ? maps.length : 0); i++) {
8579
      var map = i < 0 ? measure.map : maps[i]
8580
      for (var j = 0; j < map.length; j += 3) {
8581
        var curNode = map[j + 2]
8582
        if (curNode == textNode || curNode == topNode) {
8583
          var line = lineNo(i < 0 ? lineView.line : lineView.rest[i])
8584
          var ch = map[j] + offset
8585
          if (offset < 0 || curNode != textNode) { ch = map[j + (offset ? 1 : 0)] }
8586
          return Pos(line, ch)
8587
        }
8588
      }
8589
    }
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...
8590
  }
8591
  var found = find(textNode, topNode, offset)
8592
  if (found) { return badPos(found, bad) }
8593
8594
  // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems
8595
  for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) {
8596
    found = find(after, after.firstChild, 0)
8597
    if (found)
8598
      { return badPos(Pos(found.line, found.ch - dist), bad) }
8599
    else
8600
      { dist += after.textContent.length }
8601
  }
8602
  for (var before = topNode.previousSibling, dist$1 = offset; before; before = before.previousSibling) {
8603
    found = find(before, before.firstChild, -1)
8604
    if (found)
8605
      { return badPos(Pos(found.line, found.ch + dist$1), bad) }
8606
    else
8607
      { dist$1 += before.textContent.length }
8608
  }
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...
8609
}
8610
8611
// TEXTAREA INPUT STYLE
8612
8613
var TextareaInput = function(cm) {
8614
  this.cm = cm
8615
  // See input.poll and input.reset
8616
  this.prevInput = ""
8617
8618
  // Flag that indicates whether we expect input to appear real soon
8619
  // now (after some event like 'keypress' or 'input') and are
8620
  // polling intensively.
8621
  this.pollingFast = false
8622
  // Self-resetting timeout for the poller
8623
  this.polling = new Delayed()
8624
  // Tracks when input.reset has punted to just putting a short
8625
  // string into the textarea instead of the full selection.
8626
  this.inaccurateSelection = false
8627
  // Used to work around IE issue with selection being forgotten when focus moves away from textarea
8628
  this.hasSelection = false
8629
  this.composing = null
8630
};
8631
8632
TextareaInput.prototype.init = function (display) {
8633
    var this$1 = this;
8634
8635
  var input = this, cm = this.cm
8636
8637
  // Wraps and hides input textarea
8638
  var div = this.wrapper = hiddenTextarea()
8639
  // The semihidden textarea that is focused when the editor is
8640
  // focused, and receives input.
8641
  var te = this.textarea = div.firstChild
8642
  display.wrapper.insertBefore(div, display.wrapper.firstChild)
8643
8644
  // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)
8645
  if (ios) { te.style.width = "0px" }
8646
8647
  on(te, "input", function () {
8648
    if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = null }
8649
    input.poll()
8650
  })
8651
8652
  on(te, "paste", function (e) {
8653
    if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
8654
8655
    cm.state.pasteIncoming = true
8656
    input.fastPoll()
8657
  })
8658
8659
  function prepareCopyCut(e) {
8660
    if (signalDOMEvent(cm, e)) { return }
8661
    if (cm.somethingSelected()) {
8662
      setLastCopied({lineWise: false, text: cm.getSelections()})
8663
      if (input.inaccurateSelection) {
8664
        input.prevInput = ""
8665
        input.inaccurateSelection = false
8666
        te.value = lastCopied.text.join("\n")
8667
        selectInput(te)
8668
      }
8669
    } else if (!cm.options.lineWiseCopyCut) {
8670
      return
8671
    } else {
8672
      var ranges = copyableRanges(cm)
8673
      setLastCopied({lineWise: true, text: ranges.text})
8674
      if (e.type == "cut") {
8675
        cm.setSelections(ranges.ranges, null, sel_dontScroll)
8676
      } else {
8677
        input.prevInput = ""
8678
        te.value = ranges.text.join("\n")
8679
        selectInput(te)
8680
      }
8681
    }
8682
    if (e.type == "cut") { cm.state.cutIncoming = true }
8683
  }
8684
  on(te, "cut", prepareCopyCut)
8685
  on(te, "copy", prepareCopyCut)
8686
8687
  on(display.scroller, "paste", function (e) {
8688
    if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return }
8689
    cm.state.pasteIncoming = true
8690
    input.focus()
8691
  })
8692
8693
  // Prevent normal selection in the editor (we handle our own)
8694
  on(display.lineSpace, "selectstart", function (e) {
8695
    if (!eventInWidget(display, e)) { e_preventDefault(e) }
8696
  })
8697
8698
  on(te, "compositionstart", function () {
8699
    var start = cm.getCursor("from")
8700
    if (input.composing) { input.composing.range.clear() }
8701
    input.composing = {
8702
      start: start,
8703
      range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"})
8704
    }
8705
  })
8706
  on(te, "compositionend", function () {
8707
    if (input.composing) {
8708
      input.poll()
8709
      input.composing.range.clear()
8710
      input.composing = null
8711
    }
8712
  })
8713
};
8714
8715
TextareaInput.prototype.prepareSelection = function () {
8716
  // Redraw the selection and/or cursor
8717
  var cm = this.cm, display = cm.display, doc = cm.doc
8718
  var result = prepareSelection(cm)
8719
8720
  // Move the hidden textarea near the cursor to prevent scrolling artifacts
8721
  if (cm.options.moveInputWithCursor) {
8722
    var headPos = cursorCoords(cm, doc.sel.primary().head, "div")
8723
    var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect()
8724
    result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
8725
                                        headPos.top + lineOff.top - wrapOff.top))
8726
    result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
8727
                                         headPos.left + lineOff.left - wrapOff.left))
8728
  }
8729
8730
  return result
8731
};
8732
8733
TextareaInput.prototype.showSelection = function (drawn) {
8734
  var cm = this.cm, display = cm.display
8735
  removeChildrenAndAdd(display.cursorDiv, drawn.cursors)
8736
  removeChildrenAndAdd(display.selectionDiv, drawn.selection)
8737
  if (drawn.teTop != null) {
8738
    this.wrapper.style.top = drawn.teTop + "px"
8739
    this.wrapper.style.left = drawn.teLeft + "px"
8740
  }
8741
};
8742
8743
// Reset the input to correspond to the selection (or to be empty,
8744
// when not typing and nothing is selected)
8745
TextareaInput.prototype.reset = function (typing) {
8746
  if (this.contextMenuPending) { return }
8747
  var minimal, selected, cm = this.cm, doc = cm.doc
8748
  if (cm.somethingSelected()) {
8749
    this.prevInput = ""
8750
    var range = doc.sel.primary()
8751
    minimal = hasCopyEvent &&
8752
      (range.to().line - range.from().line > 100 || (selected = cm.getSelection()).length > 1000)
8753
    var content = minimal ? "-" : selected || cm.getSelection()
8754
    this.textarea.value = content
8755
    if (cm.state.focused) { selectInput(this.textarea) }
8756
    if (ie && ie_version >= 9) { this.hasSelection = content }
8757
  } else if (!typing) {
8758
    this.prevInput = this.textarea.value = ""
8759
    if (ie && ie_version >= 9) { this.hasSelection = null }
8760
  }
8761
  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...
8762
};
8763
8764
TextareaInput.prototype.getField = function () { return this.textarea };
8765
8766
TextareaInput.prototype.supportsTouch = function () { return false };
8767
8768
TextareaInput.prototype.focus = function () {
8769
  if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) {
8770
    try { this.textarea.focus() }
8771
    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...
8772
  }
8773
};
8774
8775
TextareaInput.prototype.blur = function () { this.textarea.blur() };
8776
8777
TextareaInput.prototype.resetPosition = function () {
8778
  this.wrapper.style.top = this.wrapper.style.left = 0
8779
};
8780
8781
TextareaInput.prototype.receivedFocus = function () { this.slowPoll() };
8782
8783
// Poll for input changes, using the normal rate of polling. This
8784
// runs as long as the editor is focused.
8785
TextareaInput.prototype.slowPoll = function () {
8786
    var this$1 = this;
8787
8788
  if (this.pollingFast) { return }
8789
  this.polling.set(this.cm.options.pollInterval, function () {
8790
    this$1.poll()
8791
    if (this$1.cm.state.focused) { this$1.slowPoll() }
8792
  })
8793
};
8794
8795
// When an event has just come in that is likely to add or change
8796
// something in the input textarea, we poll faster, to ensure that
8797
// the change appears on the screen quickly.
8798
TextareaInput.prototype.fastPoll = function () {
8799
  var missed = false, input = this
8800
  input.pollingFast = true
8801
  function p() {
8802
    var changed = input.poll()
8803
    if (!changed && !missed) {missed = true; input.polling.set(60, p)}
8804
    else {input.pollingFast = false; input.slowPoll()}
8805
  }
8806
  input.polling.set(20, p)
8807
};
8808
8809
// Read input from the textarea, and update the document to match.
8810
// When something is selected, it is present in the textarea, and
8811
// selected (unless it is huge, in which case a placeholder is
8812
// used). When nothing is selected, the cursor sits after previously
8813
// seen text (can be empty), which is stored in prevInput (we must
8814
// not reset the textarea when typing, because that breaks IME).
8815
TextareaInput.prototype.poll = function () {
8816
    var this$1 = this;
8817
8818
  var cm = this.cm, input = this.textarea, prevInput = this.prevInput
8819
  // Since this is called a *lot*, try to bail out as cheaply as
8820
  // possible when it is clear that nothing happened. hasSelection
8821
  // will be the case when there is a lot of text in the textarea,
8822
  // in which case reading its value would be expensive.
8823
  if (this.contextMenuPending || !cm.state.focused ||
8824
      (hasSelection(input) && !prevInput && !this.composing) ||
8825
      cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq)
8826
    { return false }
8827
8828
  var text = input.value
8829
  // If nothing changed, bail.
8830
  if (text == prevInput && !cm.somethingSelected()) { return false }
8831
  // Work around nonsensical selection resetting in IE9/10, and
8832
  // inexplicable appearance of private area unicode characters on
8833
  // some key combos in Mac (#2689).
8834
  if (ie && ie_version >= 9 && this.hasSelection === text ||
8835
      mac && /[\uf700-\uf7ff]/.test(text)) {
8836
    cm.display.input.reset()
8837
    return false
8838
  }
8839
8840
  if (cm.doc.sel == cm.display.selForContextMenu) {
8841
    var first = text.charCodeAt(0)
8842
    if (first == 0x200b && !prevInput) { prevInput = "\u200b" }
8843
    if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") }
8844
  }
8845
  // Find the part of the input that is actually new
8846
  var same = 0, l = Math.min(prevInput.length, text.length)
8847
  while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same }
8848
8849
  runInOp(cm, function () {
8850
    applyTextInput(cm, text.slice(same), prevInput.length - same,
8851
                   null, this$1.composing ? "*compose" : null)
8852
8853
    // Don't leave long text in the textarea, since it makes further polling slow
8854
    if (text.length > 1000 || text.indexOf("\n") > -1) { input.value = this$1.prevInput = "" }
8855
    else { this$1.prevInput = text }
8856
8857
    if (this$1.composing) {
8858
      this$1.composing.range.clear()
8859
      this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor("to"),
8860
                                         {className: "CodeMirror-composing"})
8861
    }
8862
  })
8863
  return true
8864
};
8865
8866
TextareaInput.prototype.ensurePolled = function () {
8867
  if (this.pollingFast && this.poll()) { this.pollingFast = false }
8868
};
8869
8870
TextareaInput.prototype.onKeyPress = function () {
8871
  if (ie && ie_version >= 9) { this.hasSelection = null }
8872
  this.fastPoll()
8873
};
8874
8875
TextareaInput.prototype.onContextMenu = function (e) {
8876
  var input = this, cm = input.cm, display = cm.display, te = input.textarea
8877
  var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop
8878
  if (!pos || presto) { return } // Opera is difficult.
8879
8880
  // Reset the current text selection only if the click is done outside of the selection
8881
  // and 'resetSelectionOnContextMenu' option is true.
8882
  var reset = cm.options.resetSelectionOnContextMenu
8883
  if (reset && cm.doc.sel.contains(pos) == -1)
8884
    { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll) }
8885
8886
  var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText
8887
  input.wrapper.style.cssText = "position: absolute"
8888
  var wrapperBox = input.wrapper.getBoundingClientRect()
8889
  te.style.cssText = "position: absolute; width: 30px; height: 30px;\n      top: " + (e.clientY - wrapperBox.top - 5) + "px; left: " + (e.clientX - wrapperBox.left - 5) + "px;\n      z-index: 1000; background: " + (ie ? "rgba(255, 255, 255, .05)" : "transparent") + ";\n      outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);"
8890
  var oldScrollY
8891
  if (webkit) { oldScrollY = window.scrollY } // Work around Chrome issue (#2712)
8892
  display.input.focus()
8893
  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 8891 is false. Are you sure the function scrollTo handles undefined variables?
Loading history...
8894
  display.input.reset()
8895
  // Adds "Select all" to context menu in FF
8896
  if (!cm.somethingSelected()) { te.value = input.prevInput = " " }
8897
  input.contextMenuPending = true
8898
  display.selForContextMenu = cm.doc.sel
8899
  clearTimeout(display.detectingSelectAll)
8900
8901
  // Select-all will be greyed out if there's nothing to select, so
8902
  // this adds a zero-width space so that we can later check whether
8903
  // it got selected.
8904
  function prepareSelectAllHack() {
8905
    if (te.selectionStart != null) {
8906
      var selected = cm.somethingSelected()
8907
      var extval = "\u200b" + (selected ? te.value : "")
8908
      te.value = "\u21da" // Used to catch context-menu undo
8909
      te.value = extval
8910
      input.prevInput = selected ? "" : "\u200b"
8911
      te.selectionStart = 1; te.selectionEnd = extval.length
8912
      // Re-set this, in case some other handler touched the
8913
      // selection in the meantime.
8914
      display.selForContextMenu = cm.doc.sel
8915
    }
8916
  }
8917
  function rehide() {
8918
    input.contextMenuPending = false
8919
    input.wrapper.style.cssText = oldWrapperCSS
8920
    te.style.cssText = oldCSS
8921
    if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos) }
8922
8923
    // Try to detect the user choosing select-all
8924
    if (te.selectionStart != null) {
8925
      if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack() }
8926
      var i = 0, poll = function () {
8927
        if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 &&
8928
            te.selectionEnd > 0 && input.prevInput == "\u200b")
8929
          { operation(cm, selectAll)(cm) }
8930
        else if (i++ < 10) { display.detectingSelectAll = setTimeout(poll, 500) }
8931
        else { display.input.reset() }
8932
      }
8933
      display.detectingSelectAll = setTimeout(poll, 200)
8934
    }
8935
  }
8936
8937
  if (ie && ie_version >= 9) { prepareSelectAllHack() }
8938
  if (captureRightClick) {
8939
    e_stop(e)
8940
    var mouseup = function () {
8941
      off(window, "mouseup", mouseup)
8942
      setTimeout(rehide, 20)
8943
    }
8944
    on(window, "mouseup", mouseup)
8945
  } else {
8946
    setTimeout(rehide, 50)
8947
  }
8948
};
8949
8950
TextareaInput.prototype.readOnlyChanged = function (val) {
8951
  if (!val) { this.reset() }
8952
};
8953
8954
TextareaInput.prototype.setUneditable = function () {};
8955
8956
TextareaInput.prototype.needsContentAttribute = false
8957
8958
function fromTextArea(textarea, options) {
8959
  options = options ? copyObj(options) : {}
8960
  options.value = textarea.value
8961
  if (!options.tabindex && textarea.tabIndex)
8962
    { options.tabindex = textarea.tabIndex }
8963
  if (!options.placeholder && textarea.placeholder)
8964
    { options.placeholder = textarea.placeholder }
8965
  // Set autofocus to true if this textarea is focused, or if it has
8966
  // autofocus and no other element is focused.
8967
  if (options.autofocus == null) {
8968
    var hasFocus = activeElt()
8969
    options.autofocus = hasFocus == textarea ||
8970
      textarea.getAttribute("autofocus") != null && hasFocus == document.body
8971
  }
8972
8973
  function save() {textarea.value = cm.getValue()}
8974
8975
  var realSubmit
8976
  if (textarea.form) {
8977
    on(textarea.form, "submit", save)
8978
    // Deplorable hack to make the submit method do the right thing.
8979
    if (!options.leaveSubmitMethodAlone) {
8980
      var form = textarea.form
8981
      realSubmit = form.submit
8982
      try {
8983
        var wrappedSubmit = form.submit = function () {
8984
          save()
8985
          form.submit = realSubmit
8986
          form.submit()
8987
          form.submit = wrappedSubmit
8988
        }
8989
      } 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...
8990
    }
8991
  }
8992
8993
  options.finishInit = function (cm) {
8994
    cm.save = save
8995
    cm.getTextArea = function () { return textarea; }
8996
    cm.toTextArea = function () {
8997
      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...
8998
      save()
8999
      textarea.parentNode.removeChild(cm.getWrapperElement())
9000
      textarea.style.display = ""
9001
      if (textarea.form) {
9002
        off(textarea.form, "submit", save)
9003
        if (typeof textarea.form.submit == "function")
9004
          { 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...
9005
      }
9006
    }
9007
  }
9008
9009
  textarea.style.display = "none"
9010
  var cm = CodeMirror(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); },
9011
    options)
9012
  return cm
9013
}
9014
9015
function addLegacyProps(CodeMirror) {
9016
  CodeMirror.off = off
9017
  CodeMirror.on = on
9018
  CodeMirror.wheelEventPixels = wheelEventPixels
9019
  CodeMirror.Doc = Doc
9020
  CodeMirror.splitLines = splitLinesAuto
9021
  CodeMirror.countColumn = countColumn
9022
  CodeMirror.findColumn = findColumn
9023
  CodeMirror.isWordChar = isWordCharBasic
9024
  CodeMirror.Pass = Pass
9025
  CodeMirror.signal = signal
9026
  CodeMirror.Line = Line
9027
  CodeMirror.changeEnd = changeEnd
9028
  CodeMirror.scrollbarModel = scrollbarModel
9029
  CodeMirror.Pos = Pos
9030
  CodeMirror.cmpPos = cmp
9031
  CodeMirror.modes = modes
9032
  CodeMirror.mimeModes = mimeModes
9033
  CodeMirror.resolveMode = resolveMode
9034
  CodeMirror.getMode = getMode
9035
  CodeMirror.modeExtensions = modeExtensions
9036
  CodeMirror.extendMode = extendMode
9037
  CodeMirror.copyState = copyState
9038
  CodeMirror.startState = startState
9039
  CodeMirror.innerMode = innerMode
9040
  CodeMirror.commands = commands
9041
  CodeMirror.keyMap = keyMap
9042
  CodeMirror.keyName = keyName
9043
  CodeMirror.isModifierKey = isModifierKey
9044
  CodeMirror.lookupKey = lookupKey
9045
  CodeMirror.normalizeKeyMap = normalizeKeyMap
9046
  CodeMirror.StringStream = StringStream
9047
  CodeMirror.SharedTextMarker = SharedTextMarker
9048
  CodeMirror.TextMarker = TextMarker
9049
  CodeMirror.LineWidget = LineWidget
9050
  CodeMirror.e_preventDefault = e_preventDefault
9051
  CodeMirror.e_stopPropagation = e_stopPropagation
9052
  CodeMirror.e_stop = e_stop
9053
  CodeMirror.addClass = addClass
9054
  CodeMirror.contains = contains
9055
  CodeMirror.rmClass = rmClass
9056
  CodeMirror.keyNames = keyNames
9057
}
9058
9059
// EDITOR CONSTRUCTOR
9060
9061
defineOptions(CodeMirror)
9062
9063
addEditorMethods(CodeMirror)
9064
9065
// Set up methods on CodeMirror's prototype to redirect to the editor's document.
9066
var dontDelegate = "iter insert remove copy getEditor constructor".split(" ")
9067
for (var prop in Doc.prototype) { if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
9068
  { CodeMirror.prototype[prop] = (function(method) {
9069
    return function() {return method.apply(this.doc, arguments)}
9070
  })(Doc.prototype[prop]) } }
9071
9072
eventMixin(Doc)
9073
9074
// INPUT HANDLING
9075
9076
CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput}
9077
9078
// MODE DEFINITION AND QUERYING
9079
9080
// Extra arguments are stored as the mode's dependencies, which is
9081
// used by (legacy) mechanisms like loadmode.js to automatically
9082
// load a mode. (Preferred mechanism is the require/define calls.)
9083
CodeMirror.defineMode = function(name/*, mode, …*/) {
9084
  if (!CodeMirror.defaults.mode && name != "null") { CodeMirror.defaults.mode = name }
9085
  defineMode.apply(this, arguments)
9086
}
9087
9088
CodeMirror.defineMIME = defineMIME
9089
9090
// Minimal default mode.
9091
CodeMirror.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); })
9092
CodeMirror.defineMIME("text/plain", "null")
9093
9094
// EXTENSIONS
9095
9096
CodeMirror.defineExtension = function (name, func) {
9097
  CodeMirror.prototype[name] = func
9098
}
9099
CodeMirror.defineDocExtension = function (name, func) {
9100
  Doc.prototype[name] = func
9101
}
9102
9103
CodeMirror.fromTextArea = fromTextArea
9104
9105
addLegacyProps(CodeMirror)
9106
9107
CodeMirror.version = "5.22.2"
9108
9109
return CodeMirror;
9110
9111
})));