PageSourceSearch

https://coastlinebible.org/modules/wp-views/vendor/toolset/toolset…/res/js/codemirror/lib/codemirror.js

js coastlinebible.org collected 2026-09-24 11:32:41 UTC 369,437 bytes, 9,683 lines download raw bytes

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) :
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.
18var userAgent = navigator.userAgent
19var platform = navigator.platform
20
21var gecko = /gecko\/\d/i.test(userAgent)
22var ie_upto10 = /MSIE \d/.test(userAgent)
23var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent)
24var edge = /Edge\/(\d+)/.exec(userAgent)
25var ie = ie_upto10 || ie_11up || edge
26var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1])
27var webkit = !edge && /WebKit\//.test(userAgent)
28var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent)
29var chrome = !edge && /Chrome\//.test(userAgent)
30var presto = /Opera\//.test(userAgent)
31var safari = /Apple Computer/.test(navigator.vendor)
32var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent)
33var phantom = /PhantomJS/.test(userAgent)
34
35var ios = !edge && /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent)
36var android = /Android/.test(userAgent)
37// This is woefully incomplete. Suggestions for alternative methods welcome.
38var mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent)
39var mac = ios || /Mac/.test(platform)
40var chromeOS = /\bCrOS\b/.test(userAgent)
41var windows = /win/i.test(platform)
42
43var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/)
44if (presto_version) { presto_version = Number(presto_version[1]) }
45if (presto_version && presto_version >= 15) { presto = false; webkit = true }
46// Some browsers use the wrong event properties to signal cmd/ctrl on OS X
47var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11))
48var captureRightClick = gecko || (ie && ie_version >= 9)
49
50function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") }
51
52var rmClass = function(node, cls) {
53  var current = node.className
54  var match = classTest(cls).exec(current)
55  if (match) {
56    var after = current.slice(match.index + match[0].length)
57    node.className = current.slice(0, match.index) + (after ? match[1] + after : "")
58  }
59}
60
61function removeChildren(e) {
62  for (var count = e.childNodes.length; count > 0; --count)
63    { e.removeChild(e.firstChild) }
64  return e
65}
66
67function removeChildrenAndAdd(parent, e) {
68  return removeChildren(parent).appendChild(e)
69}
70
71function elt(tag, content, className, style) {
72  var e = document.createElement(tag)
73  if (className) { e.className = className }
74  if (style) { e.style.cssText = style }
75  if (typeof content == "string") { e.appendChild(document.createTextNode(content)) }
76  else if (content) { for (var i = 0; i < content.length; ++i) { e.appendChild(content[i]) } }
77  return e
78}
79// wrapper for elt, which removes the elt from the accessibility tree
80function eltP(tag, content, className, style) {
81  var e = elt(tag, content, className, style)
82  e.setAttribute("role", "presentation")
83  return e
84}
85
86var range
87if (document.createRange) { range = function(node, start, end, endNode) {
88  var r = document.createRange()
89  r.setEnd(endNode || node, end)
90  r.setStart(node, start)
91  return r
92} }
93else { range = function(node, start, end) {
94  var r = document.body.createTextRange()
95  try { r.moveToElementText(node.parentNode) }
96  catch(e) { return r }
97  r.collapse(true)
98  r.moveEnd("character", end)
99  r.moveStart("character", start)
100  return r
101} }
102
103function contains(parent, child) {
104  if (child.nodeType == 3) // Android browser always returns false when child is a textnode
105    { child = child.parentNode }
106  if (parent.contains)
107    { return parent.contains(child) }
108  do {
109    if (child.nodeType == 11) { child = child.host }
110    if (child == parent) { return true }
111  } while (child = child.parentNode)
112}
113
114function activeElt() {
115  // IE and Edge may throw an "Unspecified Error" when accessing document.activeElement.
116  // IE < 10 will throw when accessed while the page is loading or in an iframe.
117  // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable.
118  var activeElement
119  try {
120    activeElement = document.activeElement
121  } catch(e) {
122    activeElement = document.body || null
123  }
124  while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement)
125    { activeElement = activeElement.shadowRoot.activeElement }
126  return activeElement
127}
128
129function addClass(node, cls) {
130  var current = node.className
131  if (!classTest(cls).test(current)) { node.className += (current ? " " : "") + cls }
132}
133function joinClasses(a, b) {
134  var as = a.split(" ")
135  for (var i = 0; i < as.length; i++)
136    { if (as[i] && !classTest(as[i]).test(b)) { b += " " + as[i] } }
137  return b
138}
139
140var selectInput = function(node) { node.select() }
141if (ios) // Mobile Safari apparently has a bug where select() is broken.
142  { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length } }
143else if (ie) // Suppress mysterious IE10 errors
144  { selectInput = function(node) { try { node.select() } catch(_e) {} } }
145
146function bind(f) {
147  var args = Array.prototype.slice.call(arguments, 1)
148  return function(){return f.apply(null, args)}
149}
150
151function copyObj(obj, target, overwrite) {
152  if (!target) { target = {} }
153  for (var prop in obj)
154    { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
155      { target[prop] = obj[prop] } }
156  return target
157}
158
159// Counts the column offset in a string, taking tabs into account.
160// Used mostly to find indentation.
161function countColumn(string, end, tabSize, startIndex, startValue) {
162  if (end == null) {
163    end = string.search(/[^\s\u00a0]/)
164    if (end == -1) { end = string.length }
165  }
166  for (var i = startIndex || 0, n = startValue || 0;;) {
167    var nextTab = string.indexOf("\t", i)
168    if (nextTab < 0 || nextTab >= end)
169      { return n + (end - i) }
170    n += nextTab - i
171    n += tabSize - (n % tabSize)
172    i = nextTab + 1
173  }
174}
175
176var Delayed = function() {this.id = null};
177Delayed.prototype.set = function (ms, f) {
178  clearTimeout(this.id)
179  this.id = setTimeout(f, ms)
180};
181
182function indexOf(array, elt) {
183  for (var i = 0; i < array.length; ++i)
184    { if (array[i] == elt) { return i } }
185  return -1
186}
187
188// Number of pixels added to scroller and sizer to hide scrollbar
189var scrollerGap = 30
190
191// Returned or thrown by various protocols to signal 'I'm not
192// handling this'.
193var Pass = {toString: function(){return "CodeMirror.Pass"}}
194
195// Reused option objects for setSelection & friends
196var sel_dontScroll = {scroll: false};
197var sel_mouse = {origin: "*mouse"};
198var sel_move = {origin: "+move"};
199// The inverse of countColumn -- find the offset that corresponds to
200// a particular column.
201function findColumn(string, goal, tabSize) {
202  for (var pos = 0, col = 0;;) {
203    var nextTab = string.indexOf("\t", pos)
204    if (nextTab == -1) { nextTab = string.length }
205    var skipped = nextTab - pos
206    if (nextTab == string.length || col + skipped >= goal)
207      { return pos + Math.min(skipped, goal - col) }
208    col += nextTab - pos
209    col += tabSize - (col % tabSize)
210    pos = nextTab + 1
211    if (col >= goal) { return pos }
212  }
213}
214
215var spaceStrs = [""]
216function spaceStr(n) {
217  while (spaceStrs.length <= n)
218    { spaceStrs.push(lst(spaceStrs) + " ") }
219  return spaceStrs[n]
220}
221
222function lst(arr) { return arr[arr.length-1] }
223
224function map(array, f) {
225  var out = []
226  for (var i = 0; i < array.length; i++) { out[i] = f(array[i], i) }
227  return out
228}
229
230function insertSorted(array, value, score) {
231  var pos = 0, priority = score(value)
232  while (pos < array.length && score(array[pos]) <= priority) { pos++ }
233  array.splice(pos, 0, value)
234}
235
236function nothing() {}
237
238function createObj(base, props) {
239  var inst
240  if (Object.create) {
241    inst = Object.create(base)
242  } else {
243    nothing.prototype = base
244    inst = new nothing()
245  }
246  if (props) { copyObj(props, inst) }
247  return inst
248}
249
250var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/
251function isWordCharBasic(ch) {
252  return /\w/.test(ch) || ch > "\x80" &&
253    (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch))
254}
255function isWordChar(ch, helper) {
256  if (!helper) { return isWordCharBasic(ch) }
257  if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) { return true }
258  return helper.test(ch)
259}
260
261function isEmpty(obj) {
262  for (var n in obj) { if (obj.hasOwnProperty(n) && obj[n]) { return false } }
263  return true
264}
265
266// Extending unicode characters. A series of a non-extending char +
267// any number of extending chars is treated as a single unit as far
268// as editing and measuring is concerned. This is not fully correct,
269// since some scripts/fonts/browsers also treat other configurations
270// of code points as a group.
271var 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]/
272function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) }
273
274// Returns a number from the range [`0`; `str.length`] unless `pos` is outside that range.
275function skipExtendingChars(str, pos, dir) {
276  while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(pos))) { pos += dir }
277  return pos
278}
279
280// Returns the value from the range [`from`; `to`] that satisfies
281// `pred` and is closest to `from`. Assumes that at least `to`
282// satisfies `pred`. Supports `from` being greater than `to`.
283function findFirst(pred, from, to) {
284  // At any point we are certain `to` satisfies `pred`, don't know
285  // whether `from` does.
286  var dir = from > to ? -1 : 1
287  for (;;) {
288    if (from == to) { return from }
289    var midF = (from + to) / 2, mid = dir < 0 ? Math.ceil(midF) : Math.floor(midF)
290    if (mid == from) { return pred(mid) ? from : to }
291    if (pred(mid)) { to = mid }
292    else { from = mid + dir }
293  }
294}
295
296// The display handles the DOM integration, both for input reading
297// and content drawing. It holds references to DOM nodes and
298// display-related state.
299
300function Display(place, doc, input) {
301  var d = this
302  this.input = input
303
304  // Covers bottom-right square when both scrollbars are present.
305  d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler")
306  d.scrollbarFiller.setAttribute("cm-not-content", "true")
307  // Covers bottom of gutter when coverGutterNextToScrollbar is on
308  // and h scrollbar is present.
309  d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler")
310  d.gutterFiller.setAttribute("cm-not-content", "true")
311  // Will contain the actual code, positioned to cover the viewport.
312  d.lineDiv = eltP("div", null, "CodeMirror-code")
313  // Elements are added to these to represent selection and curs
313ors.
314  d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1")
315  d.cursorDiv = elt("div", null, "CodeMirror-cursors")
316  // A visibility: hidden element used to find the size of things.
317  d.measure = elt("div", null, "CodeMirror-measure")
318  // When lines outside of the viewport are measured, they are drawn in this.
319  d.lineMeasure = elt("div", null, "CodeMirror-measure")
320  // Wraps everything that needs to exist inside the vertically-padded coordinate system
321  d.lineSpace = eltP("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],
322                    null, "position: relative; outline: none")
323  var lines = eltP("div", [d.lineSpace], "CodeMirror-lines")
324  // Moved around its parent to cover visible view.
325  d.mover = elt("div", [lines], null, "position: relative")
326  // Set to the height of the document, allowing scrolling.
327  d.sizer = elt("div", [d.mover], "CodeMirror-sizer")
328  d.sizerWidth = null
329  // Behavior of elts with overflow: auto and padding is
330  // inconsistent across browsers. This is used to ensure the
331  // scrollable area is big enough.
332  d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;")
333  // Will contain the gutters, if any.
334  d.gutters = elt("div", null, "CodeMirror-gutters")
335  d.lineGutter = null
336  // Actual scrollable element.
337  d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll")
338  d.scroller.setAttribute("tabIndex", "-1")
339  // The element in which the editor lives.
340  d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror")
341
342  // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)
343  if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0 }
344  if (!webkit && !(gecko && mobile)) { d.scroller.draggable = true }
345
346  if (place) {
347    if (place.appendChild) { place.appendChild(d.wrapper) }
348    else { place(d.wrapper) }
349  }
350
351  // Current rendered range (may be bigger than the view window).
352  d.viewFrom = d.viewTo = doc.first
353  d.reportedViewFrom = d.reportedViewTo = doc.first
354  // Information about the rendered lines.
355  d.view = []
356  d.renderedView = null
357  // Holds info about a single rendered line when it was rendered
358  // for measurement, while not in view.
359  d.externalMeasured = null
360  // Empty space (in pixels) above the view
361  d.viewOffset = 0
362  d.lastWrapHeight = d.lastWrapWidth = 0
363  d.updateLineNumbers = null
364
365  d.nativeBarWidth = d.barHeight = d.barWidth = 0
366  d.scrollbarsClipped = false
367
368  // Used to only resize the line number gutter when necessary (when
369  // the amount of lines crosses a boundary that makes its width change)
370  d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null
371  // Set to true when a non-horizontal-scrolling line widget is
372  // added. As an optimization, line widget aligning is skipped when
373  // this is false.
374  d.alignWidgets = false
375
376  d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null
377
378  // Tracks the maximum line length so that the horizontal scrollbar
379  // can be kept static when scrolling.
380  d.maxLine = null
381  d.maxLineLength = 0
382  d.maxLineChanged = false
383
384  // Used for measuring wheel scrolling granularity
385  d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null
386
387  // True when shift is held down.
388  d.shift = false
389
390  // Used to track whether anything happened since the context menu
391  // was opened.
392  d.selForContextMenu = null
393
394  d.activeTouch = null
395
396  input.init(d)
397}
398
399// Find the line object corresponding to the given line number.
400function getLine(doc, n) {
401  n -= doc.first
402  if (n < 0 || n >= doc.size) { throw new Error("There is no line " + (n + doc.first) + " in the document.") }
403  var chunk = doc
404  while (!chunk.lines) {
405    for (var i = 0;; ++i) {
406      var child = chunk.children[i], sz = child.chunkSize()
407      if (n < sz) { chunk = child; break }
408      n -= sz
409    }
410  }
411  return chunk.lines[n]
412}
413
414// Get the part of a document between two positions, as an array of
415// strings.
416function getBetween(doc, start, end) {
417  var out = [], n = start.line
418  doc.iter(start.line, end.line + 1, function (line) {
419    var text = line.text
420    if (n == end.line) { text = text.slice(0, end.ch) }
421    if (n == start.line) { text = text.slice(start.ch) }
422    out.push(text)
423    ++n
424  })
425  return out
426}
427// Get the lines between from and to, as array of strings.
428function getLines(doc, from, to) {
429  var out = []
430  doc.iter(from, to, function (line) { out.push(line.text) }) // iter aborts when callback returns truthy value
431  return out
432}
433
434// Update the height of a line, propagating the height change
435// upwards to parent nodes.
436function updateLineHeight(line, height) {
437  var diff = height - line.height
438  if (diff) { for (var n = line; n; n = n.parent) { n.height += diff } }
439}
440
441// Given a line object, find its line number by walking up through
442// its parent links.
443function lineNo(line) {
444  if (line.parent == null) { return null }
445  var cur = line.parent, no = indexOf(cur.lines, line)
446  for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
447    for (var i = 0;; ++i) {
448      if (chunk.children[i] == cur) { break }
449      no += chunk.children[i].chunkSize()
450    }
451  }
452  return no + cur.first
453}
454
455// Find the line at the given vertical position, using the height
456// information in the document tree.
457function lineAtHeight(chunk, h) {
458  var n = chunk.first
459  outer: do {
460    for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) {
461      var child = chunk.children[i$1], ch = child.height
462      if (h < ch) { chunk = child; continue outer }
463      h -= ch
464      n += child.chunkSize()
465    }
466    return n
467  } while (!chunk.lines)
468  var i = 0
469  for (; i < chunk.lines.length; ++i) {
470    var line = chunk.lines[i], lh = line.height
471    if (h < lh) { break }
472    h -= lh
473  }
474  return n + i
475}
476
477function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size}
478
479function lineNumberFor(options, i) {
480  return String(options.lineNumberFormatter(i + options.firstLineNumber))
481}
482
483// A Pos instance represents a position within the text.
484function Pos(line, ch, sticky) {
485  if ( sticky === void 0 ) sticky = null;
486
487  if (!(this instanceof Pos)) { return new Pos(line, ch, sticky) }
488  this.line = line
489  this.ch = ch
490  this.sticky = sticky
491}
492
493// Compare two positions, return 0 if they are the same, a negative
494// number when a is less, and a positive number otherwise.
495function cmp(a, b) { return a.line - b.line || a.ch - b.ch }
496
497function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 }
498
499function copyPos(x) {return Pos(x.line, x.ch)}
500function maxPos(a, b) { return cmp(a, b) < 0 ? b : a }
501function minPos(a, b) { return cmp(a, b) < 0 ? a : b }
502
503// Most of the external API clips given positions to make sure they
504// actually exist within the document.
505function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))}
506function clipPos(doc, pos) {
507  if (pos.line < doc.first) { return Pos(doc.first, 0) }
508  var last = doc.first + doc.size - 1
509  if (pos.line > last) { return Pos(last, getLine(doc, last).text.length) }
510  return clipToLen(pos, getLine(doc, pos.line).text.length)
511}
512function clipToLen(pos, linelen) {
513  var ch = pos.ch
514  if (ch == null || ch > linelen) { return Pos(pos.line, linelen) }
515  else if (ch < 0) { return Pos(pos.line, 0) }
516  else { return pos }
517}
518function clipPosArray(doc, array) {
519  var out = []
520  for (var i = 0; i < array.length; i++) { out[i] = clipPos(doc, array[i]) }
521  return out
522}
523
524// Optimize some code when these features are not used.
525var sawReadOnlySpans = false;
526var sawCollapsedSpans = false;
527function seeReadOnlySpans() {
528  sawReadOnlySpans = true
529}
530
531function seeCollapsedSpans() {
532  sawCollapsedSpans = true
533}
534
535// TEXTMARKER SPANS
536
537function MarkedSpan(marker, from, to) {
538  this.marker = marker
539  this.from = from; this.to = to
540}
541
542// Search an array of spans for a span matching the given marker.
543function getMarkedSpanFor(spans, marker) {
544  if (spans) { for (var i = 0; i < spans.length; ++i) {
545    var span = spans[i]
546    if (span.marker == marker) { return span }
547  } }
548}
549// Remove a span from an array, returning undefined if no spans are
550// left (we don't store arrays for lines without spans).
551function removeMarkedSpan(spans, span) {
552  var r
553  for (var i = 0; i < spans.length; ++i)
554    { if (spans[i] != span) { (r || (r = [])).push(spans[i]) } }
555  return r
556}
557// Add a span to a line.
558function addMarkedSpan(line, span) {
559  line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span]
560  span.marker.attachLine(line)
561}
562
563// Used for the algorithm that adjusts markers for a change in the
564// document. These functions cut an array of spans at a given
565// character position, returning an array of remaining chunks (or
566// undefined if nothing remains).
567function markedSpansBefore(old, startCh, isInsert) {
568  var nw
569  if (old) { for (var i = 0; i < old.length; ++i) {
570    var span = old[i], marker = span.marker
571    var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh)
572    if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) {
573      var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh)
574      ;(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to))
575    }
576  } }
577  return nw
578}
579function markedSpansAfter(old, endCh, isInsert) {
580  var nw
581  if (old) { for (var i = 0; i < old.length; ++i) {
582    var span = old[i], marker = span.marker
583    var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh)
584    if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) {
585      var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh)
586      ;(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh,
587                                            span.to == null ? null : span.to - endCh))
588    }
589  } }
590  return nw
591}
592
593// Given a change object, compute the new set of marker spans that
594// cover the line in which the change took place. Removes spans
595// entirely within the change, reconnects spans belonging to the
596// same marker that appear on both sides of the change, and cuts off
597// spans partially within the change. Returns an array of span
598// arrays with one element for each line in (after) the change.
599function stretchSpansOverChange(doc, change) {
600  if (change.full) { return null }
601  var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans
602  var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans
603  if (!oldFirst && !oldLast) { return null }
604
605  var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0
606  // Get the spans that 'stick out' on both sides
607  var first = markedSpansBefore(oldFirst, startCh, isInsert)
608  var last = markedSpansAfter(oldLast, endCh, isInsert)
609
610  // Next, merge those two ends
611  var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0)
612  if (first) {
613    // Fix up .to properties of first
614    for (var i = 0; i < first.length; ++i) {
615      var span = first[i]
616      if (span.to == null) {
617        var found = getMarkedSpanFor(last, span.marker)
618        if (!found) { span.to = startCh }
619        else if (sameLine) { span.to = found.to == null ? null : found.to + offset }
620      }
621    }
622  }
623  if (last) {
624    // Fix up .from in last (or move them into first in case of sameLine)
625    for (var i$1 = 0; i$1 < last.length; ++i$1) {
626      var span$1 = last[i$1]
627      if (span$1.to != null) { span$1.to += offset }
628      if (span$1.from == null) {
629        var found$1 = getMarkedSpanFor(first, span$1.marker)
630        if (!found$1) {
631          span$1.from = offset
632          if (sameLine) { (first || (first = [])).push(span$1) }
633        }
634      } else {
635        span$1.from += offset
636        if (sameLine) { (first || (first = [])).push(span$1) }
637      }
638    }
639  }
640  // Make sure we didn't create any zero-length spans
641  if (first) { first = clearEmptySpans(first) }
642  if (last && last != first) { last = clearEmptySpans(last) }
643
644  var newMarkers = [first]
645  if (!sameLine) {
646    // Fill gap with whole-line-spans
647    var gap = change.text.length - 2, gapMarkers
648    if (gap > 0 && first)
649      { for (var i$2 = 0; i$2 < first.length; ++i$2)
650        { if (first[i$2].to == null)
651          { (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i$2].marker, null, null)) } } }
652    for (var i$3 = 0; i$3 < gap; ++i$3)
653      { newMarkers.push(gapMarkers) }
654    newMarkers.push(last)
655  }
656  return newMarkers
657}
658
659// Remove spans that are empty and don't have a clearWhenEmpty
660// option of false.
661function clearEmptySpans(spans) {
662  for (var i = 0; i < spans.length; ++i) {
663    var span = spans[i]
664    if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)
665      { spans.splice(i--, 1) }
666  }
667  if (!spans.length) { return null }
668  return spans
669}
670
671// Used to 'clip' out readOnly ranges when making a change.
672function removeReadOnlyRanges(doc, from, to) {
673  var markers = null
674  doc.iter(from.line, to.line + 1, function (line) {
675    if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
676      var mark = line.markedSpans[i].marker
677      if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
678        { (markers || (markers = [])).push(mark) }
679    } }
680  })
681  if (!markers) { return null }
682  var parts = [{from: from, to: to}]
683  for (var i = 0; i < markers.length; ++i) {
684    var mk = markers[i], m = mk.find(0)
685    for (var j = 0; j < parts.length; ++j) {
686      var p = parts[j]
687      if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) { continue }
688      var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to)
689      if (dfrom < 0 || !mk.inclusiveLeft && !dfrom)
690        { newParts.push({from: p.from, to: m.from}) }
691      if (dto > 0 || !mk.inclusiveRight && !dto)
692        { newParts.push({from: m.to, to: p.to}) }
693      parts.splice.apply(parts, newParts)
694      j += newParts.length - 3
695    }
696  }
697  return parts
698}
699
700// Connect or disconnect spans from a line.
701function detachMarkedSpans(line) {
702  var spans = line.markedSpans
703  if (!spans) { return }
704  for (var i = 0; i < spans.length; ++i)
705    { spans[i].marker.detachLine(line) }
706  line.markedSpans = null
707}
708function attachMarkedSpans(line, spans) {
709  if (!spans) { return }
710  for (var i = 0; i < spans.length; ++i)
711    { spans[i].marker.attachLine(line) }
712  line.markedSpans = spans
713}
714
715// Helpers used when computing which overlapping collapsed span
716// counts as the larger one.
717function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 }
718function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 }
719
720// Returns a number indicating which of two overlapping collapsed
721// spans is larger (and thus includes the other). Falls back to
722// comparing ids when the spans cover exactly the same range.
723function compareCollapsedMarkers(a, b) {
724  var lenDiff = a.lines.length - b.lines.length
725  if (lenDiff != 0) { return lenDiff }
726  var aPos = a.find(), bPos = b.find()
727  var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b)
728  if (fromCmp) { return -fromCmp }
729  var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b)
730  if (toCmp) { return toCmp }
731  return b.id - a.id
732}
733
734// Find out whether a line ends or starts in a collapsed span. If
735// so, return the marker for that span.
736function collapsedSpanAtSide(line, start) {
737  var sps = sawCollapsedSpans && line.markedSpans, found
738  if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
739    sp = sps[i]
740    if (sp.marker.collapsed && (start ? sp.from : sp.to) == null &&
741        (!found || compareCollapsedMarkers(found, sp.marker) < 0))
742      { found = sp.marker }
743  } }
744  return found
745}
746function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) }
747function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) }
748
749function collapsedSpanAround(line, ch) {
750  var sps = sawCollapsedSpans && line.markedSpans, found
751  if (sps) { for (var i = 0; i < sps.length; ++i) {
752    var sp = sps[i]
753    if (sp.marker.collapsed && (sp.from == null || sp.from < ch) && (sp.to == null || sp.to > ch) &&
754        (!found || compareCollapsedMarkers(found, sp.marker) < 0)) { found = sp.marker }
755  } }
756  return found
757}
758
759// Test whether there exists a collapsed span that partially
760// overlaps (covers the start or end, but not both) of a new span.
761// Such overlap is not allowed.
762function conflictingCollapsedRange(doc, lineNo, from, to, marker) {
763  var line = getLine(doc, lineNo)
764  var sps = sawCollapsedSpans && line.markedSpans
765  if (sps) { for (var i = 0; i < sps.length; ++i) {
766    var sp = sps[i]
767    if (!sp.marker.collapsed) { continue }
768    var found = sp.marker.find(0)
769    var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker)
770    var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker)
771    if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue }
772    if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) ||
773        fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0))
774      { return true }
775  } }
776}
776
777
778// A visual line is a line as drawn on the screen. Folding, for
779// example, can cause multiple logical lines to appear on the same
780// visual line. This finds the start of the visual line that the
781// given line is part of (usually that is the line itself).
782function visualLine(line) {
783  var merged
784  while (merged = collapsedSpanAtStart(line))
785    { line = merged.find(-1, true).line }
786  return line
787}
788
789function visualLineEnd(line) {
790  var merged
791  while (merged = collapsedSpanAtEnd(line))
792    { line = merged.find(1, true).line }
793  return line
794}
795
796// Returns an array of logical lines that continue the visual line
797// started by the argument, or undefined if there are no such lines.
798function visualLineContinued(line) {
799  var merged, lines
800  while (merged = collapsedSpanAtEnd(line)) {
801    line = merged.find(1, true).line
802    ;(lines || (lines = [])).push(line)
803  }
804  return lines
805}
806
807// Get the line number of the start of the visual line that the
808// given line number is part of.
809function visualLineNo(doc, lineN) {
810  var line = getLine(doc, lineN), vis = visualLine(line)
811  if (line == vis) { return lineN }
812  return lineNo(vis)
813}
814
815// Get the line number of the start of the next visual line after
816// the given line.
817function visualLineEndNo(doc, lineN) {
818  if (lineN > doc.lastLine()) { return lineN }
819  var line = getLine(doc, lineN), merged
820  if (!lineIsHidden(doc, line)) { return lineN }
821  while (merged = collapsedSpanAtEnd(line))
822    { line = merged.find(1, true).line }
823  return lineNo(line) + 1
824}
825
826// Compute whether a line is hidden. Lines count as hidden when they
827// are part of a visual line that starts with another line, or when
828// they are entirely covered by collapsed, non-widget span.
829function lineIsHidden(doc, line) {
830  var sps = sawCollapsedSpans && line.markedSpans
831  if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
832    sp = sps[i]
833    if (!sp.marker.collapsed) { continue }
834    if (sp.from == null) { return true }
835    if (sp.marker.widgetNode) { continue }
836    if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
837      { return true }
838  } }
839}
840function lineIsHiddenInner(doc, line, span) {
841  if (span.to == null) {
842    var end = span.marker.find(1, true)
843    return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker))
844  }
845  if (span.marker.inclusiveRight && span.to == line.text.length)
846    { return true }
847  for (var sp = (void 0), i = 0; i < line.markedSpans.length; ++i) {
848    sp = line.markedSpans[i]
849    if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to &&
850        (sp.to == null || sp.to != span.from) &&
851        (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
852        lineIsHiddenInner(doc, line, sp)) { return true }
853  }
854}
855
856// Find the height above the given line.
857function heightAtLine(lineObj) {
858  lineObj = visualLine(lineObj)
859
860  var h = 0, chunk = lineObj.parent
861  for (var i = 0; i < chunk.lines.length; ++i) {
862    var line = chunk.lines[i]
863    if (line == lineObj) { break }
864    else { h += line.height }
865  }
866  for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
867    for (var i$1 = 0; i$1 < p.children.length; ++i$1) {
868      var cur = p.children[i$1]
869      if (cur == chunk) { break }
870      else { h += cur.height }
871    }
872  }
873  return h
874}
875
876// Compute the character length of a line, taking into account
877// collapsed ranges (see markText) that might hide parts, and join
878// other lines onto it.
879function lineLength(line) {
880  if (line.height == 0) { return 0 }
881  var len = line.text.length, merged, cur = line
882  while (merged = collapsedSpanAtStart(cur)) {
883    var found = merged.find(0, true)
884    cur = found.from.line
885    len += found.from.ch - found.to.ch
886  }
887  cur = line
888  while (merged = collapsedSpanAtEnd(cur)) {
889    var found$1 = merged.find(0, true)
890    len -= cur.text.length - found$1.from.ch
891    cur = found$1.to.line
892    len += cur.text.length - found$1.to.ch
893  }
894  return len
895}
896
897// Find the longest line in the document.
898function findMaxLine(cm) {
899  var d = cm.display, doc = cm.doc
900  d.maxLine = getLine(doc, doc.first)
901  d.maxLineLength = lineLength(d.maxLine)
902  d.maxLineChanged = true
903  doc.iter(function (line) {
904    var len = lineLength(line)
905    if (len > d.maxLineLength) {
906      d.maxLineLength = len
907      d.maxLine = line
908    }
909  })
910}
911
912// BIDI HELPERS
913
914function iterateBidiSections(order, from, to, f) {
915  if (!order) { return f(from, to, "ltr", 0) }
916  var found = false
917  for (var i = 0; i < order.length; ++i) {
918    var part = order[i]
919    if (part.from < to && part.to >
919 from || from == to && part.to == from) {
920      f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr", i)
921      found = true
922    }
923  }
924  if (!found) { f(from, to, "ltr") }
925}
926
927var bidiOther = null
928function getBidiPartAt(order, ch, sticky) {
929  var found
930  bidiOther = null
931  for (var i = 0; i < order.length; ++i) {
932    var cur = order[i]
933    if (cur.from < ch && cur.to > ch) { return i }
934    if (cur.to == ch) {
935      if (cur.from != cur.to && sticky == "before") { found = i }
936      else { bidiOther = i }
937    }
938    if (cur.from == ch) {
939      if (cur.from != cur.to && sticky != "before") { found = i }
940      else { bidiOther = i }
941    }
942  }
943  return found != null ? found : bidiOther
944}
945
946// Bidirectional ordering algorithm
947// See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
948// that this (partially) implements.
949
950// One-char codes used for character types:
951// L (L):   Left-to-Right
952// R (R):   Right-to-Left
953// r (AL):  Right-to-Left Arabic
954// 1 (EN):  European Number
955// + (ES):  European Number Separator
956// % (ET):  European Number Terminator
957// n (AN):  Arabic Number
958// , (CS):  Common Number Separator
959// m (NSM): Non-Spacing Mark
960// b (BN):  Boundary Neutral
961// s (B):   Paragraph Separator
962// t (S):   Segment Separator
963// w (WS):  Whitespace
964// N (ON):  Other Neutrals
965
966// Returns null if characters are ordered as they appear
967// (left-to-right), or an array of sections ({from, to, level}
968// objects) in the order in which they occur visually.
969var bidiOrdering = (function() {
970  // Character types for codepoints 0 to 0xff
971  var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN"
972  // Character types for codepoints 0x600 to 0x6f9
973  var arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111"
974  function charType(code) {
975    if (code <= 0xf7) { return lowTypes.charAt(code) }
976    else if (0x590 <= code && code <= 0x5f4) { return "R" }
977    else if (0x600 <= code && code <= 0x6f9) { return arabicTypes.charAt(code - 0x600) }
978    else if (0x6ee <= code && code <= 0x8ac) { return "r" }
979    else if (0x2000 <= code && code <= 0x200b) { return "w" }
980    else if (code == 0x200c) { return "b" }
981    else { return "L" }
982  }
983
984  var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/
985  var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/
986
987  function BidiSpan(level, from, to) {
988    this.level = level
989    this.from = from; this.to = to
990  }
991
992  return function(str, direction) {
993    var outerType = direction == "ltr" ? "L" : "R"
994
995    if (str.length == 0 || direction == "ltr" && !bidiRE.test(str)) { return false }
996    var len = str.length, types = []
997    for (var i = 0; i < len; ++i)
998      { types.push(charType(str.charCodeAt(i))) }
999
1000    // W1. Examine each non-spacing mark (NSM) in the level run, and
1001    // change the type of the NSM to the type of the previous
1002    // character. If the NSM is at the start of the level run, it will
1003    // get the type of sor.
1004    for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) {
1005      var type = types[i$1]
1006      if (type == "m") { types[i$1] = prev }
1007      else { prev = type }
1008    }
1009
1010    // W2. Search backwards from each instance of a European number
1011    // until the first strong type (R, L, AL, or sor) is found. If an
1012    // AL is found, change the type of the European number to Arabic
1013    // number.
1014    // W3. Change all ALs to R.
1015    for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) {
1016      var type$1 = types[i$2]
1017      if (type$1 == "1" && cur == "r") { types[i$2] = "n" }
1018      else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == "r") { types[i$2] = "R" } }
1019    }
1020
1021    // W4. A single European separator between two European numbers
1022    // changes to a European number. A single common separator between
1023    // two numbers of the same type changes to that type.
1024    for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) {
1025      var type$2 = types[i$3]
1026      if (type$2 == "+" && prev$1 == "1" && types[i$3+1] == "1") { types[i$3] = "1" }
1027      else if (type$2 == "," && prev$1 == types[i$3+1] &&
1028               (prev$1 == "1" || prev$1 == "n")) { types[i$3] = prev$1 }
1029      prev$1 = type$2
1030    }
1031
1032    // W5. A sequence of European terminators adjacent to European
1033    // numbers changes to all European numbers.
1034    // W6. Otherwise, separators and terminators change to Other
1035    // Neutral.
1036    for (var i$4 = 0; i$4 < len; ++i$4) {
1037      var type$3 = types[i$4]
1038      if (type$3 == ",") { types[i$4] = "N" }
1039      else if (type$3 == "%") {
1040        var end = (void 0)
1041        for (end = i$4 + 1; end < len && types[end] == "%"; ++end) {}
1042        var replace = (i$4 && types[i$4-1] == "!") || (end < len && types[end] == "1") ? "1" : "N"
1043        for (var j = i$4; j < end; ++j) { types[j] = replace }
1044        i$4 = end - 1
1045      }
1046    }
1047
1048    // W7. Search backwards from each instance of a European number
1049    // until the first strong type (R, L, or sor) is found. If an L is
1050    // found, then change the type of the European number to L.
1051    for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) {
1052      var type$4 = types[i$5]
1053      if (cur$1 == "L" && type$4 == "1") { types[i$5] = "L" }
1054      else if (isStrong.test(type$4)) { cur$1 = type$4 }
1055    }
1056
1057    // N1. A sequence of neutrals takes the direction of the
1058    // surrounding strong text if the text on both sides has the same
1059    // direction. European and Arabic numbers act as if they were R in
1060    // terms of their influence on neutrals. Start-of-level-run (sor)
1061    // and end-of-level-run (eor) are used at level run boundaries.
1062    // N2. Any remaining neutrals take the embedding direction.
1063    for (var i$6 = 0; i$6 < len; ++i$6) {
1064      if (isNeutral.test(types[i$6])) {
1065        var end$1 = (void 0)
1066        for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {}
1067        var before = (i$6 ? types[i$6-1] : outerType) == "L"
1068        var after = (end$1 < len ? types[end$1] : outerType) == "L"
1069        var replace$1 = before == after ? (before ? "L" : "R") : outerType
1070        for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1 }
1071        i$6 = end$1 - 1
1072      }
1073    }
1074
1075    // Here we depart from the documented algorithm, in order to avoid
1076    // building up an actual levels array. Since there are only three
1077    // levels (0, 1, 2) in an implementation that doesn't take
1078    // explicit embedding into account, we can build up the order on
1079    // the fly, without following the level-based algorithm.
1080    var order = [], m
1081    for (var i$7 = 0; i$7 < len;) {
1082      if (countsAsLeft.test(types[i$7])) {
1083        var start = i$7
1084        for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {}
1085        order.push(new BidiSpan(0, start, i$7))
1086      } else {
1087        var pos = i$7, at = order.length
1088        for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {}
1089        for (var j$2 = pos; j$2 < i$7;) {
1090          if (countsAsNum.test(types[j$2])) {
1091            if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)) }
1092            var nstart = j$2
1093            for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {}
1094            order.splice(at, 0, new BidiSpan(2, nstart, j$2))
1095            pos = j$2
1096          } else { ++j$2 }
1097        }
1098        if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)) }
1099      }
1100    }
1101    if (direction == "ltr") {
1102      if (order[0].level == 1 && (m = str.match(/^\s+/))) {
1103        order[0].from = m[0].length
1104        order.unshift(new BidiSpan(0, 0, m[0].length))
1105      }
1106      if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
1107        lst(order).to -= m[0].length
1108        order.push(new BidiSpan(0, len - m[0].length, len))
1109      }
1110    }
1111
1112    return direction == "rtl" ? order.reverse() : order
1113  }
1114})()
1115
1116// Get the bidi ordering for the given line (and cache it). Returns
1117// false for lines that are fully left-to-right, and an array of
1118// BidiSpan objects otherwise.
1119function getOrder(line, direction) {
1120  var order = line.order
1121  if (order == null) { order = line.order = bidiOrdering(line.text, direction) }
1122  return order
1123}
1124
1125// EVENT HANDLING
1126
1127// Lightweight event framework. on/off also work on DOM nodes,
1128// registering native DOM handlers.
1129
1130var noHandlers = []
1131
1132var on = function(emitter, type, f) {
1133  if (emitter.addEventListener) {
1134    emitter.addEventListener(type, f, false)
1135  } else if (emitter.attachEvent) {
1136    emitter.attachEvent("on" + type, f)
1137  } else {
1138    var map = emitter._handlers || (emitter._handlers = {})
1139    map[type] = (map[type] || noHandlers).concat(f)
1140  }
1141}
1142
1143function getHandlers(emitter, type) {
1144  return emitter._handlers && emitter._handlers[type] || noHandlers
1145}
1146
1147function off(emitter, type, f) {
1148  if (emitter.removeEventListener) {
1149    emitter.removeEventListener(type, f, false)
1150  } else if (emitter.detachEvent) {
1151    emitter.detachEvent("on" + type, f)
1152  } else {
1153    var map = emitter._handlers, arr = map && map[type]
1154    if (arr) {
1155      var index = indexOf(arr, f)
1156      if (index > -1)
1157        { map[type] = arr.slice(0, index).concat(arr.slice(index + 1)) }
1158    }
1159  }
1160}
1161
1162function signal(emitter, type /*, values...*/) {
1163  var handlers = getHandlers(emitter, type)
1164  if (!handlers.length) { return }
1165  var args = Array.prototype.slice.call(arguments, 2)
1166  for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args) }
1167}
1168
1169// The DOM events that CodeMirror handles can be overridden by
1170// registering a (non-DOM) handler on the editor for the event name,
1171// and preventDefault-ing the event in that handler.
1172function signalDOMEvent(cm, e, override) {
1173  if (typeof e == "string")
1174    { e = {type: e, preventDefault: function() { this.defaultPrevented = true }} }
1175  signal(cm, override || e.type, cm, e)
1176  return e_defaultPrevented(e) || e.codemirrorIgnore
1177}
1178
1179function signalCursorActivity(cm) {
1180  var arr = cm._handlers && cm._handlers.cursorActivity
1181  if (!arr) { return }
1182  var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = [])
1183  for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1)
1184    { set.push(arr[i]) } }
1185}
1186
1187function hasHandler(emitter, type) {
1188  return getHandlers(emitter, type).length > 0
1189}
1190
1191// Add on and off methods to a constructor's prototype, to make
1192// registering events on such objects more convenient.
1193function eventMixin(ctor) {
1194  ctor.prototype.on = function(type, f) {on(this, type, f)}
1195  ctor.prototype.off = function(type, f) {off(this, type, f)}
1196}
1197
1198// Due to the fact that we still support jurassic IE versions, some
1199// compatibility wrappers are needed.
1200
1201function e_preventDefault(e) {
1202  if (e.preventDefault) { e.preventDefault() }
1203  else { e.returnValue = false }
1204}
1205function e_stopPropagation(e) {
1206  if (e.stopPropagation) { e.stopPropagation() }
1207  else { e.cancelBubble = true }
1208}
1209function e_defaultPrevented(e) {
1210  return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false
1211}
1212function e_stop(e) {e_preventDefault(e); e_stopPropagation(e)}
1213
1214function e_target(e) {return e.target || e.srcElement}
1215function e_button(e) {
1216  var b = e.which
1217  if (b == null) {
1218    if (e.button & 1) { b = 1 }
1219    else if (e.button & 2) { b = 3 }
1220    else if (e.button & 4) { b = 2 }
1221  }
1222  if (mac && e.ctrlKey && b == 1) { b = 3 }
1223  return b
1224}
1225
1226// Detect drag-and-drop
1227var dragAndDrop = function() {
1228  // There is *some* kind of drag-and-drop support in IE6-8, but I
1229  // couldn't get it to work yet.
1230  if (ie && ie_version < 9) { return false }
1231  var div = elt('div')
1232  return "draggable" in div || "dragDrop" in div
1233}()
1234
1235var zwspSupported
1236function zeroWidthElement(measure) {
1237  if (zwspSupported == null) {
1238    var test = elt("span", "\u200b")
1239    removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]))
1240    if (measure.firstChild.offsetHeight != 0)
1241      { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8) }
1242  }
1243  var node = zwspSupported ? elt("span", "\u200b") :
1244    elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px")
1245  node.setAttribute("cm-text", "")
1246  return node
1247}
1248
1249// Feature-detect IE's crummy client rect reporting for bidi text
1250var badBidiRects
1251function hasBadBidiRects(measure) {
1252  if (badBidiRects != null) { return badBidiRects }
1253  var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA"))
1254  var r0 = range(txt, 0, 1).getBoundingClientRect()
1255  var r1 = range(txt, 1, 2).getBoundingClientRect()
1256  removeChildren(measure)
1257  if (!r0 || r0.left == r0.right) { return false } // Safari returns null in some cases (#2780)
1258  return badBidiRects = (r1.right - r0.right < 3)
1259}
1260
1261// See if "".split is the broken IE version, if so, provide an
1262// alternative way to split lines.
1263var splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function (string) {
1264  var pos = 0, result = [], l = string.length
1265  while (pos <= l) {
1266    var nl = string.indexOf("\n", pos)
1267    if (nl == -1) { nl = string.length }
1268    var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl)
1269    var rt = line.indexOf("\r")
1270    if (rt != -1) {
1271      result.push(line.slice(0, rt))
1272      pos += rt + 1
1273    } else {
1274      result.push(line)
1275      pos = nl + 1
1276    }
1277  }
1278  return result
1279} : function (string) { return string.split(/\r\n?|\n/); }
1280
1281var hasSelection = window.getSelection ? function (te) {
1282  try { return te.selectionStart != te.selectionEnd }
1283  catch(e) { return false }
1284} : function (te) {
1285  var range
1286  try {range = te.ownerDocument.selection.createRange()}
1287  catch(e) {}
1288  if (!range || range.parentElement() != te) { return false }
1289  return range.compareEndPoints("StartToEnd", range) != 0
1290}
1291
1292var hasCopyEvent = (function () {
1293  var e = elt("div")
1294  if ("oncopy" in e) { return true }
1295  e.setAttribute("oncopy", "return;")
1296  return typeof e.oncopy == "function"
1297})()
1298
1299var badZoomedRects = null
1300function hasBadZoomedRects(measure) {
1301  if (badZoomedRects != null) { return badZoomedRects }
1302  var node = removeChildrenAndAdd(measure, elt("span", "x"))
1303  var normal = node.getBoundingClientRect()
1304  var fromRange = range(node, 0, 1).getBoundingClientRect()
1305  return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1
1306}
1307
1308var modes = {};
1309var mimeModes = {};
1310// Extra arguments are stored as the mode's dependencies, which is
1311// used by (legacy) mechanisms like loadmode.js to automatically
1312// load a mode. (Preferred mechanism is the require/define calls.)
1313function defineMode(name, mode) {
1314  if (arguments.length > 2)
1315    { mode.dependencies = Array.prototype.slice.call(arguments, 2) }
1316  modes[name] = mode
1317}
1318
1319function defineMIME(mime, spec) {
1320  mimeModes[mime] = spec
1321}
1322
1323// Given a MIME type, a {name, ...options} config object, or a name
1324// string, return a mode config object.
1325function resolveMode(spec) {
1326  if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
1327    spec = mimeModes[spec]
1328  } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
1329    var found = mimeModes[spec.name]
1330    if (typeof found == "string") { found = {name: found} }
1331    spec = createObj(found, spec)
1332    spec.name = found.name
1333  } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
1334    return resolveMode("application/xml")
1335  } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) {
1336    return resolveMode("application/json")
1337  }
1338  if (typeof spec == "string") { return {name: spec} }
1339  else { return spec || {name: "null"} }
1340}
1341
1342// Given a mode spec (anything that resolveMode accepts), find and
1343// initialize an actual mode object.
1344function getMode(options, spec) {
1345  spec = resolveMode(spec)
1346  var mfactory = modes[spec.name]
1347  if (!mfactory) { return getMode(options, "text/plain") }
1348  var modeObj = mfactory(options, spec)
1349  if (modeExtensions.hasOwnProperty(spec.name)) {
1350    var exts = modeExtensions[spec.name]
1351    for (var prop in exts) {
1352      if (!exts.hasOwnProperty(prop)) { continue }
1353      if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop] }
1354      modeObj[prop] = exts[prop]
1355    }
1356  }
1357  modeObj.name = spec.name
1358  if (spec.helperType) { modeObj.helperType = spec.helperType }
1359  if (spec.modeProps) { for (var prop$1 in spec.modeProps)
1360    { modeObj[prop$1] = spec.modeProps[prop$1] } }
1361
1362  return modeObj
1363}
1364
1365// This can be used to attach properties to mode objects from
1366// outside the actual mode definition.
1367var modeExtensions = {}
1368function extendMode(mode, properties) {
1369  var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {})
1370  copyObj(properties, exts)
1371}
1372
1373function copyState(mode, state) {
1374  if (state === true) { return state }
1375  if (mode.copyState) { return mode.copyState(state) }
1376  var nstate = {}
1377  for (var n in state) {
1378    var val = state[n]
1379    if (val instanceof Array) { val = val.concat([]) }
1380    nstate[n] = val
1381  }
1382  return nstate
1383}
1384
1385// Given a mode and a state (for that mode), find the inner mode and
1386// state at the position that the state refers to.
1387function innerMode(mode, state) {
1388  var info
1389  while (mode.innerMode) {
1390    info = mode.innerMode(state)
1391    if (!info || info.mode == mode) { break }
1392    state = info.state
1393    mode = info.mode
1394  }
1395  return info || {mode: mode, state: state}
1396}
1397
1398function startState(mode, a1, a2) {
1399  return mode.startState ? mode.startState(a1, a2) : true
1400}
1401
1402// STRING STREAM
1403
1404// Fed to the mode parsers, provides helper functions to make
1405// parsers more succinct.
1406
1407var StringStream = function(string, tabSize, lineOracle) {
1408  this.pos = this.start = 0
1409  this.string = string
1410  this.tabSize = tabSize || 8
1411  this.lastColumnPos = this.lastColumnValue = 0
1412  this.lineStart = 0
1413  this.lineOracle = lineOracle
1414};
1415
1416StringStream.prototype.eol = function () {return this.pos >= this.string.length};
1417StringStream.prototype.sol = function () {return this.pos == this.lineStart};
1418StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined};
1419StringStream.prototype.next = function () {
1420  if (this.pos < this.string.length)
1421    { return this.string.charAt(this.pos++) }
1422};
1423StringStream.prototype.eat = function (match) {
1424  var ch = this.string.charAt(this.pos)
1425  var ok
1426  if (typeof match == "string") { ok = ch == match }
1427  else { ok = ch && (match.test ? match.test(ch) : match(ch)) }
1428  if (ok) {++this.pos; return ch}
1429};
1430StringStream.prototype.eatWhile = function (match) {
1431  var start = this.pos
1432  while (this.eat(match)){}
1433  return this.pos > start
1434};
1435StringStream.prototype.eatSpace = function () {
1436    var this$1 = this;
1437
1438  var start = this.pos
1439  while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this$1.pos }
1440  return this.pos > start
1441};
1442StringStream.prototype.skipToEnd = function () {this.pos = this.string.length};
1443StringStream.prototype.skipTo = function (ch) {
1444  var found = this.string.indexOf(ch, this.pos)
1445  if (found > -1) {this.pos = found; return true}
1446};
1447StringStream.prototype.backUp = function (n) {this.pos -= n};
1448StringStream.prototype.column = function () {
1449  if (this.lastColumnPos < this.start) {
1450    this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue)
1451    this.lastColumnPos = this.start
1452  }
1453  return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
1454};
1455StringStream.prototype.indentation = function () {
1456  return countColumn(this.string, null, this.tabSize) -
1457    (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
1458};
1459StringStream.prototype.match = function (pattern, consume, caseInsensitive) {
1460  if (typeof pattern == "string") {
1461    var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; }
1462    var substr = this.string.substr(this.pos, pattern.length)
1463    if (cased(substr) == cased(pattern)) {
1464      if (consume !== false) { this.pos += pattern.length }
1465      return true
1466    }
1467  } else {
1468    var match = this.string.slice(this.pos).match(pattern)
1469    if (match && match.index > 0) { return null }
1470    if (match && consume !== false) { this.pos += match[0].length }
1471    return match
1472  }
1473};
1474StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)};
1475StringStream.prototype.hideFirstChars = function (n, inner) {
1476  this.lineStart += n
1477  try { return inner() }
1478  finally { this.lineStart -= n }
1479};
1480StringStream.prototype.lookAhead = function (n) {
1481  var oracle = this.lineOracle
1482  return oracle && oracle.lookAhead(n)
1483};
1484StringStream.prototype.baseToken = function () {
1485  var oracle = this.lineOracle
1486  return oracle && oracle.baseToken(this.pos)
1487};
1488
1489var SavedContext = function(state, lookAhead) {
1490  this.state = state
1491  this.lookAhead = lookAhead
1492};
1493
1494var Context = function(doc, state, line, lookAhead) {
1495  this.state = state
1496  this.doc = doc
1497  this.line = line
1498  this.maxLookAhead = lookAhead || 0
1499  this.baseTokens = null
1500  this.baseTokenPos = 1
1501};
1502
1503Context.prototype.lookAhead = function (n) {
1504  var line = this.doc.getLine(this.line + n)
1505  if (line != null && n > this.maxLookAhead) { this.maxLookAhead = n }
1506  return line
1507};
1508
1509Context.prototype.baseToken = function (n) {
1510    var this$1 = this;
1511
1512  if (!this.baseTokens) { return null }
1513  while (this.baseTokens[this.baseTokenPos] <= n)
1514    { this$1.baseTokenPos += 2 }
1515  var type = this.baseTokens[this.baseTokenPos + 1]
1516  return {type: type && type.replace(/( |^)overlay .*/, ""),
1517          size: this.baseTokens[this.baseTokenPos] - n}
1518};
1519
1520Context.prototype.nextLine = function () {
1521  this.line++
1522  if (this.maxLookAhead > 0) { this.maxLookAhead-- }
1523};
1524
1525Context.fromSaved = function (doc, saved, line) {
1526  if (saved instanceof SavedContext)
1527    { return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead) }
1528  else
1529    { return new Context(doc, copyState(doc.mode, saved), line) }
1530}
1530;
1531
1532Context.prototype.save = function (copy) {
1533  var state = copy !== false ? copyState(this.doc.mode, this.state) : this.state
1534  return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state
1535};
1536
1537
1538// Compute a style array (an array starting with a mode generation
1539// -- for invalidation -- followed by pairs of end positions and
1540// style strings), which is used to highlight the tokens on the
1541// line.
1542function highlightLine(cm, line, context, forceToEnd) {
1543  // A styles array always starts with a number identifying the
1544  // mode/overlays that it is based on (for easy invalidation).
1545  var st = [cm.state.modeGen], lineClasses = {}
1546  // Compute the base array of styles
1547  runMode(cm, line.text, cm.doc.mode, context, function (end, style) { return st.push(end, style); },
1548          lineClasses, forceToEnd)
1549  var state = context.state
1550
1551  // Run overlays, adjust style array.
1552  var loop = function ( o ) {
1553    context.baseTokens = st
1554    var overlay = cm.state.overlays[o], i = 1, at = 0
1555    context.state = true
1556    runMode(cm, line.text, overlay.mode, context, function (end, style) {
1557      var start = i
1558      // Ensure there's a token end at the current position, and that i points at it
1559      while (at < end) {
1560        var i_end = st[i]
1561        if (i_end > end)
1562          { st.splice(i, 1, end, st[i+1], i_end) }
1563        i += 2
1564        at = Math.min(end, i_end)
1565      }
1566      if (!style) { return }
1567      if (overlay.opaque) {
1568        st.splice(start, i - start, end, "overlay " + style)
1569        i = start + 2
1570      } else {
1571        for (; start < i; start += 2) {
1572          var cur = st[start+1]
1573          st[start+1] = (cur ? cur + " " : "") + "overlay " + style
1574        }
1575      }
1576    }, lineClasses)
1577    context.state = state
1578    context.baseTokens = null
1579    context.baseTokenPos = 1
1580  };
1581
1582  for (var o = 0; o < cm.state.overlays.length; ++o) loop( o );
1583
1584  return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null}
1585}
1586
1587function getLineStyles(cm, line, updateFrontier) {
1588  if (!line.styles || line.styles[0] != cm.state.modeGen) {
1589    var context = getContextBefore(cm, lineNo(line))
1590    var resetState = line.text.length > cm.options.maxHighlightLength && copyState(cm.doc.mode, context.state)
1591    var result = highlightLine(cm, line, context)
1592    if (resetState) { context.state = resetState }
1593    line.stateAfter = context.save(!resetState)
1594    line.styles = result.styles
1595    if (result.classes) { line.styleClasses = result.classes }
1596    else if (line.styleClasses) { line.styleClasses = null }
1597    if (updateFrontier === cm.doc.highlightFrontier)
1598      { cm.doc.modeFrontier = Math.max(cm.doc.modeFrontier, ++cm.doc.highlightFrontier) }
1599  }
1600  return line.styles
1601}
1602
1603function getContextBefore(cm, n, precise) {
1604  var doc = cm.doc, display = cm.display
1605  if (!doc.mode.startState) { return new Context(doc, true, n) }
1606  var start = findStartLine(cm, n, precise)
1607  var saved = start > doc.first && getLine(doc, start - 1).stateAfter
1608  var context = saved ? Context.fromSaved(doc, saved, start) : new Context(doc, startState(doc.mode), start)
1609
1610  doc.iter(start, n, function (line) {
1611    processLine(cm, line.text, context)
1612    var pos = context.line
1613    line.stateAfter = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo ? context.save() : null
1614    context.nextLine()
1615  })
1616  if (precise) { doc.modeFrontier = context.line }
1617  return context
1618}
1619
1620// Lightweight form of highlight -- proceed over this line and
1621// update state, but don't save a style array. Used for lines that
1622// aren't currently visible.
1623function processLine(cm, text, context, startAt) {
1624  var mode = cm.doc.mode
1625  var stream = new StringStream(text, cm.options.tabSize, context)
1626  stream.start = stream.pos = startAt || 0
1627  if (text == "") { callBlankLine(mode, context.state) }
1628  while (!stream.eol()) {
1629    readToken(mode, stream, context.state)
1630    stream.start = stream.pos
1631  }
1632}
1633
1634function callBlankLine(mode, state) {
1635  if (mode.blankLine) { return mode.blankLine(state) }
1636  if (!mode.innerMode) { return }
1637  var inner = innerMode(mode, state)
1638  if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) }
1639}
1640
1641function readToken(mode, stream, state, inner) {
1642  for (var i = 0; i < 10; i++) {
1643    if (inner) { inner[0] = innerMode(mode, state).mode }
1644    var style = mode.token(stream, state)
1645    if (stream.pos > stream.start) { return style }
1646  }
1647  throw new Error("Mode " + mode.name + " failed to advance stream.")
1648}
1649
1650var Token = function(stream, type, state) {
1651  this.start = stream.start; this.end = stream.pos
1652  this.string = stream.current()
1653  this.type = type || null
1654  this.state = state
1655};
1656
1657// Utility for getTokenAt and getLineTokens
1658function takeToken(cm, pos, precise, asArray) {
1659  var doc = cm.doc, mode = doc.mode, style
1660  pos = clipPos(doc, pos)
1661  var line = getLine(doc, pos.line), context = getContextBefore(cm, pos.line, precise)
1662  var stream = new StringStream(line.text, cm.options.tabSize, context), tokens
1663  if (asArray) { tokens = [] }
1664  while ((asArray || stream.pos < pos.ch) && !stream.eol()) {
1665    stream.start = stream.pos
1666    style = readToken(mode, stream, context.state)
1667    if (asArray) { tokens.push(new Token(stream, style, copyState(doc.mode, context.state))) }
1668  }
1669  return asArray ? tokens : new Token(stream, style, context.state)
1670}
1671
1672function extractLineClasses(type, output) {
1673  if (type) { for (;;) {
1674    var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/)
1675    if (!lineClass) { break }
1676    type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length)
1677    var prop = lineClass[1] ? "bgClass" : "textClass"
1678    if (output[prop] == null)
1679      { output[prop] = lineClass[2] }
1680    else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop]))
1681      { output[prop] += " " + lineClass[2] }
1682  } }
1683  return type
1684}
1685
1686// Run the given mode's parser over a line, calling f for each token.
1687function runMode(cm, text, mode, context, f, lineClasses, forceToEnd) {
1688  var flattenSpans = mode.flattenSpans
1689  if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans }
1690  var curStart = 0, curStyle = null
1691  var stream = new StringStream(text, cm.options.tabSize, context), style
1692  var inner = cm.options.addModeClass && [null]
1693  if (text == "") { extractLineClasses(callBlankLine(mode, context.state), lineClasses) }
1694  while (!stream.eol()) {
1695    if (stream.pos > cm.options.maxHighlightLength) {
1696      flattenSpans = false
1697      if (forceToEnd) { processLine(cm, text, context, stream.pos) }
1698      stream.pos = text.length
1699      style = null
1700    } else {
1701      style = extractLineClasses(readToken(mode, stream, context.state, inner), lineClasses)
1702    }
1703    if (inner) {
1704      var mName = inner[0].name
1705      if (mName) { style = "m-" + (style ? mName + " " + style : mName) }
1706    }
1707    if (!flattenSpans || curStyle != style) {
1708      while (curStart < stream.start) {
1709        curStart = Math.min(stream.start, curStart + 5000)
1710        f(curStart, curStyle)
1711      }
1712      curStyle = style
1713    }
1714    stream.start = stream.pos
1715  }
1716  while (curStart < stream.pos) {
1717    // Webkit seems to refuse to render text nodes longer than 57444
1718    // characters, and returns inaccurate measurements in nodes
1719    // starting around 5000 chars.
1720    var pos = Math.min(stream.pos, curStart + 5000)
1721    f(pos, curStyle)
1722    curStart = pos
1723  }
1724}
1725
1726// Finds the line to start with when starting a parse. Tries to
1727// find a line with a stateAfter, so that it can start with a
1728// valid state. If that fails, it returns the line with the
1729// smallest indentation, which tends to need the least context to
1730// parse correctly.
1731function findStartLine(cm, n, precise) {
1732  var minindent, minline, doc = cm.doc
1733  var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100)
1734  for (var search = n; search > lim; --search) {
1735    if (search <= doc.first) { return doc.first }
1736    var line = getLine(doc, search - 1), after = line.stateAfter
1737    if (after && (!precise || search + (after instanceof SavedContext ? after.lookAhead : 0) <= doc.modeFrontier))
1738      { return search }
1739    var indented = countColumn(line.text, null, cm.options.tabSize)
1740    if (minline == null || minindent > indented) {
1741      minline = search - 1
1742      minindent = indented
1743    }
1744  }
1745  return minline
1746}
1747
1748function retreatFrontier(doc, n) {
1749  doc.modeFrontier = Math.min(doc.modeFrontier, n)
1750  if (doc.highlightFrontier < n - 10) { return }
1751  var start = doc.first
1752  for (var line = n - 1; line > start; line--) {
1753    var saved = getLine(doc, line).stateAfter
1754    // change is on 3
1755    // state on line 1 looked ahead 2 -- so saw 3
1756    // test 1 + 2 < 3 should cover this
1757    if (saved && (!(saved instanceof SavedContext) || line + saved.lookAhead < n)) {
1758      start = line + 1
1759      break
1760    }
1761  }
1762  doc.highlightFrontier = Math.min(doc.highlightFrontier, start)
1763}
1764
1765// LINE DATA STRUCTURE
1766
1767// Line objects. These hold state related to a line, including
1768// highlighting info (the styles array).
1769var Line = function(text, markedSpans, estimateHeight) {
1770  this.text = text
1771  attachMarkedSpans(this, markedSpans)
1772  this.height = estimateHeight ? estimateHeight(this) : 1
1773};
1774
1775Line.prototype.lineNo = function () { return lineNo(this) };
1776eventMixin(Line)
1777
1778// Change the content (text, markers) of a line. Automatically
1779// invalidates cached information and tries to re-estimate the
1780// line's height.
1781function updateLine(line, text, markedSpans, estimateHeight) {
1782  line.text = text
1783  if (line.stateAfter) { line.stateAfter = null }
1784  if (line.styles) { line.styles = null }
1785  if (line.order != null) { line.order = null }
1786  detachMarkedSpans(line)
1787  attachMarkedSpans(line, markedSpans)
1788  var estHeight = estimateHeight ? estimateHeight(line) : 1
1789  if (estHeight != line.height) { updateLineHeight(line, estHeight) }
1790}
1791
1792// Detach a line from the document tree and its markers.
1793function cleanUpLine(line) {
1794  line.parent = null
1795  detachMarkedSpans(line)
1796}
1797
1798// Convert a style as returned by a mode (either null, or a string
1799// containing one or more styles) to a CSS style. This is cached,
1800// and also looks for line-wide styles.
1801var styleToClassCache = {};
1802var styleToClassCacheWithMode = {};
1803function interpretTokenStyle(style, options) {
1804  if (!style || /^\s*$/.test(style)) { return null }
1805  var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache
1806  return cache[style] ||
1807    (cache[style] = style.replace(/\S+/g, "cm-$&"))
1808}
1809
1810// Render the DOM representation of the text of a line. Also builds
1811// up a 'line map', which points at the DOM nodes that represent
1812// specific stretches of text, and is used by the measuring code.
1813// The returned object contains the DOM node, this map, and
1814// information about line-wide styles that were set by the mode.
1815function buildLineContent(cm, lineView) {
1816  // The padding-right forces the element to have a 'border', which
1817  // is needed on Webkit to be able to get line-level bounding
1818  // rectangles for it (in measureChar).
1819  var content = eltP("span", null, null, webkit ? "padding-right: .1px" : null)
1820  var builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: content,
1821                 col: 0, pos: 0, cm: cm,
1822                 trailingSpace: false,
1823                 splitSpaces: (ie || webkit) && cm.getOption("lineWrapping")}
1824  lineView.measure = {}
1825
1826  // Iterate over the logical lines that make up this visual line.
1827  for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {
1828    var line = i ? lineView.rest[i - 1] : lineView.line, order = (void 0)
1829    builder.pos = 0
1830    builder.addToken = buildToken
1831    // Optionally wire in some hacks into the token-rendering
1832    // algorithm, to deal with browser quirks.
1833    if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction)))
1834      { builder.addToken = buildTokenBadBidi(builder.addToken, order) }
1835    builder.map = []
1836    var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line)
1837    insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate))
1838    if (line.styleClasses) {
1839      if (line.styleClasses.bgClass)
1840        { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || "") }
1841      if (line.styleClasses.textClass)
1842        { builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || "") }
1843    }
1844
1845    // Ensure at least a single node is present, for measuring.
1846    if (builder.map.length == 0)
1847      { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))) }
1848
1849    // Store the map and a cache object for the current logical line
1850    if (i == 0) {
1851      lineView.measure.map = builder.map
1852      lineView.measure.cache = {}
1853    } else {
1854      ;(lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map)
1855      ;(lineView.measure.caches || (lineView.measure.caches = [])).push({})
1856    }
1857  }
1858
1859  // See issue #2901
1860  if (webkit) {
1861    var last = builder.content.lastChild
1862    if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab")))
1863      { builder.content.className = "cm-tab-wrap-hack" }
1864  }
1865
1866  signal(cm, "renderLine", cm, lineView.line, builder.pre)
1867  if (builder.pre.className)
1868    { builder.textClass = joinClasses(builder.pre.className, builder.textClass || "") }
1869
1870  return builder
1871}
1872
1873function defaultSpecialCharPlaceholder(ch) {
1874  var token = elt("span", "\u2022", "cm-invalidchar")
1875  token.title = "\\u" + ch.charCodeAt(0).toString(16)
1876  token.setAttribute("aria-label", token.title)
1877  return token
1878}
1879
1880// Build up the DOM representation for a single token, and add it to
1881// the line map. Takes care to render special characters separately.
1882function buildToken(builder, text, style, startStyle, endStyle, title, css) {
1883  if (!text) { return }
1884  var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text
1885  var special = builder.cm.state.specialChars, mustWrap = false
1886  var content
1887  if (!special.test(text)) {
1888    builder.col += text.length
1889    content = document.createTextNode(displayText)
1890    builder.map.push(builder.pos, builder.pos + text.length, content)
1891    if (ie && ie_version < 9) { mustWrap = true }
1892    builder.pos += text.length
1893  } else {
1894    content = document.createDocumentFragment()
1895    var pos = 0
1896    while (true) {
1897      special.lastIndex = pos
1898      var m = special.exec(text)
1899      var skipped = m ? m.index - pos : text.length - pos
1900      if (skipped) {
1901        var txt = document.createTextNode(displayText.slice(pos, pos + skipped))
1902        if (ie && ie_version < 9) { content.appendChild(elt("span", [txt])) }
1903        else { content.appendChild(txt) }
1904        builder.map.push(builder.pos, builder.pos + skipped, txt)
1905        builder.col += skipped
1906        builder.pos += skipped
1907      }
1908      if (!m) { break }
1909      pos += skipped + 1
1910      var txt$1 = (void 0)
1911      if (m[0] == "\t") {
1912        var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize
1913        txt$1 = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"))
1914        txt$1.setAttribute("role", "presentation")
1915        txt$1.setAttribute("cm-text", "\t")
1916        builder.col += tabWidth
1917      } else if (m[0] == "\r" || m[0] == "\n") {
1918        txt$1 = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar"))
1919        txt$1.setAttribute("cm-text", m[0])
1920        builder.col += 1
1921      } else {
1922        txt$1 = builder.cm.options.specialCharPlaceholder(m[0])
1923        txt$1.setAttribute("cm-text", m[0])
1924        if (ie && ie_version < 9) { content.appendChild(elt("span", [txt$1])) }
1925        else { content.appendChild(txt$1) }
1926        builder.col += 1
1927      }
1928      builder.map.push(builder.pos, builder.pos + 1, txt$1)
1929      builder.pos++
1930    }
1931  }
1932  builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32
1933  if (style || startStyle || endStyle || mustWrap || css) {
1934    var fullStyle = style || ""
1935    if (startStyle) { fullStyle += startStyle }
1936    if (endStyle) { fullStyle += endStyle }
1937    var token = elt("span", [content], fullStyle, css)
1938    if (title) { token.title = title }
1939    return builder.content.appendChild(token)
1940  }
1941  builder.content.appendChild(content)
1942}
1943
1944function splitSpaces(text, trailingBefore) {
1945  if (text.length > 1 && !/  /.test(text)) { return text }
1946  var spaceBefore = trailingBefore, result = ""
1947  for (var i = 0; i < text.length; i++) {
1948    var ch = text.charAt(i)
1949    if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32))
1950      { ch = "\u00a0" }
1951    result += ch
1952    spaceBefore = ch == " "
1953  }
1954  return result
1955}
1956
1957// Work around nonsense dimensions being reported for stretches of
1958// right-to-left text.
1959function buildTokenBadBidi(inner, order) {
1960  return function (builder, text, style, startStyle, endStyle, title, css) {
1961    style = style ? style + " cm-force-border" : "cm-force-border"
1962    var start = builder.pos, end = start + text.length
1963    for (;;) {
1964      // Find the part that overlaps with the start of this text
1965      var part = (void 0)
1966      for (var i = 0; i < order.length; i++) {
1967        part = order[i]
1968        if (part.to > start && part.from <= start) { break }
1969      }
1970      if (part.to >= end) { return inner(builder, text, style, startStyle, endStyle, title, css) }
1971      inner(builder, text.slice(0, part.to - start), style, startStyle, null, title, css)
1972      startStyle = null
1973      text = text.slice(part.to - start)
1974      start = part.to
1975    }
1976  }
1977}
1978
1979function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
1980  var widget = !ignoreWidget && marker.widgetNode
1981  if (widget) { builder.map.push(builder.pos, builder.pos + size, widget) }
1982  if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {
1983    if (!widget)
1984      { widget = builder.content.appendChild(document.createElement("span")) }
1985    widget.setAttribute("cm-marker", marker.id)
1986  }
1987  if (widget) {
1988    builder.cm.display.input.setUneditable(widget)
1989    builder.content.appendChild(widget)
1990  }
1991  builder.pos += size
1992  builder.trailingSpace = false
1993}
1994
1995// Outputs a number of spans to make up a line, taking highlighting
1996// and marked text into account.
1997function insertLineContent(line, builder, styles) {
1998  var spans = line.markedSpans, allText = line.text, at = 0
1999  if (!spans) {
2000    for (var i$1 = 1; i$1 < styles.length; i$1+=2)
2001      { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpretTokenStyle(styles[i$1+1], builder.cm.options)) }
2002    return
2003  }
2004
2005  var len = allText.length, pos = 0, i = 1, text = "", style, css
2006  var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed
2007  for (;;) {
2008    if (nextChange == pos) { // Update current marker set
2009      spanStyle = spanEndStyle = spanStartStyle = title = css = ""
2010      collapsed = null; nextChange = Infinity
2011      var foundBookmarks = [], endStyles = (void 0)
2012      for (var j = 0; j < spans.length; ++j) {
2013        var sp = spans[j], m = sp.marker
2014        if (m.type == "bookmark" && sp.from == pos && m.widgetNode) {
2015          foundBookmarks.push(m)
2016        } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) {
2017          if (sp.to != null && sp.to != pos && nextChange > sp.to) {
2018            nextChange = sp.to
2019            spanEndStyle = ""
2020          }
2021          if (m.className) { spanStyle += " " + m.className }
2022          if (m.css) { css = (css ? css + ";" : "") + m.css }
2023          if (m.startStyle && sp.from == pos) { spanStartStyle += " " + m.startStyle }
2024          if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [])).push(m.endStyle, sp.to) }
2025          if (m.title && !title) { title = m.title }
2026          if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))
2027            { collapsed = sp }
2028        } else if (sp.from > pos && nextChange > sp.from) {
2029          nextChange = sp.from
2030        }
2031      }
2032      if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2)
2033        { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += " " + endStyles[j$1] } } }
2034
2035      if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2)
2036        { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]) } }
2037      if (collapsed && (collapsed.from || 0) == pos) {
2038        buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos,
2039                           collapsed.marker, collapsed.from == null)
2040        if (collapsed.to == null) { return }
2041        if (collapsed.to == pos) { collapsed = false }
2042      }
2043    }
2044    if (pos >= len) { break }
2045
2046    var upto = Math.min(len, nextChange)
2047    while (true) {
2048      if (text) {
2049        var end = pos + text.length
2050        if (!collapsed) {
2051          var tokenText = end > upto ? text.slice(0, upto - pos) : text
2052          builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
2053                           spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", title, css)
2054        }
2055        if (end >= upto) {text = text.slice(upto - pos); pos = upto; break}
2056        pos = end
2057        spanStartStyle = ""
2058      }
2059      text = allText.slice(at, at = styles[i++])
2060      style = interpretTokenStyle(styles[i++], builder.cm.options)
2061    }
2062  }
2063}
2064
2065
2066// These objects are used to represent the visible (currently drawn)
2067// part of the document. A LineView may correspond to multiple
2068// logical lines, if those are connected by collapsed ranges.
2069function LineView(doc, line, lineN) {
2070  // The starting line
2071  this.line = line
2072  // Continuing lines, if any
2073  this.rest = visualLineContinued(line)
2074  // Number of logical lines in this visual line
2075  this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1
2076  this.node = this.text = null
2077  this.hidden = lineIsHidden(doc, line)
2078}
2079
2080// Create a range of LineView objects for the given lines.
2081function buildViewArray(cm, from, to) {
2082  var array = [], nextPos
2083  for (var pos = from; pos < to; pos = nextPos) {
2084    var view = new LineView(cm.doc, getLine(cm.doc, pos), pos)
2085    nextPos = pos + view.size
2086    array.push(view)
2087  }
2088  return array
2089}
2090
2091var operationGroup = null
2092
2093function pushOperation(op) {
2094  if (operationGroup) {
2095    operationGroup.ops.push(op)
2096  } else {
2097    op.ownsGroup = operationGroup = {
2098      ops: [op],
2099      delayedCallbacks: []
2100    }
2101  }
2102}
2103
2104function fireCallbacksForOps(group) {
2105  // Calls delayed callbacks and cursorActivity handlers until no
2106  // new ones appear
2107  var callbacks = group.delayedCallbacks, i = 0
2108  do {
2109    for (; i < callbacks.length; i++)
2110      { callbacks[i].call(null) }
2111    for (var j = 0; j < group.ops.length; j++) {
2112      var op = group.ops[j]
2113      if (op.cursorActivityHandlers)
2114        { while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
2115          { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm) } }
2116    }
2117  } while (i < callbacks.length)
2118}
2119
2120function finishOperation(op, endCb) {
2121  var group = op.ownsGroup
2122  if (!group) { return }
2123
2124  try { fireCallbacksForOps(group) }
2125  finally {
2126    operationGroup = null
2127    endCb(group)
2128  }
2129}
2130
2131var orphanDelayedCallbacks = null
2132
2133// Often, we want to signal events at a point where we are in the
2134// middle of some work, but don't want the handler to start calling
2135// other methods on the editor, which might be in an inconsistent
2136// state or simply not expect any other events to happen.
2137// signalLater looks whether there are any handlers, and schedules
2138// them to be executed when the last operation ends, or, if no
2139// operation is active, when a timeout fires.
2140function signalLater(emitter, type /*, values...*/) {
2141  var arr = getHandlers(emitter, type)
2142  if (!arr.length) { return }
2143  var args = Array.prototype.slice.call(arguments, 2), list
2144  if (operationGroup) {
2145    list = operationGroup.delayedCallbacks
2146  } else if (orphanDelayedCallbacks) {
2147    list = orphanDelayedCallbacks
2148  } else {
2149    list = orphanDelayedCallbacks = []
2150    setTimeout(fireOrphanDelayed, 0)
2151  }
2152  var loop = function ( i ) {
2153    list.push(function () { return arr[i].apply(null, args); })
2154  };
2155
2156  for (var i = 0; i < arr.length; ++i)
2157    loop( i );
2158}
2159
2160function fireOrphanDelayed() {
2161  var delayed = orphanDelayedCallbacks
2162  orphanDelayedCallbacks = null
2163  for (var i = 0; i < delayed.length; ++i) { delayed[i]() }
2164}
2165
2166// When an aspect of a line changes, a string is added to
2167// lineView.changes. This updates the relevant part of the line's
2168// DOM structure.
2169function updateLineForChanges(cm, lineView, lineN, dims) {
2170  for (var j = 0; j < lineView.changes.length; j++) {
2171    var type = lineView.changes[j]
2172    if (type == "text") { updateLineText(cm, lineView) }
2173    else if (type == "gutter") { updateLineGutter(cm, lineView, lineN, dims) }
2174    else if (type == "class") { updateLineClasses(cm, lineView) }
2175    else if (type == "widget") { updateLineWidgets(cm, lineView, dims) }
2176  }
2177  lineView.changes = null
2178}
2179
2180// Lines with gutter elements, widgets or a background class need to
2181// be wrapped, and have the extra elements added to the wrapper div
2182function ensureLineWrapped(lineView) {
2183  if (lineView.node == lineView.text) {
2184    lineView.node = elt("div", null, null, "position: relative")
2185    if (lineView.text.parentNode)
2186      { lineView.text.parentNode.replaceChild(lineView.node, lineView.text) }
2187    lineView.node.appendChild(lineView.text)
2188    if (ie && ie_version < 8) { lineView.node.style.zIndex = 2 }
2189  }
2190  return lineView.node
2191}
2192
2193function updateLineBackground(cm, lineView) {
2194  var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass
2195  if (cls) { cls += " CodeMirror-linebackground" }
2196  if (lineView.background) {
2197    if (cls) { lineView.background.className = cls }
2198    else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null }
2199  } else if (cls) {
2200    var wrap = ensureLineWrapped(lineView)
2201    lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild)
2202    cm.display.input.setUneditable(lineView.background)
2203  }
2204}
2205
2206// Wrapper around buildLineContent which will reuse the structure
2207// in display.externalMeasured when possible.
2208function getLineContent(cm, lineView) {
2209  var ext = cm.display.externalMeasured
2210  if (ext && ext.line == lineView.line) {
2211    cm.display.externalMeasured = null
2212    lineView.measure = ext.measure
2213    return ext.built
2214  }
2215  return buildLineContent(cm, lineView)
2216}
2217
2218// Redraw the line's text. Interacts with the background and text
2219// classes because the mode may output tokens that influence these
2220// classes.
2221function updateLineText(cm, lineView) {
2222  var cls = lineView.text.className
2223  var built = getLineContent(cm, lineView)
2224  if (lineView.text == lineView.node) { lineView.node = built.pre }
2225  lineView.text.parentNode.replaceChild(built.pre, lineView.text)
2226  lineView.text = built.pre
2227  if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {
2228    lineView.bgClass = built.bgClass
2229    lineView.textClass = built.textClass
2230    updateLineClasses(cm, lineView)
2231  } else if (cls) {
2232    lineView.text.className = cls
2233  }
2234}
2235
2236function updateLineClasses(cm, lineView) {
2237  updateLineBackground(cm, lineView)
2238  if (lineView.line.wrapClass)
2239    { ensureLineWrapped(lineView).className = lineView.line.wrapClass }
2240  else if (lineView.node != lineView.text)
2241    { lineView.node.className = "" }
2242  var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass
2243  lineView.text.className = textClass || ""
2244}
2245
2246function updateLineGutter(cm, lineView, lineN, dims) {
2247  if (lineView.gutter) {
2248    lineView.node.removeChild(lineView.gutter)
2249    lineView.gutter = null
2250  }
2251  if (lineView.gutterBackground) {
2252    lineView.node.removeChild(lineView.gutterBackground)
2253    lineView.gutterBackground = null
2254  }
2255  if (lineView.line.gutterClass) {
2256    var wrap = ensureLineWrapped(lineView)
2257    lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass,
2258                                    ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px; width: " + (dims.gutterTotalWidth) + "px"))
2259    cm.display.input.setUneditable(lineView.gutterBackground)
2260    wrap.insertBefore(lineView.gutterBackground, lineView.text)
2261  }
2262  var markers = lineView.line.gutterMarkers
2263  if (cm.options.lineNumbers || markers) {
2264    var wrap$1 = ensureLineWrapped(lineView)
2265    var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"))
2266    cm.display.input.setUneditable(gutterWrap)
2267    wrap$1.insertBefore(gutterWrap, lineView.text)
2268    if (lineView.line.gutterClass)
2269      { gutterWrap.className += " " + lineView.line.gutterClass }
2270    if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
2271      { lineView.lineNumber = gutterWrap.appendChild(
2272        elt("div", lineNumberFor(cm.options, lineN),
2273            "CodeMirror-linenumber CodeMirror-gutter-elt",
2274            ("left: " + (dims.gutterLeft["CodeMirror-linenumbers"]) + "px; width: " + (cm.display.lineNumInnerWidth) + "px"))) }
2275    if (markers) { for (var k = 0; k < cm.options.gutters.length; ++k) {
2276      var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id]
2277      if (found)
2278        { gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt",
2279                                   ("left: " + (dims.gutterLeft[id]) + "px; width: " + (dims.gutterWidth[id]) + "px"))) }
2280    } }
2281  }
2282}
2283
2284function updateLineWidgets(cm, lineView, dims) {
2285  if (lineView.alignable) { lineView.alignable = null }
2286  for (var node = lineView.node.firstChild, next = (void 0); node; node = next) {
2287    next = node.nextSibling
2288    if (node.className == "CodeMirror-linewidget")
2289      { lineView.node.removeChild(node) }
2290  }
2291  insertLineWidgets(cm, lineView, dims)
2292}
2293
2294// Build a line's DOM representation from scratch
2295function buildLineElement(cm, lineView, lineN, dims) {
2296  var built = getLineContent(cm, lineView)
2297  lineView.text = lineView.node = built.pre
2298  if (built.bgClass) { lineView.bgClass = built.bgClass }
2299  if (built.textClass) { lineView.textClass = built.textClass }
2300
2301  updateLineClasses(cm, lineView)
2302  updateLineGutter(cm, lineView, lineN, dims)
2303  insertLineWidgets(cm, lineView, dims)
2304  return lineView.node
2305}
2306
2307// A lineView may contain multiple logical lines (when merged by
2308// collapsed spans). The widgets for all of them need to be drawn.
2309function insertLineWidgets(cm, lineView, dims) {
2310  insertLineWidgetsFor(cm, lineView.line, lineView, dims, true)
2311  if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
2312    { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false) } }
2313}
2314
2315function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {
2316  if (!line.widgets) { return }
2317  var wrap = ensureLineWrapped(lineView)
2318  for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
2319    var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget")
2320    if (!widget.handleMouseEvents) { node.setAttribute("cm-ignore-events", "true") }
2321    positionLineWidget(widget, node, lineView, dims)
2322    cm.display.input.setUneditable(node)
2323    if (allowAbove && widget.above)
2324      { wrap.insertBefore(node, lineView.gutter || lineView.text) }
2325    else
2326      { wrap.appendChild(node) }
2327    signalLater(widget, "redraw")
2328  }
2329}
2330
2331function positionLineWidget(widget, node, lineView, dims) {
2332  if (widget.noHScroll) {
2333    ;(lineView.alignable || (lineView.alignable = [])).push(node)
2334    var width = dims.wrapperWidth
2335    node.style.left = dims.fixedPos + "px"
2336    if (!widget.coverGutter) {
2337      width -= dims.gutterTotalWidth
2338      node.style.paddingLeft = dims.gutterTotalWidth + "px"
2339    }
2340    node.style.width = width + "px"
2341  }
2342  if (widget.coverGutter) {
2343    node.style.zIndex = 5
2344    node.style.position = "relative"
2345    if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + "px" }
2346  }
2347}
2348
2349function widgetHeight(widget) {
2350  if (widget.height != null) { return widget.height }
2351  var cm = widget.doc.cm
2352  if (!cm) { return 0 }
2353  if (!contains(document.body, widget.node)) {
2354    var parentStyle = "position: relative;"
2355    if (widget.coverGutter)
2356      { parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;" }
2357    if (widget.noHScroll)
2358      { parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;" }
2359    removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle))
2360  }
2361  return widget.height = widget.node.parentNode.offsetHeight
2362}
2363
2364// Return true when the given mouse event happened in a widget
2365function eventInWidget(display, e) {
2366  for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
2367    if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") ||
2368        (n.parentNode == display.sizer && n != display.mover))
2369      { return true }
2370  }
2371}
2372
2373// POSITION MEASUREMENT
2374
2375function paddingTop(display) {return display.lineSpace.offsetTop}
2376function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight}
2377function paddingH(display) {
2378  if (display.cachedPaddingH) { return display.cachedPaddingH }
2379  var e = removeChildrenAndAdd(display.measure, elt("pre", "x"))
2380  var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle
2381  var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)}
2382  if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data }
2383  return data
2384}
2385
2386function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth }
2387function displayWidth(cm) {
2388  return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth
2389}
2390function displayHeight(cm) {
2391  return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight
2392}
2393
2394// Ensure the lineView.wrapping.heights array is populated. This is
2395// an array of bottom offsets for the lines that make up a drawn
2396// line. When lineWrapping is on, there might be more than one
2397// height.
2398function ensureLineHeights(cm, lineView, rect) {
2399  var wrapping = cm.options.lineWrapping
2400  var curWidth = wrapping && displayWidth(cm)
2401  if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {
2402    var heights = lineView.measure.heights = []
2403    if (wrapping) {
2404      lineView.measure.width = curWidth
2405      var rects = lineView.text.firstChild.getClientRects()
2406      for (var i = 0; i < rects.length - 1; i++) {
2407        var cur = rects[i], next = rects[i + 1]
2408        if (Math.abs(cur.bottom - next.bottom) > 2)
2409          { heights.push((cur.bottom + next.top) / 2 - rect.top) }
2410      }
2411    }
2412    heights.push(rect.bottom - rect.top)
2413  }
2414}
2415
2416// Find a line map (mapping character offsets to text nodes) and a
2417// measurement cache for the given line number. (A line view might
2418// contain multiple lines when collapsed ranges are present.)
2419function mapFromLineView(lineView, line, lineN) {
2420  if (lineView.line == line)
2421    { return {map: lineView.measure.map, cache: lineView.measure.cache} }
2422  for (var i = 0; i < lineView.rest.length; i++)
2423    { if (lineView.rest[i] == line)
2424      { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } }
2425  for (var i$1 = 0; i$1 < lineView.rest.length; i$1++)
2426    { if (lineNo(lineView.rest[i$1]) > lineN)
2427      { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } }
2428}
2429
2430// Render a line into the hidden node display.externalMeasured. Used
2431// when measurement is needed for a line that's not in the viewport.
2432function updateExternalMeasurement(cm, line) {
2433  line = visualLine(line)
2434  var lineN = lineNo(line)
2435  var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN)
2436  view.lineN = lineN
2437  var built = view.built = buildLineContent(cm, view)
2438  view.text = built.pre
2439  removeChildrenAndAdd(cm.display.lineMeasure, built.pre)
2440  return view
2441}
2442
2443// Get a {top, bottom, left, right} box (in line-local coordinates)
2444// for a given character.
2445function measureChar(cm, line, ch, bias) {
2446  return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias)
2447}
2448
2449// Find a line view that corresponds to the given line number.
2450function findViewForLine(cm, lineN) {
2451  if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo)
2452    { return cm.display.view[findViewIndex(cm, lineN)] }
2453  var ext = cm.display.externalMeasured
2454  if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size)
2455    { return ext }
2456}
2457
2458// Measurement can be split in two steps, the set-up work that
2459// applies to the whole line, and the measurement of the actual
2460// character. Functions like coordsChar, that need to do a lot of
2461// measurements in a row, can thus ensure that the set-up work is
2462// only done once.
2463function prepareMeasureForLine(cm, line) {
2464  var lineN = lineNo(line)
2465  var view = findViewForLine(cm, lineN)
2466  if (view && !view.text) {
2467    view = null
2468  } else if (view && view.changes) {
2469    updateLineForChanges(cm, view, lineN, getDimensions(cm))
2470    cm.curOp.forceUpdate = true
2471  }
2472  if (!view)
2473    { view = updateExternalMeasurement(cm, line) }
2474
2475  var info = mapFromLineView(view, line, lineN)
2476  return {
2477    line: line, view: view, rect: null,
2478    map: info.map, cache: info.cache, before: info.before,
2479    hasHeights: false
2480  }
2481}
2482
2483// Given a prepared measurement object, measures the position of an
2484// actual character (or fetches it from the cache).
2485function measureCharPrepared(cm, prepared, ch, bias, varHeight) {
2486  if (prepared.before) { ch = -1 }
2487  var key = ch + (bias || ""), found
2488  if (prepared.cache.hasOwnProperty(key)) {
2489    found = prepared.cache[key]
2490  } else {
2491    if (!prepared.rect)
2492      { prepared.rect = prepared.view.text.getBoundingClientRect() }
2493    if (!prepared.hasHeights) {
2494      ensureLineHeights(cm, prepared.view, prepared.rect)
2495      prepared.hasHeights = true
2496    }
2497    found = measureCharInner(cm, prepared, ch, bias)
2498    if (!found.bogus) { prepared.cache[key] = found }
2499  }
2500  return {left: found.left, right: found.right,
2501          top: varHeight ? found.rtop : found.top,
2502          bottom: varHeight ? found.rbottom : found.bottom}
2503}
2504
2505var nullRect = {left: 0, right: 0, top: 0, bottom: 0}
2506
2507function nodeAndOffsetInLineMap(map, ch, bias) {
2508  var node, start, end, collapse, mStart, mEnd
2509  // First, search the line map for the text node corresponding to,
2510  // or closest to, the target character.
2511  for (var i = 0; i < map.length; i += 3) {
2512    mStart = map[i]
2513    mEnd = map[i + 1]
2514    if (ch < mStart) {
2515      start = 0; end = 1
2516      collapse = "left"
2517    } else if (ch < mEnd) {
2518      start = ch - mStart
2519      end = start + 1
2520    } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) {
2521      end = mEnd - mStart
2522      start = end - 1
2523      if (ch >= mEnd) { collapse = "right" }
2524    }
2525    if (start != null) {
2526      node = map[i + 2]
2527      if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right"))
2528        { collapse = bias }
2529      if (bias == "left" && start == 0)
2530        { while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) {
2531          node = map[(i -= 3) + 2]
2532          collapse = "left"
2533        } }
2534      if (bias == "right" && start == mEnd - mStart)
2535        { while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].insertLeft) {
2536          node = map[(i += 3) + 2]
2537          collapse = "right"
2538        } }
2539      break
2540    }
2541  }
2542  return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd}
2543}
2544
2545function getUsefulRect(rects, bias) {
2546  var rect = nullRect
2547  if (bias == "left") { for (var i = 0; i < rects.length; i++) {
2548    if ((rect = rects[i]).left != rect.right) { break }
2549  } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) {
2550    if ((rect = rects[i$1]).left != rect.right) { break }
2551  } }
2552  return rect
2553}
2554
2555function measureCharInner(cm, prepared, ch, bias) {
2556  var place = nodeAndOffsetInLineMap(prepared.map, ch, bias)
2557  var node = place.node, start = place.start, end = place.end, collapse = place.collapse
2558
2559  var rect
2560  if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.
2561    for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonsense rectangles are returned
2562      while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start }
2563      while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) { ++end }
2564      if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart)
2565        { rect = node.parentNode.getBoundingClientRect() }
2566      else
2567        { rect = getUsefulRect(range(node, start, end).getClientRects(), bias) }
2568      if (rect.left || rect.right || start == 0) { break }
2569      end = start
2570      start = start - 1
2571      collapse = "right"
2572    }
2573    if (ie && ie_version < 11) { rect = maybeUpdateRectForZooming(cm.display.measure, rect) }
2574  } else { // If it is a widget, simply get the box for the whole widget.
2575    if (start > 0) { collapse = bias = "right" }
2576    var rects
2577    if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)
2578      { rect = rects[bias == "right" ? rects.length - 1 : 0] }
2579    else
2580      { rect = node.getBoundingClientRect() }
2581  }
2582  if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {
2583    var rSpan = node.parentNode.getClientRects()[0]
2584    if (rSpan)
2585      { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom} }
2586    else
2587      { rect = nullRect }
2588  }
2589
2590  var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top
2591  var mid = (rtop + rbot) / 2
2592  var heights = prepared.view.measure.heights
2593  var i = 0
2594  for (; i < heights.length - 1; i++)
2595    { if (mid < heights[i]) { break } }
2596  var top = i ? heights[i - 1] : 0, bot = heights[i]
2597  var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left,
2598                right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left,
2599                top: top, bottom: bot}
2600  if (!rect.left && !rect.right) { result.bogus = true }
2601  if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot }
2602
2603  return result
2604}
2605
2606// Work around problem with bounding client rects on ranges being
2607// returned incorrectly when zoomed on IE10 and below.
2608function maybeUpdateRectForZooming(measure, rect) {
2609  if (!window.screen || screen.logicalXDPI == null ||
2610      screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))
2611    { return rect }
2612  var scaleX = screen.logicalXDPI / screen.deviceXDPI
2613  var scaleY = screen.logicalYDPI / screen.deviceYDPI
2614  return {left: rect.left * scaleX, right: rect.right * scaleX,
2615          top: rect.top * scaleY, bottom: rect.bottom * scaleY}
2616}
2617
2618function clearLineMeasurementCacheFor(lineView) {
2619  if (lineView.measure) {
2620    lineView.measure.cache = {}
2621    lineView.measure.heights = null
2622    if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
2623      { lineView.measure.caches[i] = {} } }
2624  }
2625}
2626
2627function clearLineMeasurementCache(cm) {
2628  cm.display.externalMeasure = null
2629  removeChildren(cm.display.lineMeasure)
2630  for (var i = 0; i < cm.display.view.length; i++)
2631    { clearLineMeasurementCacheFor(cm.display.view[i]) }
2632}
2633
2634function clearCaches(cm) {
2635  clearLineMeasurementCache(cm)
2636  cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null
2637  if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true }
2638  cm.display.lineNumChars = null
2639}
2640
2641function pageScrollX() {
2642  // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=489206
2643  // which causes page_Offset and bounding client rects to use
2644  // different reference viewports and invalidate our calculations.
2645  if (chrome && android) { return -(document.body.getBoundingClientRect().left - parseInt(getComputedStyle(document.body).marginLeft)) }
2646  return window.pageXOffset || (document.documentElement || document.body).scrollLeft
2647}
2648function pageScrollY() {
2649  if (chrome && android) { return -(document.body.getBoundingClientRect().top - parseInt(getComputedStyle(document.body).marginTop)) }
2650  return window.pageYOffset || (document.documentElement || document.body).scrollTop
2651}
2652
2653function widgetTopHeight(lineObj) {
2654  var height = 0
2655  if (lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above)
2656    { height += widgetHeight(lineObj.widgets[i]) } } }
2657  return height
2658}
2659
2660// Converts a {top, bottom, left, right} box from line-local
2661// coordinates into another coordinate system. Context may be one of
2662// "line", "div" (display.lineDiv), "local"./null (editor), "window",
2663// or "page".
2664function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) {
2665  if (!includeWidgets) {
2666    var height = widgetTopHeight(lineObj)
2667    rect.top += height; rect.bottom += height
2668  }
2669  if (context == "line") { return rect }
2670  if (!context) { context = "local" }
2671  var yOff = heightAtLine(lineObj)
2672  if (context == "local") { yOff += paddingTop(cm.display) }
2673  else { yOff -= cm.display.viewOffset }
2674  if (context == "page" || context == "window") {
2675    var lOff = cm.display.lineSpace.getBoundingClientRect()
2676    yOff += lOff.top + (context == "window" ? 0 : pageScrollY())
2677    var xOff = lOff.left + (context == "window" ? 0 : pageScrollX())
2678    rect.left += xOff; rect.right += xOff
2679  }
2680  rect.top += yOff; rect.bottom += yOff
2681  return rect
2682}
2683
2684// Coverts a box from "div" coords to another coordinate system.
2685// Context may be "window", "page", "div", or "local"./null.
2686function fromCoordSystem(cm, coords, context) {
2687  if (context == "div") { return coords }
2688  var left = coords.left, top = coords.top
2689  // First move into "page" coordinate system
2690  if (context == "page") {
2691    left -= pageScrollX()
2692    top -= pageScrollY()
2693  } else if (context == "local" || !context) {
2694    var localBox = cm.display.sizer.getBoundingClientRect()
2695    left += localBox.left
2696    top += localBox.top
2697  }
2698
2699  var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect()
2700  return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top}
2701}
2702
2703function charCoords(cm, pos, context, lineObj, bias) {
2704  if (!lineObj) { lineObj = getLine(cm.doc, pos.line) }
2705  return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context)
2706}
2707
2708// Returns a box for a given cursor position, which may have an
2709// 'other' property containing the position of the secondary cursor
2710// on a bidi boundary.
2711// A cursor Pos(line, char, "before") is on the same visual line as `char - 1`
2712// and after `char - 1` in writing order of `char - 1`
2713// A cursor Pos(line, char, "after") is on the same visual line as `char`
2714// and before `char` in writing order of `char`
2715// Examples (upper-case letters are RTL, lower-case are LTR):
2716//     Pos(0, 1, ...)
2717//     before   after
2718// ab     a|b     a|b
2719// aB     a|B     aB|
2720// Ab     |Ab     A|b
2721// AB     B|A     B|A
2722// Every position after the last character on a line is considered to stick
2723// to the last character on the line.
2724function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
2725  lineObj = lineObj || getLine(cm.doc, pos.line)
2726  if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj) }
2727  function get(ch, right) {
2728    var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight)
2729    if (right) { m.left = m.right; } else { m.right = m.left }
2730    return intoCoordSystem(cm, lineObj, m, context)
2731  }
2732  var order = getOrder(lineObj, cm.doc.direction), ch = pos.ch, sticky = pos.sticky
2733  if (ch >= lineObj.text.length) {
2734    ch = lineObj.text.length
2735    sticky = "before"
2736  } else if (ch <= 0) {
2737    ch = 0
2738    sticky = "after"
2739  }
2740  if (!order) { return get(sticky == "before" ? ch - 1 : ch, sticky == "before") }
2741
2742  function getBidi(ch, partPos, invert) {
2743    var part = order[partPos], right = part.level == 1
2744    return get(invert ? ch - 1 : ch, right != invert)
2745  }
2746  var partPos = getBidiPartAt(order, ch, sticky)
2747  var other = bidiOther
2748  var val = getBidi(ch, partPos, sticky == "before")
2749  if (other != null) { val.other = getBidi(ch, other, sticky != "before") }
2750  return val
2751}
2752
2753// Used to cheaply estimate the coordinates for a position. Used for
2754// intermediate scroll updates.
2755function estimateCoords(cm, pos) {
2756  var left = 0
2757  pos = clipPos(cm.doc, pos)
2758  if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch }
2759  var lineObj = getLine(cm.doc, pos.line)
2760  var top = heightAtLine(lineObj) + paddingTop(cm.display)
2761  return {left: left, right: left, top: top, bottom: top + lineObj.height}
2762}
2763
2764// Positions returned by coordsChar contain some extra information.
2765// xRel is the relative x position of the input coordinates compared
2766// to the found position (so xRel > 0 means the coordinates are to
2767// the right of the character position, for example). When outside
2768// is true, that means the coordinates lie outside the line's
2769// vertical range.
2770function PosWithInfo(line, ch, sticky, outside, xRel) {
2771  var pos = Pos(line, ch, sticky)
2772  pos.xRel = xRel
2773  if (outside) { pos.outside = true }
2774  return pos
2775}
2776
2777// Compute the character position closest to the given coordinates.
2778// Input must be lineSpace-local ("div" coordinate system).
2779function coordsChar(cm, x, y) {
2780  var doc = cm.doc
2781  y += cm.display.viewOffset
2782  if (y < 0) { return PosWithInfo(doc.first, 0, null, true, -1) }
2783  var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1
2784  if (lineN > last)
2785    { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, true, 1) }
2786  if (x < 0) { x = 0 }
2787
2788  var lineObj = getLine(doc, lineN)
2789  for (;;) {
2790    var found = coordsCharInner(cm, lineObj, lineN, x, y)
2791    var collapsed = collapsedSpanAround(lineObj, found.ch + (found.xRel > 0 ? 1 : 0))
2792    if (!collapsed) { return found }
2793    var rangeEnd = collapsed.find(1)
2794    if (rangeEnd.line == lineN) { return rangeEnd }
2795    lineObj = getLine(doc, lineN = rangeEnd.line)
2796  }
2797}
2798
2799function wrappedLineExtent(cm, lineObj, preparedMeasure, y) {
2800  y -= widgetTopHeight(lineObj)
2801  var end = lineObj.text.length
2802  var begin = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch - 1).bottom <= y; }, end, 0)
2803  end = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch).top > y; }, begin, end)
2804  return {begin: begin, end: end}
2805}
2806
2807function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) {
2808  if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj) }
2809  var targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), "line").top
2810  return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop)
2811}
2812
2813// Returns true if the given side of a box is after the given
2814// coordinates, in top-to-bottom, left-to-right order.
2815function boxIsAfter(box, x, y, left) {
2816  return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x
2817}
2818
2819function coordsCharInner(cm, lineObj, lineNo, x, y) {
2820  // Move y into line-local coordinate space
2821  y -= heightAtLine(lineObj)
2822  var preparedMeasure = prepareMeasureForLine(cm, lineObj)
2823  // When directly calling `measureCharPrepared`, we have to adjust
2824  // for the widgets at this line.
2825  var widgetHeight = widgetTopHeight(lineObj)
2826  var begin = 0, end = lineObj.text.length, ltr = true
2827
2828  var order = getOrder(lineObj, cm.doc.direction)
2829  // If the line isn't plain left-to-right text, first figure out
2830  // which bidi section the coordinates fall into.
2831  if (order) {
2832    var part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart)
2833                 (cm, lineObj, lineNo, preparedMeasure, order, x, y)
2834    ltr = part.level != 1
2835    // The awkward -1 offsets are needed because findFirst (called
2836    // on these below) will treat its first bound as inclusive,
2837    // second as exclusive, but we want to actually address the
2838    // characters in the part's range
2839    begin = ltr ? part.from : part.to - 1
2840    end = ltr ? part.to : part.from - 1
2841  }
2842
2843  // A binary search to find the first character whose bounding box
2844  // starts after the coordinates. If we run across any whose box wrap
2845  // the coordinates, store that.
2846  var chAround = null, boxAround = null
2847  var ch = findFirst(function (ch) {
2848    var box = measureCharPrepared(cm, preparedMeasure, ch)
2849    box.top += widgetHeight; box.bottom += widgetHeight
2850    if (!boxIsAfter(box, x, y, false)) { return false }
2851    if (box.top <= y && box.left <= x) {
2852      chAround = ch
2853      boxAround = box
2854    }
2855    return true
2856  }, begin, end)
2857
2858  var baseX, sticky, outside = false
2859  // If a box around the coordinates was found, use that
2860  if (boxAround) {
2861    // Distinguish coordinates nearer to the left or right side of the box
2862    var atLeft = x - boxAround.left < boxAround.right - x, atStart = atLeft == ltr
2863    ch = chAround + (atStart ? 0 : 1)
2864    sticky = atStart ? "after" : "before"
2865    baseX = atLeft ? boxAround.left : boxAround.right
2866  } else {
2867    // (Adjust for extended bound, if necessary.)
2868    if (!ltr && (ch == end || ch == begin)) { ch++ }
2869    // To determine which side to associate with, get the box to the
2870    // left of the character and compare it's vertical position to the
2871    // coordinates
2872    sticky = ch == 0 ? "after" : ch == lineObj.text.length ? "before" :
2873      (measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight <= y) == ltr ?
2874      "after" : "before"
2875    // Now get accurate coordinates for this place, in order to get a
2876    // base X position
2877    var coords = cursorCoords(cm, Pos(lineNo, ch, sticky), "line", lineObj, preparedMeasure)
2878    baseX = coords.left
2879    outside = y < coords.top || y >= coords.bottom
2880  }
2881
2882  ch = skipExtendingChars(lineObj.text, ch, 1)
2883  return PosWithInfo(lineNo, ch, sticky, outside, x - baseX)
2884}
2885
2886function coordsBidiPart(cm, lineObj, lineNo, preparedMeasure, order, x, y) {
2887  // Bidi parts are sorted left-to-right, and in a non-line-wrapping
2888  // situation, we can take this ordering to correspond to the visual
2889  // ordering. This finds the first part whose end is after the given
2890  // coordinates.
2891  var index = findFirst(function (i) {
2892    var part = order[i], ltr = part.level != 1
2893    return boxIsAfter(cursorCoords(cm, Pos(lineNo, ltr ? part.to : part.from, ltr ? "before" : "after"),
2894                                   "line", lineObj, preparedMeasure), x, y, true)
2895  }, 0, order.length - 1)
2896  var part = order[index]
2897  // If this isn't the first part, the part's start is also after
2898  // the coordinates, and the coordinates aren't on the same line as
2899  // that start, move one part back.
2900  if (index > 0) {
2901    var ltr = part.level != 1
2902    var start = cursorCoords(cm, Pos(lineNo, ltr ? part.from : part.to, ltr ? "after" : "before"),
2903                             "line", lineObj, preparedMeasure)
2904    if (boxIsAfter(start, x, y, true) && start.top > y)
2905      { part = order[index - 1] }
2906  }
2907  return part
2908}
2909
2910function coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x, y) {
2911  // In a wrapped line, rtl text on wrapping boundaries can do things
2912  // that don't correspond to the ordering in our `order` array at
2913  // all, so a binary search doesn't work, and we want to return a
2914  // part that only spans one line so that the binary search in
2915  // coordsCharInner is safe. As such, we first find the extent of the
2916  // wrapped line, and then do a flat search in which we discard any
2917  // spans that aren't on the line.
2918  var ref = wrappedLineExtent(cm, lineObj, preparedMeasure, y);
2919  var begin = ref.begin;
2920  var end = ref.end;
2921  if (/\s/.test(lineObj.text.charAt(end - 1))) { end-- }
2922  var part = null, closestDist = null
2923  for (var i = 0; i < order.length; i++) {
2924    var p = order[i]
2925    if (p.from >= end || p.to <= begin) { continue }
2926    var ltr = p.level != 1
2927    var endX = measureCharPrepared(cm, preparedMeasure, ltr ? Math.min(end, p.to) - 1 : Math.max(begin, p.from)).right
2928    // Weigh against spans ending before this, so that they are only
2929    // picked if nothing ends after
2930    var dist = endX < x ? x - endX + 1e9 : endX - x
2931    if (!part || closestDist > dist) {
2932      part = p
2933      closestDist = dist
2934    }
2935  }
2936  if (!part) { part = order[order.length - 1] }
2937  // Clip the part to the wrapped line.
2938  if (part.from < begin) { part = {from: begin, to: part.to, level: part.level} }
2939  if (part.to > end) { part = {from: part.from, to: end, level: part.level} }
2940  return part
2941}
2942
2943var measureText
2944// Compute the default text height.
2945function textHeight(display) {
2946  if (display.cachedTextHeight != null) { return display.cachedTextHeight }
2947  if (measureText == null) {
2948    measureText = elt("pre")
2949    // Measure a bunch of lines, for browsers that compute
2950    // fractional heights.
2951    for (var i = 0; i < 49; ++i) {
2952      measureText.appendChild(document.createTextNode("x"))
2953      measureText.appendChild(elt("br"))
2954    }
2955    measureText.appendChild(document.createTextNode("x"))
2956  }
2957  removeChildrenAndAdd(display.measure, measureText)
2958  var height = measureText.offsetHeight / 50
2959  if (height > 3) { display.cachedTextHeight = height }
2960  removeChildren(display.measure)
2961  return height || 1
2962}
2963
2964// Compute the default character width.
2965function charWidth(display) {
2966  if (display.cachedCharWidth != null) { return display.cachedCharWidth }
2967  var anchor = elt("span", "xxxxxxxxxx")
2968  var pre = elt("pre", [anchor])
2969  removeChildrenAndAdd(display.measure, pre)
2970  var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10
2971  if (width > 2) { display.cachedCharWidth = width }
2972  return width || 10
2973}
2974
2975// Do a bulk-read of the DOM positions and sizes needed to draw the
2976// view, so that we don't interleave reading and writing to the DOM.
2977function getDimensions(cm) {
2978  var d = cm.display, left = {}, width = {}
2979  var gutterLeft = d.gutters.clientLeft
2980  for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
2981    left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft
2982    width[cm.options.gutters[i]] = n.clientWidth
2983  }
2984  return {fixedPos: compensateForHScroll(d),
2985          gutterTotalWidth: d.gutters.offsetWidth,
2986          gutterLeft: left,
2987          gutterWidth: width,
2988          wrapperWidth: d.wrapper.clientWidth}
2989}
2990
2991// Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
2992// but using getBoundingClientRect to get a sub-pixel-accurate
2993// result.
2994function compensateForHScroll(display) {
2995  return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left
2996}
2997
2998// Returns a function that estimates the height of a line, to use as
2999// first approximation until the line becomes visible (and is thus
3000// properly measurable).
3001function estimateHeight(cm) {
3002  var th = textHeight(cm.display), wrapping = cm.options.lineWrapping
3003  var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3)
3004  return function (line) {
3005    if (lineIsHidden(cm.doc, line)) { return 0 }
3006
3007    var widgetsHeight = 0
3008    if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) {
3009      if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height }
3010    } }
3011
3012    if (wrapping)
3013      { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th }
3014    else
3015      { return widgetsHeight + th }
3016  }
3017}
3018
3019function estimateLineHeights(cm) {
3020  var doc = cm.doc, est = estimateHeight(cm)
3021  doc.iter(function (line) {
3022    var estHeight = est(line)
3023    if (estHeight != line.height) { updateLineHeight(line, estHeight) }
3024  })
3025}
3026
3027// Given a mouse event, find the corresponding position. If liberal
3028// is false, it checks whether a gutter or scrollbar was clicked,
3029// and returns null if it was. forRect is used by rectangular
3030// selections, and tries to estimate a character position even for
3031// coordinates beyond the right of the text.
3032function posFromMouse(cm, e, liberal, forRect) {
3033  var display = cm.display
3034  if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") { return null }
3035
3036  var x, y, space = display.lineSpace.getBoundingClientRect()
3037  // Fails unpredictably on IE[67] when mouse is dragged around quickly.
3038  try { x = e.clientX - space.left; y = e.clientY - space.top }
3039  catch (e) { return null }
3040  var coords = coordsChar(cm, x, y), line
3041  if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) {
3042    var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length
3043    coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff))
3044  }
3045  return coords
3046}
3047
3048// Find the view element corresponding to a given line. Return null
3049// when the line isn't visible.
3050function findViewIndex(cm, n) {
3051  if (n >
3051= cm.display.viewTo) { return null }
3052  n -= cm.display.viewFrom
3053  if (n < 0) { return null }
3054  var view = cm.display.view
3055  for (var i = 0; i < view.length; i++) {
3056    n -= view[i].size
3057    if (n < 0) { return i }
3058  }
3059}
3060
3061function updateSelection(cm) {
3062  cm.display.input.showSelection(cm.display.input.prepareSelection())
3063}
3064
3065function prepareSelection(cm, primary) {
3066  if ( primary === void 0 ) primary = true;
3067
3068  var doc = cm.doc, result = {}
3069  var curFragment = result.cursors = document.createDocumentFragment()
3070  var selFragment = result.selection = document.createDocumentFragment()
3071
3072  for (var i = 0; i < doc.sel.ranges.length; i++) {
3073    if (!primary && i == doc.sel.primIndex) { continue }
3074    var range = doc.sel.ranges[i]
3075    if (range.from().line >= cm.display.viewTo || range.to().line < cm.display.viewFrom) { continue }
3076    var collapsed = range.empty()
3077    if (collapsed || cm.options.showCursorWhenSelecting)
3078      { drawSelectionCursor(cm, range.head, curFragment) }
3079    if (!collapsed)
3080      { drawSelectionRange(cm, range, selFragment) }
3081  }
3082  return result
3083}
3084
3085// Draws a cursor for the given range
3086function drawSelectionCursor(cm, head, output) {
3087  var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine)
3088
3089  var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor"))
3090  cursor.style.left = pos.left + "px"
3091  cursor.style.top = pos.top + "px"
3092  cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px"
3093
3094  if (pos.other) {
3095    // Secondary cursor, shown when on a 'jump' in bi-directional text
3096    var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"))
3097    otherCursor.style.display = ""
3098    otherCursor.style.left = pos.other.left + "px"
3099    otherCursor.style.top = pos.other.top + "px"
3100    otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px"
3101  }
3102}
3103
3104function cmpCoords(a, b) { return a.top - b.top || a.left - b.left }
3105
3106// Draws the given range as a highlighted selection
3107function drawSelectionRange(cm, range, output) {
3108  var display = cm.display, doc = cm.doc
3109  var fragment = document.createDocumentFragment()
3110  var padding = paddingH(cm.display), leftSide = padding.left
3111  var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right
3112  var docLTR = doc.direction == "ltr"
3113
3114  function add(left, top, width, bottom) {
3115    if (top < 0) { top = 0 }
3116    top = Math.round(top)
3117    bottom = Math.round(bottom)
3118    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")))
3119  }
3120
3121  function drawForLine(line, fromArg, toArg) {
3122    var lineObj = getLine(doc, line)
3123    var lineLen = lineObj.text.length
3124    var start, end
3125    function coords(ch, bias) {
3126      return charCoords(cm, Pos(line, ch), "div", lineObj, bias)
3127    }
3128
3129    function wrapX(pos, dir, side) {
3130      var extent = wrappedLineExtentChar(cm, lineObj, null, pos)
3131      var prop = (dir == "ltr") == (side == "after") ? "left" : "right"
3132      var ch = side == "after" ? extent.begin : extent.end - (/\s/.test(lineObj.text.charAt(extent.end - 1)) ? 2 : 1)
3133      return coords(ch, prop)[prop]
3134    }
3135
3136    var order = getOrder(lineObj, doc.direction)
3137    iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir, i) {
3138      var ltr = dir == "ltr"
3139      var fromPos = coords(from, ltr ? "left" : "right")
3140      var toPos = coords(to - 1, ltr ? "right" : "left")
3141
3142      var openStart = fromArg == null && from == 0, openEnd = toArg == null && to == lineLen
3143      var first = i == 0, last = !order || i == order.length - 1
3144      if (toPos.top - fromPos.top <= 3) { // Single line
3145        var openLeft = (docLTR ? openStart : openEnd) && first
3146        var openRight = (docLTR ? openEnd : openStart) && last
3147        var left = openLeft ? leftSide : (ltr ? fromPos : toPos).left
3148        var right = openRight ? rightSide : (ltr ? toPos : fromPos).right
3149        add(left, fromPos.top, right - left, fromPos.bottom)
3150      } else { // Multiple lines
3151        var topLeft, topRight, botLeft, botRight
3152        if (ltr) {
3153          topLeft = docLTR && openStart && first ? leftSide : fromPos.left
3154          topRight = docLTR ? rightSide : wrapX(from, dir, "before")
3155          botLeft = docLTR ? leftSide : wrapX(to, dir, "after")
3156          botRight = docLTR && openEnd && last ? rightSide : toPos.right
3157        } else {
3158          topLeft = !docLTR ? leftSide : wrapX(from, dir, "before")
3159          topRight = !docLTR && openStart && first ? rightSide : fromPos.right
3160          botLeft = !docLTR && openEnd && last ? leftSide : toPos.left
3161          botRight = !docLTR ? rightSide : wrapX(to, dir, "after")
3162        }
3163        add(topLeft, fromPos.top, topRight - topLeft, fromPos.bottom)
3164        if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top) }
3165        add(botLeft, toPos.top, botRight - botLeft, toPos.bottom)
3166      }
3167
3168      if (!start || cmpCoords(fromPos, start) < 0) { start = fromPos }
3169      if (cmpCoords(toPos, start) < 0) { start = toPos }
3170      if (!end || cmpCoords(fromPos, end) < 0) { end = fromPos }
3171      if (cmpCoords(toPos, end) < 0) { end = toPos }
3172    })
3173    return {start: start, end: end}
3174  }
3175
3176  var sFrom = range.from(), sTo = range.to()
3177  if (sFrom.line == sTo.line) {
3178    drawForLine(sFrom.line, sFrom.ch, sTo.ch)
3179  } else {
3180    var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line)
3181    var singleVLine = visualLine(fromLine) == visualLine(toLine)
3182    var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end
3183    var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start
3184    if (singleVLine) {
3185      if (leftEnd.top < rightStart.top - 2) {
3186        add(leftEnd.right, leftEnd.top, null, leftEnd.bottom)
3187        add(leftSide, rightStart.top, rightStart.left, rightStart.bottom)
3188      } else {
3189        add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom)
3190      }
3191    }
3192    if (leftEnd.bottom < rightStart.top)
3193      { add(leftSide, leftEnd.bottom, null, rightStart.top) }
3194  }
3195
3196  output.appendChild(fragment)
3197}
3198
3199// Cursor-blinking
3200function restartBlink(cm) {
3201  if (!cm.state.focused) { return }
3202  var display = cm.display
3203  clearInterval(display.blinker)
3204  var on = true
3205  display.cursorDiv.style.visibility = ""
3206  if (cm.options.cursorBlinkRate > 0)
3207    { display.blinker = setInterval(function () { return display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; },
3208      cm.options.cursorBlinkRate) }
3209  else if (cm.options.cursorBlinkRate < 0)
3210    { display.cursorDiv.style.visibility = "hidden" }
3211}
3212
3213function ensureFocus(cm) {
3214  if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm) }
3215}
3216
3217function delayBlurEvent(cm) {
3218  cm.state.delayingBlurEvent = true
3219  setTimeout(function () { if (cm.state.delayingBlurEvent) {
3220    cm.state.delayingBlurEvent = false
3221    onBlur(cm)
3222  } }, 100)
3223}
3224
3225function onFocus(cm, e) {
3226  if (cm.state.delayingBlurEvent) { cm.state.delayingBlurEvent = false }
3227
3228  if (cm.options.readOnly == "nocursor") { return }
3229  if (!cm.state.focused) {
3230    signal(cm, "focus", cm, e)
3231    cm.state.focused = true
3232    addClass(cm.display.wrapper, "CodeMirror-focused")
3233    // This test prevents this from firing when a context
3234    // menu is closed (since the input reset would kill the
3235    // select-all detection hack)
3236    if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {
3237      cm.display.input.reset()
3238      if (webkit) { setTimeout(function () { return cm.display.input.reset(true); }, 20) } // Issue #1730
3239    }
3240    cm.display.input.receivedFocus()
3241  }
3242  restartBlink(cm)
3243}
3244function onBlur(cm, e) {
3245  if (cm.state.delayingBlurEvent) { return }
3246
3247  if (cm.state.focused) {
3248    signal(cm, "blur", cm, e)
3249    cm.state.focused = false
3250    rmClass(cm.display.wrapper, "CodeMirror-focused")
3251  }
3252  clearInterval(cm.display.blinker)
3253  setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false } }, 150)
3254}
3255
3256// Read the actual heights of the rendered lines, and update their
3257// stored heights to match.
3258function updateHeightsInViewport(cm) {
3259  var display = cm.display
3260  var prevBottom = display.lineDiv.offsetTop
3261  for (var i = 0; i < display.view.length; i++) {
3262    var cur = display.view[i], height = (void 0)
3263    if (cur.hidden) { continue }
3264    if (ie && ie_version < 8) {
3265      var bot = cur.node.offsetTop + cur.node.offsetHeight
3266      height = bot - prevBottom
3267      prevBottom = bot
3268    } else {
3269      var box = cur.node.getBoundingClientRect()
3270      height = box.bottom - box.top
3271    }
3272    var diff = cur.line.height - height
3273    if (height < 2) { height = textHeight(display) }
3274    if (diff > .005 || diff < -.005) {
3275      updateLineHeight(cur.line, height)
3276      updateWidgetHeight(cur.line)
3277      if (cur.rest) { for (var j = 0; j < cur.rest.length; j++)
3278        { updateWidgetHeight(cur.rest[j]) } }
3279    }
3280  }
3281}
3282
3283// Read and store the height of line widgets associated with the
3284// given line.
3285function updateWidgetHeight(line) {
3286  if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i) {
3287    var w = line.widgets[i], parent = w.node.parentNode
3288    if (parent) { w.height = parent.offsetHeight }
3289  } }
3290}
3291
3292// Compute the lines that are visible in a given viewport (defaults
3293// the the current scroll position). viewport may contain top,
3294// height, and ensure (see op.scrollToPos) properties.
3295function visibleLines(display, doc, viewport) {
3296  var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop
3297  top = Math.floor(top - paddingTop(display))
3298  var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight
3299
3300  var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom)
3301  // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
3302  // forces those lines into the viewport (if possible).
3303  if (viewport && viewport.ensure) {
3304    var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line
3305    if (ensureFrom < from) {
3306      from = ensureFrom
3307      to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight)
3308    } else if (Math.min(ensureTo, doc.lastLine()) >= to) {
3309      from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight)
3310      to = ensureTo
3311    }
3312  }
3313  return {from: from, to: Math.max(to, from + 1)}
3314}
3315
3316// Re-align line numbers and gutter marks to compensate for
3317// horizontal scrolling.
3318function alignHorizontally(cm) {
3319  var display = cm.display, view = display.view
3320  if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) { return }
3321  var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft
3322  var gutterW = display.gutters.offsetWidth, left = comp + "px"
3323  for (var i = 0; i < view.length; i++) { if (!view[i].hidden) {
3324    if (cm.options.fixedGutter) {
3325      if (view[i].gutter)
3326        { view[i].gutter.style.left = left }
3327      if (view[i].gutterBackground)
3328        { view[i].gutterBackground.style.left = left }
3329    }
3330    var align = view[i].alignable
3331    if (align) { for (var j = 0; j < align.length; j++)
3332      { align[j].style.left = left } }
3333  } }
3334  if (cm.options.fixedGutter)
3335    { display.gutters.style.left = (comp + gutterW) + "px" }
3336}
3337
3338// Used to ensure that the line number gutter is still the right
3339// size for the current document size. Returns true when an update
3340// is needed.
3341function maybeUpdateLineNumberWidth(cm) {
3342  if (!cm.options.lineNumbers) { return false }
3343  var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display
3344  if (last.length != display.lineNumChars) {
3345    var test = display.measure.appendChild(elt("div", [elt("div", last)],
3346                                               "CodeMirror-linenumber CodeMirror-gutter-elt"))
3347    var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW
3348    display.lineGutter.style.width = ""
3349    display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1
3350    display.lineNumWidth = display.lineNumInnerWidth + padding
3351    display.lineNumChars = display.lineNumInnerWidth ? last.length : -1
3352    display.lineGutter.style.width = display.lineNumWidth + "px"
3353    updateGutterSpace(cm)
3354    return true
3355  }
3356  return false
3357}
3358
3359// SCROLLING THINGS INTO VIEW
3360
3361// If an editor sits on the top or bottom of the window, partially
3362// scrolled out of view, this ensures that the cursor is visible.
3363function maybeScrollWindow(cm, rect) {
3364  if (signalDOMEvent(cm, "scrollCursorIntoView")) { return }
3365
3366  var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null
3367  if (rect.top + box.top < 0) { doScroll = true }
3368  else if (rect.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) { doScroll = false }
3369  if (doScroll != null && !phantom) {
3370    var scrollNode = elt("div", "\u200b", null, ("position: absolute;\n                         top: " + (rect.top - display.viewOffset - paddingTop(cm.display)) + "px;\n                         height: " + (rect.bottom - rect.top + scrollGap(cm) + display.barHeight) + "px;\n                         left: " + (rect.left) + "px; width: " + (Math.max(2, rect.right - rect.left)) + "px;"))
3371    cm.display.lineSpace.appendChild(scrollNode)
3372    scrollNode.scrollIntoView(doScroll)
3373    cm.display.lineSpace.removeChild(scrollNode)
3374  }
3375}
3376
3377// Scroll a given position into view (immediately), verifying that
3378// it actually became visible (as line heights are accurately
3379// measured, the position of something may 'drift' during drawing).
3380function scrollPosIntoView(cm, pos, end, margin) {
3381  if (margin == null) { margin = 0 }
3382  var rect
3383  if (!cm.options.lineWrapping && pos == end) {
3384    // Set pos and end to the cursor positions around the character pos sticks to
3385    // If pos.sticky == "before", that is around pos.ch - 1, otherwise around pos.ch
3386    // If pos == Pos(_, 0, "before"), pos and end are unchanged
3387    pos = pos.ch ? Pos(pos.line, pos.sticky == "before" ? pos.ch - 1 : pos.ch, "after") : pos
3388    end = pos.sticky == "before" ? Pos(pos.line, pos.ch + 1, "before") : pos
3389  }
3390  for (var limit = 0; limit < 5; limit++) {
3391    var changed = false
3392    var coords = cursorCoords(cm, pos)
3393    var endCoords = !end || end == pos ? coords : cursorCoords(cm, end)
3394    rect = {left: Math.min(coords.left, endCoords.left),
3395            top: Math.min(coords.top, endCoords.top) - margin,
3396            right: Math.max(coords.left, endCoords.left),
3397            bottom: Math.max(coords.bottom, endCoords.bottom) + margin}
3398    var scrollPos = calculateScrollPos(cm, rect)
3399    var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft
3400    if (scrollPos.scrollTop != null) {
3401      updateScrollTop(cm, scrollPos.scrollTop)
3402      if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true }
3403    }
3404    if (scrollPos.scrollLeft != null) {
3405      setScrollLeft(cm, scrollPos.scrollLeft)
3406      if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true }
3407    }
3408    if (!changed) { break }
3409  }
3410  return rect
3411}
3412
3413// Scroll a given set of coordinates into view (immediately).
3414function scrollIntoView(cm, rect) {
3415  var scrollPos = calculateScrollPos(cm, rect)
3416  if (scrollPos.scrollTop != null) { updateScrollTop(cm, scrollPos.scrollTop) }
3417  if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft) }
3418}
3419
3420// Calculate a new scroll position needed to scroll the given
3421// rectangle into view. Returns an object with scrollTop and
3422// scrollLeft properties. When these are undefined, the
3423// vertical/horizontal position does not need to be adjusted.
3424function calculateScrollPos(cm, rect) {
3425  var display = cm.display, snapMargin = textHeight(cm.display)
3426  if (rect.top < 0) { rect.top = 0 }
3427  var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop
3428  var screen = displayHeight(cm), result = {}
3429  if (rect.bottom - rect.top > screen) { rect.bottom = rect.top + screen }
3430  var docBottom = cm.doc.height + paddingVert(display)
3431  var atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMargin
3432  if (rect.top < screentop) {
3433    result.scrollTop = atTop ? 0 : rect.top
3434  } else if (rect.bottom > screentop + screen) {
3435    var newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen)
3436    if (newTop != screentop) { result.scrollTop = newTop }
3437  }
3438
3439  var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft
3440  var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0)
3441  var tooWide = rect.right - rect.left > screenw
3442  if (tooWide) { rect.right = rect.left + screenw }
3443  if (rect.left < 10)
3444    { result.scrollLeft = 0 }
3445  else if (rect.left < screenleft)
3446    { result.scrollLeft = Math.max(0, rect.left - (tooWide ? 0 : 10)) }
3447  else if (rect.right > screenw + screenleft - 3)
3448    { result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw }
3449  return result
3450}
3451
3452// Store a relative adjustment to the scroll position in the current
3453// operation (to be applied when the operation finishes).
3454function addToScrollTop(cm, top) {
3455  if (top == null) { return }
3456  resolveScrollToPos(cm)
3457  cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top
3458}
3459
3460// Make sure that at the end of the operation the current cursor is
3461// shown.
3462function ensureCursorVisible(cm) {
3463  resolveScrollToPos(cm)
3464  var cur = cm.getCursor()
3465  cm.curOp.scrollToPos = {from: cur, to: cur, margin: cm.options.cursorScrollMargin}
3466}
3467
3468function scrollToCoords(cm, x, y) {
3469  if (x != null || y != null) { resolveScrollToPos(cm) }
3470  if (x != null) { cm.curOp.scrollLeft = x }
3471  if (y != null) { cm.curOp.scrollTop = y }
3472}
3473
3474function scrollToRange(cm, range) {
3475  resolveScrollToPos(cm)
3476  cm.curOp.scrollToPos = range
3477}
3478
3479// When an operation has its scrollToPos property set, and another
3480// scroll action is applied before the end of the operation, this
3481// 'simulates' scrolling that position into view in a cheap way, so
3482// that the effect of intermediate scroll commands is not ignored.
3483function resolveScrollToPos(cm) {
3484  var range = cm.curOp.scrollToPos
3485  if (range) {
3486    cm.curOp.scrollToPos = null
3487    var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to)
3488    scrollToCoordsRange(cm, from, to, range.margin)
3489  }
3490}
3491
3492function scrollToCoordsRange(cm, from, to, margin) {
3493  var sPos = calculateScrollPos(cm, {
3494    left: Math.min(from.left, to.left),
3495    top: Math.min(from.top, to.top) - margin,
3496    right: Math.max(from.right, to.right),
3497    bottom: Math.max(from.bottom, to.bottom) + margin
3498  })
3499  scrollToCoords(cm, sPos.scrollLeft, sPos.scrollTop)
3500}
3501
3502// Sync the scrollable area and scrollbars, ensure the viewport
3503// covers the visible area.
3504function updateScrollTop(cm, val) {
3505  if (Math.abs(cm.doc.scrollTop - val) < 2) { return }
3506  if (!gecko) { updateDisplaySimple(cm, {top: val}) }
3507  setScrollTop(cm, val, true)
3508  if (gecko) { updateDisplaySimple(cm) }
3509  startWorker(cm, 100)
3510}
3511
3512function setScrollTop(cm, val, forceScroll) {
3513  val = Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val)
3514  if (cm.display.scroller.scrollTop == val && !forceScroll) { return }
3515  cm.doc.scrollTop = val
3516  cm.display.scrollbars.setScrollTop(val)
3517  if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = val }
3518}
3519
3520// Sync scroller and scrollbar, ensure the gutter elements are
3521// aligned.
3522function setScrollLeft(cm, val, isScroller, forceScroll) {
3523  val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth)
3524  if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) { return }
3525  cm.doc.scrollLeft = val
3526  alignHorizontally(cm)
3527  if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val }
3528  cm.display.scrollbars.setScrollLeft(val)
3529}
3530
3531// SCROLLBARS
3532
3533// Prepare DOM reads needed to update the scrollbars. Done in one
3534// shot to minimize update/measure roundtrips.
3535function measureForScrollbars(cm) {
3536  var d = cm.display, gutterW = d.gutters.offsetWidth
3537  var docH = Math.round(cm.doc.height + paddingVert(cm.display))
3538  return {
3539    clientHeight: d.scroller.clientHeight,
3540    viewHeight: d.wrapper.clientHeight,
3541    scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,
3542    viewWidth: d.wrapper.clientWidth,
3543    barLeft: cm.options.fixedGutter ? gutterW : 0,
3544    docHeight: docH,
3545    scrollHeight: docH + scrollGap(cm) + d.barHeight,
3546    nativeBarWidth: d.nativeBarWidth,
3547    gutterWidth: gutterW
3548  }
3549}
3550
3551var NativeScrollbars = function(place, scroll, cm) {
3552  this.cm = cm
3553  var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar")
3554  var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar")
3555  vert.tabIndex = horiz.tabIndex = -1
3556  place(vert); place(horiz)
3557
3558  on(vert, "scroll", function () {
3559    if (vert.clientHeight) { scroll(vert.scrollTop, "vertical") }
3560  })
3561  on(horiz, "scroll", function () {
3562    if (horiz.clientWidth) { scroll(horiz.scrollLeft, "horizontal") }
3563  })
3564
3565  this.checkedZeroWidth = false
3566  // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
3567  if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = "18px" }
3568};
3569
3570NativeScrollbars.prototype.update = function (measure) {
3571  var needsH = measure.scrollWidth > measure.clientWidth + 1
3572  var needsV = measure.scrollHeight > measure.clientHeight + 1
3573  var sWidth = measure.nativeBarWidth
3574
3575  if (needsV) {
3576    this.vert.style.display = "block"
3577    this.vert.style.bottom = needsH ? sWidth + "px" : "0"
3578    var totalHeight = measure.viewHeight - (needsH ? sWidth : 0)
3579    // A bug in IE8 can cause this value to be negative, so guard it.
3580    this.vert.firstChild.style.height =
3581      Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px"
3582  } else {
3583    this.vert.style.display = ""
3584    this.vert.firstChild.style.height = "0"
3585  }
3586
3587  if (needsH) {
3588    this.horiz.style.display = "block"
3589    this.horiz.style.right = needsV ? sWidth + "px" : "0"
3590    this.horiz.style.left = measure.barLeft + "px"
3591    var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0)
3592    this.horiz.firstChild.style.width =
3593      Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + "px"
3594  } else {
3595    this.horiz.style.display = ""
3596    this.horiz.firstChild.style.width = "0"
3597  }
3598
3599  if (!this.checkedZeroWidth && measure.clientHeight > 0) {
3600    if (sWidth == 0) { this.zeroWidthHack() }
3601    this.checkedZeroWidth = true
3602  }
3603
3604  return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0}
3605};
3606
3607NativeScrollbars.prototype.setScrollLeft = function (pos) {
3608  if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos }
3609  if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz, "horiz") }
3610};
3611
3612NativeScrollbars.prototype.setScrollTop = function (pos) {
3613  if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos }
3614  if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert, "vert") }
3615};
3616
3617NativeScrollbars.prototype.zeroWidthHack = function () {
3618  var w = mac && !mac_geMountainLion ? "12px" : "18px"
3619  this.horiz.style.height = this.vert.style.width = w
3620  this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none"
3621  this.disableHoriz = new Delayed
3622  this.disableVert = new Delayed
3623};
3624
3625NativeScrollbars.prototype.enableZeroWidthBar = function (bar, delay, type) {
3626  bar.style.pointerEvents = "auto"
3627  function maybeDisable() {
3628    // To find out whether the scrollbar is still visible, we
3629    // check whether the element under the pixel in the bottom
3630    // right corner of the scrollbar box is the scrollbar box
3631    // itself (when the bar is still visible) or its filler child
3632    // (when the bar is hidden). If it is still visible, we keep
3633    // it enabled, if it's hidden, we disable pointer events.
3634    var box = bar.getBoundingClientRect()
3635    var elt = type == "vert" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2)
3636        : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1)
3637    if (elt != bar) { bar.style.pointerEvents = "none" }
3638    else { delay.set(1000, maybeDisable) }
3639  }
3640  delay.set(1000, maybeDisable)
3641};
3642
3643NativeScrollbars.prototype.clear = function () {
3644  var parent = this.horiz.parentNode
3645  parent.removeChild(this.horiz)
3646  parent.removeChild(this.vert)
3647};
3648
3649var NullScrollbars = function () {};
3650
3651NullScrollbars.prototype.update = function () { return {bottom: 0, right: 0} };
3652NullScrollbars.prototype.setScrollLeft = function () {};
3653NullScrollbars.prototype.setScrollTop = function () {};
3654NullScrollbars.prototype.clear = function () {};
3655
3656function updateScrollbars(cm, measure) {
3657  if (!measure) { measure = measureForScrollbars(cm) }
3658  var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight
3659  updateScrollbarsInner(cm, measure)
3660  for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) {
3661    if (startWidth != cm.display.barWidth && cm.options.lineWrapping)
3662      { updateHeightsInViewport(cm) }
3663    updateScrollbarsInner(cm, measureForScrollbars(cm))
3664    startWidth = cm.display.barWidth; startHeight = cm.display.barHeight
3665  }
3666}
3667
3668// Re-synchronize the fake scrollbars with the actual size of the
3669// content.
3670function updateScrollbarsInner(cm, measure) {
3671  var d = cm.display
3672  var sizes = d.scrollbars.update(measure)
3673
3674  d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px"
3675  d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px"
3676  d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent"
3677
3678  if (sizes.right && sizes.bottom) {
3679    d.scrollbarFiller.style.display = "block"
3680    d.scrollbarFiller.style.height = sizes.bottom + "px"
3681    d.scrollbarFiller.style.width = sizes.right + "px"
3682  } else { d.scrollbarFiller.style.display = "" }
3683  if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
3684    d.gutterFiller.style.display = "block"
3685    d.gutterFiller.style.height = sizes.bottom + "px"
3686    d.gutterFiller.style.width = measure.gutterWidth + "px"
3687  } else { d.gutterFiller.style.display = "" }
3688}
3689
3690var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars}
3691
3692function initScrollbars(cm) {
3693  if (cm.display.scrollbars) {
3694    cm.display.scrollbars.clear()
3695    if (cm.display.scrollbars.addClass)
3696      { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass) }
3697  }
3698
3699  cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) {
3700    cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller)
3701    // Prevent clicks in the scrollbars from killing focus
3702    on(node, "mousedown", function () {
3703      if (cm.state.focused) { setTimeout(function () { return cm.display.input.focus(); }, 0) }
3704    })
3705    node.setAttribute("cm-not-content", "true")
3706  }, function (pos, axis) {
3707    if (axis == "horizontal") { setScrollLeft(cm, pos) }
3708    else { updateScrollTop(cm, pos) }
3709  }, cm)
3710  if (cm.display.scrollbars.addClass)
3711    { addClass(cm.display.wrapper, cm.display.scrollbars.addClass) }
3712}
3713
3714// Operations are used to wrap a series of changes to the editor
3715// state in such a way that each change won't have to update the
3716// cursor and display (which would be awkward, slow, and
3717// error-prone). Instead, display updates are batched and then all
3718// combined and executed at once.
3719
3720var nextOpId = 0
3721// Start a new operation.
3722function startOperation(cm) {
3723  cm.curOp = {
3724    cm: cm,
3725    viewChanged: false,      // Flag that indicates that lines might need to be redrawn
3726    startHeight: cm.doc.height, // Used to detect need to update scrollbar
3727    forceUpdate: false,      // Used to force a redraw
3728    updateInput: null,       // Whether to reset the input textarea
3729    typing: false,           // Whether this reset should be careful to leave existing text (for compositing)
3730    changeObjs: null,        // Accumulated changes, for firing change events
3731    cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on
3732    cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
3733    selectionChanged: false, // Whether the selection needs to be redrawn
3734    updateMaxLine: false,    // Set when the widest line needs to be determined anew
3735    scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
3736    scrollToPos: null,       // Used to scroll to a specific position
3737    focus: false,
3738    id: ++nextOpId           // Unique ID
3739  }
3740  pushOperation(cm.curOp)
3741}
3742
3743// Finish an operation, updating the display and signalling delayed events
3744function endOperation(cm) {
3745  var op = cm.curOp
3746  finishOperation(op, function (group) {
3747    for (var i = 0; i < group.ops.length; i++)
3748      { group.ops[i].cm.curOp = null }
3749    endOperations(group)
3750  })
3751}
3752
3753// The DOM updates done when an operation finishes are batched so
3754// that the minimum number of relayouts are required.
3755function endOperations(group) {
3756  var ops = group.ops
3757  for (var i = 0; i < ops.length; i++) // Read DOM
3758    { endOperation_R1(ops[i]) }
3759  for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe)
3760    { endOperation_W1(ops[i$1]) }
3761  for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM
3762    { endOperation_R2(ops[i$2]) }
3763  for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe)
3764    { endOperation_W2(ops[i$3]) }
3765  for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM
3766    { endOperation_finish(ops[i$4]) }
3767}
3768
3769function endOperation_R1(op) {
3770  var cm = op.cm, display = cm.display
3771  maybeClipScrollbars(cm)
3772  if (op.updateMaxLine) { findMaxLine(cm) }
3773
3774  op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||
3775    op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
3776                       op.scrollToPos.to.line >= display.viewTo) ||
3777    display.maxLineChanged && cm.options.lineWrapping
3778  op.update = op.mustUpdate &&
3779    new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate)
3780}
3781
3782function endOperation_W1(op) {
3783  op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update)
3784}
3785
3786function endOperation_R2(op) {
3787  var cm = op.cm, display = cm.display
3788  if (op.updatedDisplay) { updateHeightsInViewport(cm) }
3789
3790  op.barMeasure = measureForScrollbars(cm)
3791
3792  // If the max line changed since it was last measured, measure it,
3793  // and ensure the document's width matches it.
3794  // updateDisplay_W2 will use these properties to do the actual resizing
3795  if (display.maxLineChanged && !cm.options.lineWrapping) {
3796    op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3
3797    cm.display.sizerWidth = op.adjustWidthTo
3798    op.barMeasure.scrollWidth =
3799      Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth)
3800    op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm))
3801  }
3802
3803  if (op.updatedDisplay || op.selectionChanged)
3804    { op.preparedSelection = display.input.prepareSelection() }
3805}
3806
3807function endOperation_W2(op) {
3808  var cm = op.cm
3809
3810  if (op.adjustWidthTo != null) {
3811    cm.display.sizer.style.minWidth = op.adjustWidthTo + "px"
3812    if (op.maxScrollLeft < cm.doc.scrollLeft)
3813      { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true) }
3814    cm.display.maxLineChanged = false
3815  }
3816
3817  var takeFocus = op.focus && op.focus == activeElt()
3818  if (op.preparedSelection)
3819    { cm.display.input.showSelection(op.preparedSelection, takeFocus) }
3820  if (op.updatedDisplay || op.startHeight != cm.doc.height)
3821    { updateScrollbars(cm, op.barMeasure) }
3822  if (op.updatedDisplay)
3823    { setDocumentHeight(cm, op.barMeasure) }
3824
3825  if (op.selectionChanged) { restartBlink(cm) }
3826
3827  if (cm.state.focused && op.updateInput)
3828    { cm.display.input.reset(op.typing) }
3829  if (takeFocus) { ensureFocus(op.cm) }
3830}
3831
3832function endOperation_finish(op) {
3833  var cm = op.cm, display = cm.display, doc = cm.doc
3834
3835  if (op.updatedDisplay) { postUpdateDisplay(cm, op.update) }
3836
3837  // Abort mouse wheel delta measurement, when scrolling explicitly
3838  if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))
3839    { display.wheelStartX = display.wheelStartY = null }
3840
3841  // Propagate the scroll position to the actual DOM scroller
3842  if (op.scrollTop != null) { setScrollTop(cm, op.scrollTop, op.forceScroll) }
3843
3844  if (op.scrollLeft != null) { setScrollLeft(cm, op.scrollLeft, true, true) }
3845  // If we need to scroll a specific position into view, do so.
3846  if (op.scrollToPos) {
3847    var rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
3848                                 clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin)
3849    maybeScrollWindow(cm, rect)
3850  }
3851
3852  // Fire events for markers that are hidden/unidden by editing or
3853  // undoing
3854  var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers
3855  if (hidden) { for (var i = 0; i < hidden.length; ++i)
3856    { if (!hidden[i].lines.length) { signal(hidden[i], "hide") } } }
3857  if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1)
3858    { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], "unhide") } } }
3859
3860  if (display.wrapper.offsetHeight)
3861    { doc.scrollTop = cm.display.scroller.scrollTop }
3862
3863  // Fire change events, and delayed event handlers
3864  if (op.changeObjs)
3865    { signal(cm, "changes", cm, op.changeObjs) }
3866  if (op.update)
3867    { op.update.finish() }
3868}
3869
3870// Run the given function in an operation
3871function runInOp(cm, f) {
3872  if (cm.curOp) { return f() }
3873  startOperation(cm)
3874  try { return f() }
3875  finally { endOperation(cm) }
3876}
3877// Wraps a function in an operation. Returns the wrapped function.
3878function operation(cm, f) {
3879  return function() {
3880    if (cm.curOp) { return f.apply(cm, arguments) }
3881    startOperation(cm)
3882    try { return f.apply(cm, arguments) }
3883    finally { endOperation(cm) }
3884  }
3885}
3886// Used to add methods to editor and doc instances, wrapping them in
3887// operations.
3888function methodOp(f) {
3889  return function() {
3890    if (this.curOp) { return f.apply(this, arguments) }
3891    startOperation(this)
3892    try { return f.apply(this, arguments) }
3893    finally { endOperation(this) }
3894  }
3895}
3896function docMethodOp(f) {
3897  return function() {
3898    var cm = this.cm
3899    if (!cm || cm.curOp) { return f.apply(this, arguments) }
3900    startOperation(cm)
3901    try { return f.apply(this, arguments) }
3902    finally { endOperation(cm) }
3903  }
3904}
3905
3906// Updates the display.view data structure for a given change to the
3907// document. From and to are in pre-change coordinates. Lendiff is
3908// the amount of lines added or subtracted by the change. This is
3909// used for changes that span multiple lines, or change the way
3910// lines are divided into visual lines. regLineChange (below)
3911// registers single-line changes.
3912function regChange(cm, from, to, lendiff) {
3913  if (from == null) { from = cm.doc.first }
3914  if (to == null) { to = cm.doc.first + cm.doc.size }
3915  if (!lendiff) { lendiff = 0 }
3916
3917  var display = cm.display
3918  if (lendiff && to < display.viewTo &&
3919      (display.updateLineNumbers == null || display.updateLineNumbers > from))
3920    { display.updateLineNumbers = from }
3921
3922  cm.curOp.viewChanged = true
3923
3924  if (from >= display.viewTo) { // Change after
3925    if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)
3926      { resetView(cm) }
3927  } else if (to <= display.viewFrom) { // Change before
3928    if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {
3929      resetView(cm)
3930    } else {
3931      display.viewFrom += lendiff
3932      display.viewTo += lendiff
3933    }
3934  } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap
3935    resetView(cm)
3936  } else if (from <= display.viewFrom) { // Top overlap
3937    var cut = viewCuttingPoint(cm, to, to + lendiff, 1)
3938    if (cut) {
3939      display.view = display.view.slice(cut.index)
3940      display.viewFrom = cut.lineN
3941      display.viewTo += lendiff
3942    } else {
3943      resetView(cm)
3944    }
3945  } else if (to >= display.viewTo) { // Bottom overlap
3946    var cut$1 = viewCuttingPoint(cm, from, from, -1)
3947    if (cut$1) {
3948      display.view = display.view.slice(0, cut$1.index)
3949      display.viewTo = cut$1.lineN
3950    } else {
3951      resetView(cm)
3952    }
3953  } else { // Gap in the middle
3954    var cutTop = viewCuttingPoint(cm, from, from, -1)
3955    var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1)
3956    if (cutTop && cutBot) {
3957      display.view = display.view.slice(0, cutTop.index)
3958        .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))
3959        .concat(display.view.slice(cutBot.index))
3960      display.viewTo += lendiff
3961    } else {
3962      resetView(cm)
3963    }
3964  }
3965
3966  var ext = display.externalMeasured
3967  if (ext) {
3968    if (to < ext.lineN)
3969      { ext.lineN += lendiff }
3970    else if (from < ext.lineN + ext.size)
3971      { display.externalMeasured = null }
3972  }
3973}
3974
3975// Register a change to a single line. Type must be one of "text",
3976// "gutter", "class", "widget"
3977function regLineChange(cm, line, type) {
3978  cm.curOp.viewChanged = true
3979  var display = cm.display, ext = cm.display.externalMeasured
3980  if (ext && line >= ext.lineN && line < ext.lineN + ext.size)
3981    { display.externalMeasured = null }
3982
3983  if (line < display.viewFrom || line >= display.viewTo) { return }
3984  var lineView = display.view[findViewIndex(cm, line)]
3985  if (lineView.node == null) { return }
3986  var arr = lineView.changes || (lineView.changes = [])
3987  if (indexOf(arr, type) == -1) { arr.push(type) }
3988}
3989
3990// Clear the view.
3991function resetView(cm) {
3992  cm.display.viewFrom = cm.display.viewTo = cm.doc.first
3993  cm.display.view = []
3994  cm.display.viewOffset = 0
3995}
3996
3997function viewCuttingPoint(cm, oldN, newN, dir) {
3998  var index = findViewIndex(cm, oldN), diff, view = cm.display.view
3999  if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)
4000    { return {index: index, lineN: newN} }
4001  var n = cm.display.viewFrom
4002  for (var i = 0; i < index; i++)
4003    { n += view[i].size }
4004  if (n != oldN) {
4005    if (dir > 0) {
4006      if (index == view.length - 1) { return null }
4007      diff = (n + view[index].size) - oldN
4008      index++
4009    } else {
4010      diff = n - oldN
4011    }
4012    oldN += diff; newN += diff
4013  }
4014  while (visualLineNo(cm.doc, newN) != newN) {
4015    if (index == (dir < 0 ? 0 : view.length - 1)) { return null }
4016    newN += dir * view[index - (dir < 0 ? 1 : 0)].size
4017    index += dir
4018  }
4019  return {index: index, lineN: newN}
4020}
4021
4022// Force the view to cover a given range, adding empty view element
4023// or clipping off existing ones as needed.
4024function adjustView(cm, from, to) {
4025  var display = cm.display, view = display.view
4026  if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {
4027    display.view = buildViewArray(cm, from, to)
4028    display.viewFrom = from
4029  } else {
4030    if (display.viewFrom > from)
4031      { display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view) }
4032    else if (display.viewFrom < from)
4033      { display.view = display.view.slice(findViewIndex(cm, from)) }
4034    display.viewFrom = from
4035    if (display.viewTo < to)
4036      { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)) }
4037    else if (display.viewTo > to)
4038      { display.view = display.view.slice(0, findViewIndex(cm, to)) }
4039  }
4040  display.viewTo = to
4041}
4042
4043// Count the number of lines in the view whose DOM representation is
4044// out of date (or nonexistent).
4045function countDirtyView(cm) {
4046  var view = cm.display.view, dirty = 0
4047  for (var i = 0; i < view.length; i++) {
4048    var lineView = view[i]
4049    if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty }
4050  }
4051  return dirty
4052}
4053
4054// HIGHLIGHT WORKER
4055
4056function startWorker(cm, time) {
4057  if (cm.doc.highlightFrontier < cm.display.viewTo)
4058    { cm.state.highlight.set(time, bind(highlightWorker, cm)) }
4059}
4060
4061function highlightWorker(cm) {
4062  var doc = cm.doc
4063  if (doc.highlightFrontier >
4063= cm.display.viewTo) { return }
4064  var end = +new Date + cm.options.workTime
4065  var context = getContextBefore(cm, doc.highlightFrontier)
4066  var changedLines = []
4067
4068  doc.iter(context.line, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function (line) {
4069    if (context.line >= cm.display.viewFrom) { // Visible
4070      var oldStyles = line.styles
4071      var resetState = line.text.length > cm.options.maxHighlightLength ? copyState(doc.mode, context.state) : null
4072      var highlighted = highlightLine(cm, line, context, true)
4073      if (resetState) { context.state = resetState }
4074      line.styles = highlighted.styles
4075      var oldCls = line.styleClasses, newCls = highlighted.classes
4076      if (newCls) { line.styleClasses = newCls }
4077      else if (oldCls) { line.styleClasses = null }
4078      var ischange = !oldStyles || oldStyles.length != line.styles.length ||
4079        oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass)
4080      for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldStyles[i] != line.styles[i] }
4081      if (ischange) { changedLines.push(context.line) }
4082      line.stateAfter = context.save()
4083      context.nextLine()
4084    } else {
4085      if (line.text.length <= cm.options.maxHighlightLength)
4086        { processLine(cm, line.text, context) }
4087      line.stateAfter = context.line % 5 == 0 ? context.save() : null
4088      context.nextLine()
4089    }
4090    if (+new Date >
4090 end) {
4091      startWorker(cm, cm.options.workDelay)
4092      return true
4093    }
4094  })
4095  doc.highlightFrontier = context.line
4096  doc.modeFrontier = Math.max(doc.modeFrontier, context.line)
4097  if (changedLines.length) { runInOp(cm, function () {
4098    for (var i = 0; i < changedLines.length; i++)
4099      { regLineChange(cm, changedLines[i], "text") }
4100  }) }
4101}
4102
4103// DISPLAY DRAWING
4104
4105var DisplayUpdate = function(cm, viewport, force) {
4106  var display = cm.display
4107
4108  this.viewport = viewport
4109  // Store some values that we'll need later (but don't want to force a relayout for)
4110  this.visible = visibleLines(display, cm.doc, viewport)
4111  this.editorIsHidden = !display.wrapper.offsetWidth
4112  this.wrapperHeight = display.wrapper.clientHeight
4113  this.wrapperWidth = display.wrapper.clientWidth
4114  this.oldDisplayWidth = displayWidth(cm)
4115  this.force = force
4116  this.dims = getDimensions(cm)
4117  this.events = []
4118};
4119
4120DisplayUpdate.prototype.signal = function (emitter, type) {
4121  if (hasHandler(emitter, type))
4122    { this.events.push(arguments) }
4123};
4124DisplayUpdate.prototype.finish = function () {
4125    var this$1 = this;
4126
4127  for (var i = 0; i < this.events.length; i++)
4128    { signal.apply(null, this$1.events[i]) }
4129};
4130
4131function maybeClipScrollbars(cm) {
4132  var display = cm.display
4133  if (!display.scrollbarsClipped && display.scroller.offsetWidth) {
4134    display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth
4135    display.heightForcer.style.height = scrollGap(cm) + "px"
4136    display.sizer.style.marginBottom = -display.nativeBarWidth + "px"
4137    display.sizer.style.borderRightWidth = scrollGap(cm) + "px"
4138    display.scrollbarsClipped = true
4139  }
4140}
4141
4142function selectionSnapshot(cm) {
4143  if (cm.hasFocus()) { return null }
4144  var active = activeElt()
4145  if (!active || !contains(cm.display.lineDiv, active)) { return null }
4146  var result = {activeElt: active}
4147  if (window.getSelection) {
4148    var sel = window.getSelection()
4149    if (sel.anchorNode && sel.extend && contains(cm.display.lineDiv, sel.anchorNode)) {
4150      result.anchorNode = sel.anchorNode
4151      result.anchorOffset = sel.anchorOffset
4152      result.focusNode = sel.focusNode
4153      result.focusOffset = sel.focusOffset
4154    }
4155  }
4156  return result
4157}
4158
4159function restoreSelection(snapshot) {
4160  if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt()) { return }
4161  snapshot.activeElt.focus()
4162  if (snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) {
4163    var sel = window.getSelection(), range = document.createRange()
4164    range.setEnd(snapshot.anchorNode, snapshot.anchorOffset)
4165    range.collapse(false)
4166    sel.removeAllRanges()
4167    sel.addRange(range)
4168    sel.extend(snapshot.focusNode, snapshot.focusOffset)
4169  }
4170}
4171
4172// Does the actual updating of the line display. Bails out
4173// (returning false) when there is nothing to be done and forced is
4174// false.
4175function updateDisplayIfNeeded(cm, update) {
4176  var display = cm.display, doc = cm.doc
4177
4178  if (update.editorIsHidden) {
4179    resetView(cm)
4180    return false
4181  }
4182
4183  // Bail out if the visible area is already rendered and nothing changed.
4184  if (!update.force &&
4185      update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&
4186      (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&
4187      display.renderedView == display.view && countDirtyView(cm) == 0)
4188    { return false }
4189
4190  if (maybeUpdateLineNumberWidth(cm)) {
4191    resetView(cm)
4192    update.dims = getDimensions(cm)
4193  }
4194
4195  // Compute a suitable new viewport (from & to)
4196  var end = doc.first + doc.size
4197  var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first)
4198  var to = Math.min(end, update.visible.to + cm.options.viewportMargin)
4199  if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max(doc.first, display.viewFrom) }
4200  if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, display.viewTo) }
4201  if (sawCollapsedSpans) {
4202    from = visualLineNo(cm.doc, from)
4203    to = visualLineEndNo(cm.doc, to)
4204  }
4205
4206  var different = from != display.viewFrom || to != display.viewTo ||
4207    display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth
4208  adjustView(cm, from, to)
4209
4210  display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom))
4211  // Position the mover div to align with the current scroll position
4212  cm.display.mover.style.top = display.viewOffset + "px"
4213
4214  var toUpdate = countDirtyView(cm)
4215  if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view &&
4216      (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))
4217    { return false }
4218
4219  // For big changes, we hide the enclosing element during the
4220  // update, since that speeds up the operations on most browsers.
4221  var selSnapshot = selectionSnapshot(cm)
4222  if (toUpdate > 4) { display.lineDiv.style.display = "none" }
4223  patchDisplay(cm, display.updateLineNumbers, update.dims)
4224  if (toUpdate > 4) { display.lineDiv.style.display = "" }
4225  display.renderedView = display.view
4226  // There might have been a widget with a focused element that got
4227  // hidden or updated, if so re-focus it.
4228  restoreSelection(selSnapshot)
4229
4230  // Prevent selection and cursors from interfering with the scroll
4231  // width and height.
4232  removeChildren(display.cursorDiv)
4233  removeChildren(display.selectionDiv)
4234  display.gutters.style.height = display.sizer.style.minHeight = 0
4235
4236  if (different) {
4237    display.lastWrapHeight = update.wrapperHeight
4238    display.lastWrapWidth = update.wrapperWidth
4239    startWorker(cm, 400)
4240  }
4241
4242  display.updateLineNumbers = null
4243
4244  return true
4245}
4246
4247function postUpdateDisplay(cm, update) {
4248  var viewport = update.viewport
4249
4250  for (var first = true;; first = false) {
4251    if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) {
4252      // Clip forced viewport to actual scrollable area.
4253      if (viewport && viewport.top != null)
4254        { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)} }
4255      // Updated line heights might result in the drawn area not
4256      // actually covering the viewport. Keep looping until it does.
4257      update.visible = visibleLines(cm.display, cm.doc, viewport)
4258      if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)
4259        { break }
4260    }
4261    if (!updateDisplayIfNeeded(cm, update)) { break }
4262    updateHeightsInViewport(cm)
4263    var barMeasure = measureForScrollbars(cm)
4264    updateSelection(cm)
4265    updateScrollbars(cm, barMeasure)
4266    setDocumentHeight(cm, barMeasure)
4267    update.force = false
4268  }
4269
4270  update.signal(cm, "update", cm)
4271  if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {
4272    update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo)
4273    cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo
4274  }
4275}
4276
4277function updateDisplaySimple(cm, viewport) {
4278  var update = new DisplayUpdate(cm, viewport)
4279  if (updateDisplayIfNeeded(cm, update)) {
4280    updateHeightsInViewport(cm)
4281    postUpdateDisplay(cm, update)
4282    var barMeasure = measureForScrollbars(cm)
4283    updateSelection(cm)
4284    updateScrollbars(cm, barMeasure)
4285    setDocumentHeight(cm, barMeasure)
4286    update.finish()
4287  }
4288}
4289
4290// Sync the actual display DOM structure with display.view, removing
4291// nodes for lines that are no longer in view, and creating the ones
4292// that are not there yet, and updating the ones that are out of
4293// date.
4294function patchDisplay(cm, updateNumbersFrom, dims) {
4295  var display = cm.display, lineNumbers = cm.options.lineNumbers
4296  var container = display.lineDiv, cur = container.firstChild
4297
4298  function rm(node) {
4299    var next = node.nextSibling
4300    // Works around a throw-scroll bug in OS X Webkit
4301    if (webkit && mac && cm.display.currentWheelTarget == node)
4302      { node.style.display = "none" }
4303    else
4304      { node.parentNode.removeChild(node) }
4305    return next
4306  }
4307
4308  var view = display.view, lineN = display.viewFrom
4309  // Loop over the elements in the view, syncing cur (the DOM nodes
4310  // in display.lineDiv) with the view as we go.
4311  for (var i = 0; i < view.length; i++) {
4312    var lineView = view[i]
4313    if (lineView.hidden) {
4314    } else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet
4315      var node = buildLineElement(cm, lineView, lineN, dims)
4316      container.insertBefore(node, cur)
4317    } else { // Already drawn
4318      while (cur != lineView.node) { cur = rm(cur) }
4319      var updateNumber = lineNumbers && updateNumbersFrom != null &&
4320        updateNumbersFrom <= lineN && lineView.lineNumber
4321      if (lineView.changes) {
4322        if (indexOf(lineView.changes, "gutter") > -1) { updateNumber = false }
4323        updateLineForChanges(cm, lineView, lineN, dims)
4324      }
4325      if (updateNumber) {
4326        removeChildren(lineView.lineNumber)
4327        lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN)))
4328      }
4329      cur = lineView.node.nextSibling
4330    }
4331    lineN += lineView.size
4332  }
4333  while (cur) { cur = rm(cur) }
4334}
4335
4336function updateGutterSpace(cm) {
4337  var width = cm.display.gutters.offsetWidth
4338  cm.display.sizer.style.marginLeft = width + "px"
4339}
4340
4341function setDocumentHeight(cm, measure) {
4342  cm.display.sizer.style.minHeight = measure.docHeight + "px"
4343  cm.display.heightForcer.style.top = measure.docHeight + "px"
4344  cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px"
4345}
4346
4347// Rebuild the gutter elements, ensure the margin to the left of the
4348// code matches their width.
4349function updateGutters(cm) {
4350  var gutters = cm.display.gutters, specs = cm.options.gutters
4351  removeChildren(gutters)
4352  var i = 0
4353  for (; i < specs.length; ++i) {
4354    var gutterClass = specs[i]
4355    var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass))
4356    if (gutterClass == "CodeMirror-linenumbers") {
4357      cm.display.lineGutter = gElt
4358      gElt.style.width = (cm.display.lineNumWidth || 1) + "px"
4359    }
4360  }
4361  gutters.style.display = i ? "" : "none"
4362  updateGutterSpace(cm)
4363}
4364
4365// Make sure the gutters options contains the element
4366// "CodeMirror-linenumbers" when the lineNumbers option is true.
4367function setGuttersForLineNumbers(options) {
4368  var found = indexOf(options.gutters, "CodeMirror-linenumbers")
4369  if (found == -1 && options.lineNumbers) {
4370    options.gutters = options.gutters.concat(["CodeMirror-linenumbers"])
4371  } else if (found > -1 && !options.lineNumbers) {
4372    options.gutters = options.gutters.slice(0)
4373    options.gutters.splice(found, 1)
4374  }
4375}
4376
4377var wheelSamples = 0;
4378var wheelPixelsPerUnit = null;
4379// Fill in a browser-detected starting value on browsers where we
4380// know one. These don't have to be accurate -- the result of them
4381// being wrong would just be a slight flicker on the first wheel
4382// scroll (if it is large enough).
4383if (ie) { wheelPixelsPerUnit = -.53 }
4384else if (gecko) { wheelPixelsPerUnit = 15 }
4385else if (chrome) { wheelPixelsPerUnit = -.7 }
4386else if (safari) { wheelPixelsPerUnit = -1/3 }
4387
4388function wheelEventDelta(e) {
4389  var dx = e.wheelDeltaX, dy = e.wheelDeltaY
4390  if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail }
4391  if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail }
4392  else if (dy == null) { dy = e.wheelDelta }
4393  return {x: dx, y: dy}
4394}
4395function wheelEventPixels(e) {
4396  var delta = wheelEventDelta(e)
4397  delta.x *= wheelPixelsPerUnit
4398  delta.y *= wheelPixelsPerUnit
4399  return delta
4400}
4401
4402function onScrollWheel(cm, e) {
4403  var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y
4404
4405  var display = cm.display, scroll = display.scroller
4406  // Quit if there's nothing to scroll here
4407  var canScrollX = scroll.scrollWidth > scroll.clientWidth
4408  var canScrollY = scroll.scrollHeight > scroll.clientHeight
4409  if (!(dx && canScrollX || dy && canScrollY)) { return }
4410
4411  // Webkit browsers on OS X abort momentum scrolls when the target
4412  // of the scroll event is removed from the scrollable element.
4413  // This hack (see related code in patchDisplay) makes sure the
4414  // element is kept around.
4415  if (dy && mac && webkit) {
4416    outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {
4417      for (var i = 0; i < view.length; i++) {
4418        if (view[i].node == cur) {
4419          cm.display.currentWheelTarget = cur
4420          break outer
4421        }
4422      }
4423    }
4424  }
4425
4426  // On some browsers, horizontal scrolling will cause redraws to
4427  // happen before the gutter has been realigned, causing it to
4428  // wriggle around in a most unseemly way. When we have an
4429  // estimated pixels/delta value, we just handle horizontal
4430  // scrolling entirely here. It'll be slightly off from native, but
4431  // better than glitching out.
4432  if (dx && !gecko && !presto && wheelPixelsPerUnit != null) {
4433    if (dy && canScrollY)
4434      { updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * wheelPixelsPerUnit)) }
4435    setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * wheelPixelsPerUnit))
4436    // Only prevent default scrolling if vertical scrolling is
4437    // actually possible. Otherwise, it causes vertical scroll
4438    // jitter on OSX trackpads when deltaX is small and deltaY
4439    // is large (issue #3579)
4440    if (!dy || (dy && canScrollY))
4441      { e_preventDefault(e) }
4442    display.wheelStartX = null // Abort measurement, if in progress
4443    return
4444  }
4445
4446  // 'Project' the visible viewport to cover the area that is being
4447  // scrolled into view (if we know enough to estimate it).
4448  if (dy && wheelPixelsPerUnit != null) {
4449    var pixels = dy * wheelPixelsPerUnit
4450    var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight
4451    if (pixels < 0) { top = Math.max(0, top + pixels - 50) }
4452    else { bot = Math.min(cm.doc.height, bot + pixels + 50) }
4453    updateDisplaySimple(cm, {top: top, bottom: bot})
4454  }
4455
4456  if (wheelSamples < 20) {
4457    if (display.wheelStartX == null) {
4458      display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop
4459      display.wheelDX = dx; display.wheelDY = dy
4460      setTimeout(function () {
4461        if (display.wheelStartX == null) { return }
4462        var movedX = scroll.scrollLeft - display.wheelStartX
4463        var movedY = scroll.scrollTop - display.wheelStartY
4464        var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
4465          (movedX && display.wheelDX && movedX / display.wheelDX)
4466        display.wheelStartX = display.wheelStartY = null
4467        if (!sample) { return }
4468        wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1)
4469        ++wheelSamples
4470      }, 200)
4471    } else {
4472      display.wheelDX += dx; display.wheelDY += dy
4473    }
4474  }
4475}
4476
4477// Selection objects are immutable. A new one is created every time
4478// the selection changes. A selection is one or more non-overlapping
4479// (and non-touching) ranges, sorted, and an integer that indicates
4480// which one is the primary selection (the one that's scrolled into
4481// view, that getCursor returns, etc).
4482var Selection = function(ranges, primIndex) {
4483  this.ranges = ranges
4484  this.primIndex = primIndex
4485};
4486
4487Selection.prototype.primary = function () { return this.ranges[this.primIndex] };
4488
4489Selection.prototype.equals = function (other) {
4490    var this$1 = this;
4491
4492  if (other == this) { return true }
4493  if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false }
4494  for (var i = 0; i < this.ranges.length; i++) {
4495    var here = this$1.ranges[i], there = other.ranges[i]
4496    if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)
4496) { return false }
4497  }
4498  return true
4499};
4500
4501Selection.prototype.deepCopy = function () {
4502    var this$1 = this;
4503
4504  var out = []
4505  for (var i = 0; i < this.ranges.length; i++)
4506    { out[i] = new Range(copyPos(this$1.ranges[i].anchor), copyPos(this$1.ranges[i].head)) }
4507  return new Selection(out, this.primIndex)
4508};
4509
4510Selection.prototype.somethingSelected = function () {
4511    var this$1 = this;
4512
4513  for (var i = 0; i < this.ranges.length; i++)
4514    { if (!this$1.ranges[i].empty()) { return true } }
4515  return false
4516};
4517
4518Selection.prototype.contains = function (pos, end) {
4519    var this$1 = this;
4520
4521  if (!end) { end = pos }
4522  for (var i = 0; i < this.ranges.length; i++) {
4523    var range = this$1.ranges[i]
4524    if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)
4525      { return i }
4526  }
4527  return -1
4528};
4529
4530var Range = function(anchor, head) {
4531  this.anchor = anchor; this.head = head
4532};
4533
4534Range.prototype.from = function () { return minPos(this.anchor, this.head) };
4535Range.prototype.to = function () { return maxPos(this.anchor, this.head) };
4536Range.prototype.empty = function () { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch };
4537
4538// Take an unsorted, potentially overlapping set of ranges, and
4539// build a selection out of it. 'Consumes' ranges array (modifying
4540// it).
4541function normalizeSelection(ranges, primIndex) {
4542  var prim = ranges[primIndex]
4543  ranges.sort(function (a, b) { return cmp(a.from(), b.from()); })
4544  primIndex = indexOf(ranges, prim)
4545  for (var i = 1; i < ranges.length; i++) {
4546    var cur = ranges[i], prev = ranges[i - 1]
4547    if (cmp(prev.to(), cur.from()) >= 0) {
4548      var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to())
4549      var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head
4550      if (i <= primIndex) { --primIndex }
4551      ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to))
4552    }
4553  }
4554  return new Selection(ranges, primIndex)
4555}
4556
4557function simpleSelection(anchor, head) {
4558  return new Selection([new Range(anchor, head || anchor)], 0)
4559}
4560
4561// Compute the position of the end of a change (its 'to' property
4562// refers to the pre-change end).
4563function changeEnd(change) {
4564  if (!change.text) { return change.to }
4565  return Pos(change.from.line + change.text.length - 1,
4566             lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0))
4567}
4568
4569// Adjust a position to refer to the post-change position of the
4570// same text, or the end of the change if the change covers it.
4571function adjustForChange(pos, change) {
4572  if (cmp(pos, change.from) < 0) { return pos }
4573  if (cmp(pos, change.to) <= 0) { return changeEnd(change) }
4574
4575  var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch
4576  if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch }
4577  return Pos(line, ch)
4578}
4579
4580function computeSelAfterChange(doc, change) {
4581  var out = []
4582  for (var i = 0; i < doc.sel.ranges.length; i++) {
4583    var range = doc.sel.ranges[i]
4584    out.push(new Range(adjustForChange(range.anchor, change),
4585                       adjustForChange(range.head, change)))
4586  }
4587  return normalizeSelection(out, doc.sel.primIndex)
4588}
4589
4590function offsetPos(pos, old, nw) {
4591  if (pos.line == old.line)
4592    { return Pos(nw.line, pos.ch - old.ch + nw.ch) }
4593  else
4594    { return Pos(nw.line + (pos.line - old.line), pos.ch) }
4595}
4596
4597// Used by replaceSelections to allow moving the selection to the
4598// start or around the replaced test. Hint may be "start" or "around".
4599function computeReplacedSel(doc, changes, hint) {
4600  var out = []
4601  var oldPrev = Pos(doc.first, 0), newPrev = oldPrev
4602  for (var i = 0; i < changes.length; i++) {
4603    var change = changes[i]
4604    var from = offsetPos(change.from, oldPrev, newPrev)
4605    var to = offsetPos(changeEnd(change), oldPrev, newPrev)
4606    oldPrev = change.to
4607    newPrev = to
4608    if (hint == "around") {
4609      var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0
4610      out[i] = new Range(inv ? to : from, inv ? from : to)
4611    } else {
4612      out[i] = new Range(from, from)
4613    }
4614  }
4615  return new Selection(out, doc.sel.primIndex)
4616}
4617
4618// Used to get the editor into a consistent state again when options change.
4619
4620function loadMode(cm) {
4621  cm.doc.mode = getMode(cm.options, cm.doc.modeOption)
4622  resetModeState(cm)
4623}
4624
4625function resetModeState(cm) {
4626  cm.doc.iter(function (line) {
4627    if (line.stateAfter) { line.stateAfter = null }
4628    if (line.styles) { line.styles = null }
4629  })
4630  cm.doc.modeFrontier = cm.doc.highlightFrontier = cm.doc.first
4631  startWorker(cm, 100)
4632  cm.state.modeGen++
4633  if (cm.curOp) { regChange(cm) }
4634}
4634
4635
4636// DOCUMENT DATA STRUCTURE
4637
4638// By default, updates that start and end at the beginning of a line
4639// are treated specially, in order to make the association of line
4640// widgets and marker elements with the text behave more intuitive.
4641function isWholeLineUpdate(doc, change) {
4642  return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" &&
4643    (!doc.cm || doc.cm.options.wholeLineUpdateBefore)
4644}
4645
4646// Perform a change on the document data structure.
4647function updateDoc(doc, change, markedSpans, estimateHeight) {
4648  function spansFor(n) {return markedSpans ? markedSpans[n] : null}
4649  function update(line, text, spans) {
4650    updateLine(line, text, spans, estimateHeight)
4651    signalLater(line, "change", line, change)
4652  }
4653  function linesFor(start, end) {
4654    var result = []
4655    for (var i = start; i < end; ++i)
4656      { result.push(new Line(text[i], spansFor(i), estimateHeight)) }
4657    return result
4658  }
4659
4660  var from = change.from, to = change.to, text = change.text
4661  var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line)
4662  var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line
4663
4664  // Adjust the line structure
4665  if (change.full) {
4666    doc.insert(0, linesFor(0, text.length))
4667    doc.remove(text.length, doc.size - text.length)
4668  } else if (isWholeLineUpdate(doc, change)) {
4669    // This is a whole-line replace. Treated specially to make
4670    // sure line objects move the way they are supposed to.
4671    var added = linesFor(0, text.length - 1)
4672    update(lastLine, lastLine.text, lastSpans)
4673    if (nlines) { doc.remove(from.line, nlines) }
4674    if (added.length) { doc.insert(from.line, added) }
4675  } else if (firstLine == lastLine) {
4676    if (text.length == 1) {
4677      update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans)
4678    } else {
4679      var added$1 = linesFor(1, text.length - 1)
4680      added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight))
4681      update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0))
4682      doc.insert(from.line + 1, added$1)
4683    }
4684  } else if (text.length == 1) {
4685    update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0))
4686    doc.remove(from.line + 1, nlines)
4687  } else {
4688    update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0))
4689    update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans)
4690    var added$2 = linesFor(1, text.length - 1)
4691    if (nlines > 1) { doc.remove(from.line + 1, nlines - 1) }
4692    doc.insert(from.line + 1, added$2)
4693  }
4694
4695  signalLater(doc, "change", doc, change)
4696}
4697
4698// Call f for all linked documents.
4699function linkedDocs(doc, f, sharedHistOnly) {
4700  function propagate(doc, skip, sharedHist) {
4701    if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) {
4702      var rel = doc.linked[i]
4703      if (rel.doc == skip) { continue }
4704      var shared = sharedHist && rel.sharedHist
4705      if (sharedHistOnly && !shared) { continue }
4706      f(rel.doc, shared)
4707      propagate(rel.doc, doc, shared)
4708    } }
4709  }
4710  propagate(doc, null, true)
4711}
4712
4713// Attach a document to an editor.
4714function attachDoc(cm, doc) {
4715  if (doc.cm) { throw new Error("This document is already in use.") }
4716  cm.doc = doc
4717  doc.cm = cm
4718  estimateLineHeights(cm)
4719  loadMode(cm)
4720  setDirectionClass(cm)
4721  if (!cm.options.lineWrapping) { findMaxLine(cm) }
4722  cm.options.mode = doc.modeOption
4723  regChange(cm)
4724}
4725
4726function setDirectionClass(cm) {
4727  ;(cm.doc.direction == "rtl" ? addClass : rmClass)(cm.display.lineDiv, "CodeMirror-rtl")
4728}
4729
4730function directionChanged(cm) {
4731  runInOp(cm, function () {
4732    setDirectionClass(cm)
4733    regChange(cm)
4734  })
4735}
4736
4737function History(startGen) {
4738  // Arrays of change events and selections. Doing something adds an
4739  // event to done and clears undo. Undoing moves events from done
4740  // to undone, redoing moves them in the other direction.
4741  this.done = []; this.undone = []
4742  this.undoDepth = Infinity
4743  // Used to track when changes can be merged into a single undo
4744  // event
4745  this.lastModTime = this.lastSelTime = 0
4746  this.lastOp = this.lastSelOp = null
4747  this.lastOrigin = this.lastSelOrigin = null
4748  // Used by the isClean() method
4749  this.generation = this.maxGeneration = startGen || 1
4750}
4751
4752// Create a history change event from an updateDoc-style change
4753// object.
4754function historyChangeFromChange(doc, change) {
4755  var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)}
4756  attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1)
4757  linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); }, true)
4758  return histChange
4759}
4760
4761// Pop all selection events off the end of a history array. Stop at
4762// a change event.
4763function clearSelectionEvents(array) {
4764  while (array.length) {
4765    var last = lst(array)
4766    if (last.ranges) { array.pop() }
4767    else { break }
4768  }
4769}
4770
4771// Find the top change event in the history. Pop off selection
4772// events that are in the way.
4773function lastChangeEvent(hist, force) {
4774  if (force) {
4775    clearSelectionEvents(hist.done)
4776    return lst(hist.done)
4777  } else if (hist.done.length && !lst(hist.done).ranges) {
4778    return lst(hist.done)
4779  } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) {
4780    hist.done.pop()
4781    return lst(hist.done)
4782  }
4783}
4784
4785// Register a change in the history. Merges changes that are within
4786// a single operation, or are close together with an origin that
4787// allows merging (starting with "+") into a single event.
4788function addChangeToHistory(doc, change, selAfter, opId) {
4789  var hist = doc.history
4790  hist.undone.length = 0
4791  var time = +new Date, cur
4792  var last
4793
4794  if ((hist.lastOp == opId ||
4795       hist.lastOrigin == change.origin && change.origin &&
4796       ((change.origin.charAt(0) == "+" && hist.lastModTime > time - (doc.cm ? doc.cm.options.historyEventDelay : 500)) ||
4797        change.origin.charAt(0) == "*")) &&
4798      (cur = lastChangeEvent(hist, hist.lastOp == opId))) {
4799    // Merge this change into the last event
4800    last = lst(cur.changes)
4801    if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) {
4802      // Optimized case for simple insertion -- don't want to add
4803      // new changesets for every character typed
4804      last.to = changeEnd(change)
4805    } else {
4806      // Add new sub-event
4807      cur.changes.push(historyChangeFromChange(doc, change))
4808    }
4809  } else {
4810    // Can not be merged, start a new event.
4811    var before = lst(hist.done)
4812    if (!before || !before.ranges)
4813      { pushSelectionToHistory(doc.sel, hist.done) }
4814    cur = {changes: [historyChangeFromChange(doc, change)],
4815           generation: hist.generation}
4816    hist.done.push(cur)
4817    while (hist.done.length > hist.undoDepth) {
4818      hist.done.shift()
4819      if (!hist.done[0].ranges) { hist.done.shift() }
4820    }
4821  }
4822  hist.done.push(selAfter)
4823  hist.generation = ++hist.maxGeneration
4824  hist.lastModTime = hist.lastSelTime = time
4825  hist.lastOp = hist.lastSelOp = opId
4826  hist.lastOrigin = hist.lastSelOrigin = change.origin
4827
4828  if (!last) { signal(doc, "historyAdded") }
4829}
4830
4831function selectionEventCanBeMerged(doc, origin, prev, sel) {
4832  var ch = origin.charAt(0)
4833  return ch == "*" ||
4834    ch == "+" &&
4835    prev.ranges.length == sel.ranges.length &&
4836    prev.somethingSelected() == sel.somethingSelected() &&
4837    new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500)
4838}
4839
4840// Called whenever the selection changes, sets the new selection as
4841// the pending selection in the history, and pushes the old pending
4842// selection into the 'done' array when it was significantly
4843// different (in number of selected ranges, emptiness, or time).
4844function addSelectionToHistory(doc, sel, opId, options) {
4845  var hist = doc.history, origin = options && options.origin
4846
4847  // A new event is started when the previous origin does not match
4848  // the current, or the origins don't allow matching. Origins
4849  // starting with * are always merged, those starting with + are
4850  // merged when similar and close together in time.
4851  if (opId == hist.lastSelOp ||
4852      (origin && hist.lastSelOrigin == origin &&
4853       (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||
4854        selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))
4855    { hist.done[hist.done.length - 1] = sel }
4856  else
4857    { pushSelectionToHistory(sel, hist.done) }
4858
4859  hist.lastSelTime = +new Date
4860  hist.lastSelOrigin = origin
4861  hist.lastSelOp = opId
4862  if (options && options.clearRedo !== false)
4863    { clearSelectionEvents(hist.undone) }
4864}
4865
4866function pushSelectionToHistory(sel, dest) {
4867  var top = lst(dest)
4868  if (!(top && top.ranges && top.equals(sel)))
4869    { dest.push(sel) }
4870}
4871
4872// Used to store marked span information in the history.
4873function attachLocalSpans(doc, change, from, to) {
4874  var existing = change["spans_" + doc.id], n = 0
4875  doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function (line) {
4876    if (line.markedSpans)
4877      { (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans }
4878    ++n
4879  })
4880}
4881
4882// When un/re-doing restores text containing marked spans, those
4883// that have been explicitly cleared should not be restored.
4884function removeClearedSpans(spans) {
4885  if (!spans) { return null }
4886  var out
4887  for (var i = 0; i < spans.length; ++i) {
4888    if (spans[i].marker.explicitlyCleared) { if (!out) { out = spans.slice(0, i) } }
4889    else if (out) { out.push(spans[i]) }
4890  }
4891  return !out ? spans : out.length ? out : null
4892}
4893
4894// Retrieve and filter the old marked spans stored in a change event.
4895function getOldSpans(doc, change) {
4896  var found = change["spans_" + doc.id]
4897  if (!found) { return null }
4898  var nw = []
4899  for (var i = 0; i < change.text.length; ++i)
4900    { nw.push(removeClearedSpans(found[i])) }
4901  return nw
4902}
4903
4904// Used for un/re-doing changes from the history. Combines the
4905// result of computing the existing spans with the set of spans that
4906// existed in the history (so that deleting around a span and then
4907// undoing brings back the span).
4908function mergeOldSpans(doc, change) {
4909  var old = getOldSpans(doc, change)
4910  var stretched = stretchSpansOverChange(doc, change)
4911  if (!old) { return stretched }
4912  if (!stretched) { return old }
4913
4914  for (var i = 0; i < old.length; ++i) {
4915    var oldCur = old[i], stretchCur = stretched[i]
4916    if (oldCur && stretchCur) {
4917      spans: for (var j = 0; j < stretchCur.length; ++j) {
4918        var span = stretchCur[j]
4919        for (var k = 0; k < oldCur.length; ++k)
4920          { if (oldCur[k].marker == span.marker) { continue spans } }
4921        oldCur.push(span)
4922      }
4923    } else if (stretchCur) {
4924      old[i] = stretchCur
4925    }
4926  }
4927  return old
4928}
4929
4930// Used both to provide a JSON-safe object in .getHistory, and, when
4931// detaching a document, to split the history in two
4932function copyHistoryArray(events, newGroup, instantiateSel) {
4933  var copy = []
4934  for (var i = 0; i < events.length; ++i) {
4935    var event = events[i]
4936    if (event.ranges) {
4937      copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event)
4938      continue
4939    }
4940    var changes = event.changes, newChanges = []
4941    copy.push({changes: newChanges})
4942    for (var j = 0; j < changes.length; ++j) {
4943      var change = changes[j], m = (void 0)
4944      newChanges.push({from: change.from, to: change.to, text: change.text})
4945      if (newGroup) { for (var prop in change) { if (m = prop.match(/^spans_(\d+)$/)) {
4946        if (indexOf(newGroup, Number(m[1])) > -1) {
4947          lst(newChanges)[prop] = change[prop]
4948          delete change[prop]
4949        }
4950      } } }
4951    }
4952  }
4953  return copy
4954}
4955
4956// The 'scroll' parameter given to many of these indicated whether
4957// the new cursor position should be scrolled into view after
4958// modifying the selection.
4959
4960// If shift is held or the extend flag is set, extends a range to
4961// include a given position (and optionally a second position).
4962// Otherwise, simply returns the range between the given positions.
4963// Used for cursor motion and such.
4964function extendRange(range, head, other, extend) {
4965  if (extend) {
4966    var anchor = range.anchor
4967    if (other) {
4968      var posBefore = cmp(head, anchor) < 0
4969      if (posBefore != (cmp(other, anchor) < 0)) {
4970        anchor = head
4971        head = other
4972      } else if (posBefore != (cmp(head, other) < 0)) {
4973        head = other
4974      }
4975    }
4976    return new Range(anchor, head)
4977  } else {
4978    return new Range(other || head, head)
4979  }
4980}
4981
4982// Extend the primary selection range, discard the rest.
4983function extendSelection(doc, head, other, options, extend) {
4984  if (extend == null) { extend = doc.cm && (doc.cm.display.shift || doc.extend) }
4985  setSelection(doc, new Selection([extendRange(doc.sel.primary(), head, other, extend)], 0), options)
4986}
4987
4988// Extend all selections (pos is an array of selections with length
4989// equal the number of selections)
4990function extendSelections(doc, heads, options) {
4991  var out = []
4992  var extend = doc.cm && (doc.cm.display.shift || doc.extend)
4993  for (var i = 0; i < doc.sel.ranges.length; i++)
4994    { out[i] = extendRange(doc.sel.ranges[i], heads[i], null, extend) }
4995  var newSel = normalizeSelection(out, doc.sel.primIndex)
4996  setSelection(doc, newSel, options)
4997}
4998
4999// Updates a single range in the selection.
5000function replaceOneSelection(doc, i, range, options) {
5001  var ranges = doc.sel.ranges.slice(0)
5002  ranges[i] = range
5003  setSelection(doc, normalizeSelection(ranges, doc.sel.primIndex), options)
5004}
5005
5006// Reset the selection to a single range.
5007function setSimpleSelection(doc, anchor, head, options) {
5008  setSelection(doc, simpleSelection(anchor, head), options)
5009}
5010
5011// Give beforeSelectionChange handlers a change to influence a
5012// selection update.
5013function filterSelectionChange(doc, sel, options) {
5014  var obj = {
5015    ranges: sel.ranges,
5016    update: function(ranges) {
5017      var this$1 = this;
5018
5019      this.ranges = []
5020      for (var i = 0; i < ranges.length; i++)
5021        { this$1.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),
5022                                   clipPos(doc, ranges[i].head)) }
5023    },
5024    origin: options && options.origin
5025  }
5026  signal(doc, "beforeSelectionChange", doc, obj)
5027  if (doc.cm) { signal(doc.cm, "beforeSelectionChange", doc.cm, obj) }
5028  if (obj.ranges != sel.ranges) { return normalizeSelection(obj.ranges, obj.ranges.length - 1) }
5029  else { return sel }
5030}
5031
5032function setSelectionReplaceHistory(doc, sel, options) {
5033  var done = doc.history.done, last = lst(done)
5034  if (last && last.ranges) {
5035    done[done.length - 1] = sel
5036    setSelectionNoUndo(doc, sel, options)
5037  } else {
5038    setSelection(doc, sel, options)
5039  }
5040}
5041
5042// Set a new selection.
5043function setSelection(doc, sel, options) {
5044  setSelectionNoUndo(doc, sel, options)
5045  addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options)
5046}
5047
5048function setSelectionNoUndo(doc, sel, options) {
5049  if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange"))
5050    { sel = filterSelectionChange(doc, sel, options) }
5051
5052  var bias = options && options.bias ||
5053    (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1)
5054  setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true))
5055
5056  if (!(options && options.scroll === false) && doc.cm)
5057    { ensureCursorVisible(doc.cm) }
5058}
5059
5060function setSelectionInner(doc, sel) {
5061  if (sel.equals(doc.sel)) { return }
5062
5063  doc.sel = sel
5064
5065  if (doc.cm) {
5066    doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true
5067    signalCursorActivity(doc.cm)
5068  }
5069  signalLater(doc, "cursorActivity", doc)
5070}
5071
5072// Verify that the selection does not partially select any atomic
5073// marked ranges.
5074function reCheckSelection(doc) {
5075  setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false))
5076}
5077
5078// Return a selection that does not partially select any atomic
5079// ranges.
5080function skipAtomicInSelection(doc, sel, bias, mayClear) {
5081  var out
5082  for (var i = 0; i < sel.ranges.length; i++) {
5083    var range = sel.ranges[i]
5084    var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i]
5085    var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear)
5086    var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear)
5087    if (out || newAnchor != range.anchor || newHead != range.head) {
5088      if (!out) { out = sel.ranges.slice(0, i) }
5089      out[i] = new Range(newAnchor, newHead)
5090    }
5091  }
5092  return out ? normalizeSelection(out, sel.primIndex) : sel
5093}
5094
5095function skipAtomicInner(doc, pos, oldPos, dir, mayClear) {
5096  var line = getLine(doc, pos.line)
5097  if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
5098    var sp = line.markedSpans[i], m = sp.marker
5099    if ((sp.from == null || (m.inclusiveLeft ? sp.from <= pos.ch : sp.from < pos.ch)) &&
5100        (sp.to == null || (m.inclusiveRight ? sp.to >= pos.ch : sp.to > pos.ch))) {
5101      if (mayClear) {
5102        signal(m, "beforeCursorEnter")
5103        if (m.explicitlyCleared) {
5104          if (!line.markedSpans) { break }
5105          else {--i; continue}
5106        }
5107      }
5108      if (!m.atomic) { continue }
5109
5110      if (oldPos) {
5111        var near = m.find(dir < 0 ? 1 : -1), diff = (void 0)
5112        if (dir < 0 ? m.inclusiveRight : m.inclusiveLeft)
5113          { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null) }
5114        if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0))
5115          { return skipAtomicInner(doc, near, pos, dir, mayClear) }
5116      }
5117
5118      var far = m.find(dir < 0 ? -1 : 1)
5119      if (dir < 0 ? m.inclusiveLeft : m.inclusiveRight)
5120        { far = movePos(doc, far, dir, far.line == pos.line ? line : null) }
5121      return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null
5122    }
5123  } }
5124  return pos
5125}
5126
5127// Ensure a given position is not inside an atomic range.
5128function skipAtomic(doc, pos, oldPos, bias, mayClear) {
5129  var dir = bias || 1
5130  var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) ||
5131      (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) ||
5132      skipAtomicInner(doc, pos, oldPos, -dir, mayClear) ||
5133      (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true))
5134  if (!found) {
5135    doc.cantEdit = true
5136    return Pos(doc.first, 0)
5137  }
5138  return found
5139}
5140
5141function movePos(doc, pos, dir, line) {
5142  if (dir < 0 && pos.ch == 0) {
5143    if (pos.line > doc.first) { return clipPos(doc, Pos(pos.line - 1)) }
5144    else { return null }
5145  } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) {
5146    if (pos.line < doc.first + doc.size - 1) { return Pos(pos.line + 1, 0) }
5147    else { return null }
5148  } else {
5149    return new Pos(pos.line, pos.ch + dir)
5150  }
5151}
5152
5153function selectAll(cm) {
5154  cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll)
5155}
5156
5157// UPDATING
5158
5159// Allow "beforeChange" event handlers to influence a change
5160function filterChange(doc, change, update) {
5161  var obj = {
5162    canceled: false,
5163    from: change.from,
5164    to: change.to,
5165    text: change.text,
5166    origin: change.origin,
5167    cancel: function () { return obj.canceled = true; }
5168  }
5169  if (update) { obj.update = function (from, to, text, origin) {
5170    if (from) { obj.from = clipPos(doc, from) }
5171    if (to) { obj.to = clipPos(doc, to) }
5172    if (text) { obj.text = text }
5173    if (origin !== undefined) { obj.origin = origin }
5174  } }
5175  signal(doc, "beforeChange", doc, obj)
5176  if (doc.cm) { signal(doc.cm, "beforeChange", doc.cm, obj) }
5177
5178  if (obj.canceled) { return null }
5179  return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin}
5180}
5181
5182// Apply a change to a document, and add it to the document's
5183// history, and propagating it to all linked documents.
5184function makeChange(doc, change, ignoreReadOnly) {
5185  if (doc.cm) {
5186    if (!doc.cm.curOp) { return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) }
5187    if (doc.cm.state.suppressEdits) { return }
5188  }
5189
5190  if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
5191    change = filterChange(doc, change, true)
5192    if (!change) { return }
5193  }
5194
5195  // Possibly split or suppress the update based on the presence
5196  // of read-only spans in its range.
5197  var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to)
5198  if (split) {
5199    for (var i = split.length - 1; i >= 0; --i)
5200      { makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text, origin: change.origin}) }
5201  } else {
5202    makeChangeInner(doc, change)
5203  }
5204}
5205
5206function makeChangeInner(doc, change) {
5207  if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) { return }
5208  var selAfter = computeSelAfterChange(doc, change)
5209  addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN)
5210
5211  makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change))
5212  var rebased = []
5213
5214  linkedDocs(doc, function (doc, sharedHist) {
5215    if (!sharedHist && indexOf(rebased, doc.history) == -1) {
5216      rebaseHist(doc.history, change)
5217      rebased.push(doc.history)
5218    }
5219    makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change))
5220  })
5221}
5222
5223// Revert a change stored in a document's history.
5224function makeChangeFromHistory(doc, type, allowSelectionOnly) {
5225  var suppress = doc.cm && doc.cm.state.suppressEdits
5226  if (suppress && !allowSelectionOnly) { return }
5227
5228  var hist = doc.history, event, selAfter = doc.sel
5229  var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done
5230
5231  // Verify that there is a useable event (so that ctrl-z won't
5232  // needlessly clear selection events)
5233  var i = 0
5234  for (; i < source.length; i++) {
5235    event = source[i]
5236    if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges)
5237      { break }
5238  }
5239  if (i == source.length) { return }
5240  hist.lastOrigin = hist.lastSelOrigin = null
5241
5242  for (;;) {
5243    event = source.pop()
5244    if (event.ranges) {
5245      pushSelectionToHistory(event, dest)
5246      if (allowSelectionOnly && !event.equals(doc.sel)) {
5247        setSelection(doc, event, {clearRedo: false})
5248        return
5249      }
5250      selAfter = event
5251    } else if (suppress) {
5252      source.push(event)
5253      return
5254    } else { break }
5255  }
5256
5257  // Build up a reverse change object to add to the opposite history
5258  // stack (redo when undoing, and vice versa).
5259  var antiChanges = []
5260  pushSelectionToHistory(selAfter, dest)
5261  dest.push({changes: antiChanges, generation: hist.generation})
5262  hist.generation = event.generation || ++hist.maxGeneration
5263
5264  var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")
5265
5266  var loop = function ( i ) {
5267    var change = event.changes[i]
5268    change.origin = type
5269    if (filter && !filterChange(doc, change, false)) {
5270      source.length = 0
5271      return {}
5272    }
5273
5274    antiChanges.push(historyChangeFromChange(doc, change))
5275
5276    var after = i ? computeSelAfterChange(doc, change) : lst(source)
5277    makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change))
5278    if (!i && doc.cm) { doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}) }
5279    var rebased = []
5280
5281    // Propagate to the linked documents
5282    linkedDocs(doc, function (doc, sharedHist) {
5283      if (!sharedHist && indexOf(rebased, doc.history) == -1) {
5284        rebaseHist(doc.history, change)
5285        rebased.push(doc.history)
5286      }
5287      makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change))
5288    })
5289  };
5290
5291  for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) {
5292    var returned = loop( i$1 );
5293
5294    if ( returned ) return returned.v;
5295  }
5296}
5297
5298// Sub-views need their line numbers shifted when text is added
5299// above or below them in the parent document.
5300function shiftDoc(doc, distance) {
5301  if (distance == 0) { return }
5302  doc.first += distance
5303  doc.sel = new Selection(map(doc.sel.ranges, function (range) { return new Range(
5304    Pos(range.anchor.line + distance, range.anchor.ch),
5305    Pos(range.head.line + distance, range.head.ch)
5306  ); }), doc.sel.primIndex)
5307  if (doc.cm) {
5308    regChange(doc.cm, doc.first, doc.first - distance, distance)
5309    for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++)
5310      { regLineChange(doc.cm, l, "gutter") }
5311  }
5312}
5313
5314// More lower-level change function, handling only a single document
5315// (not linked ones).
5316function makeChangeSingleDoc(doc, change, selAfter, spans) {
5317  if (doc.cm && !doc.cm.curOp)
5318    { return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) }
5319
5320  if (change.to.line < doc.first) {
5321    shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line))
5322    return
5323  }
5324  if (change.from.line >
5324 doc.lastLine()) { return }
5325
5326  // Clip the change to the size of this doc
5327  if (change.from.line < doc.first) {
5328    var shift = change.text.length - 1 - (doc.first - change.from.line)
5329    shiftDoc(doc, shift)
5330    change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
5331              text: [lst(change.text)], origin: change.origin}
5332  }
5333  var last = doc.lastLine()
5334  if (change.to.line > last) {
5335    change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
5336              text: [change.text[0]], origin: change.origin}
5337  }
5338
5339  change.removed = getBetween(doc, change.from, change.to)
5340
5341  if (!selAfter) { selAfter = computeSelAfterChange(doc, change) }
5342  if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans) }
5343  else { updateDoc(doc, change, spans) }
5344  setSelectionNoUndo(doc, selAfter, sel_dontScroll)
5345}
5346
5347// Handle the interaction of a change to a document with the editor
5348// that this document is part of.
5349function makeChangeSingleDocInEditor(cm, change, spans) {
5350  var doc = cm.doc, display = cm.display, from = change.from, to = change.to
5351
5352  var recomputeMaxLength = false, checkWidthStart = from.line
5353  if (!cm.options.lineWrapping) {
5354    checkWidthStart = lineNo(visualLine(getLine(doc, from.line)))
5355    doc.iter(checkWidthStart, to.line + 1, function (line) {
5356      if (line == display.maxLine) {
5357        recomputeMaxLength = true
5358        return true
5359      }
5360    })
5361  }
5362
5363  if (doc.sel.contains(change.from, change.to) > -1)
5364    { signalCursorActivity(cm) }
5365
5366  updateDoc(doc, change, spans, estimateHeight(cm))
5367
5368  if (!cm.options.lineWrapping) {
5369    doc.iter(checkWidthStart, from.line + change.text.length, function (line) {
5370      var len = lineLength(line)
5371      if (len > display.maxLineLength) {
5372        display.maxLine = line
5373        display.maxLineLength = len
5374        display.maxLineChanged = true
5375        recomputeMaxLength = false
5376      }
5377    })
5378    if (recomputeMaxLength) { cm.curOp.updateMaxLine = true }
5379  }
5380
5381  retreatFrontier(doc, from.line)
5382  startWorker(cm, 400)
5383
5384  var lendiff = change.text.length - (to.line - from.line) - 1
5385  // Remember that these lines changed, for updating the display
5386  if (change.full)
5387    { regChange(cm) }
5388  else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change))
5389    { regLineChange(cm, from.line, "text") }
5390  else
5391    { regChange(cm, from.line, to.line + 1, lendiff) }
5392
5393  var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change")
5394  if (changeHandler || changesHandler) {
5395    var obj = {
5396      from: from, to: to,
5397      text: change.text,
5398      removed: change.removed,
5399      origin: change.origin
5400    }
5401    if (changeHandler) { signalLater(cm, "change", cm, obj) }
5402    if (changesHandler) { (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj) }
5403  }
5404  cm.display.selForContextMenu = null
5405}
5406
5407function replaceRange(doc, code, from, to, origin) {
5408  if (!to) { to = from }
5409  if (cmp(to, from) < 0) { var assign;
5410    (assign = [to, from], from = assign[0], to = assign[1], assign) }
5411  if (typeof code == "string") { code = doc.splitLines(code) }
5412  makeChange(doc, {from: from, to: to, text: code, origin: origin})
5413}
5414
5415// Rebasing/resetting history to deal with externally-sourced changes
5416
5417function rebaseHistSelSingle(pos, from, to, diff) {
5418  if (to < pos.line) {
5419    pos.line += diff
5420  } else if (from < pos.line) {
5421    pos.line = from
5422    pos.ch = 0
5423  }
5424}
5425
5426// Tries to rebase an array of history events given a change in the
5427// document. If the change touches the same lines as the event, the
5428// event, and everything 'behind' it, is discarded. If the change is
5429// before the event, the event's positions are updated. Uses a
5430// copy-on-write scheme for the positions, to avoid having to
5431// reallocate them all on every rebase, but also avoid problems with
5432// shared position objects being unsafely updated.
5433function rebaseHistArray(array, from, to, diff) {
5434  for (var i = 0; i < array.length; ++i) {
5435    var sub = array[i], ok = true
5436    if (sub.ranges) {
5437      if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true }
5438      for (var j = 0; j < sub.ranges.length; j++) {
5439        rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff)
5440        rebaseHistSelSingle(sub.ranges[j].head, from, to, diff)
5441      }
5442      continue
5443    }
5444    for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) {
5445      var cur = sub.changes[j$1]
5446      if (to < cur.from.line) {
5447        cur.from = Pos(cur.from.line + diff, cur.from.ch)
5448        cur.to = Pos(cur.to.line + diff, cur.to.ch)
5449      } else if (from <= cur.to.line) {
5450        ok = false
5451        break
5452      }
5453    }
5454    if (!ok) {
5455      array.splice(0, i + 1)
5456      i = 0
5457    }
5458  }
5459}
5460
5461function rebaseHist(hist, change) {
5462  var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1
5463  rebaseHistArray(hist.done, from, to, diff)
5464  rebaseHistArray(hist.undone, from, to, diff)
5465}
5466
5467// Utility for applying a change to a line by handle or number,
5468// returning the number and optionally registering the line as
5469// changed.
5470function changeLine(doc, handle, changeType, op) {
5471  var no = handle, line = handle
5472  if (typeof handle == "number") { line = getLine(doc, clipLine(doc, handle)) }
5473  else { no = lineNo(handle) }
5474  if (no == null) { return null }
5475  if (op(line, no) && doc.cm) { regLineChange(doc.cm, no, changeType) }
5476  return line
5477}
5478
5479// The document is represented as a BTree consisting of leaves, with
5480// chunk of lines in them, and branches, with up to ten leaves or
5481// other branch nodes below them. The top node is always a branch
5482// node, and is the document object itself (meaning it has
5483// additional methods and properties).
5484//
5485// All nodes have parent links. The tree is used both to go from
5486// line numbers to line objects, and to go from objects to numbers.
5487// It also indexes by height, and is used to convert between height
5488// and line object, and to find the total height of the document.
5489//
5490// See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html
5491
5492function LeafChunk(lines) {
5493  var this$1 = this;
5494
5495  this.lines = lines
5496  this.parent = null
5497  var height = 0
5498  for (var i = 0; i < lines.length; ++i) {
5499    lines[i].parent = this$1
5500    height += lines[i].height
5501  }
5502  this.height = height
5503}
5504
5505LeafChunk.prototype = {
5506  chunkSize: function chunkSize() { return this.lines.length },
5507
5508  // Remove the n lines at offset 'at'.
5509  removeInner: function removeInner(at, n) {
5510    var this$1 = this;
5511
5512    for (var i = at, e = at + n; i < e; ++i) {
5513      var line = this$1.lines[i]
5514      this$1.height -= line.height
5515      cleanUpLine(line)
5516      signalLater(line, "delete")
5517    }
5518    this.lines.splice(at, n)
5519  },
5520
5521  // Helper used to collapse a small branch into a single leaf.
5522  collapse: function collapse(lines) {
5523    lines.push.apply(lines, this.lines)
5524  },
5525
5526  // Insert the given array of lines at offset 'at', count them as
5527  // having the given height.
5528  insertInner: function insertInner(at, lines, height) {
5529    var this$1 = this;
5530
5531    this.height += height
5532    this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at))
5533    for (var i = 0; i < lines.length; ++i) { lines[i].parent = this$1 }
5534  },
5535
5536  // Used to iterate over a part of the tree.
5537  iterN: function iterN(at, n, op) {
5538    var this$1 = this;
5539
5540    for (var e = at + n; at < e; ++at)
5541      { if (op(this$1.lines[at])) { return true } }
5542  }
5543}
5544
5545function BranchChunk(children) {
5546  var this$1 = this;
5547
5548  this.children = children
5549  var size = 0, height = 0
5550  for (var i = 0; i < children.length; ++i) {
5551    var ch = children[i]
5552    size += ch.chunkSize(); height += ch.height
5553    ch.parent = this$1
5554  }
5555  this.size = size
5556  this.height = height
5557  this.parent = null
5558}
5559
5560BranchChunk.prototype = {
5561  chunkSize: function chunkSize() { return this.size },
5562
5563  removeInner: function removeInner(at, n) {
5564    var this$1 = this;
5565
5566    this.size -= n
5567    for (var i = 0; i < this.children.length; ++i) {
5568      var child = this$1.children[i], sz = child.chunkSize()
5569      if (at < sz) {
5570        var rm = Math.min(n, sz - at), oldHeight = child.height
5571        child.removeInner(at, rm)
5572        this$1.height -= oldHeight - child.height
5573        if (sz == rm) { this$1.children.splice(i--, 1); child.parent = null }
5574        if ((n -= rm) == 0) { break }
5575        at = 0
5576      } else { at -= sz }
5577    }
5578    // If the result is smaller than 25 lines, ensure that it is a
5579    // single leaf node.
5580    if (this.size - n < 25 &&
5581        (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) {
5582      var lines = []
5583      this.collapse(lines)
5584      this.children = [new LeafChunk(lines)]
5585      this.children[0].parent = this
5586    }
5587  },
5588
5589  collapse: function collapse(lines) {
5590    var this$1 = this;
5591
5592    for (var i = 0; i < this.children.length; ++i) { this$1.children[i].collapse(lines) }
5593  },
5594
5595  insertInner: function insertInner(at, lines, height) {
5596    var this$1 = this;
5597
5598    this.size += lines.length
5599    this.height += height
5600    for (var i = 0; i < this.children.length; ++i) {
5601      var child = this$1.children[i], sz = child.chunkSize()
5602      if (at <= sz) {
5603        child.insertInner(at, lines, height)
5604        if (child.lines && child.lines.length > 50) {
5605          // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced.
5606          // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest.
5607          var remaining = child.lines.length % 25 + 25
5608          for (var pos = remaining; pos < child.lines.length;) {
5609            var leaf = new LeafChunk(child.lines.slice(pos, pos += 25))
5610            child.height -= leaf.height
5611            this$1.children.splice(++i, 0, leaf)
5612            leaf.parent = this$1
5613          }
5614          child.lines = child.lines.slice(0, remaining)
5615          this$1.maybeSpill()
5616        }
5617        break
5618      }
5619      at -= sz
5620    }
5621  },
5622
5623  // When a node has grown, check whether it should be split.
5624  maybeSpill: function maybeSpill() {
5625    if (this.children.length <= 10) { return }
5626    var me = this
5627    do {
5628      var spilled = me.children.splice(me.children.length - 5, 5)
5629      var sibling = new BranchChunk(spilled)
5630      if (!me.parent) { // Become the parent node
5631        var copy = new BranchChunk(me.children)
5632        copy.parent = me
5633        me.children = [copy, sibling]
5634        me = copy
5635     } else {
5636        me.size -= sibling.size
5637        me.height -= sibling.height
5638        var myIndex = indexOf(me.parent.children, me)
5639        me.parent.children.splice(myIndex + 1, 0, sibling)
5640      }
5641      sibling.parent = me.parent
5642    } while (me.children.length > 10)
5643    me.parent.maybeSpill()
5644  },
5645
5646  iterN: function iterN(at, n, op) {
5647    var this$1 = this;
5648
5649    for (var i = 0; i < this.children.length; ++i) {
5650      var child = this$1.children[i], sz = child.chunkSize()
5651      if (at < sz) {
5652        var used = Math.min(n, sz - at)
5653        if (child.iterN(at, used, op)) { return true }
5654        if ((n -= used) == 0) { break }
5655        at = 0
5656      } else { at -= sz }
5657    }
5658  }
5659}
5660
5661// Line widgets are block elements displayed above or below a line.
5662
5663var LineWidget = function(doc, node, options) {
5664  var this$1 = this;
5665
5666  if (options) { for (var opt in options) { if (options.hasOwnProperty(opt))
5667    { this$1[opt] = options[opt] } } }
5668  this.doc = doc
5669  this.node = node
5670};
5671
5672LineWidget.prototype.clear = function () {
5673    var this$1 = this;
5674
5675  var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line)
5676  if (no == null || !ws) { return }
5677  for (var i = 0; i < ws.length; ++i) { if (ws[i] == this$1) { ws.splice(i--, 1) } }
5678  if (!ws.length) { line.widgets = null }
5679  var height = widgetHeight(this)
5680  updateLineHeight(line, Math.max(0, line.height - height))
5681  if (cm) {
5682    runInOp(cm, function () {
5683      adjustScrollWhenAboveVisible(cm, line, -height)
5684      regLineChange(cm, no, "widget")
5685    })
5686    signalLater(cm, "lineWidgetCleared", cm, this, no)
5687  }
5688};
5689
5690LineWidget.prototype.changed = function () {
5691    var this$1 = this;
5692
5693  var oldH = this.height, cm = this.doc.cm, line = this.line
5694  this.height = null
5695  var diff = widgetHeight(this) - oldH
5696  if (!diff) { return }
5697  updateLineHeight(line, line.height + diff)
5698  if (cm) {
5699    runInOp(cm, function () {
5700      cm.curOp.forceUpdate = true
5701      adjustScrollWhenAboveVisible(cm, line, diff)
5702      signalLater(cm, "lineWidgetChanged", cm, this$1, lineNo(line))
5703    })
5704  }
5705};
5706eventMixin(LineWidget)
5707
5708function adjustScrollWhenAboveVisible(cm, line, diff) {
5709  if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))
5710    { addToScrollTop(cm, diff) }
5711}
5712
5713function addLineWidget(doc, handle, node, options) {
5714  var widget = new LineWidget(doc, node, options)
5715  var cm = doc.cm
5716  if (cm && widget.noHScroll) { cm.display.alignWidgets = true }
5717  changeLine(doc, handle, "widget", function (line) {
5718    var widgets = line.widgets || (line.widgets = [])
5719    if (widget.insertAt == null) { widgets.push(widget) }
5720    else { widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget) }
5721    widget.line = line
5722    if (cm && !lineIsHidden(doc, line)) {
5723      var aboveVisible = heightAtLine(line) < doc.scrollTop
5724      updateLineHeight(line, line.height + widgetHeight(widget))
5725      if (aboveVisible) { addToScrollTop(cm, widget.height) }
5726      cm.curOp.forceUpdate = true
5727    }
5728    return true
5729  })
5730  if (cm) { signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle)) }
5731  return widget
5732}
5733
5734// TEXTMARKERS
5735
5736// Created with markText and setBookmark methods. A TextMarker is a
5737// handle that can be used to clear or find a marked position in the
5738// document. Line objects hold arrays (markedSpans) containing
5739// {from, to, marker} object pointing to such marker objects, and
5740// indicating that such a marker is present on that line. Multiple
5741// lines may point to the same marker when it spans across lines.
5742// The spans will have null for their from/to properties when the
5743// marker continues beyond the start/end of the line. Markers have
5744// links back to the lines they currently touch.
5745
5746// Collapsed markers have unique ids, in order to be able to order
5747// them, which is needed for uniquely determining an outer marker
5748// when they overlap (they may nest, but not partially overlap).
5749var nextMarkerId = 0
5750
5751var TextMarker = function(doc, type) {
5752  this.lines = []
5753  this.type = type
5754  this.doc = doc
5755  this.id = ++nextMarkerId
5756};
5757
5758// Clear the marker.
5759TextMarker.prototype.clear = function () {
5760    var this$1 = this;
5761
5762  if (this.explicitlyCleared) { return }
5763  var cm = this.doc.cm, withOp = cm && !cm.curOp
5764  if (withOp) { startOperation(cm) }
5765  if (hasHandler(this, "clear")) {
5766    var found = this.find()
5767    if (found) { signalLater(this, "clear", found.from, found.to) }
5768  }
5769  var min = null, max = null
5770  for (var i = 0; i < this.lines.length; ++i) {
5771    var line = this$1.lines[i]
5772    var span = getMarkedSpanFor(line.markedSpans, this$1)
5773    if (cm && !this$1.collapsed) { regLineChange(cm, lineNo(line), "text") }
5774    else if (cm) {
5775      if (span.to != null) { max = lineNo(line) }
5776      if (span.from != null) { min = lineNo(line) }
5777    }
5778    line.markedSpans = removeMarkedSpan(line.markedSpans, span)
5779    if (span.from == null && this$1.collapsed && !lineIsHidden(this$1.doc, line) && cm)
5780      { updateLineHeight(line, textHeight(cm.display)) }
5781  }
5782  if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) {
5783    var visual = visualLine(this$1.lines[i$1]), len = lineLength(visual)
5784    if (len > cm.display.maxLineLength) {
5785      cm.display.maxLine = visual
5786      cm.display.maxLineLength = len
5787      cm.display.maxLineChanged = true
5788    }
5789  } }
5790
5791  if (min != null && cm && this.collapsed) { regChange(cm, min, max + 1) }
5792  this.lines.length = 0
5793  this.explicitlyCleared = true
5794  if (this.atomic && this.doc.cantEdit) {
5795    this.doc.cantEdit = false
5796    if (cm) { reCheckSelection(cm.doc) }
5797  }
5798  if (cm) { signalLater(cm, "markerCleared", cm, this, min, max) }
5799  if (withOp) { endOperation(cm) }
5800  if (this.parent) { this.parent.clear() }
5801};
5802
5803// Find the position of the marker in the document. Returns a {from,
5804// to} object by default. Side can be passed to get a specific side
5805// -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the
5806// Pos objects returned contain a line object, rather than a line
5807// number (used to prevent looking up the same line twice).
5808TextMarker.prototype.find = function (side, lineObj) {
5809    var this$1 = this;
5810
5811  if (side == null && this.type == "bookmark") { side = 1 }
5812  var from, to
5813  for (var i = 0; i < this.lines.length; ++i) {
5814    var line = this$1.lines[i]
5815    var span = getMarkedSpanFor(line.markedSpans, this$1)
5816    if (span.from != null) {
5817      from = Pos(lineObj ? line : lineNo(line), span.from)
5818      if (side == -1) { return from }
5819    }
5820    if (span.to != null) {
5821      to = Pos(lineObj ? line : lineNo(line), span.to)
5822      if (side == 1) { return to }
5823    }
5824  }
5825  return from && {from: from, to: to}
5826};
5827
5828// Signals that the marker's widget changed, and surrounding layout
5829// should be recomputed.
5830TextMarker.prototype.changed = function () {
5831    var this$1 = this;
5832
5833  var pos = this.find(-1, true), widget = this, cm = this.doc.cm
5834  if (!pos || !cm) { return }
5835  runInOp(cm, function () {
5836    var line = pos.line, lineN = lineNo(pos.line)
5837    var view = findViewForLine(cm, lineN)
5838    if (view) {
5839      clearLineMeasurementCacheFor(view)
5840      cm.curOp.selectionChanged = cm.curOp.forceUpdate = true
5841    }
5842    cm.curOp.updateMaxLine = true
5843    if (!lineIsHidden(widget.doc, line) && widget.height != null) {
5844      var oldHeight = widget.height
5845      widget.height = null
5846      var dHeight = widgetHeight(widget) - oldHeight
5847      if (dHeight)
5848        { updateLineHeight(line, line.height + dHeight) }
5849    }
5850    signalLater(cm, "markerChanged", cm, this$1)
5851  })
5852};
5853
5854TextMarker.prototype.attachLine = function (line) {
5855  if (!this.lines.length && this.doc.cm) {
5856    var op = this.doc.cm.curOp
5857    if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
5858      { (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this) }
5859  }
5860  this.lines.push(line)
5861};
5862
5863TextMarker.prototype.detachLine = function (line) {
5864  this.lines.splice(indexOf(this.lines, line), 1)
5865  if (!this.lines.length && this.doc.cm) {
5866    var op = this.doc.cm.curOp
5867    ;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this)
5868  }
5869};
5870eventMixin(TextMarker)
5871
5872// Create a marker, wire it up to the right lines, and
5873function markText(doc, from, to, options, type) {
5874  // Shared markers (across linked documents) are handled separately
5875  // (markTextShared will call out to this again, once per
5876  // document).
5877  if (options && options.shared) { return markTextShared(doc, from, to, options, type) }
5878  // Ensure we are in an operation.
5879  if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, markText)(doc, from, to, options, type) }
5880
5881  var marker = new TextMarker(doc, type), diff = cmp(from, to)
5882  if (options) { copyObj(options, marker, false) }
5883  // Don't connect empty markers unless clearWhenEmpty is false
5884  if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false)
5885    { return marker }
5886  if (marker.replacedWith) {
5887    // Showing up as a widget implies collapsed (widget replaces text)
5888    marker.collapsed = true
5889    marker.widgetNode = eltP("span", [marker.replacedWith], "CodeMirror-widget")
5890    if (!options.handleMouseEvents) { marker.widgetNode.setAttribute("cm-ignore-events", "true") }
5891    if (options.insertLeft) { marker.widgetNode.insertLeft = true }
5892  }
5893  if (marker.collapsed) {
5894    if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||
5895        from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker))
5896      { throw new Error("Inserting collapsed marker partially overlapping an existing one") }
5897    seeCollapsedSpans()
5898  }
5899
5900  if (marker.addToHistory)
5901    { addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN) }
5902
5903  var curLine = from.line, cm = doc.cm, updateMaxLine
5904  doc.iter(curLine, to.line + 1, function (line) {
5905    if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.dis
5905play.maxLine)
5906      { updateMaxLine = true }
5907    if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0) }
5908    addMarkedSpan(line, new MarkedSpan(marker,
5909                                       curLine == from.line ? from.ch : null,
5910                                       curLine == to.line ? to.ch : null))
5911    ++curLine
5912  })
5913  // lineIsHidden depends on the presence of the spans, so needs a second pass
5914  if (marker.collapsed) { doc.iter(from.line, to.line + 1, function (line) {
5915    if (lineIsHidden(doc, line)) { updateLineHeight(line, 0) }
5916  }) }
5917
5918  if (marker.clearOnEnter) { on(marker, "beforeCursorEnter", function () { return marker.clear(); }) }
5919
5920  if (marker.readOnly) {
5921    seeReadOnlySpans()
5922    if (doc.history.done.length || doc.history.undone.length)
5923      { doc.clearHistory() }
5924  }
5925  if (marker.collapsed) {
5926    marker.id = ++nextMarkerId
5927    marker.atomic = true
5928  }
5929  if (cm) {
5930    // Sync editor state
5931    if (updateMaxLine) { cm.curOp.updateMaxLine = true }
5932    if (marker.collapsed)
5933      { regChange(cm, from.line, to.line + 1) }
5934    else if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.css)
5935      { for (var i = from.line; i <= to.line; i++) { regLineChange(cm, i, "text") } }
5936    if (marker.atomic) { reCheckSelection(cm.doc) }
5937    signalLater(cm, "markerAdded", cm, marker)
5938  }
5939  return marker
5940}
5941
5942// SHARED TEXTMARKERS
5943
5944// A shared marker spans multiple linked documents. It is
5945// implemented as a meta-marker-object controlling multiple normal
5946// markers.
5947var SharedTextMarker = function(markers, primary) {
5948  var this$1 = this;
5949
5950  this.markers = markers
5951  this.primary = primary
5952  for (var i = 0; i < markers.length; ++i)
5953    { markers[i].parent = this$1 }
5954};
5955
5956SharedTextMarker.prototype.clear = function () {
5957    var this$1 = this;
5958
5959  if (this.explicitlyCleared) { return }
5960  this.explicitlyCleared = true
5961  for (var i = 0; i < this.markers.length; ++i)
5962    { this$1.markers[i].clear() }
5963  signalLater(this, "clear")
5964};
5965
5966SharedTextMarker.prototype.find = function (side, lineObj) {
5967  return this.primary.find(side, lineObj)
5968};
5969eventMixin(SharedTextMarker)
5970
5971function markTextShared(doc, from, to, options, type) {
5972  options = copyObj(options)
5973  options.shared = false
5974  var markers = [markText(doc, from, to, options, type)], primary = markers[0]
5975  var widget = options.widgetNode
5976  linkedDocs(doc, function (doc) {
5977    if (widget) { options.widgetNode = widget.cloneNode(true) }
5978    markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type))
5979    for (var i = 0; i < doc.linked.length; ++i)
5980      { if (doc.linked[i].isParent) { return } }
5981    primary = lst(markers)
5982  })
5983  return new SharedTextMarker(markers, primary)
5984}
5985
5986function findSharedMarkers(doc) {
5987  return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function (m) { return m.parent; })
5988}
5989
5990function copySharedMarkers(doc, markers) {
5991  for (var i = 0; i < markers.length; i++) {
5992    var marker = markers[i], pos = marker.find()
5993    var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to)
5994    if (cmp(mFrom, mTo)) {
5995      var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type)
5996      marker.markers.push(subMark)
5997      subMark.parent = marker
5998    }
5999  }
6000}
6001
6002function detachSharedMarkers(markers) {
6003  var loop = function ( i ) {
6004    var marker = markers[i], linked = [marker.primary.doc]
6005    linkedDocs(marker.primary.doc, function (d) { return linked.push(d); })
6006    for (var j = 0; j < marker.markers.length; j++) {
6007      var subMarker = marker.markers[j]
6008      if (indexOf(linked, subMarker.doc) == -1) {
6009        subMarker.parent = null
6010        marker.markers.splice(j--, 1)
6011      }
6012    }
6013  };
6014
6015  for (var i = 0; i < markers.length; i++) loop( i );
6016}
6017
6018var nextDocId = 0
6019var Doc = function(text, mode, firstLine, lineSep, direction) {
6020  if (!(this instanceof Doc)) { return new Doc(text, mode, firstLine, lineSep, direction) }
6021  if (firstLine == null) { firstLine = 0 }
6022
6023  BranchChunk.call(this, [new LeafChunk([new Line("", null)])])
6024  this.first = firstLine
6025  this.scrollTop = this.scrollLeft = 0
6026  this.cantEdit = false
6027  this.cleanGeneration = 1
6028  this.modeFrontier = this.highlightFrontier = firstLine
6029  var start = Pos(firstLine, 0)
6030  this.sel = simpleSelection(start)
6031  this.history = new History(null)
6032  this.id = ++nextDocId
6033  this.modeOption = mode
6034  this.lineSep = lineSep
6035  this.direction = (direction == "rtl") ? "rtl" : "ltr"
6036  this.extend = false
6037
6038  if (typeof text == "string") { text = this.splitLines(text) }
6039  updateDoc(this, {from: start, to: start, text: text})
6040  setSelection(this, simpleSelection(start), sel_dontScroll)
6041}
6042
6043Doc.prototype = createObj(BranchChunk.prototype, {
6044  constructor: Doc,
6045  // Iterate over the document. Supports two forms -- with only one
6046  // argument, it calls that for each line in the document. With
6047  // three, it iterates over the range given by the first two (with
6048  // the second being non-inclusive).
6049  iter: function(from, to, op) {
6050    if (op) { this.iterN(from - this.first, to - from, op) }
6051    else { this.iterN(this.first, this.first + this.size, from) }
6052  },
6053
6054  // Non-public interface for adding and removing lines.
6055  insert: function(at, lines) {
6056    var height = 0
6057    for (var i = 0; i < lines.length; ++i) { height += lines[i].height }
6058    this.insertInner(at - this.first, lines, height)
6059  },
6060  remove: function(at, n) { this.removeInner(at - this.first, n) },
6061
6062  // From here, the methods are part of the public interface. Most
6063  // are also available from CodeMirror (editor) instances.
6064
6065  getValue: function(lineSep) {
6066    var lines = getLines(this, this.first, this.first + this.size)
6067    if (lineSep === false) { return lines }
6068    return lines.join(lineSep || this.lineSeparator())
6069  },
6070  setValue: docMethodOp(function(code) {
6071    var top = Pos(this.first, 0), last = this.first + this.size - 1
6072    makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
6073                      text: this.splitLines(code), origin: "setValue", full: true}, true)
6074    if (this.cm) { scrollToCoords(this.cm, 0, 0) }
6075    setSelection(this, simpleSelection(top), sel_dontScroll)
6076  }),
6077  replaceRange: function(code, from, to, origin) {
6078    from = clipPos(this, from)
6079    to = to ? clipPos(this, to) : from
6080    replaceRange(this, code, from, to, origin)
6081  },
6082  getRange: function(from, to, lineSep) {
6083    var lines = getBetween(this, clipPos(this, from), clipPos(this, to))
6084    if (lineSep === false) { return lines }
6085    return lines.join(lineSep || this.lineSeparator())
6086  },
6087
6088  getLine: function(line) {var l = this.getLineHandle(line); return l && l.text},
6089
6090  getLineHandle: function(line) {if (isLine(this, line)) { return getLine(this, line) }},
6091  getLineNumber: function(line) {return lineNo(line)},
6092
6093  getLineHandleVisualStart: function(line) {
6094    if (typeof line == "number") { line = getLine(this, line) }
6095    return visualLine(line)
6096  },
6097
6098  lineCount: function() {return this.size},
6099  firstLine: function() {return this.first},
6100  lastLine: function() {return this.first + this.size - 1},
6101
6102  clipPos: function(pos) {return clipPos(this, pos)},
6103
6104  getCursor: function(start) {
6105    var range = this.sel.primary(), pos
6106    if (start == null || start == "head") { pos = range.head }
6107    else if (start == "anchor") { pos = range.anchor }
6108    else if (start == "end" || start == "to" || start === false) { pos = range.to() }
6109    else { pos = range.from() }
6110    return pos
6111  },
6112  listSelections: function() { return this.sel.ranges },
6113  somethingSelected: function() {return this.sel.somethingSelected()},
6114
6115  setCursor: docMethodOp(function(line, ch, options) {
6116    setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options)
6117  }),
6118  setSelection: docMethodOp(function(anchor, head, options) {
6119    setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options)
6120  }),
6121  extendSelection: docMethodOp(function(head, other, options) {
6122    extendSelection(this, clipPos(this, head), other && clipPos(this, other), options)
6123  }),
6124  extendSelections: docMethodOp(function(heads, options) {
6125    extendSelections(this, clipPosArray(this, heads), options)
6126  }),
6127  extendSelectionsBy: docMethodOp(function(f, options) {
6128    var heads = map(this.sel.ranges, f)
6129    extendSelections(this, clipPosArray(this, heads), options)
6130  }),
6131  setSelections: docMethodOp(function(ranges, primary, options) {
6132    var this$1 = this;
6133
6134    if (!ranges.length) { return }
6135    var out = []
6136    for (var i = 0; i < ranges.length; i++)
6137      { out[i] = new Range(clipPos(this$1, ranges[i].anchor),
6138                         clipPos(this$1, ranges[i].head)) }
6139    if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex) }
6140    setSelection(this, normalizeSelection(out, primary), options)
6141  }),
6142  addSelection: docMethodOp(function(anchor, head, options) {
6143    var ranges = this.sel.ranges.slice(0)
6144    ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)))
6145    setSelection(this, normalizeSelection(ranges, ranges.length - 1), options)
6146  }),
6147
6148  getSelection: function(lineSep) {
6149    var this$1 = this;
6150
6151    var ranges = this.sel.ranges, lines
6152    for (var i = 0; i < ranges.length; i++) {
6153      var sel = getBetween(this$1, ranges[i].from(), ranges[i].to())
6154      lines = lines ? lines.concat(sel) : sel
6155    }
6156    if (lineSep === false) { return lines }
6157    else { return lines.join(lineSep || this.lineSeparator()) }
6158  },
6159  getSelections: function(lineSep) {
6160    var this$1 = this;
6161
6162    var parts = [], ranges = this.sel.ranges
6163    for (var i = 0; i < ranges.length; i++) {
6164      var sel = getBetween(this$1, ranges[i].from(), ranges[i].to())
6165      if (lineSep !== false) { sel = sel.join(lineSep || this$1.lineSeparator()) }
6166      parts[i] = sel
6167    }
6168    return parts
6169  },
6170  replaceSelection: function(code, collapse, origin) {
6171    var dup = []
6172    for (var i = 0; i < this.sel.ranges.length; i++)
6173      { dup[i] = code }
6174    this.replaceSelections(dup, collapse, origin || "+input")
6175  },
6176  replaceSelections: docMethodOp(function(code, collapse, origin) {
6177    var this$1 = this;
6178
6179    var changes = [], sel = this.sel
6180    for (var i = 0; i < sel.ranges.length; i++) {
6181      var range = sel.ranges[i]
6182      changes[i] = {from: range.from(), to: range.to(), text: this$1.splitLines(code[i]), origin: origin}
6183    }
6184    var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse)
6185    for (var i$1 = changes.length - 1; i$1 >= 0; i$1--)
6186      { makeChange(this$1, changes[i$1]) }
6187    if (newSel) { setSelectionReplaceHistory(this, newSel) }
6188    else if (this.cm) { ensureCursorVisible(this.cm) }
6189  }),
6190  undo: docMethodOp(function() {makeChangeFromHistory(this, "undo")}),
6191  redo: docMethodOp(function() {makeChangeFromHistory(this, "redo")}),
6192  undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true)}),
6193  redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true)}),
6194
6195  setExtending: function(val) {this.extend = val},
6196  getExtending: function() {return this.extend},
6197
6198  historySize: function() {
6199    var hist = this.history, done = 0, undone = 0
6200    for (var i = 0; i < hist.done.length; i++) { if (!hist.done[i].ranges) { ++done } }
6201    for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].ranges) { ++undone } }
6202    return {undo: done, redo: undone}
6203  },
6204  clearHistory: function() {this.history = new History(this.history.maxGeneration)},
6205
6206  markClean: function() {
6207    this.cleanGeneration = this.changeGeneration(true)
6208  },
6209  changeGeneration: function(forceSplit) {
6210    if (forceSplit)
6211      { this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null }
6212    return this.history.generation
6213  },
6214  isClean: function (gen) {
6215    return this.history.generation == (gen || this.cleanGeneration)
6216  },
6217
6218  getHistory: function() {
6219    return {done: copyHistoryArray(this.history.done),
6220            undone: copyHistoryArray(this.history.undone)}
6221  },
6222  setHistory: function(histData) {
6223    var hist = this.history = new History(this.history.maxGeneration)
6224    hist.done = copyHistoryArray(histData.done.slice(0), null, true)
6225    hist.undone = copyHistoryArray(histData.undone.slice(0), null, true)
6226  },
6227
6228  setGutterMarker: docMethodOp(function(line, gutterID, value) {
6229    return changeLine(this, line, "gutter", function (line) {
6230      var markers = line.gutterMarkers || (line.gutterMarkers = {})
6231      markers[gutterID] = value
6232      if (!value && isEmpty(markers)) { line.gutterMarkers = null }
6233      return true
6234    })
6235  }),
6236
6237  clearGutter: docMethodOp(function(gutterID) {
6238    var this$1 = this;
6239
6240    this.iter(function (line) {
6241      if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
6242        changeLine(this$1, line, "gutter", function () {
6243          line.gutterMarkers[gutterID] = null
6244          if (isEmpty(line.gutterMarkers)) { line.gutterMarkers = null }
6245          return true
6246        })
6247      }
6248    })
6249  }),
6250
6251  lineInfo: function(line) {
6252    var n
6253    if (typeof line == "number") {
6254      if (!isLine(this, line)) { return null }
6255      n = line
6256      line = getLine(this, line)
6257      if (!line) { return null }
6258    } else {
6259      n = lineNo(line)
6260      if (n == null) { return null }
6261    }
6262    return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
6263            textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
6264            widgets: line.widgets}
6265  },
6266
6267  addLineClass: docMethodOp(function(handle, where, cls) {
6268    return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
6269      var prop = where == "text" ? "textClass"
6270               : where == "background" ? "bgClass"
6271               : where == "gutter" ? "gutterClass" : "wrapClass"
6272      if (!line[prop]) { line[prop] = cls }
6273      else if (classTest(cls).test(line[prop])) { return false }
6274      else { line[prop] += " " + cls }
6275      return true
6276    })
6277  }),
6278  removeLineClass: docMethodOp(function(handle, where, cls) {
6279    return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
6280      var prop = where == "text" ? "textClass"
6281               : where == "background" ? "bgClass"
6282               : where == "gutter" ? "gutterClass" : "wrapClass"
6283      var cur = line[prop]
6284      if (!cur) { return false }
6285      else if (cls == null) { line[prop] = null }
6286      else {
6287        var found = cur.match(classTest(cls))
6288        if (!found) { return false }
6289        var end = found.index + found[0].length
6290        line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null
6291      }
6292      return true
6293    })
6294  }),
6295
6296  addLineWidget: docMethodOp(function(handle, node, options) {
6297    return addLineWidget(this, handle, node, options)
6298  }),
6299  removeLineWidget: function(widget) { widget.clear() },
6300
6301  markText: function(from, to, options) {
6302    return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range")
6303  },
6304  setBookmark: function(pos, options) {
6305    var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
6306                    insertLeft: options && options.insertLeft,
6307                    clearWhenEmpty: false, shared: options && options.shared,
6308                    handleMouseEvents: options && options.handleMouseEvents}
6309    pos = clipPos(this, pos)
6310    return markText(this, pos, pos, realOpts, "bookmark")
6311  },
6312  findMarksAt: function(pos) {
6313    pos = clipPos(this, pos)
6314    var markers = [], spans = getLine(this, pos.line).markedSpans
6315    if (spans) { for (var i = 0; i < spans.length; ++i) {
6316      var span = spans[i]
6317      if ((span.from == null || span.from <= pos.ch) &&
6318          (span.to == null || span.to >= pos.ch))
6319        { markers.push(span.marker.parent || span.marker) }
6320    } }
6321    return markers
6322  },
6323  findMarks: function(from, to, filter) {
6324    from = clipPos(this, from); to = clipPos(this, to)
6325    var found = [], lineNo = from.line
6326    this.iter(from.line, to.line + 1, function (line) {
6327      var spans = line.markedSpans
6328      if (spans) { for (var i = 0; i < spans.length; i++) {
6329        var span = spans[i]
6330        if (!(span.to != null && lineNo == from.line && from.ch >= span.to ||
6331              span.from == null && lineNo != from.line ||
6332              span.from != null && lineNo == to.line && span.from >= to.ch) &&
6333            (!filter || filter(span.marker)))
6334          { found.push(span.marker.parent || span.marker) }
6335      } }
6336      ++lineNo
6337    })
6338    return found
6339  },
6340  getAllMarks: function() {
6341    var markers = []
6342    this.iter(function (line) {
6343      var sps = line.markedSpans
6344      if (sps) { for (var i = 0; i < sps.length; ++i)
6345        { if (sps[i].from != null) { markers.push(sps[i].marker) } } }
6346    })
6347    return markers
6348  },
6349
6350  posFromIndex: function(off) {
6351    var ch, lineNo = this.first, sepSize = this.lineSeparator().length
6352    this.iter(function (line) {
6353      var sz = line.text.length + sepSize
6354      if (sz > off) { ch = off; return true }
6355      off -= sz
6356      ++lineNo
6357    })
6358    return clipPos(this, Pos(lineNo, ch))
6359  },
6360  indexFromPos: function (coords) {
6361    coords = clipPos(this, coords)
6362    var index = coords.ch
6363    if (coords.line < this.first || coords.ch < 0) { return 0 }
6364    var sepSize = this.lineSeparator().length
6365    this.iter(this.first, coords.line, function (line) { // iter aborts when callback returns a truthy value
6366      index += line.text.length + sepSize
6367    })
6368    return index
6369  },
6370
6371  copy: function(copyHistory) {
6372    var doc = new Doc(getLines(this, this.first, this.first + this.size),
6373                      this.modeOption, this.first, this.lineSep, this.direction)
6374    doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft
6375    doc.sel = this.sel
6376    doc.extend = false
6377    if (copyHistory) {
6378      doc.history.undoDepth = this.history.undoDepth
6379      doc.setHistory(this.getHistory())
6380    }
6381    return doc
6382  },
6383
6384  linkedDoc: function(options) {
6385    if (!options) { options = {} }
6386    var from = this.first, to = this.first + this.size
6387    if (options.from != null && options.from > from) { from = options.from }
6388    if (options.to != null && options.to < to) { to = options.to }
6389    var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep, this.direction)
6390    if (options.sharedHist) { copy.history = this.history
6391    ; }(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist})
6392    copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}]
6393    copySharedMarkers(copy, findSharedMarkers(this))
6394    return copy
6395  },
6396  unlinkDoc: function(other) {
6397    var this$1 = this;
6398
6399    if (other instanceof CodeMirror) { other = other.doc }
6400    if (this.linked) { for (var i = 0; i < this.linked.length; ++i) {
6401      var link = this$1.linked[i]
6402      if (link.doc != other) { continue }
6403      this$1.linked.splice(i, 1)
6404      other.unlinkDoc(this$1)
6405      detachSharedMarkers(findSharedMarkers(this$1))
6406      break
6407    } }
6408    // If the histories were shared, split them again
6409    if (other.history == this.history) {
6410      var splitIds = [other.id]
6411      linkedDocs(other, function (doc) { return splitIds.push(doc.id
6411); }, true)
6412      other.history = new History(null)
6413      other.history.done = copyHistoryArray(this.history.done, splitIds)
6414      other.history.undone = copyHistoryArray(this.history.undone, splitIds)
6415    }
6416  },
6417  iterLinkedDocs: function(f) {linkedDocs(this, f)},
6418
6419  getMode: function() {return this.mode},
6420  getEditor: function() {return this.cm},
6421
6422  splitLines: function(str) {
6423    if (this.lineSep) { return str.split(this.lineSep) }
6424    return splitLinesAuto(str)
6425  },
6426  lineSeparator: function() { return this.lineSep || "\n" },
6427
6428  setDirection: docMethodOp(function (dir) {
6429    if (dir != "rtl") { dir = "ltr" }
6430    if (dir == this.direction) { return }
6431    this.direction = dir
6432    this.iter(function (line) { return line.order = null; })
6433    if (this.cm) { directionChanged(this.cm) }
6434  })
6435})
6436
6437// Public alias.
6438Doc.prototype.eachLine = Doc.prototype.iter
6439
6440// Kludge to work around strange IE behavior where it'll sometimes
6441// re-fire a series of drag-related events right after the drop (#1551)
6442var lastDrop = 0
6443
6444function onDrop(e) {
6445  var cm = this
6446  clearDragCursor(cm)
6447  if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))
6448    { return }
6449  e_preventDefault(e)
6450  if (ie) { lastDrop = +new Date }
6451  var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files
6452  if (!pos || cm.isReadOnly()) { return }
6453  // Might be a file drop, in which case we simply extract the text
6454  // and insert it.
6455  if (files && files.length && window.FileReader && window.File) {
6456    var n = files.length, text = Array(n), read = 0
6457    var loadFile = function (file, i) {
6458      if (cm.options.allowDropFileTypes &&
6459          indexOf(cm.options.allowDropFileTypes, file.type) == -1)
6460        { return }
6461
6462      var reader = new FileReader
6463      reader.onload = operation(cm, function () {
6464        var content = reader.result
6465        if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) { content = "" }
6466        text[i] = content
6467        if (++read == n) {
6468          pos = clipPos(cm.doc, pos)
6469          var change = {from: pos, to: pos,
6470                        text: cm.doc.splitLines(text.join(cm.doc.lineSeparator())),
6471                        origin: "paste"}
6472          makeChange(cm.doc, change)
6473          setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change)))
6474        }
6475      })
6476      reader.readAsText(file)
6477    }
6478    for (var i = 0; i < n; ++i) { loadFile(files[i], i) }
6479  } else { // Normal drop
6480    // Don't do a replace if the drop happened inside of the selected text.
6481    if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {
6482      cm.state.draggingText(e)
6483      // Ensure the editor is re-focused
6484      setTimeout(function () { return cm.display.input.focus(); }, 20)
6485      return
6486    }
6487    try {
6488      var text$1 = e.dataTransfer.getData("Text")
6489      if (text$1) {
6490        var selected
6491        if (cm.state.draggingText && !cm.state.draggingText.copy)
6492          { selected = cm.listSelections() }
6493        setSelectionNoUndo(cm.doc, simpleSelection(pos, pos))
6494        if (selected) { for (var i$1 = 0; i$1 < selected.length; ++i$1)
6495          { replaceRange(cm.doc, "", selected[i$1].anchor, selected[i$1].head, "drag") } }
6496        cm.replaceSelection(text$1, "around", "paste")
6497        cm.display.input.focus()
6498      }
6499    }
6500    catch(e){}
6501  }
6502}
6503
6504function onDragStart(cm, e) {
6505  if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return }
6506  if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return }
6507
6508  e.dataTransfer.setData("Text", cm.getSelection())
6509  e.dataTransfer.effectAllowed = "copyMove"
6510
6511  // Use dummy image instead of default browsers image.
6512  // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
6513  if (e.dataTransfer.setDragImage && !safari) {
6514    var img = elt("img", null, null, "position: fixed; left: 0; top: 0;")
6515    img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
6516    if (presto) {
6517      img.width = img.height = 1
6518      cm.display.wrapper.appendChild(img)
6519      // Force a relayout, or Opera won't use our image for some obscure reason
6520      img._top = img.offsetTop
6521    }
6522    e.dataTransfer.setDragImage(img, 0, 0)
6523    if (presto) { img.parentNode.removeChild(img) }
6524  }
6525}
6526
6527function onDragOver(cm, e) {
6528  var pos = posFromMouse(cm, e)
6529  if (!pos) { return }
6530  var frag = document.createDocumentFragment()
6531  drawSelectionCursor(cm, pos, frag)
6532  if (!cm.display.dragCursor) {
6533    cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors")
6534    cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv)
6535  }
6536  removeChildrenAndAdd(cm.display.dragCursor, frag)
6537}
6538
6539function clearDragCursor(cm) {
6540  if (cm.display.dragCursor) {
6541    cm.display.lineSpace.removeChild(cm.display.dragCursor)
6542    cm.display.dragCursor = null
6543  }
6544}
6545
6546// These must be handled carefully, because naively registering a
6547// handler for each editor will cause the editors to never be
6548// garbage collected.
6549
6550function forEachCodeMirror(f) {
6551  if (!document.getElementsByClassName) { return }
6552  var byClass = document.getElementsByClassName("CodeMirror")
6553  for (var i = 0; i < byClass.length; i++) {
6554    var cm = byClass[i].CodeMirror
6555    if (cm) { f(cm) }
6556  }
6557}
6558
6559var globalsRegistered = false
6560function ensureGlobalHandlers() {
6561  if (globalsRegistered) { return }
6562  registerGlobalHandlers()
6563  globalsRegistered = true
6564}
6565function registerGlobalHandlers() {
6566  // When the window resizes, we need to refresh active editors.
6567  var resizeTimer
6568  on(window, "resize", function () {
6569    if (resizeTimer == null) { resizeTimer = setTimeout(function () {
6570      resizeTimer = null
6571      forEachCodeMirror(onResize)
6572    }, 100) }
6573  })
6574  // When the window loses focus, we want to show the editor as blurred
6575  on(window, "blur", function () { return forEachCodeMirror(onBlur); })
6576}
6577// Called when the window resizes
6578function onResize(cm) {
6579  var d = cm.display
6580  // Might be a text scaling operation, clear size caches.
6581  d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null
6582  d.scrollbarsClipped = false
6583  cm.setSize()
6584}
6585
6586var keyNames = {
6587  3: "Pause", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
6588  19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
6589  36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
6590  46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod",
6591  106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 127: "Delete", 145: "ScrollLock",
6592  173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
6593  221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete",
6594  63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert"
6595}
6596
6597// Number keys
6598for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i) }
6599// Alphabetic keys
6600for (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1) }
6601// Function keys
6602for (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235] = "F" + i$2 }
6603
6604var keyMap = {}
6605
6606keyMap.basic = {
6607  "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
6608  "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
6609  "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore",
6610  "Tab": "defaultTab", "Shift-Tab": "indentAuto",
6611  "Enter": "newlineAndIndent", "Insert": "toggleOverwrite",
6612  "Esc": "singleSelection"
6613}
6614// Note that the save and find-related commands aren't defined by
6615// default. User code or addons can define them. Unknown commands
6616// are simply ignored.
6617keyMap.pcDefault = {
6618  "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
6619  "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown",
6620  "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
6621  "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
6622  "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
6623  "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
6624  "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection",
6625  fallthrough: "basic"
6626}
6627// Very basic readline/emacs-style bindings, which are standard on Mac.
6628keyMap.emacsy = {
6629  "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
6630  "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
6631  "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
6632  "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars",
6633  "Ctrl-O": "openLine"
6634}
6635keyMap.macDefault = {
6636  "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
6637  "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
6638  "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore",
6639  "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
6640  "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
6641  "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight",
6642  "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd",
6643  fallthrough: ["basic", "emacsy"]
6644}
6645keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault
6646
6647// KEYMAP DISPATCH
6648
6649function normalizeKeyName(name) {
6650  var parts = name.split(/-(?!$)/)
6651  name = parts[parts.length - 1]
6652  var alt, ctrl, shift, cmd
6653  for (var i = 0; i < parts.length - 1; i++) {
6654    var mod = parts[i]
6655    if (/^(cmd|meta|m)$/i.test(mod)) { cmd = true }
6656    else if (/^a(lt)?$/i.test(mod)) { alt = true }
6657    else if (/^(c|ctrl|control)$/i.test(mod)) { ctrl = true }
6658    else if (/^s(hift)?$/i.test(mod)) { shift = true }
6659    else { throw new Error("Unrecognized modifier name: " + mod) }
6660  }
6661  if (alt) { name = "Alt-" + name }
6662  if (ctrl) { name = "Ctrl-" + name }
6663  if (cmd) { name = "Cmd-" + name }
6664  if (shift) { name = "Shift-" + name }
6665  return name
6666}
6667
6668// This is a kludge to keep keymaps mostly working as raw objects
6669// (backwards compatibility) while at the same time support features
6670// like normalization and multi-stroke key bindings. It compiles a
6671// new normalized keymap, and then updates the old object to reflect
6672// this.
6673function normalizeKeyMap(keymap) {
6674  var copy = {}
6675  for (var keyname in keymap) { if (keymap.hasOwnProperty(keyname)) {
6676    var value = keymap[keyname]
6677    if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) { continue }
6678    if (value == "...") { delete keymap[keyname]; continue }
6679
6680    var keys = map(keyname.split(" "), normalizeKeyName)
6681    for (var i = 0; i < keys.length; i++) {
6682      var val = (void 0), name = (void 0)
6683      if (i == keys.length - 1) {
6684        name = keys.join(" ")
6685        val = value
6686      } else {
6687        name = keys.slice(0, i + 1).join(" ")
6688        val = "..."
6689      }
6690      var prev = copy[name]
6691      if (!prev) { copy[name] = val }
6692      else if (prev != val) { throw new Error("Inconsistent bindings for " + name) }
6693    }
6694    delete keymap[keyname]
6695  } }
6696  for (var prop in copy) { keymap[prop] = copy[prop] }
6697  return keymap
6698}
6699
6700function lookupKey(key, map, handle, context) {
6701  map = getKeyMap(map)
6702  var found = map.call ? map.call(key, context) : map[key]
6703  if (found === false) { return "nothing" }
6704  if (found === "...") { return "multi" }
6705  if (found != null && handle(found)) { return "handled" }
6706
6707  if (map.fallthrough) {
6708    if (Object.prototype.toString.call(map.fallthrough) != "[object Array]")
6709      { return lookupKey(key, map.fallthrough, handle, context) }
6710    for (var i = 0; i < map.fallthrough.length; i++) {
6711      var result = lookupKey(key, map.fallthrough[i], handle, context)
6712      if (result) { return result }
6713    }
6714  }
6715}
6716
6717// Modifier key presses don't count as 'real' key presses for the
6718// purpose of keymap fallthrough.
6719function isModifierKey(value) {
6720  var name = typeof value == "string" ? value : keyNames[value.keyCode]
6721  return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod"
6722}
6723
6724function addModifierNames(name, event, noShift) {
6725  var base = name
6726  if (event.altKey && base != "Alt") { name = "Alt-" + name }
6727  if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") { name = "Ctrl-" + name }
6728  if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") { name = "Cmd-" + name }
6729  if (!noShift && event.shiftKey && base != "Shift") { name = "Shift-" + name }
6730  return name
6731}
6732
6733// Look up the name of a key as indicated by an event object.
6734function keyName(event, noShift) {
6735  if (presto && event.keyCode == 34 && event["char"]) { return false }
6736  var name = keyNames[event.keyCode]
6737  if (name == null || event.altGraphKey) { return false }
6738  // Ctrl-ScrollLock has keyCode 3, same as Ctrl-Pause,
6739  // so we'll use event.code when available (Chrome 48+, FF 38+, Safari 10.1+)
6740  if (event.keyCode == 3 && event.code) { name = event.code }
6741  return addModifierNames(name, event, noShift)
6742}
6743
6744function getKeyMap(val) {
6745  return typeof val == "string" ? keyMap[val] : val
6746}
6747
6748// Helper for deleting text near the selection(s), used to implement
6749// backspace, delete, and similar functionality.
6750function deleteNearSelection(cm, compute) {
6751  var ranges = cm.doc.sel.ranges, kill = []
6752  // Build up a set of ranges to kill first, merging overlapping
6753  // ranges.
6754  for (var i = 0; i < ranges.length; i++) {
6755    var toKill = compute(ranges[i])
6756    while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {
6757      var replaced = kill.pop()
6758      if (cmp(replaced.from, toKill.from) < 0) {
6759        toKill.from = replaced.from
6760        break
6761      }
6762    }
6763    kill.push(toKill)
6764  }
6765  // Next, remove those actual ranges.
6766  runInOp(cm, function () {
6767    for (var i = kill.length - 1; i >= 0; i--)
6768      { replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete") }
6769    ensureCursorVisible(cm)
6770  })
6771}
6772
6773function moveCharLogically(line, ch, dir) {
6774  var target = skipExtendingChars(line.text, ch + dir, dir)
6775  return target < 0 || target > line.text.length ? null : target
6776}
6777
6778function moveLogically(line, start, dir) {
6779  var ch = moveCharLogically(line, start.ch, dir)
6780  return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before")
6781}
6782
6783function endOfLine(visually, cm, lineObj, lineNo, dir) {
6784  if (visually) {
6785    var order = getOrder(lineObj, cm.doc.direction)
6786    if (order) {
6787      var part = dir < 0 ? lst(order) : order[0]
6788      var moveInStorageOrder = (dir < 0) == (part.level == 1)
6789      var sticky = moveInStorageOrder ? "after" : "before"
6790      var ch
6791      // With a wrapped rtl chunk (possibly spanning multiple bidi parts),
6792      // it could be that the last bidi part is not on the last visual line,
6793      // since visual lines contain content order-consecutive chunks.
6794      // Thus, in rtl, we are looking for the first (content-order) character
6795      // in the rtl chunk that is on the last line (that is, the same line
6796      // as the last (content-order) character).
6797      if (part.level > 0 || cm.doc.direction == "rtl") {
6798        var prep = prepareMeasureForLine(cm, lineObj)
6799        ch = dir < 0 ? lineObj.text.length - 1 : 0
6800        var targetTop = measureCharPrepared(cm, prep, ch).top
6801        ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch)
6802        if (sticky == "before") { ch = moveCharLogically(lineObj, ch, 1) }
6803      } else { ch = dir < 0 ? part.to : part.from }
6804      return new Pos(lineNo, ch, sticky)
6805    }
6806  }
6807  return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after")
6808}
6809
6810function moveVisually(cm, line, start, dir) {
6811  var bidi = getOrder(line, cm.doc.direction)
6812  if (!bidi) { return moveLogically(line, start, dir) }
6813  if (start.ch >= line.text.length) {
6814    start.ch = line.text.length
6815    start.sticky = "before"
6816  } else if (start.ch <= 0) {
6817    start.ch = 0
6818    start.sticky = "after"
6819  }
6820  var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos]
6821  if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) {
6822    // Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines,
6823    // nothing interesting happens.
6824    return moveLogically(line, start, dir)
6825  }
6826
6827  var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); }
6828  var prep
6829  var getWrappedLineExtent = function (ch) {
6830    if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} }
6831    prep = prep || prepareMeasureForLine(cm, line)
6832    return wrappedLineExtentChar(cm, line, prep, ch)
6833  }
6834  var wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch)
6835
6836  if (cm.doc.direction == "rtl" || part.level == 1) {
6837    var moveInStorageOrder = (part.level == 1) == (dir < 0)
6838    var ch = mv(start, moveInStorageOrder ? 1 : -1)
6839    if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) {
6840      // Case 2: We move within an rtl part or in an rtl editor on the same visual line
6841      var sticky = moveInStorageOrder ? "before" : "after"
6842      return new Pos(start.line, ch, sticky)
6843    }
6844  }
6845
6846  // Case 3: Could not move within this bidi part in this visual line, so leave
6847  // the current bidi part
6848
6849  var searchInVisualLine = function (partPos, dir, wrappedLineExtent) {
6850    var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder
6851      ? new Pos(start.line, mv(ch, 1), "before")
6852      : new Pos(start.line, ch, "after"); }
6853
6854    for (; partPos >= 0 && partPos < bidi.length; partPos += dir) {
6855      var part = bidi[partPos]
6856      var moveInStorageOrder = (dir > 0) == (part.level != 1)
6857      var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1)
6858      if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) }
6859      ch = moveInStorageOrder ? part.from : mv(part.to, -1)
6860      if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) }
6861    }
6862  }
6863
6864  // Case 3a: Look for other bidi parts on the same visual line
6865  var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent)
6866  if (res) { return res }
6867
6868  // Case 3b: Look for other bidi parts on the next visual line
6869  var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1)
6870  if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) {
6871    res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh))
6872    if (res) { return res }
6873  }
6874
6875  // Case 4: Nowhere to move
6876  return null
6877}
6878
6879// Commands are parameter-less actions that can be performed on an
6880// editor, mostly used for keybindings.
6881var commands = {
6882  selectAll: selectAll,
6883  singleSelection: function (cm) { return cm.setSelection(cm.getCursor("anch
6883or"), cm.getCursor("head"), sel_dontScroll); },
6884  killLine: function (cm) { return deleteNearSelection(cm, function (range) {
6885    if (range.empty()) {
6886      var len = getLine(cm.doc, range.head.line).text.length
6887      if (range.head.ch == len && range.head.line < cm.lastLine())
6888        { return {from: range.head, to: Pos(range.head.line + 1, 0)} }
6889      else
6890        { return {from: range.head, to: Pos(range.head.line, len)} }
6891    } else {
6892      return {from: range.from(), to: range.to()}
6893    }
6894  }); },
6895  deleteLine: function (cm) { return deleteNearSelection(cm, function (range) { return ({
6896    from: Pos(range.from().line, 0),
6897    to: clipPos(cm.doc, Pos(range.to().line + 1, 0))
6898  }); }); },
6899  delLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { return ({
6900    from: Pos(range.from().line, 0), to: range.from()
6901  }); }); },
6902  delWrappedLineLeft: function (cm) { return deleteNearSelection(cm, function (range) {
6903    var top = cm.charCoords(range.head, "div").top + 5
6904    var leftPos = cm.coordsChar({left: 0, top: top}, "div")
6905    return {from: leftPos, to: range.from()}
6906  }); },
6907  delWrappedLineRight: function (cm) { return deleteNearSelection(cm, function (range) {
6908    var top = cm.charCoords(range.head, "div").top + 5
6909    var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div")
6910    return {from: range.from(), to: rightPos }
6911  }); },
6912  undo: function (cm) { return cm.undo(); },
6913  redo: function (cm) { return cm.redo(); },
6914  undoSelection: function (cm) { return cm.undoSelection(); },
6915  redoSelection: function (cm) { return cm.redoSelection(); },
6916  goDocStart: function (cm) { return cm.extendSelection(Pos(cm.firstLine(), 0)); },
6917  goDocEnd: function (cm) { return cm.extendSelection(Pos(cm.lastLine())); },
6918  goLineStart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStart(cm, range.head.line); },
6919    {origin: "+move", bias: 1}
6920  ); },
6921  goLineStartSmart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStartSmart(cm, range.head); },
6922    {origin: "+move", bias: 1}
6923  ); },
6924  goLineEnd: function (cm) { return cm.extendSelectionsBy(function (range) { return lineEnd(cm, range.head.line); },
6925    {origin: "+move", bias: -1}
6926  ); },
6927  goLineRight: function (cm) { return cm.extendSelectionsBy(function (range) {
6928    var top = cm.cursorCoords(range.head, "div").top + 5
6929    return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div")
6930  }, sel_move); },
6931  goLineLeft: function (cm) { return cm.extendSelectionsBy(function (range) {
6932    var top = cm.cursorCoords(range.head, "div").top + 5
6933    return cm.coordsChar({left: 0, top: top}, "div")
6934  }, sel_move); },
6935  goLineLeftSmart: function (cm) { return cm.extendSelectionsBy(function (range) {
6936    var top = cm.cursorCoords(range.head, "div").top + 5
6937    var pos = cm.coordsChar({left: 0, top: top}, "div")
6938    if (pos.ch < cm.getLine(pos.line).search(/\S/)) { return lineStartSmart(cm, range.head) }
6939    return pos
6940  }, sel_move); },
6941  goLineUp: function (cm) { return cm.moveV(-1, "line"); },
6942  goLineDown: function (cm) { return cm.moveV(1, "line"); },
6943  goPageUp: function (cm) { return cm.moveV(-1, "page"); },
6944  goPageDown: function (cm) { return cm.moveV(1, "page"); },
6945  goCharLeft: function (cm) { return cm.moveH(-1, "char"); },
6946  goCharRight: function (cm) { return cm.moveH(1, "char"); },
6947  goColumnLeft: function (cm) { return cm.moveH(-1, "column"); },
6948  goColumnRight: function (cm) { return cm.moveH(1, "column"); },
6949  goWordLeft: function (cm) { return cm.moveH(-1, "word"); },
6950  goGroupRight: function (cm) { return cm.moveH(1, "group"); },
6951  goGroupLeft: function (cm) { return cm.moveH(-1, "group"); },
6952  goWordRight: function (cm) { return cm.moveH(1, "word"); },
6953  delCharBefore: function (cm) { return cm.deleteH(-1, "char"); },
6954  delCharAfter: function (cm) { return cm.deleteH(1, "char"); },
6955  delWordBefore: function (cm) { return cm.deleteH(-1, "word"); },
6956  delWordAfter: function (cm) { return cm.deleteH(1, "word"); },
6957  delGroupBefore: function (cm) { return cm.deleteH(-1, "group"); },
6958  delGroupAfter: function (cm) { return cm.deleteH(1, "group"); },
6959  indentAuto: function (cm) { return cm.indentSelection("smart"); },
6960  indentMore: function (cm) { return cm.indentSelection("add"); },
6961  indentLess: function (cm) { return cm.indentSelection("subtract"); },
6962  insertTab: function (cm) { return cm.replaceSelection("\t"); },
6963  insertSoftTab: function (cm) {
6964    var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize
6965    for (var i = 0; i < ranges.length; i++) {
6966      var pos = ranges[i].from()
6967      var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize)
6968      spaces.push(spaceStr(tabSize - col % tabSize))
6969    }
6970    cm.replaceSelections(spaces)
6971  },
6972  defaultTab: function (cm) {
6973    if (cm.somethingSelected()) { cm.indentSelection("add") }
6974    else { cm.execCommand("insertTab") }
6975  },
6976  // Swap the two chars left and right of each selection's head.
6977  // Move cursor behind the two swapped characters afterwards.
6978  //
6979  // Doesn't consider line feeds a character.
6980  // Doesn't scan more than one line above to find a character.
6981  // Doesn't do anything on an empty line.
6982  // Doesn't do anything with non-empty selections.
6983  transposeChars: function (cm) { return runInOp(cm, function () {
6984    var ranges = cm.listSelections(), newSel = []
6985    for (var i = 0; i < ranges.length; i++) {
6986      if (!ranges[i].empty()) { continue }
6987      var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text
6988      if (line) {
6989        if (cur.ch == line.length) { cur = new Pos(cur.line, cur.ch - 1) }
6990        if (cur.ch > 0) {
6991          cur = new Pos(cur.line, cur.ch + 1)
6992          cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),
6993                          Pos(cur.line, cur.ch - 2), cur, "+transpose")
6994        } else if (cur.line > cm.doc.first) {
6995          var prev = getLine(cm.doc, cur.line - 1).text
6996          if (prev) {
6997            cur = new Pos(cur.line, 1)
6998            cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() +
6999                            prev.charAt(prev.length - 1),
7000                            Pos(cur.line - 1, prev.length - 1), cur, "+transpose")
7001          }
7002        }
7003      }
7004      newSel.push(new Range(cur, cur))
7005    }
7006    cm.setSelections(newSel)
7007  }); },
7008  newlineAndIndent: function (cm) { return runInOp(cm, function () {
7009    var sels = cm.listSelections()
7010    for (var i = sels.length - 1; i >= 0; i--)
7011      { cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input") }
7012    sels = cm.listSelections()
7013    for (var i$1 = 0; i$1 < sels.length; i$1++)
7014      { cm.indentLine(sels[i$1].from().line, null, true) }
7015    ensureCursorVisible(cm)
7016  }); },
7017  openLine: function (cm) { return cm.replaceSelection("\n", "start"); },
7018  toggleOverwrite: function (cm) { return cm.toggleOverwrite(); }
7019}
7020
7021
7022function lineStart(cm, lineN) {
7023  var line = getLine(cm.doc, lineN)
7024  var visual = visualLine(line)
7025  if (visual != line) { lineN = lineNo(visual) }
7026  return endOfLine(true, cm, visual, lineN, 1)
7027}
7028function lineEnd(cm, lineN) {
7029  var line = getLine(cm.doc, lineN)
7030  var visual = visualLineEnd(line)
7031  if (visual != line) { lineN = lineNo(visual) }
7032  return endOfLine(true, cm, line, lineN, -1)
7033}
7034function lineStartSmart(cm, pos) {
7035  var start = lineStart(cm, pos.line)
7036  var line = getLine(cm.doc, start.line)
7037  var order = getOrder(line, cm.doc.direction)
7038  if (!order || order[0].level == 0) {
7039    var firstNonWS = Math.max(0, line.text.search(/\S/))
7040    var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch
7041    return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky)
7042  }
7043  return start
7044}
7045
7046// Run a handler that was bound to a key.
7047function doHandleBinding(cm, bound, dropShift) {
7048  if (typeof bound == "string") {
7049    bound = commands[bound]
7050    if (!bound) { return false }
7051  }
7052  // Ensure previous input has been read, so that the handler sees a
7053  // consistent view of the document
7054  cm.display.input.ensurePolled()
7055  var prevShift = cm.display.shift, done = false
7056  try {
7057    if (cm.isReadOnly()) { cm.state.suppressEdits = true }
7058    if (dropShift) { cm.display.shift = false }
7059    done = bound(cm) != Pass
7060  } finally {
7061    cm.display.shift = prevShift
7062    cm.state.suppressEdits = false
7063  }
7064  return done
7065}
7066
7067function lookupKeyForEditor(cm, name, handle) {
7068  for (var i = 0; i < cm.state.keyMaps.length; i++) {
7069    var result = lookupKey(name, cm.state.keyMaps[i], handle, cm)
7070    if (result) { return result }
7071  }
7072  return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm))
7073    || lookupKey(name, cm.options.keyMap, handle, cm)
7074}
7075
7076// Note that, despite the name, this function is also used to check
7077// for bound mouse clicks.
7078
7079var stopSeq = new Delayed
7080
7081function dispatchKey(cm, name, e, handle) {
7082  var seq = cm.state.keySeq
7083  if (seq) {
7084    if (isModifierKey(name)) { return "handled" }
7085    if (/\'$/.test(name))
7086      { cm.state.keySeq = null }
7087    else
7088      { stopSeq.set(50, function () {
7089        if (cm.state.keySeq == seq) {
7090          cm.state.keySeq = null
7091          cm.display.input.reset()
7092        }
7093      }) }
7094    if (dispatchKeyInner(cm, seq + " " + name, e, handle)) { return true }
7095  }
7096  return dispatchKeyInner(cm, name, e, handle)
7097}
7098
7099function dispatchKeyInner(cm, name, e, handle) {
7100  var result = lookupKeyForEditor(cm, name, handle)
7101
7102  if (result == "multi")
7103    { cm.state.keySeq = name }
7104  if (result == "handled")
7105    { signalLater(cm, "keyHandled", cm, name, e) }
7106
7107  if (result == "handled" || result == "multi") {
7108    e_preventDefault(e)
7109    restartBlink(cm)
7110  }
7111
7112  return !!result
7113}
7114
7115// Handle a key from the keydown event.
7116function handleKeyBinding(cm, e) {
7117  var name = keyName(e, true)
7118  if (!name) { return false }
7119
7120  if (e.shiftKey && !cm.state.keySeq) {
7121    // First try to resolve full name (including 'Shift-'). Failing
7122    // that, see if there is a cursor-motion command (starting with
7123    // 'go') bound to the keyname without 'Shift-'.
7124    return dispatchKey(cm, "Shift-" + name, e, function (b) { return doHandleBinding(cm, b, true); })
7125        || dispatchKey(cm, name, e, function (b) {
7126             if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
7127               { return doHandleBinding(cm, b) }
7128           })
7129  } else {
7130    return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b); })
7131  }
7132}
7133
7134// Handle a key from the keypress event
7135function handleCharBinding(cm, e, ch) {
7136  return dispatchKey(cm, "'" + ch + "'", e, function (b) { return doHandleBinding(cm, b, true); })
7137}
7138
7139var lastStoppedKey = null
7140function onKeyDown(e) {
7141  var cm = this
7142  cm.curOp.focus = activeElt()
7143  if (signalDOMEvent(cm, e)) { return }
7144  // IE does strange things with escape.
7145  if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false }
7146  var code = e.keyCode
7147  cm.display.shift = code == 16 || e.shiftKey
7148  var handled = handleKeyBinding(cm, e)
7149  if (presto) {
7150    lastStoppedKey = handled ? code : null
7151    // Opera has no cut event... we try to at least catch the key combo
7152    if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
7153      { cm.replaceSelection("", null, "cut") }
7154  }
7155
7156  // Turn mouse into crosshair when Alt is held on Mac.
7157  if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className))
7158    { showCrossHair(cm) }
7159}
7160
7161function showCrossHair(cm) {
7162  var lineDiv = cm.display.lineDiv
7163  addClass(lineDiv, "CodeMirror-crosshair")
7164
7165  function up(e) {
7166    if (e.keyCode == 18 || !e.altKey) {
7167      rmClass(lineDiv, "CodeMirror-crosshair")
7168      off(document, "keyup", up)
7169      off(document, "mouseover", up)
7170    }
7171  }
7172  on(document, "keyup", up)
7173  on(document, "mouseover", up)
7174}
7175
7176function onKeyUp(e) {
7177  if (e.keyCode == 16) { this.doc.sel.shift = false }
7178  signalDOMEvent(this, e)
7179}
7180
7181function onKeyPress(e) {
7182  var cm = this
7183  if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return }
7184  var keyCode = e.keyCode, charCode = e.charCode
7185  if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return}
7186  if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) { return }
7187  var ch = String.fromCharCode(charCode == null ? keyCode : charCode)
7188  // Some browsers fire keypress events for backspace
7189  if (ch == "\x08") { return }
7190  if (handleCharBinding(cm, e, ch)) { return }
7191  cm.display.input.onKeyPress(e)
7192}
7193
7194var DOUBLECLICK_DELAY = 400
7195
7196var PastClick = function(time, pos, button) {
7197  this.time = time
7198  this.pos = pos
7199  this.button = button
7200};
7201
7202PastClick.prototype.compare = function (time, pos, button) {
7203  return this.time + DOUBLECLICK_DELAY > time &&
7204    cmp(pos, this.pos) == 0 && button == this.button
7205};
7206
7207var lastClick;
7208var lastDoubleClick;
7209function clickRepeat(pos, button) {
7210  var now = +new Date
7211  if (lastDoubleClick && lastDoubleClick.compare(now, pos, button)) {
7212    lastClick = lastDoubleClick = null
7213    return "triple"
7214  } else if (lastClick && lastClick.compare(now, pos, button)) {
7215    lastDoubleClick = new PastClick(now, pos, button)
7216    lastClick = null
7217    return "double"
7218  } else {
7219    lastClick = new PastClick(now, pos, button)
7220    lastDoubleClick = null
7221    return "single"
7222  }
7223}
7224
7225// A mouse down can be a single click, double click, triple click,
7226// start of selection drag, start of text drag, new cursor
7227// (ctrl-click), rectangle drag (alt-drag), or xwin
7228// middle-click-paste. Or it might be a click on something we should
7229// not interfere with, such as a scrollbar or widget.
7230function onMouseDown(e) {
7231  var cm = this, display = cm.display
7232  if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) { return }
7233  display.input.ensurePolled()
7234  display.shift = e.shiftKey
7235
7236  if (eventInWidget(display, e)) {
7237    if (!webkit) {
7238      // Briefly turn off draggability, to allow widgets to do
7239      // normal dragging things.
7240      display.scroller.draggable = false
7241      setTimeout(function () { return display.scroller.draggable = true; }, 100)
7242    }
7243    return
7244  }
7245  if (clickInGutter(cm, e)) { return }
7246  var pos = posFromMouse(cm, e), button = e_button(e), repeat = pos ? clickRepeat(pos, button) : "single"
7247  window.focus()
7248
7249  // #3261: make sure, that we're not starting a second selection
7250  if (button == 1 && cm.state.selectingText)
7251    { cm.state.selectingText(e) }
7252
7253  if (pos && handleMappedButton(cm, button, pos, repeat, e)) { return }
7254
7255  if (button == 1) {
7256    if (pos) { leftButtonDown(cm, pos, repeat, e) }
7257    else if (e_target(e) == display.scroller) { e_preventDefault(e) }
7258  } else if (button == 2) {
7259    if (pos) { extendSelection(cm.doc, pos) }
7260    setTimeout(function () { return display.input.focus(); }, 20)
7261  } else if (button == 3) {
7262    if (captureRightClick) { onContextMenu(cm, e) }
7263    else { delayBlurEvent(cm) }
7264  }
7265}
7266
7267function handleMappedButton(cm, button, pos, repeat, event) {
7268  var name = "Click"
7269  if (repeat == "double") { name = "Double" + name }
7270  else if (repeat == "triple") { name = "Triple" + name }
7271  name = (button == 1 ? "Left" : button == 2 ? "Middle" : "Right") + name
7272
7273  return dispatchKey(cm,  addModifierNames(name, event), event, function (bound) {
7274    if (typeof bound == "string") { bound = commands[bound] }
7275    if (!bound) { return false }
7276    var done = false
7277    try {
7278      if (cm.isReadOnly()) { cm.state.suppressEdits = true }
7279      done = bound(cm, pos) != Pass
7280    } finally {
7281      cm.state.suppressEdits = false
7282    }
7283    return done
7284  })
7285}
7286
7287function configureMouse(cm, repeat, event) {
7288  var option = cm.getOption("configureMouse")
7289  var value = option ? option(cm, repeat, event) : {}
7290  if (value.unit == null) {
7291    var rect = chromeOS ? event.shiftKey && event.metaKey : event.altKey
7292    value.unit = rect ? "rectangle" : repeat == "single" ? "char" : repeat == "double" ? "word" : "line"
7293  }
7294  if (value.extend == null || cm.doc.extend) { value.extend = cm.doc.extend || event.shiftKey }
7295  if (value.addNew == null) { value.addNew = mac ? event.metaKey : event.ctrlKey }
7296  if (value.moveOnDrag == null) { value.moveOnDrag = !(mac ? event.altKey : event.ctrlKey) }
7297  return value
7298}
7299
7300function leftButtonDown(cm, pos, repeat, event) {
7301  if (ie) { setTimeout(bind(ensureFocus, cm), 0) }
7302  else { cm.curOp.focus = activeElt() }
7303
7304  var behavior = configureMouse(cm, repeat, event)
7305
7306  var sel = cm.doc.sel, contained
7307  if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() &&
7308      repeat == "single" && (contained = sel.contains(pos)) > -1 &&
7309      (cmp((contained = sel.ranges[contained]).from(), pos) < 0 || pos.xRel > 0) &&
7310      (cmp(contained.to(), pos) > 0 || pos.xRel < 0))
7311    { leftButtonStartDrag(cm, event, pos, behavior) }
7312  else
7313    { leftButtonSelect(cm, event, pos, behavior) }
7314}
7315
7316// Start a text drag. When it ends, see if any dragging actually
7317// happen, and treat as a click if it didn't.
7318function leftButtonStartDrag(cm, event, pos, behavior) {
7319  var display = cm.display, moved = false
7320  var dragEnd = operation(cm, function (e) {
7321    if (webkit) { display.scroller.draggable = false }
7322    cm.state.draggingText = false
7323    off(display.wrapper.ownerDocument, "mouseup", dragEnd)
7324    off(display.wrapper.ownerDocument, "mousemove", mouseMove)
7325    off(display.scroller, "dragstart", dragStart)
7326    off(display.scroller, "drop", dragEnd)
7327    if (!moved) {
7328      e_preventDefault(e)
7329      if (!behavior.addNew)
7330        { extendSelection(cm.doc, pos, null, null, behavior.extend) }
7331      // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)
7332      if (webkit || ie && ie_version == 9)
7333        { setTimeout(function () {display.wrapper.ownerDocument.body.focus(); display.input.focus()}, 20) }
7334      else
7335        { display.input.focus() }
7336    }
7337  })
7338  var mouseMove = function(e2) {
7339    moved = moved || Math.abs(event.clientX - e2.clientX) + Math.abs(event.clientY - e2.clientY) >= 10
7340  }
7341  var dragStart = function () { return moved = true; }
7342  // Let the drag handler handle this.
7343  if (webkit) { display.scroller.draggable = true }
7344  cm.state.draggingText = dragEnd
7345  dragEnd.copy = !behavior.moveOnDrag
7346  // IE's approach to draggable
7347  if (display.scroller.dragDrop) { display.scroller.dragDrop() }
7348  on(display.wrapper.ownerDocument, "mouseup", dragEnd)
7349  on(display.wrapper.ownerDocument, "mousemove", mouseMove)
7350  on(display.scroller, "dragstart", dragStart)
7351  on(display.scroller, "drop", dragEnd)
7352
7353  delayBlurEvent(cm)
7354  setTimeout(function () { return display.input.focus(); }, 20)
7355}
7356
7357function rangeForUnit(cm, pos, unit) {
7358  if (unit == "char") { return new Range(pos, pos) }
7359  if (unit == "word") { return cm.findWordAt(pos) }
7360  if (unit == "line") { return new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) }
7361  var result = unit(cm, pos)
7362  return new Range(result.from, result.to)
7363}
7364
7365// Normal selection, as opposed to text dragging.
7366function leftButtonSelect(cm, event, start, behavior) {
7367  var display = cm.display, doc = cm.doc
7368  e_preventDefault(event)
7369
7370  var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges
7371  if (behavior.addNew && !behavior.extend) {
7372    ourIndex = doc.sel.contains(start)
7373    if (ourIndex > -1)
7374      { ourRange = ranges[ourIndex] }
7375    else
7376      { ourRange = new Range(start, start) }
7377  } else {
7378    ourRange = doc.sel.primary()
7379    ourIndex = doc.sel.primIndex
7380  }
7381
7382  if (behavior.unit == "rectangle") {
7383    if (!behavior.addNew) { ourRange = new Range(start, start) }
7384    start = posFromMouse(cm, event, true, true)
7385    ourIndex = -1
7386  } else {
7387    var range = rangeForUnit(cm, start, behavior.unit)
7388    if (behavior.extend)
7389      { ourRange = extendRange(ourRange, range.anchor, range.head, behavior.extend) }
7390    else
7391      { ourRange = range }
7392  }
7393
7394  if (!behavior.addNew) {
7395    ourIndex = 0
7396    setSelection(doc, new Selection([ourRange], 0), sel_mouse)
7397    startSel = doc.sel
7398  } else if (ourIndex == -1) {
7399    ourIndex = ranges.length
7400    setSelection(doc, normalizeSelection(ranges.concat([ourRange]), ourIndex),
7401                 {scroll: false, origin: "*mouse"})
7402  } else if (ranges.length > 1 && ranges[ourIndex].empty() && behavior.unit == "char" && !behavior.extend) {
7403    setSelection(doc, normalizeSelection(ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0),
7404                 {scroll: false, origin: "*mouse"})
7405    startSel = doc.sel
7406  } else {
7407    replaceOneSelection(doc, ourIndex, ourRange, sel_mouse)
7408  }
7409
7410  var lastPos = start
7411  function extendTo(pos) {
7412    if (cmp(lastPos, pos) == 0) { return }
7413    lastPos = pos
7414
7415    if (behavior.unit == "rectangle") {
7416      var ranges = [], tabSize = cm.options.tabSize
7417      var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize)
7418      var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize)
7419      var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol)
7420      for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line));
7421           line <= end; line++) {
7422        var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize)
7423        if (left == right)
7424          { ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))) }
7425        else if (text.length > leftPos)
7426          { ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))) }
7427      }
7428      if (!ranges.length) { ranges.push(new Range(start, start)) }
7429      setSelection(doc, normalizeSelection(start
7429Sel.ranges.slice(0, ourIndex).concat(ranges), ourIndex),
7430                   {origin: "*mouse", scroll: false})
7431      cm.scrollIntoView(pos)
7432    } else {
7433      var oldRange = ourRange
7434      var range = rangeForUnit(cm, pos, behavior.unit)
7435      var anchor = oldRange.anchor, head
7436      if (cmp(range.anchor, anchor) > 0) {
7437        head = range.head
7438        anchor = minPos(oldRange.from(), range.anchor)
7439      } else {
7440        head = range.anchor
7441        anchor = maxPos(oldRange.to(), range.head)
7442      }
7443      var ranges$1 = startSel.ranges.slice(0)
7444      ranges$1[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc, anchor), head))
7445      setSelection(doc, normalizeSelection(ranges$1, ourIndex), sel_mouse)
7446    }
7447  }
7448
7449  var editorSize = display.wrapper.getBoundingClientRect()
7450  // Used to ensure timeout re-tries don't fire when another extend
7451  // happened in the meantime (clearTimeout isn't reliable -- at
7452  // least on Chrome, the timeouts still happen even when cleared,
7453  // if the clear happens after their scheduled firing time).
7454  var counter = 0
7455
7456  function extend(e) {
7457    var curCount = ++counter
7458    var cur = posFromMouse(cm, e, true, behavior.unit == "rectangle")
7459    if (!cur) { return }
7460    if (cmp(cur, lastPos) != 0) {
7461      cm.curOp.focus = activeElt()
7462      extendTo(cur)
7463      var visible = visibleLines(display, doc)
7464      if (cur.line >= visible.to || cur.line < visible.from)
7465        { setTimeout(operation(cm, function () {if (counter == curCount) { extend(e) }}), 150) }
7466    } else {
7467      var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0
7468      if (outside) { setTimeout(operation(cm, function () {
7469        if (counter != curCount) { return }
7470        display.scroller.scrollTop += outside
7471        extend(e)
7472      }), 50) }
7473    }
7474  }
7475
7476  function done(e) {
7477    cm.state.selectingText = false
7478    counter = Infinity
7479    e_preventDefault(e)
7480    display.input.focus()
7481    off(display.wrapper.ownerDocument, "mousemove", move)
7482    off(display.wrapper.ownerDocument, "mouseup", up)
7483    doc.history.lastSelOrigin = null
7484  }
7485
7486  var move = operation(cm, function (e) {
7487    if (e.buttons === 0 || !e_button(e)) { done(e) }
7488    else { extend(e) }
7489  })
7490  var up = operation(cm, done)
7491  cm.state.selectingText = up
7492  on(display.wrapper.ownerDocument, "mousemove", move)
7493  on(display.wrapper.ownerDocument, "mouseup", up)
7494}
7495
7496// Used when mouse-selecting to adjust the anchor to the proper side
7497// of a bidi jump depending on the visual position of the head.
7498function bidiSimplify(cm, range) {
7499  var anchor = range.anchor;
7500  var head = range.head;
7501  var anchorLine = getLine(cm.doc, anchor.line)
7502  if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) { return range }
7503  var order = getOrder(anchorLine)
7504  if (!order) { return range }
7505  var index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index]
7506  if (part.from != anchor.ch && part.to != anchor.ch) { return range }
7507  var boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1)
7508  if (boundary == 0 || boundary == order.length) { return range }
7509
7510  // Compute the relative visual position of the head compared to the
7511  // anchor (<0 is to the left, >0 to the right)
7512  var leftSide
7513  if (head.line != anchor.line) {
7514    leftSide = (head.line - anchor.line) * (cm.doc.direction == "ltr" ? 1 : -1) > 0
7515  } else {
7516    var headIndex = getBidiPartAt(order, head.ch, head.sticky)
7517    var dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1)
7518    if (headIndex == boundary - 1 || headIndex == boundary)
7519      { leftSide = dir < 0 }
7520    else
7521      { leftSide = dir > 0 }
7522  }
7523
7524  var usePart = order[boundary + (leftSide ? -1 : 0)]
7525  var from = leftSide == (usePart.level == 1)
7526  var ch = from ? usePart.from : usePart.to, sticky = from ? "after" : "before"
7527  return anchor.ch == ch && anchor.sticky == sticky ? range : new Range(new Pos(anchor.line, ch, sticky), head)
7528}
7529
7530
7531// Determines whether an event happened in the gutter, and fires the
7532// handlers for the corresponding event.
7533function gutterEvent(cm, e, type, prevent) {
7534  var mX, mY
7535  if (e.touches) {
7536    mX = e.touches[0].clientX
7537    mY = e.touches[0].clientY
7538  } else {
7539    try { mX = e.clientX; mY = e.clientY }
7540    catch(e) { return false }
7541  }
7542  if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false }
7543  if (prevent) { e_preventDefault(e) }
7544
7545  var display = cm.display
7546  var lineBox = display.lineDiv.getBoundingClientRect()
7547
7548  if (mY > lineBox.bottom || !hasHandler(cm, type)) { return e_defaultPrevented(e) }
7549  mY -= lineBox.top - display.viewOffset
7550
7551  for (var i = 0; i < cm.options.gutters.length; ++i) {
7552    var g = display.gutters.childNodes[i]
7553    if (g && g.getBoundingClientRect().right >= mX) {
7554      var line = lineAtHeight(cm.doc, mY)
7555      var gutter = cm.options.gutters[i]
7556      signal(cm, type, cm, line, gutter, e)
7557      return e_defaultPrevented(e)
7558    }
7559  }
7560}
7561
7562function clickInGutter(cm, e) {
7563  return gutterEvent(cm, e, "gutterClick", true)
7564}
7565
7566// CONTEXT MENU HANDLING
7567
7568// To make the context menu work, we need to briefly unhide the
7569// textarea (making it as unobtrusive as possible) to let the
7570// right-click take effect on it.
7571function onContextMenu(cm, e) {
7572  if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) { return }
7573  if (signalDOMEvent(cm, e, "contextmenu")) { return }
7574  cm.display.input.onContextMenu(e)
7575}
7576
7577function contextMenuInGutter(cm, e) {
7578  if (!hasHandler(cm, "gutterContextMenu")) { return false }
7579  return gutterEvent(cm, e, "gutterContextMenu", false)
7580}
7581
7582function themeChanged(cm) {
7583  cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
7584    cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-")
7585  clearCaches(cm)
7586}
7587
7588var Init = {toString: function(){return "CodeMirror.Init"}}
7589
7590var defaults = {}
7591var optionHandlers = {}
7592
7593function defineOptions(CodeMirror) {
7594  var optionHandlers = CodeMirror.optionHandlers
7595
7596  function option(name, deflt, handle, notOnInit) {
7597    CodeMirror.defaults[name] = deflt
7598    if (handle) { optionHandlers[name] =
7599      notOnInit ? function (cm, val, old) {if (old != Init) { handle(cm, val, old) }} : handle }
7600  }
7601
7602  CodeMirror.defineOption = option
7603
7604  // Passed to option handlers when there is no old value.
7605  CodeMirror.Init = Init
7606
7607  // These two are, on init, called from the constructor because they
7608  // have to be initialized before the editor can start at all.
7609  option("value", "", function (cm, val) { return cm.setValue(val); }, true)
7610  option("mode", null, function (cm, val) {
7611    cm.doc.modeOption = val
7612    loadMode(cm)
7613  }, true)
7614
7615  option("indentUnit", 2, loadMode, true)
7616  option("indentWithTabs", false)
7617  option("smartIndent", true)
7618  option("tabSize", 4, function (cm) {
7619    resetModeState(cm)
7620    clearCaches(cm)
7621    regChange(cm)
7622  }, true)
7623
7624  option("lineSeparator", null, function (cm, val) {
7625    cm.doc.lineSep = val
7626    if (!val) { return }
7627    var newBreaks = [], lineNo = cm.doc.first
7628    cm.doc.iter(function (line) {
7629      for (var pos = 0;;) {
7630        var found = line.text.indexOf(val, pos)
7631        if (found == -1) { break }
7632        pos = found + val.length
7633        newBreaks.push(Pos(lineNo, found))
7634      }
7635      lineNo++
7636    })
7637    for (var i = newBreaks.length - 1; i >= 0; i--)
7638      { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)) }
7639  })
7640  option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff]/g, function (cm, val, old) {
7641    cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g")
7642    if (old != Init) { cm.refresh() }
7643  })
7644  option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function (cm) { return cm.refresh(); }, true)
7645  option("electricChars", true)
7646  option("inputStyle", mobile ? "contenteditable" : "textarea", function () {
7647    throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME
7648  }, true)
7649  option("spellcheck", false, function (cm, val) { return cm.getInputField().spellcheck = val; }, true)
7650  option("rtlMoveVisually", !windows)
7651  option("wholeLineUpdateBefore", true)
7652
7653  option("theme", "default", function (cm) {
7654    themeChanged(cm)
7655    guttersChanged(cm)
7656  }, true)
7657  option("keyMap", "default", function (cm, val, old) {
7658    var next = getKeyMap(val)
7659    var prev = old != Init && getKeyMap(old)
7660    if (prev && prev.detach) { prev.detach(cm, next) }
7661    if (next.attach) { next.attach(cm, prev || null) }
7662  })
7663  option("extraKeys", null)
7664  option("configureMouse", null)
7665
7666  option("lineWrapping", false, wrappingChanged, true)
7667  option("gutters", [], function (cm) {
7668    setGuttersForLineNumbers(cm.options)
7669    guttersChanged(cm)
7670  }, true)
7671  option("fixedGutter", true, function (cm, val) {
7672    cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0"
7673    cm.refresh()
7674  }, true)
7675  option("coverGutterNextToScrollbar", false, function (cm) { return updateScrollbars(cm); }, true)
7676  option("scrollbarStyle", "native", function (cm) {
7677    initScrollbars(cm)
7678    updateScrollbars(cm)
7679    cm.display.scrollbars.setScrollTop(cm.doc.scrollTop)
7680    cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft)
7681  }, true)
7682  option("lineNumbers", false, function (cm) {
7683    setGuttersForLineNumbers(cm.options)
7684    guttersChanged(cm)
7685  }, true)
7686  option("firstLineNumber", 1, guttersChanged, true)
7687  option("lineNumberFormatter", function (integer) { return integer; }, guttersChanged, true)
7688  option("showCursorWhenSelecting", false, updateSelection, true)
7689
7690  option("resetSelectionOnContextMenu", true)
7691  option("lineWiseCopyCut", true)
7692  option("pasteLinesPerSelection", true)
7693
7694  option("readOnly", false, function (cm, val) {
7695    if (val == "nocursor") {
7696      onBlur(cm)
7697      cm.display.input.blur()
7698    }
7699    cm.display.input.readOnlyChanged(val)
7700  })
7701  option("disableInput", false, function (cm, val) {if (!val) { cm.display.input.reset() }}, true)
7702  option("dragDrop", true, dragDropChanged)
7703  option("allowDropFileTypes", null)
7704
7705  option("cursorBlinkRate", 530)
7706  option("cursorScrollMargin", 0)
7707  option("cursorHeight", 1, updateSelection, true)
7708  option("singleCursorHeightPerLine", true, updateSelection, true)
7709  option("workTime", 100)
7710  option("workDelay", 100)
7711  option("flattenSpans", true, resetModeState, true)
7712  option("addModeClass", false, resetModeState, true)
7713  option("pollInterval", 100)
7714  option("undoDepth", 200, function (cm, val) { return cm.doc.history.undoDepth = val; })
7715  option("historyEventDelay", 1250)
7716  option("viewportMargin", 10, function (cm) { return cm.refresh(); }, true)
7717  option("maxHighlightLength", 10000, resetModeState, true)
7718  option("moveInputWithCursor", true, function (cm, val) {
7719    if (!val) { cm.display.input.resetPosition() }
7720  })
7721
7722  option("tabindex", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || ""; })
7723  option("autofocus", null)
7724  option("direction", "ltr", function (cm, val) { return cm.doc.setDirection(val); }, true)
7725}
7726
7727function guttersChanged(cm) {
7728  updateGutters(cm)
7729  regChange(cm)
7730  alignHorizontally(cm)
7731}
7732
7733function dragDropChanged(cm, value, old) {
7734  var wasOn = old && old != Init
7735  if (!value != !wasOn) {
7736    var funcs = cm.display.dragFunctions
7737    var toggle = value ? on : off
7738    toggle(cm.display.scroller, "dragstart", funcs.start)
7739    toggle(cm.display.scroller, "dragenter", funcs.enter)
7740    toggle(cm.display.scroller, "dragover", funcs.over)
7741    toggle(cm.display.scroller, "dragleave", funcs.leave)
7742    toggle(cm.display.scroller, "drop", funcs.drop)
7743  }
7744}
7745
7746function wrappingChanged(cm) {
7747  if (cm.options.lineWrapping) {
7748    addClass(cm.display.wrapper, "CodeMirror-wrap")
7749    cm.display.sizer.style.minWidth = ""
7750    cm.display.sizerWidth = null
7751  } else {
7752    rmClass(cm.display.wrapper, "CodeMirror-wrap")
7753    findMaxLine(cm)
7754  }
7755  estimateLineHeights(cm)
7756  regChange(cm)
7757  clearCaches(cm)
7758  setTimeout(function () { return updateScrollbars(cm); }, 100)
7759}
7760
7761// A CodeMirror instance represents an editor. This is the object
7762// that user code is usually dealing with.
7763
7764function CodeMirror(place, options) {
7765  var this$1 = this;
7766
7767  if (!(this instanceof CodeMirror)) { return new CodeMirror(place, options) }
7768
7769  this.options = options = options ? copyObj(options) : {}
7770  // Determine effective options based on given values and defaults.
7771  copyObj(defaults, options, false)
7772  setGuttersForLineNumbers(options)
7773
7774  var doc = options.value
7775  if (typeof doc == "string") { doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction) }
7776  this.doc = doc
7777
7778  var input = new CodeMirror.inputStyles[options.inputStyle](this)
7779  var display = this.display = new Display(place, doc, input)
7780  display.wrapper.CodeMirror = this
7781  updateGutters(this)
7782  themeChanged(this)
7783  if (options.lineWrapping)
7784    { this.display.wrapper.className += " CodeMirror-wrap" }
7785  initScrollbars(this)
7786
7787  this.state = {
7788    keyMaps: [],  // stores maps added by addKeyMap
7789    overlays: [], // highlighting overlays, as added by addOverlay
7790    modeGen: 0,   // bumped when mode/overlay changes, used to invalidate highlighting info
7791    overwrite: false,
7792    delayingBlurEvent: false,
7793    focused: false,
7794    suppressEdits: false, // used to disable editing during key handlers when in readOnly mode
7795    pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edits in input.poll
7796    selectingText: false,
7797    draggingText: false,
7798    highlight: new Delayed(), // stores highlight worker timeout
7799    keySeq: null,  // Unfinished key sequence
7800    specialChars: null
7801  }
7802
7803  if (options.autofocus && !mobile) { display.input.focus() }
7804
7805  // Override magic textarea content restore that IE sometimes does
7806  // on our hidden textarea on reload
7807  if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20) }
7808
7809  registerEventHandlers(this)
7810  ensureGlobalHandlers()
7811
7812  startOperation(this)
7813  this.curOp.forceUpdate = true
7814  attachDoc(this, doc)
7815
7816  if ((options.autofocus && !mobile) || this.hasFocus())
7817    { setTimeout(bind(onFocus, this), 20) }
7818  else
7819    { onBlur(this) }
7820
7821  for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt))
7822    { optionHandlers[opt](this$1, options[opt], Init) } }
7823  maybeUpdateLineNumberWidth(this)
7824  if (options.finishInit) { options.finishInit(this) }
7825  for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this$1) }
7826  endOperation(this)
7827  // Suppress optimizelegibility in Webkit, since it breaks text
7828  // measuring on line wrapping boundaries.
7829  if (webkit && options.lineWrapping &&
7830      getComputedStyle(display.lineDiv).textRendering == "optimizelegibility")
7831    { display.lineDiv.style.textRendering = "auto" }
7832}
7833
7834// The default configuration options.
7835CodeMirror.defaults = defaults
7836// Functions to run when options are changed.
7837CodeMirror.optionHandlers = optionHandlers
7838
7839// Attach the necessary event handlers when initializing the editor
7840function registerEventHandlers(cm) {
7841  var d = cm.display
7842  on(d.scroller, "mousedown", operation(cm, onMouseDown))
7843  // Older IE's will not fire a second mousedown for a double click
7844  if (ie && ie_version < 11)
7845    { on(d.scroller, "dblclick", operation(cm, function (e) {
7846      if (signalDOMEvent(cm, e)) { return }
7847      var pos = posFromMouse(cm, e)
7848      if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) { return }
7849      e_preventDefault(e)
7850      var word = cm.findWordAt(pos)
7851      extendSelection(cm.doc, word.anchor, word.head)
7852    })) }
7853  else
7854    { on(d.scroller, "dblclick", function (e) { return signalDOMEvent(cm, e) || e_preventDefault(e); }) }
7855  // Some browsers fire contextmenu *after* opening the menu, at
7856  // which point we can't mess with it anymore. Context menu is
7857  // handled in onMouseDown for these browsers.
7858  if (!captureRightClick) { on(d.scroller, "contextmenu", function (e) { return onContextMenu(cm, e); }) }
7859
7860  // Used to suppress mouse event handling when a touch happens
7861  var touchFinished, prevTouch = {end: 0}
7862  function finishTouch() {
7863    if (d.activeTouch) {
7864      touchFinished = setTimeout(function () { return d.activeTouch = null; }, 1000)
7865      prevTouch = d.activeTouch
7866      prevTouch.end = +new Date
7867    }
7868  }
7869  function isMouseLikeTouchEvent(e) {
7870    if (e.touches.length != 1) { return false }
7871    var touch = e.touches[0]
7872    return touch.radiusX <= 1 && touch.radiusY <= 1
7873  }
7874  function farAway(touch, other) {
7875    if (other.left == null) { return true }
7876    var dx = other.left - touch.left, dy = other.top - touch.top
7877    return dx * dx + dy * dy > 20 * 20
7878  }
7879  on(d.scroller, "touchstart", function (e) {
7880    if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e) && !clickInGutter(cm, e)) {
7881      d.input.ensurePolled()
7882      clearTimeout(touchFinished)
7883      var now = +new Date
7884      d.activeTouch = {start: now, moved: false,
7885                       prev: now - prevTouch.end <= 300 ? prevTouch : null}
7886      if (e.touches.length == 1) {
7887        d.activeTouch.left = e.touches[0].pageX
7888        d.activeTouch.top = e.touches[0].pageY
7889      }
7890    }
7891  })
7892  on(d.scroller, "touchmove", function () {
7893    if (d.activeTouch) { d.activeTouch.moved = true }
7894  })
7895  on(d.scroller, "touchend", function (e) {
7896    var touch = d.activeTouch
7897    if (touch && !eventInWidget(d, e) && touch.left != null &&
7898        !touch.moved && new Date - touch.start < 300) {
7899      var pos = cm.coordsChar(d.activeTouch, "page"), range
7900      if (!touch.prev || farAway(touch, touch.prev)) // Single tap
7901        { range = new Range(pos, pos) }
7902      else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap
7903        { range = cm.findWordAt(pos) }
7904      else // Triple tap
7905        { range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) }
7906      cm.setSelection(range.anchor, range.head)
7907      cm.focus()
7908      e_preventDefault(e)
7909    }
7910    finishTouch()
7911  })
7912  on(d.scroller, "touchcancel", finishTouch)
7913
7914  // Sync scrolling between fake scrollbars and real scrollable
7915  // area, ensure viewport is updated when scrolling.
7916  on(d.scroller, "scroll", function () {
7917    if (d.scroller.clientHeight) {
7918      updateScrollTop(cm, d.scroller.scrollTop)
7919      setScrollLeft(cm, d.scroller.scrollLeft, true)
7920      signal(cm, "scroll", cm)
7921    }
7922  })
7923
7924  // Listen to wheel events in order to try and update the viewport on time.
7925  on(d.scroller, "mousewheel", function (e) { return onScrollWheel(cm, e); })
7926  on(d.scroller, "DOMMouseScroll", function (e) { return onScrollWheel(cm, e); })
7927
7928  // Prevent wrapper from ever scrolling
7929  on(d.wrapper, "scroll", function () { return d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; })
7930
7931  d.dragFunctions = {
7932    enter: function (e) {if (!signalDOMEvent(cm, e)) { e_stop(e) }},
7933    over: function (e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e) }},
7934    start: function (e) { return onDragStart(cm, e); },
7935    drop: operation(cm, onDrop),
7936    leave: function (e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm) }}
7937  }
7938
7939  var inp = d.input.getField()
7940  on(inp, "keyup", function (e) { return onKeyUp.call(cm, e); })
7941  on(inp, "keydown", operation(cm, onKeyDown))
7942  on(inp, "keypress", operation(cm, onKeyPress))
7943  on(inp, "focus", function (e) { return onFocus(cm, e); })
7944  on(inp, "blur", function (e) { return onBlur(cm, e); })
7945}
7946
7947var initHooks = []
7948CodeMirror.defineInitHook = function (f) { return initHooks.push(f); }
7949
7950// Indent the given line. The how parameter can be "smart",
7951// "add"/null, "subtract", or "prev". When aggressive is false
7952// (typically set to true for forced single-line indents), empty
7953// lines are not indented, and places where the mode returns Pass
7954// are left alone.
7955function indentLine(cm, n, how, aggressive) {
7956  var doc = cm.doc, state
7957  if (how == null) { how = "add" }
7958  if (how == "smart") {
7959    // Fall back to "prev" when the mode doesn't have an indentation
7960    // method.
7961    if (!doc.mode.indent) { how = "prev" }
7962    else { state = getContextBefore(cm, n).state }
7963  }
7964
7965  var tabSize = cm.options.tabSize
7966  var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize)
7967  if (line.stateAfter) { line.stateAfter = null }
7968  var curSpaceString = line.text.match(/^\s*/)[0], indentation
7969  if (!aggressive && !/\S/.test(line.text)) {
7970    indentation = 0
7971    how = "not"
7972  } else if (how == "smart") {
7973    indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text)
7974    if (indentation == Pass || indentation > 150) {
7975      if (!aggressive) { return }
7976      how = "prev"
7977    }
7978  }
7979  if (how == "prev") {
7980    if (n > doc.first) { indentation = countColumn(getLine(doc, n-1).text, null, tabSize) }
7981    else { indentation = 0 }
7982  } else if (how == "add") {
7983    indentation = curSpace + cm.options.indentUnit
7984  } else if (how == "subtract") {
7985    indentation = curSpace - cm.options.indentUnit
7986  } else if (typeof how == "number") {
7987    indentation = curSpace + how
7988  }
7989  indentation = Math.max(0, indentation)
7990
7991  var indentString = "", pos = 0
7992  if (cm.options.indentWithTabs)
7993    { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t"} }
7994  if (pos < indentation) { indentString += spaceStr(indentation - pos) }
7995
7996  if (indentString != curSpaceString) {
7997    replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input")
7998    line.stateAfter = null
7999    return true
8000  } else {
8001    // Ensure that, if the cursor was in the whitespace at the start
8002    // of the line, it is moved to the end of that space.
8003    for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) {
8004      var range = doc.sel.ranges[i$1]
8005      if (range.head.line == n && range.head.ch < curSpaceString.length) {
8006        var pos$1 = Pos(n, curSpaceString.length)
8007        replaceOneSelection(doc, i$1, new Range(pos$1, pos$1))
8008        break
8009      }
8010    }
8011  }
8012}
8013
8014// This will be set to a {lineWise: bool, text: [string]} object, so
8015// that, when pasting, we know what kind of selections the copied
8016// text was made out of.
8017var lastCopied = null
8018
8019function setLastCopied(newLastCopied) {
8020  lastCopied = newLastCopied
8021}
8022
8023function applyTextInput(cm, inserted, deleted, sel, origin) {
8024  var doc = cm.doc
8025  cm.display.shift = false
8026  if (!sel) { sel = doc.sel }
8027
8028  var paste = cm.state.pasteIncoming || origin == "paste"
8029  var textLines = splitLinesAuto(inserted), multiPaste = null
8030  // When pasting N lines into N selections, insert one line per selection
8031  if (paste && sel.ranges.length > 1) {
8032    if (lastCopied && lastCopied.text.join("\n") == inserted) {
8033      if (sel.ranges.length % lastCopied.text.length == 0) {
8034        multiPaste = []
8035        for (var i = 0; i < lastCopied.text.length; i++)
8036          { multiPaste.push(doc.splitLines(lastCopied.text[i])) }
8037      }
8038    } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) {
8039      multiPaste = map(textLines, function (l) { return [l]; })
8040    }
8041  }
8042
8043  var updateInput
8044  // Normal behavior is to insert the new text into every selection
8045  for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) {
8046    var range = sel.ranges[i$1]
8047    var from = range.from(), to = range.to()
8048    if (range.empty()) {
8049      if (deleted && deleted > 0) // Handle deletion
8050        { from = Pos(from.line, from.ch - deleted) }
8051      else if (cm.state.overwrite && !paste) // Handle overwrite
8052        { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)) }
8053      else if (lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == inserted)
8054        { from = to = Pos(from.line, 0) }
8055    }
8056    updateInput = cm.curOp.updateInput
8057    var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines,
8058                       origin: origin || (paste ? "paste" : cm.state.cutIncoming ? "cut" : "+input")}
8059    makeChange(cm.doc, changeEvent)
8060    signalLater(cm, "inputRead", cm, changeEvent)
8061  }
8062  if (inserted && !paste)
8063    { triggerElectric(cm, inserted) }
8064
8065  ensureCursorVisible(cm)
8066  cm.curOp.updateInput = updateInput
8067  cm.curOp.typing = true
8068  cm.state.pasteIncoming = cm.state.cutIncoming = false
8069}
8070
8071function handlePaste(e, cm) {
8072  var pasted = e.clipboardData && e.clipboardData.getData("Text")
8073  if (pasted) {
8074    e.preventDefault()
8075    if (!cm.isReadOnly() && !cm.options.disableInput)
8076      { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, "paste"); }) }
8077    return true
8078  }
8079}
8080
8081function triggerElectric(cm, inserted) {
8082  // When an 'electric' character is inserted, immediately trigger a reindent
8083  if (!cm.options.electricChars || !cm.options.smartIndent) { return }
8084  var sel = cm.doc.sel
8085
8086  for (var i = sel.ranges.length - 1; i >= 0; i--) {
8087    var range = sel.ranges[i]
8088    if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head.line)) { continue }
8089    var mode = cm.getModeAt(range.head)
8090    var indented = false
8091    if (mode.electricChars) {
8092      for (var j = 0; j < mode.electricChars.length; j++)
8093        { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
8094          indented = indentLine(cm, range.head.line, "smart")
8095          break
8096        } }
8097    } else if (mode.electricInput) {
8098      if (mode.electricInput.test(getLine(cm.doc, range.head.line).text.slice(0, range.head.ch)))
8099        { indented = indentLine(cm, range.head.line, "smart") }
8100    }
8101    if (indented) { signalLater(cm, "electricInput", cm, range.head.line) }
8102  }
8103}
8104
8105function copyableRanges(cm) {
8106  var text = [], ranges = []
8107  for (var i = 0; i < cm.doc.sel.ranges.length; i++) {
8108    var line = cm.doc.sel.ranges[i].head.line
8109    var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)}
8110    ranges.push(lineRange)
8111    text.push(cm.getRange(lineRange.anchor, lineRange.head))
8112  }
8113  return {text: text, ranges: ranges}
8114}
8115
8116function disableBrowserMagic(field, spellcheck) {
8117  field.setAttribute("autocorrect", "off")
8118  field.setAttribute("autocapitalize", "off")
8119  field.setAttribute("spellcheck", !!spellcheck)
8120}
8121
8122function hiddenTextarea() {
8123  var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none")
8124  var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;")
8125  // The textarea is kept positioned near the cursor to prevent the
8126  // fact that it'll be scrolled into view on input from scrolling
8127  // our fake cursor out of view. On webkit, when wrap=off, paste is
8128  // very slow. So make the area wide instead.
8129  if (webkit) { te.style.width = "1000px" }
8130  else { te.setAttribute("wrap", "off") }
8131  // If border: 0; -- iOS fails to open keyboard (issue #1287)
8132  if (ios) { te.style.border = "1px solid black" }
8133  disableBrowserMagic(te)
8134  return div
8135}
8136
8137// The publicly visible API. Note that methodOp(f) means
8138// 'wrap f in an operation, performed on its `this` parameter'.
8139
8140// This is not the complete set of editor methods. Most of the
8141// methods defined on the Doc type are also injected into
8142// CodeMirror.prototype, for backwards compatibility and
8143// convenience.
8144
8145function addEditorMethods(CodeMirror) {
8146  var optionHandlers = CodeMirror.optionHandlers
8147
8148  var helpers = CodeMirror.helpers = {}
8149
8150  CodeMirror.prototype = {
8151    constructor: CodeMirror,
8152    focus: function(){window.focus(); this.display.input.focus()},
8153
8154    setOption: function(option, value) {
8155      var options = this.options, old = options[option]
8156      if (options[option] == value && option != "mode") { return }
8157      options[option] = value
8158      if (optionHandlers.hasOwnProperty(option))
8159        { operation(this, optionHandlers[option])(this, value, old) }
8160      signal(this, "optionChange", this, option)
8161    },
8162
8163    getOption: function(option) {return this.options[option]},
8164    getDoc: function() {return this.doc},
8165
8166    addKeyMap: function(map, bottom) {
8167      this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map))
8168    },
8169    removeKeyMap: function(map) {
8170      var maps = this.state.keyMaps
8171      for (var i = 0; i < maps.length; ++i)
8172        { if (maps[i] == map || maps[i].name == map) {
8173          maps.splice(i, 1)
8174          return true
8175        } }
8176    },
8177
8178    addOverlay: methodOp(function(spec, options) {
8179      var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec)
8180      if (mode.startState) { throw new Error("Overlays may not be stateful.") }
8181      insertSorted(this.state.overlays,
8182                   {mode: mode, modeSpec: spec, opaque: options && options.opaque,
8183                    priority: (options && options.priority) || 0},
8184                   function (overlay) { return overlay.priority; })
8185      this.state.modeGen++
8186      regChange(this)
8187    }),
8188    removeOverlay: methodOp(function(spec) {
8189      var this$1 = this;
8190
8191      var overlays = this.state.overlays
8192      for (var i = 0; i < overlays.length; ++i) {
8193        var cur = overlays[i].modeSpec
8194        if (cur == spec || typeof spec == "string" && cur.name == spec) {
8195          overlays.splice(i, 1)
8196          this$1.state.modeGen++
8197          regChange(this$1)
8198          return
8199        }
8200      }
8201    }),
8202
8203    indentLine: methodOp(function(n, dir, aggressive) {
8204      if (typeof dir != "string" && typeof dir != "number") {
8205        if (dir == null) { dir = this.options.smartIndent ? "smart" : "prev" }
8206        else { dir = dir ? "add" : "subtract" }
8207      }
8208      if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive) }
8209    }),
8210    indentSelection: methodOp(function(how) {
8211      var this$1 = this;
8212
8213      var ranges = this.doc.sel.ranges, end = -1
8214      for (var i = 0; i < ranges.length; i++) {
8215        var range = ranges[i]
8216        if (!range.empty()) {
8217          var from = range.from(), to = range.to()
8218          var start = Math.max(end, from.line)
8219          end = Math.min(this$1.lastLine(), to.line - (to.ch ? 0 : 1)) + 1
8220          for (var j = start; j < end; ++j)
8221            { indentLine(this$1, j, how) }
8222          var newRanges = this$1.doc.sel.ranges
8223          if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0)
8224            { replaceOneSelection(this$1.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll) }
8225        } else if (range.head.line > end) {
8226          indentLine(this$1, range.head.line, how, true)
8227          end = range.head.line
8228          if (i == this$1.doc.sel.primIndex) { ensureCursorVisible(this$1) }
8229        }
8230      }
8231    }),
8232
8233    // Fetch the parser token for a given character. Useful for hacks
8234    // that want to inspect the mode state (say, for completion).
8235    getTokenAt: function(pos, precise) {
8236      return takeToken(this, pos, precise)
8237    },
8238
8239    getLineTokens: function(line, precise) {
8240      return takeToken(this, Pos(line), precise, true)
8241    },
8242
8243    getTokenTypeAt: function(pos) {
8244      pos = clipPos(this.doc, pos)
8245      var styles = getLineStyles(this, getLine(this.doc, pos.line))
8246      var before = 0, after = (styles.length - 1) / 2, ch = pos.ch
8247      var type
8248      if (ch == 0) { type = styles[2] }
8249      else { for (;;) {
8250        var mid = (before + after) >> 1
8251        if ((mid ? styles[mid * 2 - 1] : 0) >= ch) { after = mid }
8252        else if (styles[mid * 2 + 1] < ch) { before = mid + 1 }
8253        else { type = styles[mid * 2 + 2]; break }
8254      } }
8255      var cut = type ? type.indexOf("overlay ") : -1
8256      return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1)
8257    },
8258
8259    getModeAt: function(pos) {
8260      var mode = this.doc.mode
8261      if (!mode.innerMode) { return mode }
8262      return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode
8263    },
8264
8265    getHelper: function(pos, type) {
8266      return this.getHelpers(pos, type)[0]
8267    },
8268
8269    getHelpers: function(pos, type) {
8270      var this$1 = this;
8271
8272      var found = []
8273      if (!helpers.hasOwnProperty(type)) { return found }
8274      var help = helpers[type], mode = this.getModeAt(pos)
8275      if (typeof mode[type] == "string") {
8276        if (help[mode[type]]) { found.push(help[mode[type]]) }
8277      } else if (mode[type]) {
8278        for (var i = 0; i < mode[type].length; i++) {
8279          var val = help[mode[type][i]]
8280          if (val) { found.push(val) }
8281        }
8282      } else if (mode.helperType && help[mode.helperType]) {
8283        found.push(help[mode.helperType])
8284      } else if (help[mode.name]) {
8285        found.push(help[mode.name])
8286      }
8287      for (var i$1 = 0; i$1 < help._global.length; i$1++) {
8288        var cur = help._global[i$1]
8289        if (cur.pred(mode, this$1) && indexOf(found, cur.val) == -1)
8290          { found.push(cur.val) }
8291      }
8292      return found
8293    },
8294
8295    getStateAfter: function(line, precise) {
8296      var doc = this.doc
8297      line = clipLine(doc, line == null ? doc.first + doc.size - 1: line)
8298      return getContextBefore(this, line + 1, precise).state
8299    },
8300
8301    cursorCoords: function(start, mode) {
8302      var pos, range = this.doc.sel.primary()
8303      if (start == null) { pos = range.head }
8304      else if (typeof start == "object") { pos = clipPos(this.doc, start) }
8305      else { pos = start ? range.from() : range.to() }
8306      return cursorCoords(this, pos, mode || "page")
8307    },
8308
8309    charCoords: function(pos, mode) {
8310      return charCoords(this, clipPos(this.doc, pos), mode || "page")
8311    },
8312
8313    coordsChar: function(coords, mode) {
8314      coords = fromCoordSystem(this, coords, mode || "page")
8315      return coordsChar(this, coords.left, coords.top)
8316    },
8317
8318    lineAtHeight: function(height, mode) {
8319      height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top
8320      return lineAtHeight(this.doc, height + this.display.viewOffset)
8321    },
8322    heightAtLine: function(line, mode, includeWidgets) {
8323      var end = false, lineObj
8324      if (typeof line == "number") {
8325        var last = this.doc.first + this.doc.size - 1
8326        if (line < this.doc.first) { line = this.doc.first }
8327        else if (line > last) { line = last; end = true }
8328        lineObj = getLine(this.doc, line)
8329      } else {
8330        lineObj = line
8331      }
8332      return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page", includeWidgets || end).top +
8333        (end ? this.doc.height - heightAtLine(lineObj) : 0)
8334    },
8335
8336    defaultTextHeight: function() { return textHeight(this.display) },
8337    defaultCharWidth: function() { return charWidth(this.display) },
8338
8339    getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}},
8340
8341    addWidget: function(pos, node, scroll, vert, horiz) {
8342      var display = this.display
8343      pos = cursorCoords(this, clipPos(this.doc, pos))
8344      var top = pos.bottom, left = pos.left
8345      node.style.position = "absolute"
8346      node.setAttribute("cm-ignore-events", "true")
8347      this.display.input.setUneditable(node)
8348      display.sizer.appendChild(node)
8349      if (vert == "over") {
8350        top = pos.top
8351      } else if (vert == "above" || vert == "near") {
8352        var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
8353        hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth)
8354        // Default to positioning above (if specified and possible); otherwise default to positioning below
8355        if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
8356          { top = pos.top - node.offsetHeight }
8357        else if (pos.bottom + node.offsetHeight <= vspace)
8358          { top = pos.bottom }
8359        if (left + node.offsetWidth > hspace)
8360          { left = hspace - node.offsetWidth }
8361      }
8362      node.style.top = top + "px"
8363      node.style.left = node.style.right = ""
8364      if (horiz == "right") {
8365        left = display.sizer.clientWidth - node.offsetWidth
8366        node.style.right = "0px"
8367      } else {
8368        if (horiz == "left") { left = 0 }
8369        else if (horiz == "middle") { left = (display.sizer.clientWidth - node.offsetWidth) / 2 }
8370        node.style.left = left + "px"
8371      }
8372      if (scroll)
8373        { scrollIntoView(this, {left: left, top: top, right: left + node.offsetWidth, bottom: top + node.offsetHeight}) }
8374    },
8375
8376    triggerOnKeyDown: methodOp(onKeyDown),
8377    triggerOnKeyPress: methodOp(onKeyPress),
8378    triggerOnKeyUp: onKeyUp,
8379    triggerOnMouseDown: methodOp(onMouseDown),
8380
8381    execCommand: function(cmd) {
8382      if (commands.hasOwnProperty(cmd))
8383        { return commands[cmd].call(null, this) }
8384    },
8385
8386    triggerElectric: methodOp(function(text) { triggerElectric(this, text) }),
8387
8388    findPosH: function(from, amount, unit, visually) {
8389      var this$1 = this;
8390
8391      var dir = 1
8392      if (amount < 0) { dir = -1; amount = -amount }
8393      var cur = clipPos(this.doc, from)
8394      for (var i = 0; i < amount; ++i) {
8395        cur = findPosH(this$1.doc, cur, dir, unit, visually)
8396        if (cur.hitSide) { break }
8397      }
8398      return cur
8399    },
8400
8401    moveH: methodOp(function(dir, unit) {
8402      var this$1 = this;
8403
8404      this.extendSelectionsBy(function (range) {
8405        if (this$1.display.shift || this$1.doc.extend || range.empty())
8406          { return findPosH(this$1.doc, range.head, dir, unit, this$1.options.rtlMoveVisually) }
8407        else
8408          { return dir < 0 ? range.from() : range.to() }
8409      }, sel_move)
8410    }),
8411
8412    deleteH: methodOp(function(dir, unit) {
8413      var sel = this.doc.sel, doc = this.doc
8414      if (sel.somethingSelected())
8415        { doc.replaceSelection("", null, "+delete") }
8416      else
8417        { deleteNearSelection(this, function (range) {
8418          var other = findPosH(doc, range.head, dir, unit, false)
8419          return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other}
8420        }) }
8421    }),
8422
8423    findPosV: function(from, amount, unit, goalColumn) {
8424      var this$1 = this;
8425
8426      var dir = 1, x = goalColumn
8427      if (amount < 0) { dir = -1; amount = -amount }
8428      var cur = clipPos(this.doc, from)
8429      for (var i = 0; i < amount; ++i) {
8430        var coords = cursorCoords(this$1, cur, "div")
8431        if (x == null) { x = coords.left }
8432        else { coords.left = x }
8433        cur = findPosV(this$1, coords, dir, unit)
8434        if (cur.hitSide) { break }
8435      }
8436      return cur
8437    },
8438
8439    moveV: methodOp(function(dir, unit) {
8440      var this$1 = this;
8441
8442      var doc = this.doc, goals = []
8443      var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected()
8444      doc.extendSelectionsBy(function (range) {
8445        if (collapse)
8446          { return dir < 0 ? range.from() : range.to() }
8447        var headPos = cursorCoords(this$1, range.head, "div")
8448        if (range.goalColumn != null) { headPos.left = range.goalColumn }
8449        goals.push(headPos.left)
8450        var pos = findPosV(this$1, headPos, dir, unit)
8451        if (unit == "page" && range == doc.sel.primary())
8452          { addToScrollTop(this$1, charCoords(this$1, pos, "div").top - headPos.top) }
8453        return pos
8454      }, sel_move)
8455      if (goals.length) { for (var i = 0; i < doc.sel.ranges.length; i++)
8456        { doc.sel.ranges[i].goalColumn = goals[i] } }
8457    }),
8458
8459    // Find the word at the given position (as returned by coordsChar).
8460    findWordAt: function(pos) {
8461      var doc = this.doc, line = getLine(doc, pos.line).text
8462      var start = pos.ch, end = pos.ch
8463      if (line) {
8464        var helper = this.getHelper(pos, "wordChars")
8465        if ((pos.sticky == "before" || end == line.length) && start) { --start; } else { ++end }
8466        var startChar = line.charAt(start)
8467        var check = isWordChar(startChar, helper)
8468          ? function (ch) { return isWordChar(ch, helper); }
8469          : /\s/.test(startChar) ? function (ch) { return /\s/.test(ch); }
8470          : function (ch) { return (!/\s/.test(ch) && !isWordChar(ch)); }
8471        while (start > 0 && check(line.charAt(start - 1))) { --start }
8472        while (end < line.length && check(line.charAt(end))) { ++end }
8473      }
8474      return new Range(Pos(pos.line, start), Pos(pos.line, end))
8475    },
8476
8477    toggleOverwrite: function(value) {
8478      if (value != null && value == this.state.overwrite) { return }
8479      if (this.state.overwrite = !this.state.overwrite)
8480        { addClass(this.display.cursorDiv, "CodeMirror-overwrite") }
8481      else
8482        { rmClass(this.display.cursorDiv, "CodeMirror-overwrite") }
8483
8484      signal(this, "overwriteToggle", this, this.state.overwrite)
8485    },
8486    hasFocus: function() { return this.display.input.getField() == activeElt() },
8487    isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) },
8488
8489    scrollTo: methodOp(function (x, y) { scrollToCoords(this, x, y) }),
8490    getScrollInfo: function() {
8491      var scroller = this.display.scroller
8492      return {left: scroller.scrollLeft, top: scroller.scrollTop,
8493              height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight,
8494              width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth,
8495              clientHeight: displayHeight(this), clientWidth: displayWidth(this)}
8496    },
8497
8498    scrollIntoView: methodOp(function(range, margin) {
8499      if (range == null) {
8500        range = {from: this.doc.sel.primary().head, to: null}
8501        if (margin == null) { margin = this.options.cursorScrollMargin }
8502      } else if (typeof range == "number") {
8503        range = {from: Pos(range, 0), to: null}
8504      } else if (range.from == null) {
8505        range = {from: range, to: null}
8506      }
8507      if (!range.to) { range.to = range.from }
8508      range.margin = margin || 0
8509
8510      if (range.from.line != null) {
8511        scrollToRange(this, range)
8512      } else {
8513        scrollToCoordsRange(this, range.from, range.to, range.margin)
8514      }
8515    }),
8516
8517    setSize: methodOp(function(width, height) {
8518      var this$1 = this;
8519
8520      var interpret = function (val) { return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; }
8521      if (width != null) { this.display.wrapper.style.width = interpret(width) }
8522      if (height != null) { this.display.wrapper.style.height = interpret(height) }
8523      if (this.options.lineWrapping) { clearLineMeasurementCache(this) }
8524      var lineNo = this.display.viewFrom
8525      this.doc.iter(lineNo, this.display.viewTo, function (line) {
8526        if (line.widgets) { for (var i = 0; i < line.widgets.length; i++)
8527          { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo, "widget"); break } } }
8528        ++lineNo
8529      })
8530      this.curOp.forceUpdate = true
8531      signal(this, "refresh", this)
8532    }),
8533
8534    operation: function(f){return runInOp(this, f)},
8535    startOperation: function(){return startOperation(this)},
8536    endOperation: function(){return endOperation(this)},
8537
8538    refresh: methodOp(function() {
8539      var oldHeight = this.display.cachedTextHeight
8540      regChange(this)
8541      this.curOp.forceUpdate = true
8542      clearCaches(this)
8543      scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop)
8544      updateGutterSpace(this)
8545      if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5)
8546        { estimateLineHeights(this) }
8547      signal(this, "refresh", this)
8548    }),
8549
8550    swapDoc: methodOp(function(doc) {
8551      var old = this.doc
8552      old.cm = null
8553      attachDoc(this, doc)
8554      clearCaches(this)
8555      this.display.input.reset()
8556      scrollToCoords(this, doc.scrollLeft, doc.scrollTop)
8557      this.curOp.forceScroll = true
8558      signalLater(this, "swapDoc", this, old)
8559      return old
8560    }),
8561
8562    getInputField: function(){return this.display.input.getField()},
8563    getWrapperElement: function(){return this.display.wrapper},
8564    getScrollerElement: function(){return this.display.scroller},
8565    getGutterElement: function(){return this.display.gutters}
8566  }
8567  eventMixin(CodeMirror)
8568
8569  CodeMirror.registerHelper = function(type, name, value) {
8570    if (!helpers.hasOwnProperty(type)) { helpers[type] = CodeMirror[type] = {_global: []} }
8571    helpers[type][name] = value
8572  }
8573  CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {
8574    CodeMirror.registerHelper(type, name, value)
8575    helpers[type]._global.push({pred: predicate, val: value})
8576  }
8577}
8578
8579// Used for horizontal relative motion. Dir is -1 or 1 (left or
8580// right), unit can be "char", "column" (like char, but doesn't
8581// cross line boundaries), "word" (across next word), or "group" (to
8582// the start of next group of word or non-word-non-whitespace
8583// chars). The visually param controls whether, in right-to-left
8584// text, direction 1 means to move towards the next index in the
8585// string, or towards the character to the right of the current
8586// position. The resulting position will have a hitSide=true
8587// property if it reached the end of the document.
8588function findPosH(doc, pos, dir, unit, visually) {
8589  var oldPos = pos
8590  var origDir = dir
8591  var lineObj = getLine(doc, pos.line)
8592  function findNextLine() {
8593    var l = pos.line + dir
8594    if (l < doc.first || l >= doc.first + doc.size) { return false }
8595    pos = new Pos(l, pos.ch, pos.sticky)
8596    return lineObj = getLine(doc, l)
8597  }
8598  function moveOnce(boundToLine) {
8599    var next
8600    if (visually) {
8601      next = moveVisually(doc.cm, lineObj, pos, dir)
8602    } else {
8603      next = moveLogically(lineObj, pos, dir)
8604    }
8605    if (next == null) {
8606      if (!boundToLine && findNextLine())
8607        { pos = endOfLine(visually, doc.cm, lineObj, pos.line, dir) }
8608      else
8609        { return false }
8610    } else {
8611      pos = next
8612    }
8613    return true
8614  }
8615
8616  if (unit == "char") {
8617    moveOnce()
8618  } else if (unit == "column") {
8619    moveOnce(true)
8620  } else if (unit == "word" || unit == "group") {
8621    var sawType = null, group = unit == "group"
8622    var helper = doc.cm && doc.cm.getHelper(pos, "wordChars")
8623    for (var first = true;; first = false) {
8624      if (dir < 0 && !moveOnce(!first)) { break }
8625      var cur = lineObj.text.charAt(pos.ch) || "\n"
8626      var type = isWordChar(cur, helper) ? "w"
8627        : group && cur == "\n" ? "n"
8628        : !group || /\s/.test(cur) ? null
8629        : "p"
8630      if (group && !first && !type) { type = "s" }
8631      if (sawType && sawType != type) {
8632        if (dir < 0) {dir = 1; moveOnce(); pos.sticky = "after"}
8633        break
8634      }
8635
8636      if (type) { sawType = type }
8637      if (dir > 0 && !moveOnce(!first)) { break }
8638    }
8639  }
8640  var result = skipAtomic(doc, pos, oldPos, origDir, true)
8641  if (equalCursorPos(oldPos, result)) { result.hitSide = true }
8642  return result
8643}
8644
8645// For relative vertical movement. Dir may be -1 or 1. Unit can be
8646// "page" or "line". The resulting position will have a hitSide=true
8647// property if it reached the end of the document.
8648function findPosV(cm, pos, dir, unit) {
8649  var doc = cm.doc, x = pos.left, y
8650  if (unit == "page") {
8651    var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight)
8652    var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3)
8653    y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount
8654
8655  } else if (unit == "line") {
8656    y = dir > 0 ? pos.bottom + 3 : pos.top - 3
8657  }
8658  var target
8659  for (;;) {
8660    target = coordsChar(cm, x, y)
8661    if (!target.outside) { break }
8662    if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break }
8663    y += dir * 5
8664  }
8665  return target
8666}
8667
8668// CONTENTEDITABLE INPUT STYLE
8669
8670var ContentEditableInput = function(cm) {
8671  this.cm = cm
8672  this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null
8673  this.polling = new Delayed()
8674  this.composing = null
8675  this.gracePeriod = false
8676  this.readDOMTimeout = null
8677};
8678
8679ContentEditableInput.prototype.init = function (display) {
8680    var this$1 = this;
8681
8682  var input = this, cm = input.cm
8683  var div = input.div = display.lineDiv
8684  disableBrowserMagic(div, cm.options.spellcheck)
8685
8686  on(div, "paste", function (e) {
8687    if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
8688    // IE doesn't fire input events, so we schedule a read for the pasted content in this way
8689    if (ie_version <= 11) { setTimeout(operation(cm, function () { return this$1.updateFromDOM(); }), 20) }
8690  })
8691
8692  on(div, "compositionstart", function (e) {
8693    this$1.composing = {data: e.data, done: false}
8694  })
8695  on(div, "compositionupdate", function (e) {
8696    if (!this$1.composing) { this$1.composing = {data: e.data, done: false} }
8697  })
8698  on(div, "compositionend", function (e) {
8699    if (this$1.composing) {
8700      if (e.data != this$1.composing.data) { this$1.readFromDOMSoon() }
8701      this$1.composing.done = true
8702    }
8703  })
8704
8705  on(div, "touchstart", function () { return input.forceCompositionEnd(); })
8706
8707  on(div, "input", function () {
8708    if (!this$1.composing) { this$1.readFromDOMSoon() }
8709  })
8710
8711  function onCopyCut(e) {
8712    if (signalDOMEvent(cm, e)) { return }
8713    if (cm.somethingSelected()) {
8714      setLastCopied({lineWise: false, text: cm.getSelections()})
8715      if (e.type == "cut") { cm.replaceSelection("", null, "cut") }
8716    } else if (!cm.options.lineWiseCopyCut) {
8717      return
8718    } else {
8719      var ranges = copyableRanges(cm)
8720      setLastCopied({lineWise: true, text: ranges.text})
8721      if (e.type == "cut") {
8722        cm.operation(function () {
8723          cm.setSelections(ranges.ranges, 0, sel_dontScroll)
8724          cm.replaceSelection("", null, "cut")
8725        })
8726      }
8727    }
8728    if (e.clipboardData) {
8729      e.clipboardData.clearData()
8730      var content = lastCopied.text.join("\n")
8731      // iOS exposes the clipboard API, but seems to discard content inserted into it
8732      e.clipboardData.setData("Text", content)
8733      if (e.clipboardData.getData("Text") == content) {
8734        e.preventDefault()
8735        return
8736      }
8737    }
8738    // Old-fashioned briefly-focus-a-textarea hack
8739    var kludge = hiddenTextarea(), te = kludge.firstChild
8740    cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild)
8741    te.value = lastCopied.text.join("\n")
8742    var hadFocus = document.activeElement
8743    selectInput(te)
8744    setTimeout(function () {
8745      cm.display.lineSpace.removeChild(kludge)
8746      hadFocus.focus()
8747      if (hadFocus == div) { input.showPrimarySelection() }
8748    }, 50)
8749  }
8750  on(div, "copy", onCopyCut)
8751  on(div, "cut", onCopyCut)
8752};
8753
8754ContentEditableInput.prototype.prepareSelection = function () {
8755  var result = prepareSelection(this.cm, false)
8756  result.focus = this.cm.state.focused
8757  return result
8758};
8759
8760ContentEditableInput.prototype.showSelection = function (info, takeFocus) {
8761  if (!info || !this.cm.display.view.length) { return }
8762  if (info.focus || takeFocus) { this.showPrimarySelection() }
8763  this.showMultipleSelections(info)
8764};
8765
8766ContentEditableInput.prototype.getSelection = function () {
8767  return this.cm.display.wrapper.ownerDocument.getSelection()
8768};
8769
8770ContentEditableInput.prototype.showPrimarySelection = function () {
8771  var sel = this.getSelection(), cm = this.cm, prim = cm.doc.sel.primary()
8772  var from = prim.from(), to = prim.to()
8773
8774  if (cm.display.viewTo == cm.display.viewFrom || from.line >= cm.display.viewTo || to.line < cm.display.viewFrom) {
8775    sel.removeAllRanges()
8776    return
8777  }
8778
8779  var curAnchor = domToPos(cm, sel.anchorNode, sel.anchorOffset)
8780  var curFocus = domToPos(cm, sel.focusNode, sel.focusOffset)
8781  if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad &&
8782      cmp(minPos(curAnchor, curFocus), from) == 0 &&
8783      cmp(maxPos(curAnchor, curFocus), to) == 0)
8784    { return }
8785
8786  var view = cm.display.view
8787  var start = (from.line >= cm.display.viewFrom && posToDOM(cm, from)) ||
8788      {node: view[0].measure.map[2], offset: 0}
8789  var end = to.line < cm.display.viewTo && posToDOM(cm, to)
8790  if (!end) {
8791    var measure = view[view.length - 1].measure
8792    var map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map
8793    end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map.length - 3]}
8794  }
8795
8796  if (!start || !end) {
8797    sel.removeAllRanges()
8798    return
8799  }
8800
8801  var old = sel.rangeCount && sel.getRangeAt(0), rng
8802  try { rng = range(start.node, start.offset, end.offset, end.node) }
8803  catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible
8804  if (rng) {
8805    if (!gecko && cm.state.focused) {
8806      sel.collapse(start.node, start.offset)
8807      if (!rng.collapsed) {
8808        sel.removeAllRanges()
8809        sel.addRange(rng)
8810      }
8811    } else {
8812      sel.removeAllRanges()
8813      sel.addRange(rng)
8814    }
8815    if (old && sel.anchorNode == null) { sel.addRange(old) }
8816    else if (gecko) { this.startGracePeriod() }
8817  }
8818  this.rememberSelection()
8819};
8820
8821ContentEditableInput.prototype.startGracePeriod = function () {
8822    var this$1 = this;
8823
8824  clearTimeout(this.gracePeriod)
8825  this.gracePeriod = setTimeout(function () {
8826    this$1.gracePeriod = false
8827    if (this$1.selectionChanged())
8828      { this$1.cm.operation(function () { return this$1.cm.curOp.selectionChanged = true; }) }
8829  }, 20)
8830};
8831
8832ContentEditableInput.prototype.showMultipleSelections = function (info) {
8833  removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors)
8834  removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection)
8835};
8836
8837ContentEditableInput.prototype.rememberSelection = function () {
8838  var sel = this.getSelection()
8839  this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset
8840  this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset
8841};
8842
8843ContentEditableInput.prototype.selectionInEditor = function () {
8844  var sel = this.getSelection()
8845  if (!sel.rangeCount) { return false }
8846  var node = sel.getRangeAt(0).commonAncestorContainer
8847  return contains(this.div, node)
8848};
8849
8850ContentEditableInput.prototype.focus = function () {
8851  if (this.cm.options.readOnly != "nocursor") {
8852    if (!this.selectionInEditor())
8853      { this.showSelection(this.prepareSelection(), true) }
8854    this.div.focus()
8855  }
8856};
8857ContentEditableInput.prototype.blur = function () { this.div.blur() };
8858ContentEditableInput.prototype.getField = function () { return this.div };
8859
8860ContentEditableInput.prototype.supportsTouch = function () { return true };
8861
8862ContentEditableInput.prototype.receivedFocus = function () {
8863  var input = this
8864  if (this.selectionInEditor())
8865    { this.pollSelection() }
8866  else
8867    { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }) }
8868
8869  function poll() {
8870    if (input.cm.state.focused) {
8871      input.pollSelection()
8872      input.polling.set(input.cm.options.pollInterval, poll)
8873    }
8874  }
8875  this.polling.set(this.cm.options.pollInterval, poll)
8876};
8877
8878ContentEditableInput.prototype.selectionChanged = function () {
8879  var sel = this.getSelection()
8880  return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||
8881    sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset
8882};
8883
8884ContentEditableInput.prototype.pollSelection = function () {
8885  if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) { return }
8886  var sel = this.getSelection(), cm = this.cm
8887  // On Android Chrome (version 56, at least), backspacing into an
8888  // uneditable block element will put the cursor in that element,
8889  // and then, because it's not editable, hide the virtual keyboard.
8890  // Because Android doesn't allow us to actually detect backspace
8891  // presses in a sane way, this code checks for when that happens
8892  // and simulates a backspace press in this case.
8893  if (android && chrome && this.cm.options.gutters.length && isInGutter(sel.anchorNode)) {
8894    this.cm.triggerOnKeyDown({type: "keydown", keyCode: 8, preventDefault: Math.abs})
8895    this.blur()
8896    this.focus()
8897    return
8898  }
8899  if (this.composing) { return }
8900  this.rememberSelection()
8901  var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset)
8902  var head = domToPos(cm, sel.focusNode, sel.focusOffset)
8903  if (anchor && head) { runInOp(cm, function () {
8904    setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll)
8905    if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true }
8906  }) }
8907};
8908
8909ContentEditableInput.prototype.pollContent = function () {
8910  if (this.readDOMTimeout != null) {
8911    clearTimeout(this.readDOMTimeout)
8912    this.readDOMTimeout = null
8913  }
8914
8915  var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary()
8916  var from = sel.from(), to = sel.to()
8917  if (from.ch == 0 && from.line > cm.firstLine())
8918    { from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length) }
8919  if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine())
8920    { to = Pos(to.line + 1, 0) }
8921  if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return false }
8922
8923  var fromIndex, fromLine, fromNode
8924  if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) {
8925    fromLine = lineNo(display.view[0].line)
8926    fromNode = display.view[0].node
8927  } else {
8928    fromLine = lineNo(display.view[fromIndex].line)
8929    fromNode = display.view[fromIndex - 1].node.nextSibling
8930  }
8931  var toIndex = findViewIndex(cm, to.line)
8932  var toLine, toNode
8933  if (toIndex == display.view.length - 1) {
8934    toLine = display.viewTo - 1
8935    toNode = display.lineDiv.lastChild
8936  } else {
8937    toLine = lineNo(display.view[toIndex + 1].line) - 1
8938    toNode = display.view[toIndex + 1].node.previousSibling
8939  }
8940
8941  if (!fromNode) { return false }
8942  var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine))
8943  var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length))
8944  while (newText.length > 1 && oldText.length > 1) {
8945    if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine-- }
8946    else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++ }
8947    else { break }
8948  }
8949
8950  var cutFront = 0, cutEnd = 0
8951  var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length)
8952  while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront))
8953    { ++cutFront }
8954  var newBot = lst(newText), oldBot = lst(oldText)
8955  var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0),
8956                           oldBot.length - (oldText.length == 1 ? cutFront : 0))
8957  while (cutEnd < maxCutEnd &&
8958         newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1))
8959    { ++cutEnd }
8960  // Try to move start of change to start of selection if ambiguous
8961  if (newText.length == 1 && oldText.length == 1 && fromLine == from.line) {
8962    while (cutFront && cutFront > from.ch &&
8963           newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) {
8964      cutFront--
8965      cutEnd++
8966    }
8967  }
8968
8969  newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, "")
8970  newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, "")
8971
8972  var chFrom = Pos(fromLine, cutFront)
8973  var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0)
8974  if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {
8975    replaceRange(cm.doc, newText, chFrom, chTo, "+input")
8976    return true
8977  }
8978};
8979
8980ContentEditableInput.prototype.ensurePolled = function () {
8981  this.forceCompositionEnd()
8982};
8983ContentEditableInput.prototype.reset = function () {
8984  this.forceCompositionEnd()
8985};
8986ContentEditableInput.prototype.forceCompositionEnd = function () {
8987  if (!this.composing) { return }
8988  clearTimeout(this.readDOMTimeout)
8989  this.composing = null
8990  this.updateFromDOM()
8991  this.div.blur()
8992  this.div.focus()
8993};
8994ContentEditableInput.prototype.readFromDOMSoon = function () {
8995    var this$1 = this;
8996
8997  if (this.readDOMTimeout != null) { return }
8998  this.readDOMTimeout = setTimeout(function () {
8999    this$1.readDOMTimeout = null
9000    if (this$1.composing) {
9001      if (this$1.composing.done) { this$1.composing = null }
9002      else { return }
9003    }
9004    this$1.updateFromDOM()
9005  }, 80)
9006};
9007
9008ContentEditableInput.prototype.updateFromDOM = function () {
9009    var this$1 = this;
9010
9011  if (this.cm.isReadOnly() || !this.pollContent())
9012    { runInOp(this.cm, function () { return regChange(this$1.cm); }) }
9013};
9014
9015ContentEditableInput.prototype.setUneditable = function (node) {
9016  node.contentEditable = "false"
9017};
9018
9019ContentEditableInput.prototype.onKeyPress = function (e) {
9020  if (e.charCode == 0 || this.composing) { return }
9021  e.preventDefault()
9022  if (!this.cm.isReadOnly())
9023    { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0) }
9024};
9025
9026ContentEditableInput.prototype.readOnlyChanged = function (val) {
9027  this.div.contentEditable = String(val != "nocursor")
9028};
9029
9030ContentEditableInput.prototype.onContextMenu = function () {};
9031ContentEditableInput.prototype.resetPosition = function () {};
9032
9033ContentEditableInput.prototype.needsContentAttribute = true
9034
9035function posToDOM(cm, pos) {
9036  var view = findViewForLine(cm, pos.line)
9037  if (!view || view.hidden) { return null }
9038  var line = getLine(cm.doc, pos.line)
9039  var info = mapFromLineView(view, line, pos.line)
9040
9041  var order = getOrder(line, cm.doc.direction), side = "left"
9042  if (order) {
9043    var partPos = getBidiPartAt(order, pos.ch)
9044    side = partPos % 2 ? "right" : "left"
9045  }
9046  var result = nodeAndOffsetInLineMap(info.map, pos.ch, side)
9047  result.offset = result.collapse == "right" ? result.end : result.start
9048  return result
9049}
9050
9051function isInGutter(node) {
9052  for (var scan = node; scan; scan = scan.parentNode)
9053    { if (/CodeMirror-gutter-wrapper/.test(scan.className)) { return true } }
9054  return false
9055}
9056
9057function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos }
9058
9059function domTextBetween(cm, from, to, fromLine, toLine) {
9060  var text = "", closing = false, lineSep = cm.doc.lineSeparator(), extraLinebreak = false
9061  function recognizeMarker(id) { return function (marker) { return marker.id == id; } }
9062  function close() {
9063    if (closing) {
9064      text += lineSep
9065      if (extraLinebreak) { text += lineSep }
9066      closing = extraLinebreak = false
9067    }
9068  }
9069  function addText(str) {
9070    if (str) {
9071      close()
9072      text += str
9073    }
9074  }
9075  function walk(node) {
9076    if (node.nodeType == 1) {
9077      var cmText = node.getAttribute("cm-text")
9078      if (cmText) {
9079        addText(cmText)
9080        return
9081      }
9082      var markerID = node.getAttribute("cm-marker"), range
9083      if (markerID) {
9084        var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID))
9085        if (found.length && (range = found[0].find(0)))
9086          { addText(getBetween(cm.doc, range.from, range.to).join(lineSep)) }
9087        return
9088      }
9089      if (node.getAttribute("contenteditable") == "false") { return }
9090      var isBlock = /^(pre|div|p|li|table|br)$/i.test(node.nodeName)
9091      if (!/^br$/i.test(node.nodeName) && node.textContent.length == 0) { return }
9092
9093      if (isBlock) { close() }
9094      for (var i = 0; i < node.childNodes.length; i++)
9095        { walk(node.childNodes[i]) }
9096
9097      if (/^(pre|p)$/i.test(node.nodeName)) { extraLinebreak = true }
9098      if (isBlock) { closing = true }
9099    } else if (node.nodeType == 3) {
9100      addText(node.nodeValue.replace(/\u200b/g, "").replace(/\u00a0/g, " "))
9101    }
9102  }
9103  for (;;) {
9104    walk(from)
9105    if (from == to) { break }
9106    from = from.nextSibling
9107    extraLinebreak = false
9108  }
9109  return text
9110}
9111
9112function domToPos(cm, node, offset) {
9113  var lineNode
9114  if (node == cm.display.lineDiv) {
9115    lineNode = cm.display.lineDiv.childNodes[offset]
9116    if (!lineNode) { return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) }
9117    node = null; offset = 0
9118  } else {
9119    for (lineNode = node;; lineNode = lineNode.parentNode) {
9120      if (!lineNode || lineNode == cm.display.lineDiv) { return null }
9121      if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) { break }
9122    }
9123  }
9124  for (var i = 0; i < cm.display.view.length; i++) {
9125    var lineView = cm.display.view[i]
9126    if (lineView.node == lineNode)
9127      { return locateNodeInLineView(lineView, node, offset) }
9128  }
9129}
9130
9131function locateNodeInLineView(lineView, node, offset) {
9132  var wrapper = lineView.text.firstChild, bad = false
9133  if (!node || !contains(wrapper, node)) { return badPos(Pos(lineNo(lineView.line), 0), true) }
9134  if (node == wrapper) {
9135    bad = true
9136    node = wrapper.childNodes[offset]
9137    offset = 0
9138    if (!node) {
9139      var line = lineView.rest ? lst(lineView.rest) : lineView.line
9140      return badPos(Pos(lineNo(line), line.text.length), bad)
9141    }
9142  }
9143
9144  var textNode = node.nodeType == 3 ? node : null, topNode = node
9145  if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {
9146    textNode = node.firstChild
9147    if (offset) { offset = textNode.nodeValue.length }
9148  }
9149  while (topNode.parentNode != wrapper) { topNode = topNode.parentNode }
9150  var measure = lineView.measure, maps = measure.maps
9151
9152  function find(textNode, topNode, offset) {
9153    for (var i = -1; i < (maps ? maps.length : 0); i++) {
9154      var map = i < 0 ? measure.map : maps[i]
9155      for (var j = 0; j < map.length; j += 3) {
9156        var curNode = map[j + 2]
9157        if (curNode == textNode || curNode == topNode) {
9158          var line = lineNo(i < 0 ? lineView.line : lineView.rest[i])
9159          var ch = map[j] + offset
9160          if (offset < 0 || curNode != textNode) { ch = map[j + (offset ? 1 : 0)] }
9161          return Pos(line, ch)
9162        }
9163      }
9164    }
9165  }
9166  var found = find(textNode, topNode, offset)
9167  if (found) { return badPos(found, bad) }
9168
9169  // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems
9170  for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) {
9171    found = find(after, after.firstChild, 0)
9172    if (found)
9173      { return badPos(Pos(found.line, found.ch - dist), bad) }
9174    else
9175      { dist += after.textContent.length }
9176  }
9177  for (var before = topNode.previousSibling, dist$1 = offset; before; before = before.previousSibling) {
9178    found = find(before, before.firstChild, -1)
9179    if (found)
9180      { return badPos(Pos(found.line, found.ch + dist$1), bad) }
9181    else
9182      { dist$1 += before.textContent.length }
9183  }
9184}
9185
9186// TEXTAREA INPUT STYLE
9187
9188var TextareaInput = function(cm) {
9189  this.cm = cm
9190  // See input.poll and input.reset
9191  this.prevInput = ""
9192
9193  // Flag that indicates whether we expect input to appear real soon
9194  // now (after some event like 'keypress' or 'input') and are
9195  // polling intensively.
9196  this.pollingFast = false
9197  // Self-resetting timeout for the poller
9198  this.polling = new Delayed()
9199  // Used to work around IE issue with selection being forgotten when focus moves away from textarea
9200  this.hasSelection = false
9201  this.composing = null
9202};
9203
9204TextareaInput.prototype.init = function (display) {
9205    var this$1 = this;
9206
9207  var input = this, cm = this.cm
9208  this.createField(display)
9209  var te = this.textarea
9210
9211  display.wrapper.insertBefore(this.wrapper, display.wrapper.firstChild)
9212
9213  // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)
9214  if (ios) { te.style.width = "0px" }
9215
9216  on(te, "input", function () {
9217    if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = null }
9218    input.poll()
9219  })
9220
9221  on(te, "paste", function (e) {
9222    if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
9223
9224    cm.state.pasteIncoming = true
9225    input.fastPoll()
9226  })
9227
9228  function prepareCopyCut(e) {
9229    if (signalDOMEvent(cm, e)) { return }
9230    if (cm.somethingSelected()) {
9231      setLastCopied({lineWise: false, text: cm.getSelections()})
9232    } else if (!cm.options.lineWiseCopyCut) {
9233      return
9234    } else {
9235      var ranges = copyableRanges(cm)
9236      setLastCopied({lineWise: true, text: ranges.text})
9237      if (e.type == "cut") {
9238        cm.setSelections(ranges.ranges, null, sel_dontScroll)
9239      } else {
9240        input.prevInput = ""
9241        te.value = ranges.text.join("\n")
9242        selectInput(te)
9243      }
9244    }
9245    if (e.type == "cut") { cm.state.cutIncoming = true }
9246  }
9247  on(te, "cut", prepareCopyCut)
9248  on(te, "copy", prepareCopyCut)
9249
9250  on(display.scroller, "paste", function (e) {
9251    if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return }
9252    cm.state.pasteIncoming = true
9253    input.focus()
9254  })
9255
9256  // Prevent normal selection in the editor (we handle our own)
9257  on(display.lineSpace, "selectstart", function (e) {
9258    if (!eventInWidget(display, e)) { e_preventDefault(e) }
9259  })
9260
9261  on(te, "compositionstart", function () {
9262    var start = cm.getCursor("from")
9263    if (input.composing) { input.composing.range.clear() }
9264    input.composing = {
9265      start: start,
9266      range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"})
9267    }
9268  })
9269  on(te, "compositionend", function () {
9270    if (input.composing) {
9271      input.poll()
9272      input.composing.range.clear()
9273      input.composing = null
9274    }
9275  })
9276};
9277
9278TextareaInput.prototype.createField = function (_display) {
9279  // Wraps and hides input textarea
9280  this.wrapper = hiddenTextarea()
9281  // The semihidden textarea that is focused when the editor is
9282  // focused, and receives input.
9283  this.textarea = this.wrapper.firstChild
9284};
9285
9286TextareaInput.prototype.prepareSelection = function () {
9287  // Redraw the selection and/or cursor
9288  var cm = this.cm, display = cm.display, doc = cm.doc
9289  var result = prepareSelection(cm)
9290
9291  // Move the hidden textarea near the cursor to prevent scrolling artifacts
9292  if (cm.options.moveInputWithCursor) {
9293    var headPos = cursorCoords(cm, doc.sel.primary().head, "div")
9294    var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect()
9295    result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
9296                                        headPos.top + lineOff.top - wrapOff.top))
9297    result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
9298                                         headPos.left + lineOff.left - wrapOff.left))
9299  }
9300
9301  return result
9302};
9303
9304TextareaInput.prototype.showSelection = function (drawn) {
9305  var cm = this.cm, display = cm.display
9306  removeChildrenAndAdd(display.cursorDiv, drawn.cursors)
9307  removeChildrenAndAdd(display.selectionDiv, drawn.selection)
9308  if (drawn.teTop != null) {
9309    this.wrapper.style.top = drawn.teTop + "px"
9310    this.wrapper.style.left = drawn.teLeft + "px"
9311  }
9312};
9313
9314// Reset the input to correspond to the selection (or to be empty,
9315// when not typing and nothing is selected)
9316TextareaInput.prototype.reset = function (typing) {
9317  if (this.contextMenuPending || this.composing) { return }
9318  var cm = this.cm
9319  if (cm.somethingSelected()) {
9320    this.prevInput = ""
9321    var content = cm.getSelection()
9322    this.textarea.value = content
9323    if (cm.state.focused) { selectInput(this.textarea) }
9324    if (ie && ie_version >= 9) { this.hasSelection = content }
9325  } else if (!typing) {
9326    this.prevInput = this.textarea.value = ""
9327    if (ie && ie_version >= 9) { this.hasSelection = null }
9328  }
9329};
9330
9331TextareaInput.prototype.getField = function () { return this.textarea };
9332
9333TextareaInput.prototype.supportsTouch = function () { return false };
9334
9335TextareaInput.prototype.focus = function () {
9336  if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) {
9337    try { this.textarea.focus() }
9338    catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM
9339  }
9340};
9341
9342TextareaInput.prototype.blur = function () { this.textarea.blur() };
9343
9344TextareaInput.prototype.resetPosition = function () {
9345  this.wrapper.style.top = this.wrapper.style.left = 0
9346};
9347
9348TextareaInput.prototype.receivedFocus = function () { this.slowPoll() };
9349
9350// Poll for input changes, using the normal rate of polling. This
9351// runs as long as the editor is focused.
9352TextareaInput.prototype.slowPoll = function () {
9353    var this$1 = this;
9354
9355  if (this.pollingFast) { return }
9356  this.polling.set(this.cm.options.pollInterval, function () {
9357    this$1.poll()
9358    if (this$1.cm.state.focused) { this$1.slowPoll() }
9359  })
9360};
9361
9362// When an event has just come in that is likely to add or change
9363// something in the input textarea, we poll faster, to ensure that
9364// the change appears on the screen quickly.
9365TextareaInput.prototype.fastPoll = function () {
9366  var missed = false, input = this
9367  input.pollingFast = true
9368  function p() {
9369    var changed = input.poll()
9370    if (!changed && !missed) {missed = true; input.polling.set(60, p)}
9371    else {input.pollingFast = false; input.slowPoll()}
9372  }
9373  input.polling.set(20, p)
9374};
9375
9376// Read input from the textarea, and update the document to match.
9377// When something is selected, it is present in the textarea, and
9378// selected (unless it is huge, in which case a placeholder is
9379// used). When nothing is selected, the cursor sits after previously
9380// seen text (can be empty), which is stored in prevInput (we must
9381// not reset the textarea when typing, because that breaks IME).
9382TextareaInput.prototype.poll = function () {
9383    var this$1 = this;
9384
9385  var cm = this.cm, input = this.textarea, prevInput = this.prevInput
9386  // Since this is called a *lot*, try to bail out as cheaply as
9387  // possible when it is clear that nothing happened. hasSelection
9388  // will be the case when there is a lot of text in the textarea,
9389  // in which case reading its value would be expensive.
9390  if (this.contextMenuPending || !cm.state.focused ||
9391      (hasSelection(input) && !prevInput && !this.composing) ||
9392      cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq)
9393    { return false }
9394
9395  var text = input.value
9396  // If nothing changed, bail.
9397  if (text == prevInput && !cm.somethingSelected()) { return false }
9398  // Work around nonsensical selection resetting in IE9/10, and
9399  // inexplicable appearance of private area unicode characters on
9400  // some key combos in Mac (#2689).
9401  if (ie && ie_version >= 9 && this.hasSelection === text ||
9402      mac && /[\uf700-\uf7ff]/.test(text)) {
9403    cm.display.input.reset()
9404    return false
9405  }
9406
9407  if (cm.doc.sel == cm.display.selForContextMenu) {
9408    var first = text.charCodeAt(0)
9409    if (first == 0x200b && !prevInput) { prevInput = "\u200b" }
9410    if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") }
9411  }
9412  // Find the part of the input that is actually new
9413  var same = 0, l = Math.min(prevInput.length, text.length)
9414  while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same }
9415
9416  runInOp(cm, function () {
9417    applyTextInput(cm, text.slice(same), prevInput.length - same,
9418                   null, this$1.composing ? "*compose" : null)
9419
9420    // Don't leave long text in the textarea, since it makes further polling slow
9421    if (text.length > 1000 || text.indexOf("\n") > -1) { input.value = this$1.prevInput = "" }
9422    else { this$1.prevInput = text }
9423
9424    if (this$1.composing) {
9425      this$1.composing.range.clear()
9426      this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor("to"),
9427                                         {className: "CodeMirror-composing"})
9428    }
9429  })
9430  return true
9431};
9432
9433TextareaInput.prototype.ensurePolled = function () {
9434  if (this.pollingFast && this.poll()) { this.pollingFast = false }
9435};
9436
9437TextareaInput.prototype.onKeyPress = function () {
9438  if (ie && ie_version >= 9) { this.hasSelection = null }
9439  this.fastPoll()
9440};
9441
9442TextareaInput.prototype.onContextMenu = function (e) {
9443  var input = this, cm = input.cm, display = cm.display, te = input.textarea
9444  var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop
9445  if (!pos || presto) { return } // Opera is difficult.
9446
9447  // Reset the current text selection only if the click is done outside of the selection
9448  // and 'resetSelectionOnContextMenu' option is true.
9449  var reset = cm.options.resetSelectionOnContextMenu
9450  if (reset && cm.doc.sel.contains(pos) == -1)
9451    { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll) }
9452
9453  var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText
9454  input.wrapper.style.cssText = "position: absolute"
9455  var wrapperBox = input.wrapper.getBoundingClientRect()
9456  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);"
9457  var oldScrollY
9458  if (webkit) { oldScrollY = window.scrollY } // Work around Chrome issue (#2712)
9459  display.input.focus()
9460  if (webkit) { window.scrollTo(null, oldScrollY) }
9461  display.input.reset()
9462  // Adds "Select all" to context menu in FF
9463  if (!cm.somethingSelected()) { te.value = input.prevInput = " " }
9464  input.contextMenuPending = true
9465  display.selForContextMenu = cm.doc.sel
9466  clearTimeout(display.detectingSelectAll)
9467
9468  // Select-all will be greyed out if there's nothing to select, so
9469  // this adds a zero-width space so that we can later check whether
9470  // it got selected.
9471  function prepareSelectAllHack() {
9472    if (te.selectionStart != null) {
9473      var selected = cm.somethingSelected()
9474      var extval = "\u200b" + (selected ? te.value : "")
9475      te.value = "\u21da" // Used to catch context-menu undo
9476      te.value = extval
9477      input.prevInput = selected ? "" : "\u200b"
9478      te.selectionStart = 1; te.selectionEnd = extval.length
9479      // Re-set this, in case some other handler touched the
9480      // selection in the meantime.
9481      display.selForContextMenu = cm.doc.sel
9482    }
9483  }
9484  function rehide() {
9485    input.contextMenuPending = false
9486    input.wrapper.style.cssText = oldWrapperCSS
9487    te.style.cssText = oldCSS
9488    if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos) }
9489
9490    // Try to detect the user choosing select-all
9491    if (te.selectionStart != null) {
9492      if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack() }
9493      var i = 0, poll = function () {
9494        if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 &&
9495            te.selectionEnd > 0 && input.prevInput == "\u200b") {
9496          operation(cm, selectAll)(cm)
9497        } else if (i++ < 10) {
9498          display.detectingSelectAll = setTimeout(poll, 500)
9499        } else {
9500          display.selForContextMenu = null
9501          display.input.reset()
9502        }
9503      }
9504      display.detectingSelectAll = setTimeout(poll, 200)
9505    }
9506  }
9507
9508  if (ie && ie_version >= 9) { prepareSelectAllHack() }
9509  if (captureRightClick) {
9510    e_stop(e)
9511    var mouseup = function () {
9512      off(window, "mouseup", mouseup)
9513      setTimeout(rehide, 20)
9514    }
9515    on(window, "mouseup", mouseup)
9516  } else {
9517    setTimeout(rehide, 50)
9518  }
9519};
9520
9521TextareaInput.prototype.readOnlyChanged = function (val) {
9522  if (!val) { this.reset() }
9523  this.textarea.disabled = val == "nocursor"
9524};
9525
9526TextareaInput.prototype.setUneditable = function () {};
9527
9528TextareaInput.prototype.needsContentAttribute = false
9529
9530function fromTextArea(textarea, options) {
9531  options = options ? copyObj(options) : {}
9532  options.value = textarea.value
9533  if (!options.tabindex && textarea.tabIndex)
9534    { options.tabindex = textarea.tabIndex }
9535  if (!options.placeholder && textarea.placeholder)
9536    { options.placeholder = textarea.placeholder }
9537  // Set autofocus to true if this textarea is focused, or if it has
9538  // autofocus and no other element is focused.
9539  if (options.autofocus == null) {
9540    var hasFocus = activeElt()
9541    options.autofocus = hasFocus == textarea ||
9542      textarea.getAttribute("autofocus") != null && hasFocus == document.body
9543  }
9544
9545  function save() {textarea.value = cm.getValue()}
9546
9547  var realSubmit
9548  if (textarea.form) {
9549    on(textarea.form, "submit", save)
9550    // Deplorable hack to make the submit method do the right thing.
9551    if (!options.leaveSubmitMethodAlone) {
9552      var form = textarea.form
9553      realSubmit = form.submit
9554      try {
9555        var wrappedSubmit = form.submit = function () {
9556          save()
9557          form.submit = realSubmit
9558          form.submit()
9559          form.submit = wrappedSubmit
9560        }
9561      } catch(e) {}
9562    }
9563  }
9564
9565  options.finishInit = function (cm) {
9566    cm.save = save
9567    cm.getTextArea = function () { return textarea; }
9568    cm.toTextArea = function () {
9569      cm.toTextArea = isNaN // Prevent this from being ran twice
9570      save()
9571      textarea.parentNode.removeChild(cm.getWrapperElement())
9572      textarea.style.display = ""
9573      if (textarea.form) {
9574        off(textarea.form, "submit", save)
9575        if (typeof textarea.form.submit == "function")
9576          { textarea.form.submit = realSubmit }
9577      }
9578    }
9579  }
9580
9581  textarea.style.display = "none"
9582  var cm = CodeMirror(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); },
9583    options)
9584  return cm
9585}
9586
9587function addLegacyProps(CodeMirror) {
9588  CodeMirror.off = off
9589  CodeMirror.on = on
9590  CodeMirror.wheelEventPixels = wheelEventPixels
9591  CodeMirror.Doc = Doc
9592  CodeMirror.splitLines = splitLinesAuto
9593  CodeMirror.countColumn = countColumn
9594  CodeMirror.findColumn = findColumn
9595  CodeMirror.isWordChar = isWordCharBasic
9596  CodeMirror.Pass = Pass
9597  CodeMirror.signal = signal
9598  CodeMirror.Line = Line
9599  CodeMirror.changeEnd = changeEnd
9600  CodeMirror.scrollbarModel = scrollbarModel
9601  CodeMirror.Pos = Pos
9602  CodeMirror.cmpPos = cmp
9603  CodeMirror.modes = modes
9604  CodeMirror.mimeModes = mimeModes
9605  CodeMirror.resolveMode = resolveMode
9606  CodeMirror.getMode = getMode
9607  CodeMirror.modeExtensions = modeExtensions
9608  CodeMirror.extendMode = extendMode
9609  CodeMirror.copyState = copyState
9610  CodeMirror.startState = startState
9611  CodeMirror.innerMode = innerMode
9612  CodeMirror.commands = commands
9613  CodeMirror.keyMap = keyMap
9614  CodeMirror.keyName = keyName
9615  CodeMirror.isModifierKey = isModifierKey
9616  CodeMirror.lookupKey = lookupKey
9617  CodeMirror.normalizeKeyMap = normalizeKeyMap
9618  CodeMirror.StringStream = StringStream
9619  CodeMirror.SharedTextMarker = SharedTextMarker
9620  CodeMirror.TextMarker = TextMarker
9621  CodeMirror.LineWidget = LineWidget
9622  CodeMirror.e_preventDefault = e_preventDefault
9623  CodeMirror.e_stopPropagation = e_stopPropagation
9624  CodeMirror.e_stop = e_stop
9625  CodeMirror.addClass = addClass
9626  CodeMirror.contains = contains
9627  CodeMirror.rmClass = rmClass
9628  CodeMirror.keyNames = keyNames
9629}
9630
9631// EDITOR CONSTRUCTOR
9632
9633defineOptions(CodeMirror)
9634
9635addEditorMethods(CodeMirror)
9636
9637// Set up methods on CodeMirror's prototype to redirect to the editor's document.
9638var dontDelegate = "iter insert remove copy getEditor constructor".split(" ")
9639for (var prop in Doc.prototype) { if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
9640  { CodeMirror.prototype[prop] = (function(method) {
9641    return function() {return method.apply(this.doc, arguments)}
9642  })(Doc.prototype[prop]) } }
9643
9644eventMixin(Doc)
9645
9646// INPUT HANDLING
9647
9648CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput}
9649
9650// MODE DEFINITION AND QUERYING
9651
9652// Extra arguments are stored as the mode's dependencies, which is
9653// used by (legacy) mechanisms like loadmode.js to automatically
9654// load a mode. (Preferred mechanism is the require/define calls.)
9655CodeMirror.defineMode = function(name/*, mode, …*/) {
9656  if (!CodeMirror.defaults.mode && name != "null") { CodeMirror.defaults.mode = name }
9657  defineMode.apply(this, arguments)
9658}
9659
9660CodeMirror.defineMIME = defineMIME
9661
9662// Minimal default mode.
9663CodeMirror.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); })
9664CodeMirror.defineMIME("text/plain", "null")
9665
9666// EXTENSIONS
9667
9668CodeMirror.defineExtension = function (name, func) {
9669  CodeMirror.prototype[name] = func
9670}
9671CodeMirror.defineDocExtension = function (name, func) {
9672  Doc.prototype[name] = func
9673}
9674
9675CodeMirror.fromTextArea = fromTextArea
9676
9677addLegacyProps(CodeMirror)
9678
9679CodeMirror.version = "5.39.0"
9680
9681return CodeMirror;
9682
9683})));

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.