[ Index ]

PHP Cross Reference of MyBB 1.8.33

title

Body

[close]

/admin/jscripts/codemirror/lib/ -> codemirror.js (source)

   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.
  18  var userAgent = navigator.userAgent
  19  var platform = navigator.platform
  20  
  21  var gecko = /gecko\/\d/i.test(userAgent)
  22  var ie_upto10 = /MSIE \d/.test(userAgent)
  23  var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent)
  24  var edge = /Edge\/(\d+)/.exec(userAgent)
  25  var ie = ie_upto10 || ie_11up || edge
  26  var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1])
  27  var webkit = !edge && /WebKit\//.test(userAgent)
  28  var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent)
  29  var chrome = !edge && /Chrome\//.test(userAgent)
  30  var presto = /Opera\//.test(userAgent)
  31  var safari = /Apple Computer/.test(navigator.vendor)
  32  var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent)
  33  var phantom = /PhantomJS/.test(userAgent)
  34  
  35  var ios = !edge && /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent)
  36  var android = /Android/.test(userAgent)
  37  // This is woefully incomplete. Suggestions for alternative methods welcome.
  38  var mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent)
  39  var mac = ios || /Mac/.test(platform)
  40  var chromeOS = /\bCrOS\b/.test(userAgent)
  41  var windows = /win/i.test(platform)
  42  
  43  var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/)
  44  if (presto_version) { presto_version = Number(presto_version[1]) }
  45  if (presto_version && presto_version >= 15) { presto = false; webkit = true }
  46  // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
  47  var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11))
  48  var captureRightClick = gecko || (ie && ie_version >= 9)
  49  
  50  function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") }
  51  
  52  var 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  
  61  function removeChildren(e) {
  62    for (var count = e.childNodes.length; count > 0; --count)
  63      { e.removeChild(e.firstChild) }
  64    return e
  65  }
  66  
  67  function removeChildrenAndAdd(parent, e) {
  68    return removeChildren(parent).appendChild(e)
  69  }
  70  
  71  function 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
  80  function eltP(tag, content, className, style) {
  81    var e = elt(tag, content, className, style)
  82    e.setAttribute("role", "presentation")
  83    return e
  84  }
  85  
  86  var range
  87  if (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  } }
  93  else { 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  
 103  function 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  
 114  function 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  
 129  function addClass(node, cls) {
 130    var current = node.className
 131    if (!classTest(cls).test(current)) { node.className += (current ? " " : "") + cls }
 132  }
 133  function 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  
 140  var selectInput = function(node) { node.select() }
 141  if (ios) // Mobile Safari apparently has a bug where select() is broken.
 142    { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length } }
 143  else if (ie) // Suppress mysterious IE10 errors
 144    { selectInput = function(node) { try { node.select() } catch(_e) {} } }
 145  
 146  function bind(f) {
 147    var args = Array.prototype.slice.call(arguments, 1)
 148    return function(){return f.apply(null, args)}
 149  }
 150  
 151  function 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.
 161  function 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  
 176  var Delayed = function() {this.id = null};
 177  Delayed.prototype.set = function (ms, f) {
 178    clearTimeout(this.id)
 179    this.id = setTimeout(f, ms)
 180  };
 181  
 182  function 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
 189  var scrollerGap = 30
 190  
 191  // Returned or thrown by various protocols to signal 'I'm not
 192  // handling this'.
 193  var Pass = {toString: function(){return "CodeMirror.Pass"}}
 194  
 195  // Reused option objects for setSelection & friends
 196  var sel_dontScroll = {scroll: false};
 197  var sel_mouse = {origin: "*mouse"};
 198  var sel_move = {origin: "+move"};
 199  // The inverse of countColumn -- find the offset that corresponds to
 200  // a particular column.
 201  function 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  
 215  var spaceStrs = [""]
 216  function spaceStr(n) {
 217    while (spaceStrs.length <= n)
 218      { spaceStrs.push(lst(spaceStrs) + " ") }
 219    return spaceStrs[n]
 220  }
 221  
 222  function lst(arr) { return arr[arr.length-1] }
 223  
 224  function 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  
 230  function 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  
 236  function nothing() {}
 237  
 238  function 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  
 250  var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/
 251  function isWordCharBasic(ch) {
 252    return /\w/.test(ch) || ch > "\x80" &&
 253      (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch))
 254  }
 255  function 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  
 261  function 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.
 271  var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/
 272  function 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.
 275  function 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`.
 283  function 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  
 300  function 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 cursors.
 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.
 400  function 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.
 416  function 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.
 428  function 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.
 436  function 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.
 443  function 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.
 457  function 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  
 477  function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size}
 478  
 479  function lineNumberFor(options, i) {
 480    return String(options.lineNumberFormatter(i + options.firstLineNumber))
 481  }
 482  
 483  // A Pos instance represents a position within the text.
 484  function 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.
 495  function cmp(a, b) { return a.line - b.line || a.ch - b.ch }
 496  
 497  function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 }
 498  
 499  function copyPos(x) {return Pos(x.line, x.ch)}
 500  function maxPos(a, b) { return cmp(a, b) < 0 ? b : a }
 501  function 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.
 505  function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))}
 506  function 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  }
 512  function 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  }
 518  function 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.
 525  var sawReadOnlySpans = false;
 526  var sawCollapsedSpans = false;
 527  function seeReadOnlySpans() {
 528    sawReadOnlySpans = true
 529  }
 530  
 531  function seeCollapsedSpans() {
 532    sawCollapsedSpans = true
 533  }
 534  
 535  // TEXTMARKER SPANS
 536  
 537  function 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.
 543  function 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).
 551  function 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.
 558  function 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).
 567  function 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  }
 579  function 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.
 599  function 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.
 661  function 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.
 672  function 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.
 701  function 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  }
 708  function 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.
 717  function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 }
 718  function 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.
 723  function 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.
 736  function 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  }
 746  function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) }
 747  function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) }
 748  
 749  // Test whether there exists a collapsed span that partially
 750  // overlaps (covers the start or end, but not both) of a new span.
 751  // Such overlap is not allowed.
 752  function conflictingCollapsedRange(doc, lineNo, from, to, marker) {
 753    var line = getLine(doc, lineNo)
 754    var sps = sawCollapsedSpans && line.markedSpans
 755    if (sps) { for (var i = 0; i < sps.length; ++i) {
 756      var sp = sps[i]
 757      if (!sp.marker.collapsed) { continue }
 758      var found = sp.marker.find(0)
 759      var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker)
 760      var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker)
 761      if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue }
 762      if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) ||
 763          fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0))
 764        { return true }
 765    } }
 766  }
 767  
 768  // A visual line is a line as drawn on the screen. Folding, for
 769  // example, can cause multiple logical lines to appear on the same
 770  // visual line. This finds the start of the visual line that the
 771  // given line is part of (usually that is the line itself).
 772  function visualLine(line) {
 773    var merged
 774    while (merged = collapsedSpanAtStart(line))
 775      { line = merged.find(-1, true).line }
 776    return line
 777  }
 778  
 779  function visualLineEnd(line) {
 780    var merged
 781    while (merged = collapsedSpanAtEnd(line))
 782      { line = merged.find(1, true).line }
 783    return line
 784  }
 785  
 786  // Returns an array of logical lines that continue the visual line
 787  // started by the argument, or undefined if there are no such lines.
 788  function visualLineContinued(line) {
 789    var merged, lines
 790    while (merged = collapsedSpanAtEnd(line)) {
 791      line = merged.find(1, true).line
 792      ;(lines || (lines = [])).push(line)
 793    }
 794    return lines
 795  }
 796  
 797  // Get the line number of the start of the visual line that the
 798  // given line number is part of.
 799  function visualLineNo(doc, lineN) {
 800    var line = getLine(doc, lineN), vis = visualLine(line)
 801    if (line == vis) { return lineN }
 802    return lineNo(vis)
 803  }
 804  
 805  // Get the line number of the start of the next visual line after
 806  // the given line.
 807  function visualLineEndNo(doc, lineN) {
 808    if (lineN > doc.lastLine()) { return lineN }
 809    var line = getLine(doc, lineN), merged
 810    if (!lineIsHidden(doc, line)) { return lineN }
 811    while (merged = collapsedSpanAtEnd(line))
 812      { line = merged.find(1, true).line }
 813    return lineNo(line) + 1
 814  }
 815  
 816  // Compute whether a line is hidden. Lines count as hidden when they
 817  // are part of a visual line that starts with another line, or when
 818  // they are entirely covered by collapsed, non-widget span.
 819  function lineIsHidden(doc, line) {
 820    var sps = sawCollapsedSpans && line.markedSpans
 821    if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
 822      sp = sps[i]
 823      if (!sp.marker.collapsed) { continue }
 824      if (sp.from == null) { return true }
 825      if (sp.marker.widgetNode) { continue }
 826      if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
 827        { return true }
 828    } }
 829  }
 830  function lineIsHiddenInner(doc, line, span) {
 831    if (span.to == null) {
 832      var end = span.marker.find(1, true)
 833      return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker))
 834    }
 835    if (span.marker.inclusiveRight && span.to == line.text.length)
 836      { return true }
 837    for (var sp = (void 0), i = 0; i < line.markedSpans.length; ++i) {
 838      sp = line.markedSpans[i]
 839      if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to &&
 840          (sp.to == null || sp.to != span.from) &&
 841          (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
 842          lineIsHiddenInner(doc, line, sp)) { return true }
 843    }
 844  }
 845  
 846  // Find the height above the given line.
 847  function heightAtLine(lineObj) {
 848    lineObj = visualLine(lineObj)
 849  
 850    var h = 0, chunk = lineObj.parent
 851    for (var i = 0; i < chunk.lines.length; ++i) {
 852      var line = chunk.lines[i]
 853      if (line == lineObj) { break }
 854      else { h += line.height }
 855    }
 856    for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
 857      for (var i$1 = 0; i$1 < p.children.length; ++i$1) {
 858        var cur = p.children[i$1]
 859        if (cur == chunk) { break }
 860        else { h += cur.height }
 861      }
 862    }
 863    return h
 864  }
 865  
 866  // Compute the character length of a line, taking into account
 867  // collapsed ranges (see markText) that might hide parts, and join
 868  // other lines onto it.
 869  function lineLength(line) {
 870    if (line.height == 0) { return 0 }
 871    var len = line.text.length, merged, cur = line
 872    while (merged = collapsedSpanAtStart(cur)) {
 873      var found = merged.find(0, true)
 874      cur = found.from.line
 875      len += found.from.ch - found.to.ch
 876    }
 877    cur = line
 878    while (merged = collapsedSpanAtEnd(cur)) {
 879      var found$1 = merged.find(0, true)
 880      len -= cur.text.length - found$1.from.ch
 881      cur = found$1.to.line
 882      len += cur.text.length - found$1.to.ch
 883    }
 884    return len
 885  }
 886  
 887  // Find the longest line in the document.
 888  function findMaxLine(cm) {
 889    var d = cm.display, doc = cm.doc
 890    d.maxLine = getLine(doc, doc.first)
 891    d.maxLineLength = lineLength(d.maxLine)
 892    d.maxLineChanged = true
 893    doc.iter(function (line) {
 894      var len = lineLength(line)
 895      if (len > d.maxLineLength) {
 896        d.maxLineLength = len
 897        d.maxLine = line
 898      }
 899    })
 900  }
 901  
 902  // BIDI HELPERS
 903  
 904  function iterateBidiSections(order, from, to, f) {
 905    if (!order) { return f(from, to, "ltr", 0) }
 906    var found = false
 907    for (var i = 0; i < order.length; ++i) {
 908      var part = order[i]
 909      if (part.from < to && part.to > from || from == to && part.to == from) {
 910        f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr", i)
 911        found = true
 912      }
 913    }
 914    if (!found) { f(from, to, "ltr") }
 915  }
 916  
 917  var bidiOther = null
 918  function getBidiPartAt(order, ch, sticky) {
 919    var found
 920    bidiOther = null
 921    for (var i = 0; i < order.length; ++i) {
 922      var cur = order[i]
 923      if (cur.from < ch && cur.to > ch) { return i }
 924      if (cur.to == ch) {
 925        if (cur.from != cur.to && sticky == "before") { found = i }
 926        else { bidiOther = i }
 927      }
 928      if (cur.from == ch) {
 929        if (cur.from != cur.to && sticky != "before") { found = i }
 930        else { bidiOther = i }
 931      }
 932    }
 933    return found != null ? found : bidiOther
 934  }
 935  
 936  // Bidirectional ordering algorithm
 937  // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
 938  // that this (partially) implements.
 939  
 940  // One-char codes used for character types:
 941  // L (L):   Left-to-Right
 942  // R (R):   Right-to-Left
 943  // r (AL):  Right-to-Left Arabic
 944  // 1 (EN):  European Number
 945  // + (ES):  European Number Separator
 946  // % (ET):  European Number Terminator
 947  // n (AN):  Arabic Number
 948  // , (CS):  Common Number Separator
 949  // m (NSM): Non-Spacing Mark
 950  // b (BN):  Boundary Neutral
 951  // s (B):   Paragraph Separator
 952  // t (S):   Segment Separator
 953  // w (WS):  Whitespace
 954  // N (ON):  Other Neutrals
 955  
 956  // Returns null if characters are ordered as they appear
 957  // (left-to-right), or an array of sections ({from, to, level}
 958  // objects) in the order in which they occur visually.
 959  var bidiOrdering = (function() {
 960    // Character types for codepoints 0 to 0xff
 961    var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN"
 962    // Character types for codepoints 0x600 to 0x6f9
 963    var arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111"
 964    function charType(code) {
 965      if (code <= 0xf7) { return lowTypes.charAt(code) }
 966      else if (0x590 <= code && code <= 0x5f4) { return "R" }
 967      else if (0x600 <= code && code <= 0x6f9) { return arabicTypes.charAt(code - 0x600) }
 968      else if (0x6ee <= code && code <= 0x8ac) { return "r" }
 969      else if (0x2000 <= code && code <= 0x200b) { return "w" }
 970      else if (code == 0x200c) { return "b" }
 971      else { return "L" }
 972    }
 973  
 974    var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/
 975    var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/
 976  
 977    function BidiSpan(level, from, to) {
 978      this.level = level
 979      this.from = from; this.to = to
 980    }
 981  
 982    return function(str, direction) {
 983      var outerType = direction == "ltr" ? "L" : "R"
 984  
 985      if (str.length == 0 || direction == "ltr" && !bidiRE.test(str)) { return false }
 986      var len = str.length, types = []
 987      for (var i = 0; i < len; ++i)
 988        { types.push(charType(str.charCodeAt(i))) }
 989  
 990      // W1. Examine each non-spacing mark (NSM) in the level run, and
 991      // change the type of the NSM to the type of the previous
 992      // character. If the NSM is at the start of the level run, it will
 993      // get the type of sor.
 994      for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) {
 995        var type = types[i$1]
 996        if (type == "m") { types[i$1] = prev }
 997        else { prev = type }
 998      }
 999  
1000      // W2. Search backwards from each instance of a European number
1001      // until the first strong type (R, L, AL, or sor) is found. If an
1002      // AL is found, change the type of the European number to Arabic
1003      // number.
1004      // W3. Change all ALs to R.
1005      for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) {
1006        var type$1 = types[i$2]
1007        if (type$1 == "1" && cur == "r") { types[i$2] = "n" }
1008        else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == "r") { types[i$2] = "R" } }
1009      }
1010  
1011      // W4. A single European separator between two European numbers
1012      // changes to a European number. A single common separator between
1013      // two numbers of the same type changes to that type.
1014      for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) {
1015        var type$2 = types[i$3]
1016        if (type$2 == "+" && prev$1 == "1" && types[i$3+1] == "1") { types[i$3] = "1" }
1017        else if (type$2 == "," && prev$1 == types[i$3+1] &&
1018                 (prev$1 == "1" || prev$1 == "n")) { types[i$3] = prev$1 }
1019        prev$1 = type$2
1020      }
1021  
1022      // W5. A sequence of European terminators adjacent to European
1023      // numbers changes to all European numbers.
1024      // W6. Otherwise, separators and terminators change to Other
1025      // Neutral.
1026      for (var i$4 = 0; i$4 < len; ++i$4) {
1027        var type$3 = types[i$4]
1028        if (type$3 == ",") { types[i$4] = "N" }
1029        else if (type$3 == "%") {
1030          var end = (void 0)
1031          for (end = i$4 + 1; end < len && types[end] == "%"; ++end) {}
1032          var replace = (i$4 && types[i$4-1] == "!") || (end < len && types[end] == "1") ? "1" : "N"
1033          for (var j = i$4; j < end; ++j) { types[j] = replace }
1034          i$4 = end - 1
1035        }
1036      }
1037  
1038      // W7. Search backwards from each instance of a European number
1039      // until the first strong type (R, L, or sor) is found. If an L is
1040      // found, then change the type of the European number to L.
1041      for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) {
1042        var type$4 = types[i$5]
1043        if (cur$1 == "L" && type$4 == "1") { types[i$5] = "L" }
1044        else if (isStrong.test(type$4)) { cur$1 = type$4 }
1045      }
1046  
1047      // N1. A sequence of neutrals takes the direction of the
1048      // surrounding strong text if the text on both sides has the same
1049      // direction. European and Arabic numbers act as if they were R in
1050      // terms of their influence on neutrals. Start-of-level-run (sor)
1051      // and end-of-level-run (eor) are used at level run boundaries.
1052      // N2. Any remaining neutrals take the embedding direction.
1053      for (var i$6 = 0; i$6 < len; ++i$6) {
1054        if (isNeutral.test(types[i$6])) {
1055          var end$1 = (void 0)
1056          for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {}
1057          var before = (i$6 ? types[i$6-1] : outerType) == "L"
1058          var after = (end$1 < len ? types[end$1] : outerType) == "L"
1059          var replace$1 = before == after ? (before ? "L" : "R") : outerType
1060          for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1 }
1061          i$6 = end$1 - 1
1062        }
1063      }
1064  
1065      // Here we depart from the documented algorithm, in order to avoid
1066      // building up an actual levels array. Since there are only three
1067      // levels (0, 1, 2) in an implementation that doesn't take
1068      // explicit embedding into account, we can build up the order on
1069      // the fly, without following the level-based algorithm.
1070      var order = [], m
1071      for (var i$7 = 0; i$7 < len;) {
1072        if (countsAsLeft.test(types[i$7])) {
1073          var start = i$7
1074          for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {}
1075          order.push(new BidiSpan(0, start, i$7))
1076        } else {
1077          var pos = i$7, at = order.length
1078          for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {}
1079          for (var j$2 = pos; j$2 < i$7;) {
1080            if (countsAsNum.test(types[j$2])) {
1081              if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)) }
1082              var nstart = j$2
1083              for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {}
1084              order.splice(at, 0, new BidiSpan(2, nstart, j$2))
1085              pos = j$2
1086            } else { ++j$2 }
1087          }
1088          if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)) }
1089        }
1090      }
1091      if (order[0].level == 1 && (m = str.match(/^\s+/))) {
1092        order[0].from = m[0].length
1093        order.unshift(new BidiSpan(0, 0, m[0].length))
1094      }
1095      if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
1096        lst(order).to -= m[0].length
1097        order.push(new BidiSpan(0, len - m[0].length, len))
1098      }
1099  
1100      return direction == "rtl" ? order.reverse() : order
1101    }
1102  })()
1103  
1104  // Get the bidi ordering for the given line (and cache it). Returns
1105  // false for lines that are fully left-to-right, and an array of
1106  // BidiSpan objects otherwise.
1107  function getOrder(line, direction) {
1108    var order = line.order
1109    if (order == null) { order = line.order = bidiOrdering(line.text, direction) }
1110    return order
1111  }
1112  
1113  // EVENT HANDLING
1114  
1115  // Lightweight event framework. on/off also work on DOM nodes,
1116  // registering native DOM handlers.
1117  
1118  var noHandlers = []
1119  
1120  var on = function(emitter, type, f) {
1121    if (emitter.addEventListener) {
1122      emitter.addEventListener(type, f, false)
1123    } else if (emitter.attachEvent) {
1124      emitter.attachEvent("on" + type, f)
1125    } else {
1126      var map = emitter._handlers || (emitter._handlers = {})
1127      map[type] = (map[type] || noHandlers).concat(f)
1128    }
1129  }
1130  
1131  function getHandlers(emitter, type) {
1132    return emitter._handlers && emitter._handlers[type] || noHandlers
1133  }
1134  
1135  function off(emitter, type, f) {
1136    if (emitter.removeEventListener) {
1137      emitter.removeEventListener(type, f, false)
1138    } else if (emitter.detachEvent) {
1139      emitter.detachEvent("on" + type, f)
1140    } else {
1141      var map = emitter._handlers, arr = map && map[type]
1142      if (arr) {
1143        var index = indexOf(arr, f)
1144        if (index > -1)
1145          { map[type] = arr.slice(0, index).concat(arr.slice(index + 1)) }
1146      }
1147    }
1148  }
1149  
1150  function signal(emitter, type /*, values...*/) {
1151    var handlers = getHandlers(emitter, type)
1152    if (!handlers.length) { return }
1153    var args = Array.prototype.slice.call(arguments, 2)
1154    for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args) }
1155  }
1156  
1157  // The DOM events that CodeMirror handles can be overridden by
1158  // registering a (non-DOM) handler on the editor for the event name,
1159  // and preventDefault-ing the event in that handler.
1160  function signalDOMEvent(cm, e, override) {
1161    if (typeof e == "string")
1162      { e = {type: e, preventDefault: function() { this.defaultPrevented = true }} }
1163    signal(cm, override || e.type, cm, e)
1164    return e_defaultPrevented(e) || e.codemirrorIgnore
1165  }
1166  
1167  function signalCursorActivity(cm) {
1168    var arr = cm._handlers && cm._handlers.cursorActivity
1169    if (!arr) { return }
1170    var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = [])
1171    for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1)
1172      { set.push(arr[i]) } }
1173  }
1174  
1175  function hasHandler(emitter, type) {
1176    return getHandlers(emitter, type).length > 0
1177  }
1178  
1179  // Add on and off methods to a constructor's prototype, to make
1180  // registering events on such objects more convenient.
1181  function eventMixin(ctor) {
1182    ctor.prototype.on = function(type, f) {on(this, type, f)}
1183    ctor.prototype.off = function(type, f) {off(this, type, f)}
1184  }
1185  
1186  // Due to the fact that we still support jurassic IE versions, some
1187  // compatibility wrappers are needed.
1188  
1189  function e_preventDefault(e) {
1190    if (e.preventDefault) { e.preventDefault() }
1191    else { e.returnValue = false }
1192  }
1193  function e_stopPropagation(e) {
1194    if (e.stopPropagation) { e.stopPropagation() }
1195    else { e.cancelBubble = true }
1196  }
1197  function e_defaultPrevented(e) {
1198    return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false
1199  }
1200  function e_stop(e) {e_preventDefault(e); e_stopPropagation(e)}
1201  
1202  function e_target(e) {return e.target || e.srcElement}
1203  function e_button(e) {
1204    var b = e.which
1205    if (b == null) {
1206      if (e.button & 1) { b = 1 }
1207      else if (e.button & 2) { b = 3 }
1208      else if (e.button & 4) { b = 2 }
1209    }
1210    if (mac && e.ctrlKey && b == 1) { b = 3 }
1211    return b
1212  }
1213  
1214  // Detect drag-and-drop
1215  var dragAndDrop = function() {
1216    // There is *some* kind of drag-and-drop support in IE6-8, but I
1217    // couldn't get it to work yet.
1218    if (ie && ie_version < 9) { return false }
1219    var div = elt('div')
1220    return "draggable" in div || "dragDrop" in div
1221  }()
1222  
1223  var zwspSupported
1224  function zeroWidthElement(measure) {
1225    if (zwspSupported == null) {
1226      var test = elt("span", "\u200b")
1227      removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]))
1228      if (measure.firstChild.offsetHeight != 0)
1229        { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8) }
1230    }
1231    var node = zwspSupported ? elt("span", "\u200b") :
1232      elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px")
1233    node.setAttribute("cm-text", "")
1234    return node
1235  }
1236  
1237  // Feature-detect IE's crummy client rect reporting for bidi text
1238  var badBidiRects
1239  function hasBadBidiRects(measure) {
1240    if (badBidiRects != null) { return badBidiRects }
1241    var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA"))
1242    var r0 = range(txt, 0, 1).getBoundingClientRect()
1243    var r1 = range(txt, 1, 2).getBoundingClientRect()
1244    removeChildren(measure)
1245    if (!r0 || r0.left == r0.right) { return false } // Safari returns null in some cases (#2780)
1246    return badBidiRects = (r1.right - r0.right < 3)
1247  }
1248  
1249  // See if "".split is the broken IE version, if so, provide an
1250  // alternative way to split lines.
1251  var splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function (string) {
1252    var pos = 0, result = [], l = string.length
1253    while (pos <= l) {
1254      var nl = string.indexOf("\n", pos)
1255      if (nl == -1) { nl = string.length }
1256      var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl)
1257      var rt = line.indexOf("\r")
1258      if (rt != -1) {
1259        result.push(line.slice(0, rt))
1260        pos += rt + 1
1261      } else {
1262        result.push(line)
1263        pos = nl + 1
1264      }
1265    }
1266    return result
1267  } : function (string) { return string.split(/\r\n?|\n/); }
1268  
1269  var hasSelection = window.getSelection ? function (te) {
1270    try { return te.selectionStart != te.selectionEnd }
1271    catch(e) { return false }
1272  } : function (te) {
1273    var range
1274    try {range = te.ownerDocument.selection.createRange()}
1275    catch(e) {}
1276    if (!range || range.parentElement() != te) { return false }
1277    return range.compareEndPoints("StartToEnd", range) != 0
1278  }
1279  
1280  var hasCopyEvent = (function () {
1281    var e = elt("div")
1282    if ("oncopy" in e) { return true }
1283    e.setAttribute("oncopy", "return;")
1284    return typeof e.oncopy == "function"
1285  })()
1286  
1287  var badZoomedRects = null
1288  function hasBadZoomedRects(measure) {
1289    if (badZoomedRects != null) { return badZoomedRects }
1290    var node = removeChildrenAndAdd(measure, elt("span", "x"))
1291    var normal = node.getBoundingClientRect()
1292    var fromRange = range(node, 0, 1).getBoundingClientRect()
1293    return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1
1294  }
1295  
1296  var modes = {};
1297  var mimeModes = {};
1298  // Extra arguments are stored as the mode's dependencies, which is
1299  // used by (legacy) mechanisms like loadmode.js to automatically
1300  // load a mode. (Preferred mechanism is the require/define calls.)
1301  function defineMode(name, mode) {
1302    if (arguments.length > 2)
1303      { mode.dependencies = Array.prototype.slice.call(arguments, 2) }
1304    modes[name] = mode
1305  }
1306  
1307  function defineMIME(mime, spec) {
1308    mimeModes[mime] = spec
1309  }
1310  
1311  // Given a MIME type, a {name, ...options} config object, or a name
1312  // string, return a mode config object.
1313  function resolveMode(spec) {
1314    if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
1315      spec = mimeModes[spec]
1316    } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
1317      var found = mimeModes[spec.name]
1318      if (typeof found == "string") { found = {name: found} }
1319      spec = createObj(found, spec)
1320      spec.name = found.name
1321    } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
1322      return resolveMode("application/xml")
1323    } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) {
1324      return resolveMode("application/json")
1325    }
1326    if (typeof spec == "string") { return {name: spec} }
1327    else { return spec || {name: "null"} }
1328  }
1329  
1330  // Given a mode spec (anything that resolveMode accepts), find and
1331  // initialize an actual mode object.
1332  function getMode(options, spec) {
1333    spec = resolveMode(spec)
1334    var mfactory = modes[spec.name]
1335    if (!mfactory) { return getMode(options, "text/plain") }
1336    var modeObj = mfactory(options, spec)
1337    if (modeExtensions.hasOwnProperty(spec.name)) {
1338      var exts = modeExtensions[spec.name]
1339      for (var prop in exts) {
1340        if (!exts.hasOwnProperty(prop)) { continue }
1341        if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop] }
1342        modeObj[prop] = exts[prop]
1343      }
1344    }
1345    modeObj.name = spec.name
1346    if (spec.helperType) { modeObj.helperType = spec.helperType }
1347    if (spec.modeProps) { for (var prop$1 in spec.modeProps)
1348      { modeObj[prop$1] = spec.modeProps[prop$1] } }
1349  
1350    return modeObj
1351  }
1352  
1353  // This can be used to attach properties to mode objects from
1354  // outside the actual mode definition.
1355  var modeExtensions = {}
1356  function extendMode(mode, properties) {
1357    var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {})
1358    copyObj(properties, exts)
1359  }
1360  
1361  function copyState(mode, state) {
1362    if (state === true) { return state }
1363    if (mode.copyState) { return mode.copyState(state) }
1364    var nstate = {}
1365    for (var n in state) {
1366      var val = state[n]
1367      if (val instanceof Array) { val = val.concat([]) }
1368      nstate[n] = val
1369    }
1370    return nstate
1371  }
1372  
1373  // Given a mode and a state (for that mode), find the inner mode and
1374  // state at the position that the state refers to.
1375  function innerMode(mode, state) {
1376    var info
1377    while (mode.innerMode) {
1378      info = mode.innerMode(state)
1379      if (!info || info.mode == mode) { break }
1380      state = info.state
1381      mode = info.mode
1382    }
1383    return info || {mode: mode, state: state}
1384  }
1385  
1386  function startState(mode, a1, a2) {
1387    return mode.startState ? mode.startState(a1, a2) : true
1388  }
1389  
1390  // STRING STREAM
1391  
1392  // Fed to the mode parsers, provides helper functions to make
1393  // parsers more succinct.
1394  
1395  var StringStream = function(string, tabSize, lineOracle) {
1396    this.pos = this.start = 0
1397    this.string = string
1398    this.tabSize = tabSize || 8
1399    this.lastColumnPos = this.lastColumnValue = 0
1400    this.lineStart = 0
1401    this.lineOracle = lineOracle
1402  };
1403  
1404  StringStream.prototype.eol = function () {return this.pos >= this.string.length};
1405  StringStream.prototype.sol = function () {return this.pos == this.lineStart};
1406  StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined};
1407  StringStream.prototype.next = function () {
1408    if (this.pos < this.string.length)
1409      { return this.string.charAt(this.pos++) }
1410  };
1411  StringStream.prototype.eat = function (match) {
1412    var ch = this.string.charAt(this.pos)
1413    var ok
1414    if (typeof match == "string") { ok = ch == match }
1415    else { ok = ch && (match.test ? match.test(ch) : match(ch)) }
1416    if (ok) {++this.pos; return ch}
1417  };
1418  StringStream.prototype.eatWhile = function (match) {
1419    var start = this.pos
1420    while (this.eat(match)){}
1421    return this.pos > start
1422  };
1423  StringStream.prototype.eatSpace = function () {
1424      var this$1 = this;
1425  
1426    var start = this.pos
1427    while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this$1.pos }
1428    return this.pos > start
1429  };
1430  StringStream.prototype.skipToEnd = function () {this.pos = this.string.length};
1431  StringStream.prototype.skipTo = function (ch) {
1432    var found = this.string.indexOf(ch, this.pos)
1433    if (found > -1) {this.pos = found; return true}
1434  };
1435  StringStream.prototype.backUp = function (n) {this.pos -= n};
1436  StringStream.prototype.column = function () {
1437    if (this.lastColumnPos < this.start) {
1438      this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue)
1439      this.lastColumnPos = this.start
1440    }
1441    return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
1442  };
1443  StringStream.prototype.indentation = function () {
1444    return countColumn(this.string, null, this.tabSize) -
1445      (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
1446  };
1447  StringStream.prototype.match = function (pattern, consume, caseInsensitive) {
1448    if (typeof pattern == "string") {
1449      var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; }
1450      var substr = this.string.substr(this.pos, pattern.length)
1451      if (cased(substr) == cased(pattern)) {
1452        if (consume !== false) { this.pos += pattern.length }
1453        return true
1454      }
1455    } else {
1456      var match = this.string.slice(this.pos).match(pattern)
1457      if (match && match.index > 0) { return null }
1458      if (match && consume !== false) { this.pos += match[0].length }
1459      return match
1460    }
1461  };
1462  StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)};
1463  StringStream.prototype.hideFirstChars = function (n, inner) {
1464    this.lineStart += n
1465    try { return inner() }
1466    finally { this.lineStart -= n }
1467  };
1468  StringStream.prototype.lookAhead = function (n) {
1469    var oracle = this.lineOracle
1470    return oracle && oracle.lookAhead(n)
1471  };
1472  
1473  var SavedContext = function(state, lookAhead) {
1474    this.state = state
1475    this.lookAhead = lookAhead
1476  };
1477  
1478  var Context = function(doc, state, line, lookAhead) {
1479    this.state = state
1480    this.doc = doc
1481    this.line = line
1482    this.maxLookAhead = lookAhead || 0
1483  };
1484  
1485  Context.prototype.lookAhead = function (n) {
1486    var line = this.doc.getLine(this.line + n)
1487    if (line != null && n > this.maxLookAhead) { this.maxLookAhead = n }
1488    return line
1489  };
1490  
1491  Context.prototype.nextLine = function () {
1492    this.line++
1493    if (this.maxLookAhead > 0) { this.maxLookAhead-- }
1494  };
1495  
1496  Context.fromSaved = function (doc, saved, line) {
1497    if (saved instanceof SavedContext)
1498      { return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead) }
1499    else
1500      { return new Context(doc, copyState(doc.mode, saved), line) }
1501  };
1502  
1503  Context.prototype.save = function (copy) {
1504    var state = copy !== false ? copyState(this.doc.mode, this.state) : this.state
1505    return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state
1506  };
1507  
1508  
1509  // Compute a style array (an array starting with a mode generation
1510  // -- for invalidation -- followed by pairs of end positions and
1511  // style strings), which is used to highlight the tokens on the
1512  // line.
1513  function highlightLine(cm, line, context, forceToEnd) {
1514    // A styles array always starts with a number identifying the
1515    // mode/overlays that it is based on (for easy invalidation).
1516    var st = [cm.state.modeGen], lineClasses = {}
1517    // Compute the base array of styles
1518    runMode(cm, line.text, cm.doc.mode, context, function (end, style) { return st.push(end, style); },
1519            lineClasses, forceToEnd)
1520    var state = context.state
1521  
1522    // Run overlays, adjust style array.
1523    var loop = function ( o ) {
1524      var overlay = cm.state.overlays[o], i = 1, at = 0
1525      context.state = true
1526      runMode(cm, line.text, overlay.mode, context, function (end, style) {
1527        var start = i
1528        // Ensure there's a token end at the current position, and that i points at it
1529        while (at < end) {
1530          var i_end = st[i]
1531          if (i_end > end)
1532            { st.splice(i, 1, end, st[i+1], i_end) }
1533          i += 2
1534          at = Math.min(end, i_end)
1535        }
1536        if (!style) { return }
1537        if (overlay.opaque) {
1538          st.splice(start, i - start, end, "overlay " + style)
1539          i = start + 2
1540        } else {
1541          for (; start < i; start += 2) {
1542            var cur = st[start+1]
1543            st[start+1] = (cur ? cur + " " : "") + "overlay " + style
1544          }
1545        }
1546      }, lineClasses)
1547    };
1548  
1549    for (var o = 0; o < cm.state.overlays.length; ++o) loop( o );
1550    context.state = state
1551  
1552    return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null}
1553  }
1554  
1555  function getLineStyles(cm, line, updateFrontier) {
1556    if (!line.styles || line.styles[0] != cm.state.modeGen) {
1557      var context = getContextBefore(cm, lineNo(line))
1558      var resetState = line.text.length > cm.options.maxHighlightLength && copyState(cm.doc.mode, context.state)
1559      var result = highlightLine(cm, line, context)
1560      if (resetState) { context.state = resetState }
1561      line.stateAfter = context.save(!resetState)
1562      line.styles = result.styles
1563      if (result.classes) { line.styleClasses = result.classes }
1564      else if (line.styleClasses) { line.styleClasses = null }
1565      if (updateFrontier === cm.doc.highlightFrontier)
1566        { cm.doc.modeFrontier = Math.max(cm.doc.modeFrontier, ++cm.doc.highlightFrontier) }
1567    }
1568    return line.styles
1569  }
1570  
1571  function getContextBefore(cm, n, precise) {
1572    var doc = cm.doc, display = cm.display
1573    if (!doc.mode.startState) { return new Context(doc, true, n) }
1574    var start = findStartLine(cm, n, precise)
1575    var saved = start > doc.first && getLine(doc, start - 1).stateAfter
1576    var context = saved ? Context.fromSaved(doc, saved, start) : new Context(doc, startState(doc.mode), start)
1577  
1578    doc.iter(start, n, function (line) {
1579      processLine(cm, line.text, context)
1580      var pos = context.line
1581      line.stateAfter = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo ? context.save() : null
1582      context.nextLine()
1583    })
1584    if (precise) { doc.modeFrontier = context.line }
1585    return context
1586  }
1587  
1588  // Lightweight form of highlight -- proceed over this line and
1589  // update state, but don't save a style array. Used for lines that
1590  // aren't currently visible.
1591  function processLine(cm, text, context, startAt) {
1592    var mode = cm.doc.mode
1593    var stream = new StringStream(text, cm.options.tabSize, context)
1594    stream.start = stream.pos = startAt || 0
1595    if (text == "") { callBlankLine(mode, context.state) }
1596    while (!stream.eol()) {
1597      readToken(mode, stream, context.state)
1598      stream.start = stream.pos
1599    }
1600  }
1601  
1602  function callBlankLine(mode, state) {
1603    if (mode.blankLine) { return mode.blankLine(state) }
1604    if (!mode.innerMode) { return }
1605    var inner = innerMode(mode, state)
1606    if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) }
1607  }
1608  
1609  function readToken(mode, stream, state, inner) {
1610    for (var i = 0; i < 10; i++) {
1611      if (inner) { inner[0] = innerMode(mode, state).mode }
1612      var style = mode.token(stream, state)
1613      if (stream.pos > stream.start) { return style }
1614    }
1615    throw new Error("Mode " + mode.name + " failed to advance stream.")
1616  }
1617  
1618  var Token = function(stream, type, state) {
1619    this.start = stream.start; this.end = stream.pos
1620    this.string = stream.current()
1621    this.type = type || null
1622    this.state = state
1623  };
1624  
1625  // Utility for getTokenAt and getLineTokens
1626  function takeToken(cm, pos, precise, asArray) {
1627    var doc = cm.doc, mode = doc.mode, style
1628    pos = clipPos(doc, pos)
1629    var line = getLine(doc, pos.line), context = getContextBefore(cm, pos.line, precise)
1630    var stream = new StringStream(line.text, cm.options.tabSize, context), tokens
1631    if (asArray) { tokens = [] }
1632    while ((asArray || stream.pos < pos.ch) && !stream.eol()) {
1633      stream.start = stream.pos
1634      style = readToken(mode, stream, context.state)
1635      if (asArray) { tokens.push(new Token(stream, style, copyState(doc.mode, context.state))) }
1636    }
1637    return asArray ? tokens : new Token(stream, style, context.state)
1638  }
1639  
1640  function extractLineClasses(type, output) {
1641    if (type) { for (;;) {
1642      var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/)
1643      if (!lineClass) { break }
1644      type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length)
1645      var prop = lineClass[1] ? "bgClass" : "textClass"
1646      if (output[prop] == null)
1647        { output[prop] = lineClass[2] }
1648      else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop]))
1649        { output[prop] += " " + lineClass[2] }
1650    } }
1651    return type
1652  }
1653  
1654  // Run the given mode's parser over a line, calling f for each token.
1655  function runMode(cm, text, mode, context, f, lineClasses, forceToEnd) {
1656    var flattenSpans = mode.flattenSpans
1657    if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans }
1658    var curStart = 0, curStyle = null
1659    var stream = new StringStream(text, cm.options.tabSize, context), style
1660    var inner = cm.options.addModeClass && [null]
1661    if (text == "") { extractLineClasses(callBlankLine(mode, context.state), lineClasses) }
1662    while (!stream.eol()) {
1663      if (stream.pos > cm.options.maxHighlightLength) {
1664        flattenSpans = false
1665        if (forceToEnd) { processLine(cm, text, context, stream.pos) }
1666        stream.pos = text.length
1667        style = null
1668      } else {
1669        style = extractLineClasses(readToken(mode, stream, context.state, inner), lineClasses)
1670      }
1671      if (inner) {
1672        var mName = inner[0].name
1673        if (mName) { style = "m-" + (style ? mName + " " + style : mName) }
1674      }
1675      if (!flattenSpans || curStyle != style) {
1676        while (curStart < stream.start) {
1677          curStart = Math.min(stream.start, curStart + 5000)
1678          f(curStart, curStyle)
1679        }
1680        curStyle = style
1681      }
1682      stream.start = stream.pos
1683    }
1684    while (curStart < stream.pos) {
1685      // Webkit seems to refuse to render text nodes longer than 57444
1686      // characters, and returns inaccurate measurements in nodes
1687      // starting around 5000 chars.
1688      var pos = Math.min(stream.pos, curStart + 5000)
1689      f(pos, curStyle)
1690      curStart = pos
1691    }
1692  }
1693  
1694  // Finds the line to start with when starting a parse. Tries to
1695  // find a line with a stateAfter, so that it can start with a
1696  // valid state. If that fails, it returns the line with the
1697  // smallest indentation, which tends to need the least context to
1698  // parse correctly.
1699  function findStartLine(cm, n, precise) {
1700    var minindent, minline, doc = cm.doc
1701    var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100)
1702    for (var search = n; search > lim; --search) {
1703      if (search <= doc.first) { return doc.first }
1704      var line = getLine(doc, search - 1), after = line.stateAfter
1705      if (after && (!precise || search + (after instanceof SavedContext ? after.lookAhead : 0) <= doc.modeFrontier))
1706        { return search }
1707      var indented = countColumn(line.text, null, cm.options.tabSize)
1708      if (minline == null || minindent > indented) {
1709        minline = search - 1
1710        minindent = indented
1711      }
1712    }
1713    return minline
1714  }
1715  
1716  function retreatFrontier(doc, n) {
1717    doc.modeFrontier = Math.min(doc.modeFrontier, n)
1718    if (doc.highlightFrontier < n - 10) { return }
1719    var start = doc.first
1720    for (var line = n - 1; line > start; line--) {
1721      var saved = getLine(doc, line).stateAfter
1722      // change is on 3
1723      // state on line 1 looked ahead 2 -- so saw 3
1724      // test 1 + 2 < 3 should cover this
1725      if (saved && (!(saved instanceof SavedContext) || line + saved.lookAhead < n)) {
1726        start = line + 1
1727        break
1728      }
1729    }
1730    doc.highlightFrontier = Math.min(doc.highlightFrontier, start)
1731  }
1732  
1733  // LINE DATA STRUCTURE
1734  
1735  // Line objects. These hold state related to a line, including
1736  // highlighting info (the styles array).
1737  var Line = function(text, markedSpans, estimateHeight) {
1738    this.text = text
1739    attachMarkedSpans(this, markedSpans)
1740    this.height = estimateHeight ? estimateHeight(this) : 1
1741  };
1742  
1743  Line.prototype.lineNo = function () { return lineNo(this) };
1744  eventMixin(Line)
1745  
1746  // Change the content (text, markers) of a line. Automatically
1747  // invalidates cached information and tries to re-estimate the
1748  // line's height.
1749  function updateLine(line, text, markedSpans, estimateHeight) {
1750    line.text = text
1751    if (line.stateAfter) { line.stateAfter = null }
1752    if (line.styles) { line.styles = null }
1753    if (line.order != null) { line.order = null }
1754    detachMarkedSpans(line)
1755    attachMarkedSpans(line, markedSpans)
1756    var estHeight = estimateHeight ? estimateHeight(line) : 1
1757    if (estHeight != line.height) { updateLineHeight(line, estHeight) }
1758  }
1759  
1760  // Detach a line from the document tree and its markers.
1761  function cleanUpLine(line) {
1762    line.parent = null
1763    detachMarkedSpans(line)
1764  }
1765  
1766  // Convert a style as returned by a mode (either null, or a string
1767  // containing one or more styles) to a CSS style. This is cached,
1768  // and also looks for line-wide styles.
1769  var styleToClassCache = {};
1770  var styleToClassCacheWithMode = {};
1771  function interpretTokenStyle(style, options) {
1772    if (!style || /^\s*$/.test(style)) { return null }
1773    var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache
1774    return cache[style] ||
1775      (cache[style] = style.replace(/\S+/g, "cm-$&"))
1776  }
1777  
1778  // Render the DOM representation of the text of a line. Also builds
1779  // up a 'line map', which points at the DOM nodes that represent
1780  // specific stretches of text, and is used by the measuring code.
1781  // The returned object contains the DOM node, this map, and
1782  // information about line-wide styles that were set by the mode.
1783  function buildLineContent(cm, lineView) {
1784    // The padding-right forces the element to have a 'border', which
1785    // is needed on Webkit to be able to get line-level bounding
1786    // rectangles for it (in measureChar).
1787    var content = eltP("span", null, null, webkit ? "padding-right: .1px" : null)
1788    var builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: content,
1789                   col: 0, pos: 0, cm: cm,
1790                   trailingSpace: false,
1791                   splitSpaces: (ie || webkit) && cm.getOption("lineWrapping")}
1792    lineView.measure = {}
1793  
1794    // Iterate over the logical lines that make up this visual line.
1795    for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {
1796      var line = i ? lineView.rest[i - 1] : lineView.line, order = (void 0)
1797      builder.pos = 0
1798      builder.addToken = buildToken
1799      // Optionally wire in some hacks into the token-rendering
1800      // algorithm, to deal with browser quirks.
1801      if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction)))
1802        { builder.addToken = buildTokenBadBidi(builder.addToken, order) }
1803      builder.map = []
1804      var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line)
1805      insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate))
1806      if (line.styleClasses) {
1807        if (line.styleClasses.bgClass)
1808          { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || "") }
1809        if (line.styleClasses.textClass)
1810          { builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || "") }
1811      }
1812  
1813      // Ensure at least a single node is present, for measuring.
1814      if (builder.map.length == 0)
1815        { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))) }
1816  
1817      // Store the map and a cache object for the current logical line
1818      if (i == 0) {
1819        lineView.measure.map = builder.map
1820        lineView.measure.cache = {}
1821      } else {
1822        ;(lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map)
1823        ;(lineView.measure.caches || (lineView.measure.caches = [])).push({})
1824      }
1825    }
1826  
1827    // See issue #2901
1828    if (webkit) {
1829      var last = builder.content.lastChild
1830      if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab")))
1831        { builder.content.className = "cm-tab-wrap-hack" }
1832    }
1833  
1834    signal(cm, "renderLine", cm, lineView.line, builder.pre)
1835    if (builder.pre.className)
1836      { builder.textClass = joinClasses(builder.pre.className, builder.textClass || "") }
1837  
1838    return builder
1839  }
1840  
1841  function defaultSpecialCharPlaceholder(ch) {
1842    var token = elt("span", "\u2022", "cm-invalidchar")
1843    token.title = "\\u" + ch.charCodeAt(0).toString(16)
1844    token.setAttribute("aria-label", token.title)
1845    return token
1846  }
1847  
1848  // Build up the DOM representation for a single token, and add it to
1849  // the line map. Takes care to render special characters separately.
1850  function buildToken(builder, text, style, startStyle, endStyle, title, css) {
1851    if (!text) { return }
1852    var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text
1853    var special = builder.cm.state.specialChars, mustWrap = false
1854    var content
1855    if (!special.test(text)) {
1856      builder.col += text.length
1857      content = document.createTextNode(displayText)
1858      builder.map.push(builder.pos, builder.pos + text.length, content)
1859      if (ie && ie_version < 9) { mustWrap = true }
1860      builder.pos += text.length
1861    } else {
1862      content = document.createDocumentFragment()
1863      var pos = 0
1864      while (true) {
1865        special.lastIndex = pos
1866        var m = special.exec(text)
1867        var skipped = m ? m.index - pos : text.length - pos
1868        if (skipped) {
1869          var txt = document.createTextNode(displayText.slice(pos, pos + skipped))
1870          if (ie && ie_version < 9) { content.appendChild(elt("span", [txt])) }
1871          else { content.appendChild(txt) }
1872          builder.map.push(builder.pos, builder.pos + skipped, txt)
1873          builder.col += skipped
1874          builder.pos += skipped
1875        }
1876        if (!m) { break }
1877        pos += skipped + 1
1878        var txt$1 = (void 0)
1879        if (m[0] == "\t") {
1880          var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize
1881          txt$1 = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"))
1882          txt$1.setAttribute("role", "presentation")
1883          txt$1.setAttribute("cm-text", "\t")
1884          builder.col += tabWidth
1885        } else if (m[0] == "\r" || m[0] == "\n") {
1886          txt$1 = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar"))
1887          txt$1.setAttribute("cm-text", m[0])
1888          builder.col += 1
1889        } else {
1890          txt$1 = builder.cm.options.specialCharPlaceholder(m[0])
1891          txt$1.setAttribute("cm-text", m[0])
1892          if (ie && ie_version < 9) { content.appendChild(elt("span", [txt$1])) }
1893          else { content.appendChild(txt$1) }
1894          builder.col += 1
1895        }
1896        builder.map.push(builder.pos, builder.pos + 1, txt$1)
1897        builder.pos++
1898      }
1899    }
1900    builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32
1901    if (style || startStyle || endStyle || mustWrap || css) {
1902      var fullStyle = style || ""
1903      if (startStyle) { fullStyle += startStyle }
1904      if (endStyle) { fullStyle += endStyle }
1905      var token = elt("span", [content], fullStyle, css)
1906      if (title) { token.title = title }
1907      return builder.content.appendChild(token)
1908    }
1909    builder.content.appendChild(content)
1910  }
1911  
1912  function splitSpaces(text, trailingBefore) {
1913    if (text.length > 1 && !/  /.test(text)) { return text }
1914    var spaceBefore = trailingBefore, result = ""
1915    for (var i = 0; i < text.length; i++) {
1916      var ch = text.charAt(i)
1917      if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32))
1918        { ch = "\u00a0" }
1919      result += ch
1920      spaceBefore = ch == " "
1921    }
1922    return result
1923  }
1924  
1925  // Work around nonsense dimensions being reported for stretches of
1926  // right-to-left text.
1927  function buildTokenBadBidi(inner, order) {
1928    return function (builder, text, style, startStyle, endStyle, title, css) {
1929      style = style ? style + " cm-force-border" : "cm-force-border"
1930      var start = builder.pos, end = start + text.length
1931      for (;;) {
1932        // Find the part that overlaps with the start of this text
1933        var part = (void 0)
1934        for (var i = 0; i < order.length; i++) {
1935          part = order[i]
1936          if (part.to > start && part.from <= start) { break }
1937        }
1938        if (part.to >= end) { return inner(builder, text, style, startStyle, endStyle, title, css) }
1939        inner(builder, text.slice(0, part.to - start), style, startStyle, null, title, css)
1940        startStyle = null
1941        text = text.slice(part.to - start)
1942        start = part.to
1943      }
1944    }
1945  }
1946  
1947  function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
1948    var widget = !ignoreWidget && marker.widgetNode
1949    if (widget) { builder.map.push(builder.pos, builder.pos + size, widget) }
1950    if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {
1951      if (!widget)
1952        { widget = builder.content.appendChild(document.createElement("span")) }
1953      widget.setAttribute("cm-marker", marker.id)
1954    }
1955    if (widget) {
1956      builder.cm.display.input.setUneditable(widget)
1957      builder.content.appendChild(widget)
1958    }
1959    builder.pos += size
1960    builder.trailingSpace = false
1961  }
1962  
1963  // Outputs a number of spans to make up a line, taking highlighting
1964  // and marked text into account.
1965  function insertLineContent(line, builder, styles) {
1966    var spans = line.markedSpans, allText = line.text, at = 0
1967    if (!spans) {
1968      for (var i$1 = 1; i$1 < styles.length; i$1+=2)
1969        { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpretTokenStyle(styles[i$1+1], builder.cm.options)) }
1970      return
1971    }
1972  
1973    var len = allText.length, pos = 0, i = 1, text = "", style, css
1974    var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed
1975    for (;;) {
1976      if (nextChange == pos) { // Update current marker set
1977        spanStyle = spanEndStyle = spanStartStyle = title = css = ""
1978        collapsed = null; nextChange = Infinity
1979        var foundBookmarks = [], endStyles = (void 0)
1980        for (var j = 0; j < spans.length; ++j) {
1981          var sp = spans[j], m = sp.marker
1982          if (m.type == "bookmark" && sp.from == pos && m.widgetNode) {
1983            foundBookmarks.push(m)
1984          } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) {
1985            if (sp.to != null && sp.to != pos && nextChange > sp.to) {
1986              nextChange = sp.to
1987              spanEndStyle = ""
1988            }
1989            if (m.className) { spanStyle += " " + m.className }
1990            if (m.css) { css = (css ? css + ";" : "") + m.css }
1991            if (m.startStyle && sp.from == pos) { spanStartStyle += " " + m.startStyle }
1992            if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [])).push(m.endStyle, sp.to) }
1993            if (m.title && !title) { title = m.title }
1994            if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))
1995              { collapsed = sp }
1996          } else if (sp.from > pos && nextChange > sp.from) {
1997            nextChange = sp.from
1998          }
1999        }
2000        if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2)
2001          { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += " " + endStyles[j$1] } } }
2002  
2003        if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2)
2004          { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]) } }
2005        if (collapsed && (collapsed.from || 0) == pos) {
2006          buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos,
2007                             collapsed.marker, collapsed.from == null)
2008          if (collapsed.to == null) { return }
2009          if (collapsed.to == pos) { collapsed = false }
2010        }
2011      }
2012      if (pos >= len) { break }
2013  
2014      var upto = Math.min(len, nextChange)
2015      while (true) {
2016        if (text) {
2017          var end = pos + text.length
2018          if (!collapsed) {
2019            var tokenText = end > upto ? text.slice(0, upto - pos) : text
2020            builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
2021                             spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", title, css)
2022          }
2023          if (end >= upto) {text = text.slice(upto - pos); pos = upto; break}
2024          pos = end
2025          spanStartStyle = ""
2026        }
2027        text = allText.slice(at, at = styles[i++])
2028        style = interpretTokenStyle(styles[i++], builder.cm.options)
2029      }
2030    }
2031  }
2032  
2033  
2034  // These objects are used to represent the visible (currently drawn)
2035  // part of the document. A LineView may correspond to multiple
2036  // logical lines, if those are connected by collapsed ranges.
2037  function LineView(doc, line, lineN) {
2038    // The starting line
2039    this.line = line
2040    // Continuing lines, if any
2041    this.rest = visualLineContinued(line)
2042    // Number of logical lines in this visual line
2043    this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1
2044    this.node = this.text = null
2045    this.hidden = lineIsHidden(doc, line)
2046  }
2047  
2048  // Create a range of LineView objects for the given lines.
2049  function buildViewArray(cm, from, to) {
2050    var array = [], nextPos
2051    for (var pos = from; pos < to; pos = nextPos) {
2052      var view = new LineView(cm.doc, getLine(cm.doc, pos), pos)
2053      nextPos = pos + view.size
2054      array.push(view)
2055    }
2056    return array
2057  }
2058  
2059  var operationGroup = null
2060  
2061  function pushOperation(op) {
2062    if (operationGroup) {
2063      operationGroup.ops.push(op)
2064    } else {
2065      op.ownsGroup = operationGroup = {
2066        ops: [op],
2067        delayedCallbacks: []
2068      }
2069    }
2070  }
2071  
2072  function fireCallbacksForOps(group) {
2073    // Calls delayed callbacks and cursorActivity handlers until no
2074    // new ones appear
2075    var callbacks = group.delayedCallbacks, i = 0
2076    do {
2077      for (; i < callbacks.length; i++)
2078        { callbacks[i].call(null) }
2079      for (var j = 0; j < group.ops.length; j++) {
2080        var op = group.ops[j]
2081        if (op.cursorActivityHandlers)
2082          { while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
2083            { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm) } }
2084      }
2085    } while (i < callbacks.length)
2086  }
2087  
2088  function finishOperation(op, endCb) {
2089    var group = op.ownsGroup
2090    if (!group) { return }
2091  
2092    try { fireCallbacksForOps(group) }
2093    finally {
2094      operationGroup = null
2095      endCb(group)
2096    }
2097  }
2098  
2099  var orphanDelayedCallbacks = null
2100  
2101  // Often, we want to signal events at a point where we are in the
2102  // middle of some work, but don't want the handler to start calling
2103  // other methods on the editor, which might be in an inconsistent
2104  // state or simply not expect any other events to happen.
2105  // signalLater looks whether there are any handlers, and schedules
2106  // them to be executed when the last operation ends, or, if no
2107  // operation is active, when a timeout fires.
2108  function signalLater(emitter, type /*, values...*/) {
2109    var arr = getHandlers(emitter, type)
2110    if (!arr.length) { return }
2111    var args = Array.prototype.slice.call(arguments, 2), list
2112    if (operationGroup) {
2113      list = operationGroup.delayedCallbacks
2114    } else if (orphanDelayedCallbacks) {
2115      list = orphanDelayedCallbacks
2116    } else {
2117      list = orphanDelayedCallbacks = []
2118      setTimeout(fireOrphanDelayed, 0)
2119    }
2120    var loop = function ( i ) {
2121      list.push(function () { return arr[i].apply(null, args); })
2122    };
2123  
2124    for (var i = 0; i < arr.length; ++i)
2125      loop( i );
2126  }
2127  
2128  function fireOrphanDelayed() {
2129    var delayed = orphanDelayedCallbacks
2130    orphanDelayedCallbacks = null
2131    for (var i = 0; i < delayed.length; ++i) { delayed[i]() }
2132  }
2133  
2134  // When an aspect of a line changes, a string is added to
2135  // lineView.changes. This updates the relevant part of the line's
2136  // DOM structure.
2137  function updateLineForChanges(cm, lineView, lineN, dims) {
2138    for (var j = 0; j < lineView.changes.length; j++) {
2139      var type = lineView.changes[j]
2140      if (type == "text") { updateLineText(cm, lineView) }
2141      else if (type == "gutter") { updateLineGutter(cm, lineView, lineN, dims) }
2142      else if (type == "class") { updateLineClasses(cm, lineView) }
2143      else if (type == "widget") { updateLineWidgets(cm, lineView, dims) }
2144    }
2145    lineView.changes = null
2146  }
2147  
2148  // Lines with gutter elements, widgets or a background class need to
2149  // be wrapped, and have the extra elements added to the wrapper div
2150  function ensureLineWrapped(lineView) {
2151    if (lineView.node == lineView.text) {
2152      lineView.node = elt("div", null, null, "position: relative")
2153      if (lineView.text.parentNode)
2154        { lineView.text.parentNode.replaceChild(lineView.node, lineView.text) }
2155      lineView.node.appendChild(lineView.text)
2156      if (ie && ie_version < 8) { lineView.node.style.zIndex = 2 }
2157    }
2158    return lineView.node
2159  }
2160  
2161  function updateLineBackground(cm, lineView) {
2162    var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass
2163    if (cls) { cls += " CodeMirror-linebackground" }
2164    if (lineView.background) {
2165      if (cls) { lineView.background.className = cls }
2166      else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null }
2167    } else if (cls) {
2168      var wrap = ensureLineWrapped(lineView)
2169      lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild)
2170      cm.display.input.setUneditable(lineView.background)
2171    }
2172  }
2173  
2174  // Wrapper around buildLineContent which will reuse the structure
2175  // in display.externalMeasured when possible.
2176  function getLineContent(cm, lineView) {
2177    var ext = cm.display.externalMeasured
2178    if (ext && ext.line == lineView.line) {
2179      cm.display.externalMeasured = null
2180      lineView.measure = ext.measure
2181      return ext.built
2182    }
2183    return buildLineContent(cm, lineView)
2184  }
2185  
2186  // Redraw the line's text. Interacts with the background and text
2187  // classes because the mode may output tokens that influence these
2188  // classes.
2189  function updateLineText(cm, lineView) {
2190    var cls = lineView.text.className
2191    var built = getLineContent(cm, lineView)
2192    if (lineView.text == lineView.node) { lineView.node = built.pre }
2193    lineView.text.parentNode.replaceChild(built.pre, lineView.text)
2194    lineView.text = built.pre
2195    if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {
2196      lineView.bgClass = built.bgClass
2197      lineView.textClass = built.textClass
2198      updateLineClasses(cm, lineView)
2199    } else if (cls) {
2200      lineView.text.className = cls
2201    }
2202  }
2203  
2204  function updateLineClasses(cm, lineView) {
2205    updateLineBackground(cm, lineView)
2206    if (lineView.line.wrapClass)
2207      { ensureLineWrapped(lineView).className = lineView.line.wrapClass }
2208    else if (lineView.node != lineView.text)
2209      { lineView.node.className = "" }
2210    var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass
2211    lineView.text.className = textClass || ""
2212  }
2213  
2214  function updateLineGutter(cm, lineView, lineN, dims) {
2215    if (lineView.gutter) {
2216      lineView.node.removeChild(lineView.gutter)
2217      lineView.gutter = null
2218    }
2219    if (lineView.gutterBackground) {
2220      lineView.node.removeChild(lineView.gutterBackground)
2221      lineView.gutterBackground = null
2222    }
2223    if (lineView.line.gutterClass) {
2224      var wrap = ensureLineWrapped(lineView)
2225      lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass,
2226                                      ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px; width: " + (dims.gutterTotalWidth) + "px"))
2227      cm.display.input.setUneditable(lineView.gutterBackground)
2228      wrap.insertBefore(lineView.gutterBackground, lineView.text)
2229    }
2230    var markers = lineView.line.gutterMarkers
2231    if (cm.options.lineNumbers || markers) {
2232      var wrap$1 = ensureLineWrapped(lineView)
2233      var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"))
2234      cm.display.input.setUneditable(gutterWrap)
2235      wrap$1.insertBefore(gutterWrap, lineView.text)
2236      if (lineView.line.gutterClass)
2237        { gutterWrap.className += " " + lineView.line.gutterClass }
2238      if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
2239        { lineView.lineNumber = gutterWrap.appendChild(
2240          elt("div", lineNumberFor(cm.options, lineN),
2241              "CodeMirror-linenumber CodeMirror-gutter-elt",
2242              ("left: " + (dims.gutterLeft["CodeMirror-linenumbers"]) + "px; width: " + (cm.display.lineNumInnerWidth) + "px"))) }
2243      if (markers) { for (var k = 0; k < cm.options.gutters.length; ++k) {
2244        var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id]
2245        if (found)
2246          { gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt",
2247                                     ("left: " + (dims.gutterLeft[id]) + "px; width: " + (dims.gutterWidth[id]) + "px"))) }
2248      } }
2249    }
2250  }
2251  
2252  function updateLineWidgets(cm, lineView, dims) {
2253    if (lineView.alignable) { lineView.alignable = null }
2254    for (var node = lineView.node.firstChild, next = (void 0); node; node = next) {
2255      next = node.nextSibling
2256      if (node.className == "CodeMirror-linewidget")
2257        { lineView.node.removeChild(node) }
2258    }
2259    insertLineWidgets(cm, lineView, dims)
2260  }
2261  
2262  // Build a line's DOM representation from scratch
2263  function buildLineElement(cm, lineView, lineN, dims) {
2264    var built = getLineContent(cm, lineView)
2265    lineView.text = lineView.node = built.pre
2266    if (built.bgClass) { lineView.bgClass = built.bgClass }
2267    if (built.textClass) { lineView.textClass = built.textClass }
2268  
2269    updateLineClasses(cm, lineView)
2270    updateLineGutter(cm, lineView, lineN, dims)
2271    insertLineWidgets(cm, lineView, dims)
2272    return lineView.node
2273  }
2274  
2275  // A lineView may contain multiple logical lines (when merged by
2276  // collapsed spans). The widgets for all of them need to be drawn.
2277  function insertLineWidgets(cm, lineView, dims) {
2278    insertLineWidgetsFor(cm, lineView.line, lineView, dims, true)
2279    if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
2280      { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false) } }
2281  }
2282  
2283  function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {
2284    if (!line.widgets) { return }
2285    var wrap = ensureLineWrapped(lineView)
2286    for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
2287      var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget")
2288      if (!widget.handleMouseEvents) { node.setAttribute("cm-ignore-events", "true") }
2289      positionLineWidget(widget, node, lineView, dims)
2290      cm.display.input.setUneditable(node)
2291      if (allowAbove && widget.above)
2292        { wrap.insertBefore(node, lineView.gutter || lineView.text) }
2293      else
2294        { wrap.appendChild(node) }
2295      signalLater(widget, "redraw")
2296    }
2297  }
2298  
2299  function positionLineWidget(widget, node, lineView, dims) {
2300    if (widget.noHScroll) {
2301      ;(lineView.alignable || (lineView.alignable = [])).push(node)
2302      var width = dims.wrapperWidth
2303      node.style.left = dims.fixedPos + "px"
2304      if (!widget.coverGutter) {
2305        width -= dims.gutterTotalWidth
2306        node.style.paddingLeft = dims.gutterTotalWidth + "px"
2307      }
2308      node.style.width = width + "px"
2309    }
2310    if (widget.coverGutter) {
2311      node.style.zIndex = 5
2312      node.style.position = "relative"
2313      if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + "px" }
2314    }
2315  }
2316  
2317  function widgetHeight(widget) {
2318    if (widget.height != null) { return widget.height }
2319    var cm = widget.doc.cm
2320    if (!cm) { return 0 }
2321    if (!contains(document.body, widget.node)) {
2322      var parentStyle = "position: relative;"
2323      if (widget.coverGutter)
2324        { parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;" }
2325      if (widget.noHScroll)
2326        { parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;" }
2327      removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle))
2328    }
2329    return widget.height = widget.node.parentNode.offsetHeight
2330  }
2331  
2332  // Return true when the given mouse event happened in a widget
2333  function eventInWidget(display, e) {
2334    for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
2335      if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") ||
2336          (n.parentNode == display.sizer && n != display.mover))
2337        { return true }
2338    }
2339  }
2340  
2341  // POSITION MEASUREMENT
2342  
2343  function paddingTop(display) {return display.lineSpace.offsetTop}
2344  function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight}
2345  function paddingH(display) {
2346    if (display.cachedPaddingH) { return display.cachedPaddingH }
2347    var e = removeChildrenAndAdd(display.measure, elt("pre", "x"))
2348    var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle
2349    var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)}
2350    if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data }
2351    return data
2352  }
2353  
2354  function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth }
2355  function displayWidth(cm) {
2356    return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth
2357  }
2358  function displayHeight(cm) {
2359    return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight
2360  }
2361  
2362  // Ensure the lineView.wrapping.heights array is populated. This is
2363  // an array of bottom offsets for the lines that make up a drawn
2364  // line. When lineWrapping is on, there might be more than one
2365  // height.
2366  function ensureLineHeights(cm, lineView, rect) {
2367    var wrapping = cm.options.lineWrapping
2368    var curWidth = wrapping && displayWidth(cm)
2369    if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {
2370      var heights = lineView.measure.heights = []
2371      if (wrapping) {
2372        lineView.measure.width = curWidth
2373        var rects = lineView.text.firstChild.getClientRects()
2374        for (var i = 0; i < rects.length - 1; i++) {
2375          var cur = rects[i], next = rects[i + 1]
2376          if (Math.abs(cur.bottom - next.bottom) > 2)
2377            { heights.push((cur.bottom + next.top) / 2 - rect.top) }
2378        }
2379      }
2380      heights.push(rect.bottom - rect.top)
2381    }
2382  }
2383  
2384  // Find a line map (mapping character offsets to text nodes) and a
2385  // measurement cache for the given line number. (A line view might
2386  // contain multiple lines when collapsed ranges are present.)
2387  function mapFromLineView(lineView, line, lineN) {
2388    if (lineView.line == line)
2389      { return {map: lineView.measure.map, cache: lineView.measure.cache} }
2390    for (var i = 0; i < lineView.rest.length; i++)
2391      { if (lineView.rest[i] == line)
2392        { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } }
2393    for (var i$1 = 0; i$1 < lineView.rest.length; i$1++)
2394      { if (lineNo(lineView.rest[i$1]) > lineN)
2395        { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } }
2396  }
2397  
2398  // Render a line into the hidden node display.externalMeasured. Used
2399  // when measurement is needed for a line that's not in the viewport.
2400  function updateExternalMeasurement(cm, line) {
2401    line = visualLine(line)
2402    var lineN = lineNo(line)
2403    var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN)
2404    view.lineN = lineN
2405    var built = view.built = buildLineContent(cm, view)
2406    view.text = built.pre
2407    removeChildrenAndAdd(cm.display.lineMeasure, built.pre)
2408    return view
2409  }
2410  
2411  // Get a {top, bottom, left, right} box (in line-local coordinates)
2412  // for a given character.
2413  function measureChar(cm, line, ch, bias) {
2414    return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias)
2415  }
2416  
2417  // Find a line view that corresponds to the given line number.
2418  function findViewForLine(cm, lineN) {
2419    if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo)
2420      { return cm.display.view[findViewIndex(cm, lineN)] }
2421    var ext = cm.display.externalMeasured
2422    if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size)
2423      { return ext }
2424  }
2425  
2426  // Measurement can be split in two steps, the set-up work that
2427  // applies to the whole line, and the measurement of the actual
2428  // character. Functions like coordsChar, that need to do a lot of
2429  // measurements in a row, can thus ensure that the set-up work is
2430  // only done once.
2431  function prepareMeasureForLine(cm, line) {
2432    var lineN = lineNo(line)
2433    var view = findViewForLine(cm, lineN)
2434    if (view && !view.text) {
2435      view = null
2436    } else if (view && view.changes) {
2437      updateLineForChanges(cm, view, lineN, getDimensions(cm))
2438      cm.curOp.forceUpdate = true
2439    }
2440    if (!view)
2441      { view = updateExternalMeasurement(cm, line) }
2442  
2443    var info = mapFromLineView(view, line, lineN)
2444    return {
2445      line: line, view: view, rect: null,
2446      map: info.map, cache: info.cache, before: info.before,
2447      hasHeights: false
2448    }
2449  }
2450  
2451  // Given a prepared measurement object, measures the position of an
2452  // actual character (or fetches it from the cache).
2453  function measureCharPrepared(cm, prepared, ch, bias, varHeight) {
2454    if (prepared.before) { ch = -1 }
2455    var key = ch + (bias || ""), found
2456    if (prepared.cache.hasOwnProperty(key)) {
2457      found = prepared.cache[key]
2458    } else {
2459      if (!prepared.rect)
2460        { prepared.rect = prepared.view.text.getBoundingClientRect() }
2461      if (!prepared.hasHeights) {
2462        ensureLineHeights(cm, prepared.view, prepared.rect)
2463        prepared.hasHeights = true
2464      }
2465      found = measureCharInner(cm, prepared, ch, bias)
2466      if (!found.bogus) { prepared.cache[key] = found }
2467    }
2468    return {left: found.left, right: found.right,
2469            top: varHeight ? found.rtop : found.top,
2470            bottom: varHeight ? found.rbottom : found.bottom}
2471  }
2472  
2473  var nullRect = {left: 0, right: 0, top: 0, bottom: 0}
2474  
2475  function nodeAndOffsetInLineMap(map, ch, bias) {
2476    var node, start, end, collapse, mStart, mEnd
2477    // First, search the line map for the text node corresponding to,
2478    // or closest to, the target character.
2479    for (var i = 0; i < map.length; i += 3) {
2480      mStart = map[i]
2481      mEnd = map[i + 1]
2482      if (ch < mStart) {
2483        start = 0; end = 1
2484        collapse = "left"
2485      } else if (ch < mEnd) {
2486        start = ch - mStart
2487        end = start + 1
2488      } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) {
2489        end = mEnd - mStart
2490        start = end - 1
2491        if (ch >= mEnd) { collapse = "right" }
2492      }
2493      if (start != null) {
2494        node = map[i + 2]
2495        if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right"))
2496          { collapse = bias }
2497        if (bias == "left" && start == 0)
2498          { while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) {
2499            node = map[(i -= 3) + 2]
2500            collapse = "left"
2501          } }
2502        if (bias == "right" && start == mEnd - mStart)
2503          { while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].insertLeft) {
2504            node = map[(i += 3) + 2]
2505            collapse = "right"
2506          } }
2507        break
2508      }
2509    }
2510    return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd}
2511  }
2512  
2513  function getUsefulRect(rects, bias) {
2514    var rect = nullRect
2515    if (bias == "left") { for (var i = 0; i < rects.length; i++) {
2516      if ((rect = rects[i]).left != rect.right) { break }
2517    } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) {
2518      if ((rect = rects[i$1]).left != rect.right) { break }
2519    } }
2520    return rect
2521  }
2522  
2523  function measureCharInner(cm, prepared, ch, bias) {
2524    var place = nodeAndOffsetInLineMap(prepared.map, ch, bias)
2525    var node = place.node, start = place.start, end = place.end, collapse = place.collapse
2526  
2527    var rect
2528    if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.
2529      for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonsense rectangles are returned
2530        while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start }
2531        while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) { ++end }
2532        if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart)
2533          { rect = node.parentNode.getBoundingClientRect() }
2534        else
2535          { rect = getUsefulRect(range(node, start, end).getClientRects(), bias) }
2536        if (rect.left || rect.right || start == 0) { break }
2537        end = start
2538        start = start - 1
2539        collapse = "right"
2540      }
2541      if (ie && ie_version < 11) { rect = maybeUpdateRectForZooming(cm.display.measure, rect) }
2542    } else { // If it is a widget, simply get the box for the whole widget.
2543      if (start > 0) { collapse = bias = "right" }
2544      var rects
2545      if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)
2546        { rect = rects[bias == "right" ? rects.length - 1 : 0] }
2547      else
2548        { rect = node.getBoundingClientRect() }
2549    }
2550    if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {
2551      var rSpan = node.parentNode.getClientRects()[0]
2552      if (rSpan)
2553        { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom} }
2554      else
2555        { rect = nullRect }
2556    }
2557  
2558    var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top
2559    var mid = (rtop + rbot) / 2
2560    var heights = prepared.view.measure.heights
2561    var i = 0
2562    for (; i < heights.length - 1; i++)
2563      { if (mid < heights[i]) { break } }
2564    var top = i ? heights[i - 1] : 0, bot = heights[i]
2565    var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left,
2566                  right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left,
2567                  top: top, bottom: bot}
2568    if (!rect.left && !rect.right) { result.bogus = true }
2569    if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot }
2570  
2571    return result
2572  }
2573  
2574  // Work around problem with bounding client rects on ranges being
2575  // returned incorrectly when zoomed on IE10 and below.
2576  function maybeUpdateRectForZooming(measure, rect) {
2577    if (!window.screen || screen.logicalXDPI == null ||
2578        screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))
2579      { return rect }
2580    var scaleX = screen.logicalXDPI / screen.deviceXDPI
2581    var scaleY = screen.logicalYDPI / screen.deviceYDPI
2582    return {left: rect.left * scaleX, right: rect.right * scaleX,
2583            top: rect.top * scaleY, bottom: rect.bottom * scaleY}
2584  }
2585  
2586  function clearLineMeasurementCacheFor(lineView) {
2587    if (lineView.measure) {
2588      lineView.measure.cache = {}
2589      lineView.measure.heights = null
2590      if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
2591        { lineView.measure.caches[i] = {} } }
2592    }
2593  }
2594  
2595  function clearLineMeasurementCache(cm) {
2596    cm.display.externalMeasure = null
2597    removeChildren(cm.display.lineMeasure)
2598    for (var i = 0; i < cm.display.view.length; i++)
2599      { clearLineMeasurementCacheFor(cm.display.view[i]) }
2600  }
2601  
2602  function clearCaches(cm) {
2603    clearLineMeasurementCache(cm)
2604    cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null
2605    if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true }
2606    cm.display.lineNumChars = null
2607  }
2608  
2609  function pageScrollX() {
2610    // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=489206
2611    // which causes page_Offset and bounding client rects to use
2612    // different reference viewports and invalidate our calculations.
2613    if (chrome && android) { return -(document.body.getBoundingClientRect().left - parseInt(getComputedStyle(document.body).marginLeft)) }
2614    return window.pageXOffset || (document.documentElement || document.body).scrollLeft
2615  }
2616  function pageScrollY() {
2617    if (chrome && android) { return -(document.body.getBoundingClientRect().top - parseInt(getComputedStyle(document.body).marginTop)) }
2618    return window.pageYOffset || (document.documentElement || document.body).scrollTop
2619  }
2620  
2621  function widgetTopHeight(lineObj) {
2622    var height = 0
2623    if (lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above)
2624      { height += widgetHeight(lineObj.widgets[i]) } } }
2625    return height
2626  }
2627  
2628  // Converts a {top, bottom, left, right} box from line-local
2629  // coordinates into another coordinate system. Context may be one of
2630  // "line", "div" (display.lineDiv), "local"./null (editor), "window",
2631  // or "page".
2632  function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) {
2633    if (!includeWidgets) {
2634      var height = widgetTopHeight(lineObj)
2635      rect.top += height; rect.bottom += height
2636    }
2637    if (context == "line") { return rect }
2638    if (!context) { context = "local" }
2639    var yOff = heightAtLine(lineObj)
2640    if (context == "local") { yOff += paddingTop(cm.display) }
2641    else { yOff -= cm.display.viewOffset }
2642    if (context == "page" || context == "window") {
2643      var lOff = cm.display.lineSpace.getBoundingClientRect()
2644      yOff += lOff.top + (context == "window" ? 0 : pageScrollY())
2645      var xOff = lOff.left + (context == "window" ? 0 : pageScrollX())
2646      rect.left += xOff; rect.right += xOff
2647    }
2648    rect.top += yOff; rect.bottom += yOff
2649    return rect
2650  }
2651  
2652  // Coverts a box from "div" coords to another coordinate system.
2653  // Context may be "window", "page", "div", or "local"./null.
2654  function fromCoordSystem(cm, coords, context) {
2655    if (context == "div") { return coords }
2656    var left = coords.left, top = coords.top
2657    // First move into "page" coordinate system
2658    if (context == "page") {
2659      left -= pageScrollX()
2660      top -= pageScrollY()
2661    } else if (context == "local" || !context) {
2662      var localBox = cm.display.sizer.getBoundingClientRect()
2663      left += localBox.left
2664      top += localBox.top
2665    }
2666  
2667    var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect()
2668    return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top}
2669  }
2670  
2671  function charCoords(cm, pos, context, lineObj, bias) {
2672    if (!lineObj) { lineObj = getLine(cm.doc, pos.line) }
2673    return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context)
2674  }
2675  
2676  // Returns a box for a given cursor position, which may have an
2677  // 'other' property containing the position of the secondary cursor
2678  // on a bidi boundary.
2679  // A cursor Pos(line, char, "before") is on the same visual line as `char - 1`
2680  // and after `char - 1` in writing order of `char - 1`
2681  // A cursor Pos(line, char, "after") is on the same visual line as `char`
2682  // and before `char` in writing order of `char`
2683  // Examples (upper-case letters are RTL, lower-case are LTR):
2684  //     Pos(0, 1, ...)
2685  //     before   after
2686  // ab     a|b     a|b
2687  // aB     a|B     aB|
2688  // Ab     |Ab     A|b
2689  // AB     B|A     B|A
2690  // Every position after the last character on a line is considered to stick
2691  // to the last character on the line.
2692  function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
2693    lineObj = lineObj || getLine(cm.doc, pos.line)
2694    if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj) }
2695    function get(ch, right) {
2696      var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight)
2697      if (right) { m.left = m.right; } else { m.right = m.left }
2698      return intoCoordSystem(cm, lineObj, m, context)
2699    }
2700    var order = getOrder(lineObj, cm.doc.direction), ch = pos.ch, sticky = pos.sticky
2701    if (ch >= lineObj.text.length) {
2702      ch = lineObj.text.length
2703      sticky = "before"
2704    } else if (ch <= 0) {
2705      ch = 0
2706      sticky = "after"
2707    }
2708    if (!order) { return get(sticky == "before" ? ch - 1 : ch, sticky == "before") }
2709  
2710    function getBidi(ch, partPos, invert) {
2711      var part = order[partPos], right = part.level == 1
2712      return get(invert ? ch - 1 : ch, right != invert)
2713    }
2714    var partPos = getBidiPartAt(order, ch, sticky)
2715    var other = bidiOther
2716    var val = getBidi(ch, partPos, sticky == "before")
2717    if (other != null) { val.other = getBidi(ch, other, sticky != "before") }
2718    return val
2719  }
2720  
2721  // Used to cheaply estimate the coordinates for a position. Used for
2722  // intermediate scroll updates.
2723  function estimateCoords(cm, pos) {
2724    var left = 0
2725    pos = clipPos(cm.doc, pos)
2726    if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch }
2727    var lineObj = getLine(cm.doc, pos.line)
2728    var top = heightAtLine(lineObj) + paddingTop(cm.display)
2729    return {left: left, right: left, top: top, bottom: top + lineObj.height}
2730  }
2731  
2732  // Positions returned by coordsChar contain some extra information.
2733  // xRel is the relative x position of the input coordinates compared
2734  // to the found position (so xRel > 0 means the coordinates are to
2735  // the right of the character position, for example). When outside
2736  // is true, that means the coordinates lie outside the line's
2737  // vertical range.
2738  function PosWithInfo(line, ch, sticky, outside, xRel) {
2739    var pos = Pos(line, ch, sticky)
2740    pos.xRel = xRel
2741    if (outside) { pos.outside = true }
2742    return pos
2743  }
2744  
2745  // Compute the character position closest to the given coordinates.
2746  // Input must be lineSpace-local ("div" coordinate system).
2747  function coordsChar(cm, x, y) {
2748    var doc = cm.doc
2749    y += cm.display.viewOffset
2750    if (y < 0) { return PosWithInfo(doc.first, 0, null, true, -1) }
2751    var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1
2752    if (lineN > last)
2753      { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, true, 1) }
2754    if (x < 0) { x = 0 }
2755  
2756    var lineObj = getLine(doc, lineN)
2757    for (;;) {
2758      var found = coordsCharInner(cm, lineObj, lineN, x, y)
2759      var merged = collapsedSpanAtEnd(lineObj)
2760      var mergedPos = merged && merged.find(0, true)
2761      if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0))
2762        { lineN = lineNo(lineObj = mergedPos.to.line) }
2763      else
2764        { return found }
2765    }
2766  }
2767  
2768  function wrappedLineExtent(cm, lineObj, preparedMeasure, y) {
2769    y -= widgetTopHeight(lineObj)
2770    var end = lineObj.text.length
2771    var begin = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch - 1).bottom <= y; }, end, 0)
2772    end = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch).top > y; }, begin, end)
2773    return {begin: begin, end: end}
2774  }
2775  
2776  function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) {
2777    if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj) }
2778    var targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), "line").top
2779    return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop)
2780  }
2781  
2782  // Returns true if the given side of a box is after the given
2783  // coordinates, in top-to-bottom, left-to-right order.
2784  function boxIsAfter(box, x, y, left) {
2785    return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x
2786  }
2787  
2788  function coordsCharInner(cm, lineObj, lineNo, x, y) {
2789    // Move y into line-local coordinate space
2790    y -= heightAtLine(lineObj)
2791    var preparedMeasure = prepareMeasureForLine(cm, lineObj)
2792    // When directly calling `measureCharPrepared`, we have to adjust
2793    // for the widgets at this line.
2794    var widgetHeight = widgetTopHeight(lineObj)
2795    var begin = 0, end = lineObj.text.length, ltr = true
2796  
2797    var order = getOrder(lineObj, cm.doc.direction)
2798    // If the line isn't plain left-to-right text, first figure out
2799    // which bidi section the coordinates fall into.
2800    if (order) {
2801      var part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart)
2802                   (cm, lineObj, lineNo, preparedMeasure, order, x, y)
2803      ltr = part.level != 1
2804      // The awkward -1 offsets are needed because findFirst (called
2805      // on these below) will treat its first bound as inclusive,
2806      // second as exclusive, but we want to actually address the
2807      // characters in the part's range
2808      begin = ltr ? part.from : part.to - 1
2809      end = ltr ? part.to : part.from - 1
2810    }
2811  
2812    // A binary search to find the first character whose bounding box
2813    // starts after the coordinates. If we run across any whose box wrap
2814    // the coordinates, store that.
2815    var chAround = null, boxAround = null
2816    var ch = findFirst(function (ch) {
2817      var box = measureCharPrepared(cm, preparedMeasure, ch)
2818      box.top += widgetHeight; box.bottom += widgetHeight
2819      if (!boxIsAfter(box, x, y, false)) { return false }
2820      if (box.top <= y && box.left <= x) {
2821        chAround = ch
2822        boxAround = box
2823      }
2824      return true
2825    }, begin, end)
2826  
2827    var baseX, sticky, outside = false
2828    // If a box around the coordinates was found, use that
2829    if (boxAround) {
2830      // Distinguish coordinates nearer to the left or right side of the box
2831      var atLeft = x - boxAround.left < boxAround.right - x, atStart = atLeft == ltr
2832      ch = chAround + (atStart ? 0 : 1)
2833      sticky = atStart ? "after" : "before"
2834      baseX = atLeft ? boxAround.left : boxAround.right
2835    } else {
2836      // (Adjust for extended bound, if necessary.)
2837      if (!ltr && (ch == end || ch == begin)) { ch++ }
2838      // To determine which side to associate with, get the box to the
2839      // left of the character and compare it's vertical position to the
2840      // coordinates
2841      sticky = ch == 0 ? "after" : ch == lineObj.text.length ? "before" :
2842        (measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight <= y) == ltr ?
2843        "after" : "before"
2844      // Now get accurate coordinates for this place, in order to get a
2845      // base X position
2846      var coords = cursorCoords(cm, Pos(lineNo, ch, sticky), "line", lineObj, preparedMeasure)
2847      baseX = coords.left
2848      outside = y < coords.top || y >= coords.bottom
2849    }
2850  
2851    ch = skipExtendingChars(lineObj.text, ch, 1)
2852    return PosWithInfo(lineNo, ch, sticky, outside, x - baseX)
2853  }
2854  
2855  function coordsBidiPart(cm, lineObj, lineNo, preparedMeasure, order, x, y) {
2856    // Bidi parts are sorted left-to-right, and in a non-line-wrapping
2857    // situation, we can take this ordering to correspond to the visual
2858    // ordering. This finds the first part whose end is after the given
2859    // coordinates.
2860    var index = findFirst(function (i) {
2861      var part = order[i], ltr = part.level != 1
2862      return boxIsAfter(cursorCoords(cm, Pos(lineNo, ltr ? part.to : part.from, ltr ? "before" : "after"),
2863                                     "line", lineObj, preparedMeasure), x, y, true)
2864    }, 0, order.length - 1)
2865    var part = order[index]
2866    // If this isn't the first part, the part's start is also after
2867    // the coordinates, and the coordinates aren't on the same line as
2868    // that start, move one part back.
2869    if (index > 0) {
2870      var ltr = part.level != 1
2871      var start = cursorCoords(cm, Pos(lineNo, ltr ? part.from : part.to, ltr ? "after" : "before"),
2872                               "line", lineObj, preparedMeasure)
2873      if (boxIsAfter(start, x, y, true) && start.top > y)
2874        { part = order[index - 1] }
2875    }
2876    return part
2877  }
2878  
2879  function coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x, y) {
2880    // In a wrapped line, rtl text on wrapping boundaries can do things
2881    // that don't correspond to the ordering in our `order` array at
2882    // all, so a binary search doesn't work, and we want to return a
2883    // part that only spans one line so that the binary search in
2884    // coordsCharInner is safe. As such, we first find the extent of the
2885    // wrapped line, and then do a flat search in which we discard any
2886    // spans that aren't on the line.
2887    var ref = wrappedLineExtent(cm, lineObj, preparedMeasure, y);
2888    var begin = ref.begin;
2889    var end = ref.end;
2890    var part = null, closestDist = null
2891    for (var i = 0; i < order.length; i++) {
2892      var p = order[i]
2893      if (p.from >= end || p.to <= begin) { continue }
2894      var ltr = p.level != 1
2895      var endX = measureCharPrepared(cm, preparedMeasure, ltr ? Math.min(end, p.to) - 1 : Math.max(begin, p.from)).right
2896      // Weigh against spans ending before this, so that they are only
2897      // picked if nothing ends after
2898      var dist = endX < x ? x - endX + 1e9 : endX - x
2899      if (!part || closestDist > dist) {
2900        part = p
2901        closestDist = dist
2902      }
2903    }
2904    if (!part) { part = order[order.length - 1] }
2905    // Clip the part to the wrapped line.
2906    if (part.from < begin) { part = {from: begin, to: part.to, level: part.level} }
2907    if (part.to > end) { part = {from: part.from, to: end, level: part.level} }
2908    return part
2909  }
2910  
2911  var measureText
2912  // Compute the default text height.
2913  function textHeight(display) {
2914    if (display.cachedTextHeight != null) { return display.cachedTextHeight }
2915    if (measureText == null) {
2916      measureText = elt("pre")
2917      // Measure a bunch of lines, for browsers that compute
2918      // fractional heights.
2919      for (var i = 0; i < 49; ++i) {
2920        measureText.appendChild(document.createTextNode("x"))
2921        measureText.appendChild(elt("br"))
2922      }
2923      measureText.appendChild(document.createTextNode("x"))
2924    }
2925    removeChildrenAndAdd(display.measure, measureText)
2926    var height = measureText.offsetHeight / 50
2927    if (height > 3) { display.cachedTextHeight = height }
2928    removeChildren(display.measure)
2929    return height || 1
2930  }
2931  
2932  // Compute the default character width.
2933  function charWidth(display) {
2934    if (display.cachedCharWidth != null) { return display.cachedCharWidth }
2935    var anchor = elt("span", "xxxxxxxxxx")
2936    var pre = elt("pre", [anchor])
2937    removeChildrenAndAdd(display.measure, pre)
2938    var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10
2939    if (width > 2) { display.cachedCharWidth = width }
2940    return width || 10
2941  }
2942  
2943  // Do a bulk-read of the DOM positions and sizes needed to draw the
2944  // view, so that we don't interleave reading and writing to the DOM.
2945  function getDimensions(cm) {
2946    var d = cm.display, left = {}, width = {}
2947    var gutterLeft = d.gutters.clientLeft
2948    for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
2949      left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft
2950      width[cm.options.gutters[i]] = n.clientWidth
2951    }
2952    return {fixedPos: compensateForHScroll(d),
2953            gutterTotalWidth: d.gutters.offsetWidth,
2954            gutterLeft: left,
2955            gutterWidth: width,
2956            wrapperWidth: d.wrapper.clientWidth}
2957  }
2958  
2959  // Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
2960  // but using getBoundingClientRect to get a sub-pixel-accurate
2961  // result.
2962  function compensateForHScroll(display) {
2963    return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left
2964  }
2965  
2966  // Returns a function that estimates the height of a line, to use as
2967  // first approximation until the line becomes visible (and is thus
2968  // properly measurable).
2969  function estimateHeight(cm) {
2970    var th = textHeight(cm.display), wrapping = cm.options.lineWrapping
2971    var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3)
2972    return function (line) {
2973      if (lineIsHidden(cm.doc, line)) { return 0 }
2974  
2975      var widgetsHeight = 0
2976      if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) {
2977        if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height }
2978      } }
2979  
2980      if (wrapping)
2981        { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th }
2982      else
2983        { return widgetsHeight + th }
2984    }
2985  }
2986  
2987  function estimateLineHeights(cm) {
2988    var doc = cm.doc, est = estimateHeight(cm)
2989    doc.iter(function (line) {
2990      var estHeight = est(line)
2991      if (estHeight != line.height) { updateLineHeight(line, estHeight) }
2992    })
2993  }
2994  
2995  // Given a mouse event, find the corresponding position. If liberal
2996  // is false, it checks whether a gutter or scrollbar was clicked,
2997  // and returns null if it was. forRect is used by rectangular
2998  // selections, and tries to estimate a character position even for
2999  // coordinates beyond the right of the text.
3000  function posFromMouse(cm, e, liberal, forRect) {
3001    var display = cm.display
3002    if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") { return null }
3003  
3004    var x, y, space = display.lineSpace.getBoundingClientRect()
3005    // Fails unpredictably on IE[67] when mouse is dragged around quickly.
3006    try { x = e.clientX - space.left; y = e.clientY - space.top }
3007    catch (e) { return null }
3008    var coords = coordsChar(cm, x, y), line
3009    if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) {
3010      var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length
3011      coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff))
3012    }
3013    return coords
3014  }
3015  
3016  // Find the view element corresponding to a given line. Return null
3017  // when the line isn't visible.
3018  function findViewIndex(cm, n) {
3019    if (n >= cm.display.viewTo) { return null }
3020    n -= cm.display.viewFrom
3021    if (n < 0) { return null }
3022    var view = cm.display.view
3023    for (var i = 0; i < view.length; i++) {
3024      n -= view[i].size
3025      if (n < 0) { return i }
3026    }
3027  }
3028  
3029  function updateSelection(cm) {
3030    cm.display.input.showSelection(cm.display.input.prepareSelection())
3031  }
3032  
3033  function prepareSelection(cm, primary) {
3034    if ( primary === void 0 ) primary = true;
3035  
3036    var doc = cm.doc, result = {}
3037    var curFragment = result.cursors = document.createDocumentFragment()
3038    var selFragment = result.selection = document.createDocumentFragment()
3039  
3040    for (var i = 0; i < doc.sel.ranges.length; i++) {
3041      if (!primary && i == doc.sel.primIndex) { continue }
3042      var range = doc.sel.ranges[i]
3043      if (range.from().line >= cm.display.viewTo || range.to().line < cm.display.viewFrom) { continue }
3044      var collapsed = range.empty()
3045      if (collapsed || cm.options.showCursorWhenSelecting)
3046        { drawSelectionCursor(cm, range.head, curFragment) }
3047      if (!collapsed)
3048        { drawSelectionRange(cm, range, selFragment) }
3049    }
3050    return result
3051  }
3052  
3053  // Draws a cursor for the given range
3054  function drawSelectionCursor(cm, head, output) {
3055    var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine)
3056  
3057    var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor"))
3058    cursor.style.left = pos.left + "px"
3059    cursor.style.top = pos.top + "px"
3060    cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px"
3061  
3062    if (pos.other) {
3063      // Secondary cursor, shown when on a 'jump' in bi-directional text
3064      var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"))
3065      otherCursor.style.display = ""
3066      otherCursor.style.left = pos.other.left + "px"
3067      otherCursor.style.top = pos.other.top + "px"
3068      otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px"
3069    }
3070  }
3071  
3072  function cmpCoords(a, b) { return a.top - b.top || a.left - b.left }
3073  
3074  // Draws the given range as a highlighted selection
3075  function drawSelectionRange(cm, range, output) {
3076    var display = cm.display, doc = cm.doc
3077    var fragment = document.createDocumentFragment()
3078    var padding = paddingH(cm.display), leftSide = padding.left
3079    var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right
3080  
3081    function add(left, top, width, bottom) {
3082      if (top < 0) { top = 0 }
3083      top = Math.round(top)
3084      bottom = Math.round(bottom)
3085      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")))
3086    }
3087  
3088    function drawForLine(line, fromArg, toArg) {
3089      var lineObj = getLine(doc, line)
3090      var lineLen = lineObj.text.length
3091      var start, end
3092      function coords(ch, bias) {
3093        return charCoords(cm, Pos(line, ch), "div", lineObj, bias)
3094      }
3095  
3096      var order = getOrder(lineObj, doc.direction)
3097      iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir, i) {
3098        var fromPos = coords(from, dir == "ltr" ? "left" : "right")
3099        var toPos = coords(to - 1, dir == "ltr" ? "right" : "left")
3100        if (dir == "ltr") {
3101          var fromLeft = fromArg == null && from == 0 ? leftSide : fromPos.left
3102          var toRight = toArg == null && to == lineLen ? rightSide : toPos.right
3103          if (toPos.top - fromPos.top <= 3) { // Single line
3104            add(fromLeft, toPos.top, toRight - fromLeft, toPos.bottom)
3105          } else { // Multiple lines
3106            add(fromLeft, fromPos.top, null, fromPos.bottom)
3107            if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top) }
3108            add(leftSide, toPos.top, toPos.right, toPos.bottom)
3109          }
3110        } else if (from < to) { // RTL
3111          var fromRight = fromArg == null && from == 0 ? rightSide : fromPos.right
3112          var toLeft = toArg == null && to == lineLen ? leftSide : toPos.left
3113          if (toPos.top - fromPos.top <= 3) { // Single line
3114            add(toLeft, toPos.top, fromRight - toLeft, toPos.bottom)
3115          } else { // Multiple lines
3116            var topLeft = leftSide
3117            if (i) {
3118              var topEnd = wrappedLineExtentChar(cm, lineObj, null, from).end
3119              // The coordinates returned for an RTL wrapped space tend to
3120              // be complete bogus, so try to skip that here.
3121              topLeft = coords(topEnd - (/\s/.test(lineObj.text.charAt(topEnd - 1)) ? 2 : 1), "left").left
3122            }
3123            add(topLeft, fromPos.top, fromRight - topLeft, fromPos.bottom)
3124            if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top) }
3125            var botWidth = null
3126            if (i < order.length  - 1 || true) {
3127              var botStart = wrappedLineExtentChar(cm, lineObj, null, to).begin
3128              botWidth = coords(botStart, "right").right - toLeft
3129            }
3130            add(toLeft, toPos.top, botWidth, toPos.bottom)
3131          }
3132        }
3133  
3134        if (!start || cmpCoords(fromPos, start) < 0) { start = fromPos }
3135        if (cmpCoords(toPos, start) < 0) { start = toPos }
3136        if (!end || cmpCoords(fromPos, end) < 0) { end = fromPos }
3137        if (cmpCoords(toPos, end) < 0) { end = toPos }
3138      })
3139      return {start: start, end: end}
3140    }
3141  
3142    var sFrom = range.from(), sTo = range.to()
3143    if (sFrom.line == sTo.line) {
3144      drawForLine(sFrom.line, sFrom.ch, sTo.ch)
3145    } else {
3146      var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line)
3147      var singleVLine = visualLine(fromLine) == visualLine(toLine)
3148      var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end
3149      var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start
3150      if (singleVLine) {
3151        if (leftEnd.top < rightStart.top - 2) {
3152          add(leftEnd.right, leftEnd.top, null, leftEnd.bottom)
3153          add(leftSide, rightStart.top, rightStart.left, rightStart.bottom)
3154        } else {
3155          add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom)
3156        }
3157      }
3158      if (leftEnd.bottom < rightStart.top)
3159        { add(leftSide, leftEnd.bottom, null, rightStart.top) }
3160    }
3161  
3162    output.appendChild(fragment)
3163  }
3164  
3165  // Cursor-blinking
3166  function restartBlink(cm) {
3167    if (!cm.state.focused) { return }
3168    var display = cm.display
3169    clearInterval(display.blinker)
3170    var on = true
3171    display.cursorDiv.style.visibility = ""
3172    if (cm.options.cursorBlinkRate > 0)
3173      { display.blinker = setInterval(function () { return display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; },
3174        cm.options.cursorBlinkRate) }
3175    else if (cm.options.cursorBlinkRate < 0)
3176      { display.cursorDiv.style.visibility = "hidden" }
3177  }
3178  
3179  function ensureFocus(cm) {
3180    if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm) }
3181  }
3182  
3183  function delayBlurEvent(cm) {
3184    cm.state.delayingBlurEvent = true
3185    setTimeout(function () { if (cm.state.delayingBlurEvent) {
3186      cm.state.delayingBlurEvent = false
3187      onBlur(cm)
3188    } }, 100)
3189  }
3190  
3191  function onFocus(cm, e) {
3192    if (cm.state.delayingBlurEvent) { cm.state.delayingBlurEvent = false }
3193  
3194    if (cm.options.readOnly == "nocursor") { return }
3195    if (!cm.state.focused) {
3196      signal(cm, "focus", cm, e)
3197      cm.state.focused = true
3198      addClass(cm.display.wrapper, "CodeMirror-focused")
3199      // This test prevents this from firing when a context
3200      // menu is closed (since the input reset would kill the
3201      // select-all detection hack)
3202      if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {
3203        cm.display.input.reset()
3204        if (webkit) { setTimeout(function () { return cm.display.input.reset(true); }, 20) } // Issue #1730
3205      }
3206      cm.display.input.receivedFocus()
3207    }
3208    restartBlink(cm)
3209  }
3210  function onBlur(cm, e) {
3211    if (cm.state.delayingBlurEvent) { return }
3212  
3213    if (cm.state.focused) {
3214      signal(cm, "blur", cm, e)
3215      cm.state.focused = false
3216      rmClass(cm.display.wrapper, "CodeMirror-focused")
3217    }
3218    clearInterval(cm.display.blinker)
3219    setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false } }, 150)
3220  }
3221  
3222  // Read the actual heights of the rendered lines, and update their
3223  // stored heights to match.
3224  function updateHeightsInViewport(cm) {
3225    var display = cm.display
3226    var prevBottom = display.lineDiv.offsetTop
3227    for (var i = 0; i < display.view.length; i++) {
3228      var cur = display.view[i], height = (void 0)
3229      if (cur.hidden) { continue }
3230      if (ie && ie_version < 8) {
3231        var bot = cur.node.offsetTop + cur.node.offsetHeight
3232        height = bot - prevBottom
3233        prevBottom = bot
3234      } else {
3235        var box = cur.node.getBoundingClientRect()
3236        height = box.bottom - box.top
3237      }
3238      var diff = cur.line.height - height
3239      if (height < 2) { height = textHeight(display) }
3240      if (diff > .005 || diff < -.005) {
3241        updateLineHeight(cur.line, height)
3242        updateWidgetHeight(cur.line)
3243        if (cur.rest) { for (var j = 0; j < cur.rest.length; j++)
3244          { updateWidgetHeight(cur.rest[j]) } }
3245      }
3246    }
3247  }
3248  
3249  // Read and store the height of line widgets associated with the
3250  // given line.
3251  function updateWidgetHeight(line) {
3252    if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i)
3253      { line.widgets[i].height = line.widgets[i].node.parentNode.offsetHeight } }
3254  }
3255  
3256  // Compute the lines that are visible in a given viewport (defaults
3257  // the the current scroll position). viewport may contain top,
3258  // height, and ensure (see op.scrollToPos) properties.
3259  function visibleLines(display, doc, viewport) {
3260    var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop
3261    top = Math.floor(top - paddingTop(display))
3262    var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight
3263  
3264    var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom)
3265    // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
3266    // forces those lines into the viewport (if possible).
3267    if (viewport && viewport.ensure) {
3268      var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line
3269      if (ensureFrom < from) {
3270        from = ensureFrom
3271        to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight)
3272      } else if (Math.min(ensureTo, doc.lastLine()) >= to) {
3273        from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight)
3274        to = ensureTo
3275      }
3276    }
3277    return {from: from, to: Math.max(to, from + 1)}
3278  }
3279  
3280  // Re-align line numbers and gutter marks to compensate for
3281  // horizontal scrolling.
3282  function alignHorizontally(cm) {
3283    var display = cm.display, view = display.view
3284    if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) { return }
3285    var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft
3286    var gutterW = display.gutters.offsetWidth, left = comp + "px"
3287    for (var i = 0; i < view.length; i++) { if (!view[i].hidden) {
3288      if (cm.options.fixedGutter) {
3289        if (view[i].gutter)
3290          { view[i].gutter.style.left = left }
3291        if (view[i].gutterBackground)
3292          { view[i].gutterBackground.style.left = left }
3293      }
3294      var align = view[i].alignable
3295      if (align) { for (var j = 0; j < align.length; j++)
3296        { align[j].style.left = left } }
3297    } }
3298    if (cm.options.fixedGutter)
3299      { display.gutters.style.left = (comp + gutterW) + "px" }
3300  }
3301  
3302  // Used to ensure that the line number gutter is still the right
3303  // size for the current document size. Returns true when an update
3304  // is needed.
3305  function maybeUpdateLineNumberWidth(cm) {
3306    if (!cm.options.lineNumbers) { return false }
3307    var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display
3308    if (last.length != display.lineNumChars) {
3309      var test = display.measure.appendChild(elt("div", [elt("div", last)],
3310                                                 "CodeMirror-linenumber CodeMirror-gutter-elt"))
3311      var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW
3312      display.lineGutter.style.width = ""
3313      display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1
3314      display.lineNumWidth = display.lineNumInnerWidth + padding
3315      display.lineNumChars = display.lineNumInnerWidth ? last.length : -1
3316      display.lineGutter.style.width = display.lineNumWidth + "px"
3317      updateGutterSpace(cm)
3318      return true
3319    }
3320    return false
3321  }
3322  
3323  // SCROLLING THINGS INTO VIEW
3324  
3325  // If an editor sits on the top or bottom of the window, partially
3326  // scrolled out of view, this ensures that the cursor is visible.
3327  function maybeScrollWindow(cm, rect) {
3328    if (signalDOMEvent(cm, "scrollCursorIntoView")) { return }
3329  
3330    var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null
3331    if (rect.top + box.top < 0) { doScroll = true }
3332    else if (rect.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) { doScroll = false }
3333    if (doScroll != null && !phantom) {
3334      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;"))
3335      cm.display.lineSpace.appendChild(scrollNode)
3336      scrollNode.scrollIntoView(doScroll)
3337      cm.display.lineSpace.removeChild(scrollNode)
3338    }
3339  }
3340  
3341  // Scroll a given position into view (immediately), verifying that
3342  // it actually became visible (as line heights are accurately
3343  // measured, the position of something may 'drift' during drawing).
3344  function scrollPosIntoView(cm, pos, end, margin) {
3345    if (margin == null) { margin = 0 }
3346    var rect
3347    if (!cm.options.lineWrapping && pos == end) {
3348      // Set pos and end to the cursor positions around the character pos sticks to
3349      // If pos.sticky == "before", that is around pos.ch - 1, otherwise around pos.ch
3350      // If pos == Pos(_, 0, "before"), pos and end are unchanged
3351      pos = pos.ch ? Pos(pos.line, pos.sticky == "before" ? pos.ch - 1 : pos.ch, "after") : pos
3352      end = pos.sticky == "before" ? Pos(pos.line, pos.ch + 1, "before") : pos
3353    }
3354    for (var limit = 0; limit < 5; limit++) {
3355      var changed = false
3356      var coords = cursorCoords(cm, pos)
3357      var endCoords = !end || end == pos ? coords : cursorCoords(cm, end)
3358      rect = {left: Math.min(coords.left, endCoords.left),
3359              top: Math.min(coords.top, endCoords.top) - margin,
3360              right: Math.max(coords.left, endCoords.left),
3361              bottom: Math.max(coords.bottom, endCoords.bottom) + margin}
3362      var scrollPos = calculateScrollPos(cm, rect)
3363      var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft
3364      if (scrollPos.scrollTop != null) {
3365        updateScrollTop(cm, scrollPos.scrollTop)
3366        if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true }
3367      }
3368      if (scrollPos.scrollLeft != null) {
3369        setScrollLeft(cm, scrollPos.scrollLeft)
3370        if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true }
3371      }
3372      if (!changed) { break }
3373    }
3374    return rect
3375  }
3376  
3377  // Scroll a given set of coordinates into view (immediately).
3378  function scrollIntoView(cm, rect) {
3379    var scrollPos = calculateScrollPos(cm, rect)
3380    if (scrollPos.scrollTop != null) { updateScrollTop(cm, scrollPos.scrollTop) }
3381    if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft) }
3382  }
3383  
3384  // Calculate a new scroll position needed to scroll the given
3385  // rectangle into view. Returns an object with scrollTop and
3386  // scrollLeft properties. When these are undefined, the
3387  // vertical/horizontal position does not need to be adjusted.
3388  function calculateScrollPos(cm, rect) {
3389    var display = cm.display, snapMargin = textHeight(cm.display)
3390    if (rect.top < 0) { rect.top = 0 }
3391    var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop
3392    var screen = displayHeight(cm), result = {}
3393    if (rect.bottom - rect.top > screen) { rect.bottom = rect.top + screen }
3394    var docBottom = cm.doc.height + paddingVert(display)
3395    var atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMargin
3396    if (rect.top < screentop) {
3397      result.scrollTop = atTop ? 0 : rect.top
3398    } else if (rect.bottom > screentop + screen) {
3399      var newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen)
3400      if (newTop != screentop) { result.scrollTop = newTop }
3401    }
3402  
3403    var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft
3404    var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0)
3405    var tooWide = rect.right - rect.left > screenw
3406    if (tooWide) { rect.right = rect.left + screenw }
3407    if (rect.left < 10)
3408      { result.scrollLeft = 0 }
3409    else if (rect.left < screenleft)
3410      { result.scrollLeft = Math.max(0, rect.left - (tooWide ? 0 : 10)) }
3411    else if (rect.right > screenw + screenleft - 3)
3412      { result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw }
3413    return result
3414  }
3415  
3416  // Store a relative adjustment to the scroll position in the current
3417  // operation (to be applied when the operation finishes).
3418  function addToScrollTop(cm, top) {
3419    if (top == null) { return }
3420    resolveScrollToPos(cm)
3421    cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top
3422  }
3423  
3424  // Make sure that at the end of the operation the current cursor is
3425  // shown.
3426  function ensureCursorVisible(cm) {
3427    resolveScrollToPos(cm)
3428    var cur = cm.getCursor()
3429    cm.curOp.scrollToPos = {from: cur, to: cur, margin: cm.options.cursorScrollMargin}
3430  }
3431  
3432  function scrollToCoords(cm, x, y) {
3433    if (x != null || y != null) { resolveScrollToPos(cm) }
3434    if (x != null) { cm.curOp.scrollLeft = x }
3435    if (y != null) { cm.curOp.scrollTop = y }
3436  }
3437  
3438  function scrollToRange(cm, range) {
3439    resolveScrollToPos(cm)
3440    cm.curOp.scrollToPos = range
3441  }
3442  
3443  // When an operation has its scrollToPos property set, and another
3444  // scroll action is applied before the end of the operation, this
3445  // 'simulates' scrolling that position into view in a cheap way, so
3446  // that the effect of intermediate scroll commands is not ignored.
3447  function resolveScrollToPos(cm) {
3448    var range = cm.curOp.scrollToPos
3449    if (range) {
3450      cm.curOp.scrollToPos = null
3451      var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to)
3452      scrollToCoordsRange(cm, from, to, range.margin)
3453    }
3454  }
3455  
3456  function scrollToCoordsRange(cm, from, to, margin) {
3457    var sPos = calculateScrollPos(cm, {
3458      left: Math.min(from.left, to.left),
3459      top: Math.min(from.top, to.top) - margin,
3460      right: Math.max(from.right, to.right),
3461      bottom: Math.max(from.bottom, to.bottom) + margin
3462    })
3463    scrollToCoords(cm, sPos.scrollLeft, sPos.scrollTop)
3464  }
3465  
3466  // Sync the scrollable area and scrollbars, ensure the viewport
3467  // covers the visible area.
3468  function updateScrollTop(cm, val) {
3469    if (Math.abs(cm.doc.scrollTop - val) < 2) { return }
3470    if (!gecko) { updateDisplaySimple(cm, {top: val}) }
3471    setScrollTop(cm, val, true)
3472    if (gecko) { updateDisplaySimple(cm) }
3473    startWorker(cm, 100)
3474  }
3475  
3476  function setScrollTop(cm, val, forceScroll) {
3477    val = Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val)
3478    if (cm.display.scroller.scrollTop == val && !forceScroll) { return }
3479    cm.doc.scrollTop = val
3480    cm.display.scrollbars.setScrollTop(val)
3481    if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = val }
3482  }
3483  
3484  // Sync scroller and scrollbar, ensure the gutter elements are
3485  // aligned.
3486  function setScrollLeft(cm, val, isScroller, forceScroll) {
3487    val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth)
3488    if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) { return }
3489    cm.doc.scrollLeft = val
3490    alignHorizontally(cm)
3491    if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val }
3492    cm.display.scrollbars.setScrollLeft(val)
3493  }
3494  
3495  // SCROLLBARS
3496  
3497  // Prepare DOM reads needed to update the scrollbars. Done in one
3498  // shot to minimize update/measure roundtrips.
3499  function measureForScrollbars(cm) {
3500    var d = cm.display, gutterW = d.gutters.offsetWidth
3501    var docH = Math.round(cm.doc.height + paddingVert(cm.display))
3502    return {
3503      clientHeight: d.scroller.clientHeight,
3504      viewHeight: d.wrapper.clientHeight,
3505      scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,
3506      viewWidth: d.wrapper.clientWidth,
3507      barLeft: cm.options.fixedGutter ? gutterW : 0,
3508      docHeight: docH,
3509      scrollHeight: docH + scrollGap(cm) + d.barHeight,
3510      nativeBarWidth: d.nativeBarWidth,
3511      gutterWidth: gutterW
3512    }
3513  }
3514  
3515  var NativeScrollbars = function(place, scroll, cm) {
3516    this.cm = cm
3517    var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar")
3518    var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar")
3519    place(vert); place(horiz)
3520  
3521    on(vert, "scroll", function () {
3522      if (vert.clientHeight) { scroll(vert.scrollTop, "vertical") }
3523    })
3524    on(horiz, "scroll", function () {
3525      if (horiz.clientWidth) { scroll(horiz.scrollLeft, "horizontal") }
3526    })
3527  
3528    this.checkedZeroWidth = false
3529    // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
3530    if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = "18px" }
3531  };
3532  
3533  NativeScrollbars.prototype.update = function (measure) {
3534    var needsH = measure.scrollWidth > measure.clientWidth + 1
3535    var needsV = measure.scrollHeight > measure.clientHeight + 1
3536    var sWidth = measure.nativeBarWidth
3537  
3538    if (needsV) {
3539      this.vert.style.display = "block"
3540      this.vert.style.bottom = needsH ? sWidth + "px" : "0"
3541      var totalHeight = measure.viewHeight - (needsH ? sWidth : 0)
3542      // A bug in IE8 can cause this value to be negative, so guard it.
3543      this.vert.firstChild.style.height =
3544        Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px"
3545    } else {
3546      this.vert.style.display = ""
3547      this.vert.firstChild.style.height = "0"
3548    }
3549  
3550    if (needsH) {
3551      this.horiz.style.display = "block"
3552      this.horiz.style.right = needsV ? sWidth + "px" : "0"
3553      this.horiz.style.left = measure.barLeft + "px"
3554      var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0)
3555      this.horiz.firstChild.style.width =
3556        Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + "px"
3557    } else {
3558      this.horiz.style.display = ""
3559      this.horiz.firstChild.style.width = "0"
3560    }
3561  
3562    if (!this.checkedZeroWidth && measure.clientHeight > 0) {
3563      if (sWidth == 0) { this.zeroWidthHack() }
3564      this.checkedZeroWidth = true
3565    }
3566  
3567    return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0}
3568  };
3569  
3570  NativeScrollbars.prototype.setScrollLeft = function (pos) {
3571    if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos }
3572    if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz, "horiz") }
3573  };
3574  
3575  NativeScrollbars.prototype.setScrollTop = function (pos) {
3576    if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos }
3577    if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert, "vert") }
3578  };
3579  
3580  NativeScrollbars.prototype.zeroWidthHack = function () {
3581    var w = mac && !mac_geMountainLion ? "12px" : "18px"
3582    this.horiz.style.height = this.vert.style.width = w
3583    this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none"
3584    this.disableHoriz = new Delayed
3585    this.disableVert = new Delayed
3586  };
3587  
3588  NativeScrollbars.prototype.enableZeroWidthBar = function (bar, delay, type) {
3589    bar.style.pointerEvents = "auto"
3590    function maybeDisable() {
3591      // To find out whether the scrollbar is still visible, we
3592      // check whether the element under the pixel in the bottom
3593      // right corner of the scrollbar box is the scrollbar box
3594      // itself (when the bar is still visible) or its filler child
3595      // (when the bar is hidden). If it is still visible, we keep
3596      // it enabled, if it's hidden, we disable pointer events.
3597      var box = bar.getBoundingClientRect()
3598      var elt = type == "vert" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2)
3599          : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1)
3600      if (elt != bar) { bar.style.pointerEvents = "none" }
3601      else { delay.set(1000, maybeDisable) }
3602    }
3603    delay.set(1000, maybeDisable)
3604  };
3605  
3606  NativeScrollbars.prototype.clear = function () {
3607    var parent = this.horiz.parentNode
3608    parent.removeChild(this.horiz)
3609    parent.removeChild(this.vert)
3610  };
3611  
3612  var NullScrollbars = function () {};
3613  
3614  NullScrollbars.prototype.update = function () { return {bottom: 0, right: 0} };
3615  NullScrollbars.prototype.setScrollLeft = function () {};
3616  NullScrollbars.prototype.setScrollTop = function () {};
3617  NullScrollbars.prototype.clear = function () {};
3618  
3619  function updateScrollbars(cm, measure) {
3620    if (!measure) { measure = measureForScrollbars(cm) }
3621    var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight
3622    updateScrollbarsInner(cm, measure)
3623    for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) {
3624      if (startWidth != cm.display.barWidth && cm.options.lineWrapping)
3625        { updateHeightsInViewport(cm) }
3626      updateScrollbarsInner(cm, measureForScrollbars(cm))
3627      startWidth = cm.display.barWidth; startHeight = cm.display.barHeight
3628    }
3629  }
3630  
3631  // Re-synchronize the fake scrollbars with the actual size of the
3632  // content.
3633  function updateScrollbarsInner(cm, measure) {
3634    var d = cm.display
3635    var sizes = d.scrollbars.update(measure)
3636  
3637    d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px"
3638    d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px"
3639    d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent"
3640  
3641    if (sizes.right && sizes.bottom) {
3642      d.scrollbarFiller.style.display = "block"
3643      d.scrollbarFiller.style.height = sizes.bottom + "px"
3644      d.scrollbarFiller.style.width = sizes.right + "px"
3645    } else { d.scrollbarFiller.style.display = "" }
3646    if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
3647      d.gutterFiller.style.display = "block"
3648      d.gutterFiller.style.height = sizes.bottom + "px"
3649      d.gutterFiller.style.width = measure.gutterWidth + "px"
3650    } else { d.gutterFiller.style.display = "" }
3651  }
3652  
3653  var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars}
3654  
3655  function initScrollbars(cm) {
3656    if (cm.display.scrollbars) {
3657      cm.display.scrollbars.clear()
3658      if (cm.display.scrollbars.addClass)
3659        { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass) }
3660    }
3661  
3662    cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) {
3663      cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller)
3664      // Prevent clicks in the scrollbars from killing focus
3665      on(node, "mousedown", function () {
3666        if (cm.state.focused) { setTimeout(function () { return cm.display.input.focus(); }, 0) }
3667      })
3668      node.setAttribute("cm-not-content", "true")
3669    }, function (pos, axis) {
3670      if (axis == "horizontal") { setScrollLeft(cm, pos) }
3671      else { updateScrollTop(cm, pos) }
3672    }, cm)
3673    if (cm.display.scrollbars.addClass)
3674      { addClass(cm.display.wrapper, cm.display.scrollbars.addClass) }
3675  }
3676  
3677  // Operations are used to wrap a series of changes to the editor
3678  // state in such a way that each change won't have to update the
3679  // cursor and display (which would be awkward, slow, and
3680  // error-prone). Instead, display updates are batched and then all
3681  // combined and executed at once.
3682  
3683  var nextOpId = 0
3684  // Start a new operation.
3685  function startOperation(cm) {
3686    cm.curOp = {
3687      cm: cm,
3688      viewChanged: false,      // Flag that indicates that lines might need to be redrawn
3689      startHeight: cm.doc.height, // Used to detect need to update scrollbar
3690      forceUpdate: false,      // Used to force a redraw
3691      updateInput: null,       // Whether to reset the input textarea
3692      typing: false,           // Whether this reset should be careful to leave existing text (for compositing)
3693      changeObjs: null,        // Accumulated changes, for firing change events
3694      cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on
3695      cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
3696      selectionChanged: false, // Whether the selection needs to be redrawn
3697      updateMaxLine: false,    // Set when the widest line needs to be determined anew
3698      scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
3699      scrollToPos: null,       // Used to scroll to a specific position
3700      focus: false,
3701      id: ++nextOpId           // Unique ID
3702    }
3703    pushOperation(cm.curOp)
3704  }
3705  
3706  // Finish an operation, updating the display and signalling delayed events
3707  function endOperation(cm) {
3708    var op = cm.curOp
3709    finishOperation(op, function (group) {
3710      for (var i = 0; i < group.ops.length; i++)
3711        { group.ops[i].cm.curOp = null }
3712      endOperations(group)
3713    })
3714  }
3715  
3716  // The DOM updates done when an operation finishes are batched so
3717  // that the minimum number of relayouts are required.
3718  function endOperations(group) {
3719    var ops = group.ops
3720    for (var i = 0; i < ops.length; i++) // Read DOM
3721      { endOperation_R1(ops[i]) }
3722    for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe)
3723      { endOperation_W1(ops[i$1]) }
3724    for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM
3725      { endOperation_R2(ops[i$2]) }
3726    for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe)
3727      { endOperation_W2(ops[i$3]) }
3728    for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM
3729      { endOperation_finish(ops[i$4]) }
3730  }
3731  
3732  function endOperation_R1(op) {
3733    var cm = op.cm, display = cm.display
3734    maybeClipScrollbars(cm)
3735    if (op.updateMaxLine) { findMaxLine(cm) }
3736  
3737    op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||
3738      op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
3739                         op.scrollToPos.to.line >= display.viewTo) ||
3740      display.maxLineChanged && cm.options.lineWrapping
3741    op.update = op.mustUpdate &&
3742      new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate)
3743  }
3744  
3745  function endOperation_W1(op) {
3746    op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update)
3747  }
3748  
3749  function endOperation_R2(op) {
3750    var cm = op.cm, display = cm.display
3751    if (op.updatedDisplay) { updateHeightsInViewport(cm) }
3752  
3753    op.barMeasure = measureForScrollbars(cm)
3754  
3755    // If the max line changed since it was last measured, measure it,
3756    // and ensure the document's width matches it.
3757    // updateDisplay_W2 will use these properties to do the actual resizing
3758    if (display.maxLineChanged && !cm.options.lineWrapping) {
3759      op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3
3760      cm.display.sizerWidth = op.adjustWidthTo
3761      op.barMeasure.scrollWidth =
3762        Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth)
3763      op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm))
3764    }
3765  
3766    if (op.updatedDisplay || op.selectionChanged)
3767      { op.preparedSelection = display.input.prepareSelection() }
3768  }
3769  
3770  function endOperation_W2(op) {
3771    var cm = op.cm
3772  
3773    if (op.adjustWidthTo != null) {
3774      cm.display.sizer.style.minWidth = op.adjustWidthTo + "px"
3775      if (op.maxScrollLeft < cm.doc.scrollLeft)
3776        { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true) }
3777      cm.display.maxLineChanged = false
3778    }
3779  
3780    var takeFocus = op.focus && op.focus == activeElt()
3781    if (op.preparedSelection)
3782      { cm.display.input.showSelection(op.preparedSelection, takeFocus) }
3783    if (op.updatedDisplay || op.startHeight != cm.doc.height)
3784      { updateScrollbars(cm, op.barMeasure) }
3785    if (op.updatedDisplay)
3786      { setDocumentHeight(cm, op.barMeasure) }
3787  
3788    if (op.selectionChanged) { restartBlink(cm) }
3789  
3790    if (cm.state.focused && op.updateInput)
3791      { cm.display.input.reset(op.typing) }
3792    if (takeFocus) { ensureFocus(op.cm) }
3793  }
3794  
3795  function endOperation_finish(op) {
3796    var cm = op.cm, display = cm.display, doc = cm.doc
3797  
3798    if (op.updatedDisplay) { postUpdateDisplay(cm, op.update) }
3799  
3800    // Abort mouse wheel delta measurement, when scrolling explicitly
3801    if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))
3802      { display.wheelStartX = display.wheelStartY = null }
3803  
3804    // Propagate the scroll position to the actual DOM scroller
3805    if (op.scrollTop != null) { setScrollTop(cm, op.scrollTop, op.forceScroll) }
3806  
3807    if (op.scrollLeft != null) { setScrollLeft(cm, op.scrollLeft, true, true) }
3808    // If we need to scroll a specific position into view, do so.
3809    if (op.scrollToPos) {
3810      var rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
3811                                   clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin)
3812      maybeScrollWindow(cm, rect)
3813    }
3814  
3815    // Fire events for markers that are hidden/unidden by editing or
3816    // undoing
3817    var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers
3818    if (hidden) { for (var i = 0; i < hidden.length; ++i)
3819      { if (!hidden[i].lines.length) { signal(hidden[i], "hide") } } }
3820    if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1)
3821      { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], "unhide") } } }
3822  
3823    if (display.wrapper.offsetHeight)
3824      { doc.scrollTop = cm.display.scroller.scrollTop }
3825  
3826    // Fire change events, and delayed event handlers
3827    if (op.changeObjs)
3828      { signal(cm, "changes", cm, op.changeObjs) }
3829    if (op.update)
3830      { op.update.finish() }
3831  }
3832  
3833  // Run the given function in an operation
3834  function runInOp(cm, f) {
3835    if (cm.curOp) { return f() }
3836    startOperation(cm)
3837    try { return f() }
3838    finally { endOperation(cm) }
3839  }
3840  // Wraps a function in an operation. Returns the wrapped function.
3841  function operation(cm, f) {
3842    return function() {
3843      if (cm.curOp) { return f.apply(cm, arguments) }
3844      startOperation(cm)
3845      try { return f.apply(cm, arguments) }
3846      finally { endOperation(cm) }
3847    }
3848  }
3849  // Used to add methods to editor and doc instances, wrapping them in
3850  // operations.
3851  function methodOp(f) {
3852    return function() {
3853      if (this.curOp) { return f.apply(this, arguments) }
3854      startOperation(this)
3855      try { return f.apply(this, arguments) }
3856      finally { endOperation(this) }
3857    }
3858  }
3859  function docMethodOp(f) {
3860    return function() {
3861      var cm = this.cm
3862      if (!cm || cm.curOp) { return f.apply(this, arguments) }
3863      startOperation(cm)
3864      try { return f.apply(this, arguments) }
3865      finally { endOperation(cm) }
3866    }
3867  }
3868  
3869  // Updates the display.view data structure for a given change to the
3870  // document. From and to are in pre-change coordinates. Lendiff is
3871  // the amount of lines added or subtracted by the change. This is
3872  // used for changes that span multiple lines, or change the way
3873  // lines are divided into visual lines. regLineChange (below)
3874  // registers single-line changes.
3875  function regChange(cm, from, to, lendiff) {
3876    if (from == null) { from = cm.doc.first }
3877    if (to == null) { to = cm.doc.first + cm.doc.size }
3878    if (!lendiff) { lendiff = 0 }
3879  
3880    var display = cm.display
3881    if (lendiff && to < display.viewTo &&
3882        (display.updateLineNumbers == null || display.updateLineNumbers > from))
3883      { display.updateLineNumbers = from }
3884  
3885    cm.curOp.viewChanged = true
3886  
3887    if (from >= display.viewTo) { // Change after
3888      if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)
3889        { resetView(cm) }
3890    } else if (to <= display.viewFrom) { // Change before
3891      if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {
3892        resetView(cm)
3893      } else {
3894        display.viewFrom += lendiff
3895        display.viewTo += lendiff
3896      }
3897    } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap
3898      resetView(cm)
3899    } else if (from <= display.viewFrom) { // Top overlap
3900      var cut = viewCuttingPoint(cm, to, to + lendiff, 1)
3901      if (cut) {
3902        display.view = display.view.slice(cut.index)
3903        display.viewFrom = cut.lineN
3904        display.viewTo += lendiff
3905      } else {
3906        resetView(cm)
3907      }
3908    } else if (to >= display.viewTo) { // Bottom overlap
3909      var cut$1 = viewCuttingPoint(cm, from, from, -1)
3910      if (cut$1) {
3911        display.view = display.view.slice(0, cut$1.index)
3912        display.viewTo = cut$1.lineN
3913      } else {
3914        resetView(cm)
3915      }
3916    } else { // Gap in the middle
3917      var cutTop = viewCuttingPoint(cm, from, from, -1)
3918      var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1)
3919      if (cutTop && cutBot) {
3920        display.view = display.view.slice(0, cutTop.index)
3921          .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))
3922          .concat(display.view.slice(cutBot.index))
3923        display.viewTo += lendiff
3924      } else {
3925        resetView(cm)
3926      }
3927    }
3928  
3929    var ext = display.externalMeasured
3930    if (ext) {
3931      if (to < ext.lineN)
3932        { ext.lineN += lendiff }
3933      else if (from < ext.lineN + ext.size)
3934        { display.externalMeasured = null }
3935    }
3936  }
3937  
3938  // Register a change to a single line. Type must be one of "text",
3939  // "gutter", "class", "widget"
3940  function regLineChange(cm, line, type) {
3941    cm.curOp.viewChanged = true
3942    var display = cm.display, ext = cm.display.externalMeasured
3943    if (ext && line >= ext.lineN && line < ext.lineN + ext.size)
3944      { display.externalMeasured = null }
3945  
3946    if (line < display.viewFrom || line >= display.viewTo) { return }
3947    var lineView = display.view[findViewIndex(cm, line)]
3948    if (lineView.node == null) { return }
3949    var arr = lineView.changes || (lineView.changes = [])
3950    if (indexOf(arr, type) == -1) { arr.push(type) }
3951  }
3952  
3953  // Clear the view.
3954  function resetView(cm) {
3955    cm.display.viewFrom = cm.display.viewTo = cm.doc.first
3956    cm.display.view = []
3957    cm.display.viewOffset = 0
3958  }
3959  
3960  function viewCuttingPoint(cm, oldN, newN, dir) {
3961    var index = findViewIndex(cm, oldN), diff, view = cm.display.view
3962    if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)
3963      { return {index: index, lineN: newN} }
3964    var n = cm.display.viewFrom
3965    for (var i = 0; i < index; i++)
3966      { n += view[i].size }
3967    if (n != oldN) {
3968      if (dir > 0) {
3969        if (index == view.length - 1) { return null }
3970        diff = (n + view[index].size) - oldN
3971        index++
3972      } else {
3973        diff = n - oldN
3974      }
3975      oldN += diff; newN += diff
3976    }
3977    while (visualLineNo(cm.doc, newN) != newN) {
3978      if (index == (dir < 0 ? 0 : view.length - 1)) { return null }
3979      newN += dir * view[index - (dir < 0 ? 1 : 0)].size
3980      index += dir
3981    }
3982    return {index: index, lineN: newN}
3983  }
3984  
3985  // Force the view to cover a given range, adding empty view element
3986  // or clipping off existing ones as needed.
3987  function adjustView(cm, from, to) {
3988    var display = cm.display, view = display.view
3989    if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {
3990      display.view = buildViewArray(cm, from, to)
3991      display.viewFrom = from
3992    } else {
3993      if (display.viewFrom > from)
3994        { display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view) }
3995      else if (display.viewFrom < from)
3996        { display.view = display.view.slice(findViewIndex(cm, from)) }
3997      display.viewFrom = from
3998      if (display.viewTo < to)
3999        { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)) }
4000      else if (display.viewTo > to)
4001        { display.view = display.view.slice(0, findViewIndex(cm, to)) }
4002    }
4003    display.viewTo = to
4004  }
4005  
4006  // Count the number of lines in the view whose DOM representation is
4007  // out of date (or nonexistent).
4008  function countDirtyView(cm) {
4009    var view = cm.display.view, dirty = 0
4010    for (var i = 0; i < view.length; i++) {
4011      var lineView = view[i]
4012      if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty }
4013    }
4014    return dirty
4015  }
4016  
4017  // HIGHLIGHT WORKER
4018  
4019  function startWorker(cm, time) {
4020    if (cm.doc.highlightFrontier < cm.display.viewTo)
4021      { cm.state.highlight.set(time, bind(highlightWorker, cm)) }
4022  }
4023  
4024  function highlightWorker(cm) {
4025    var doc = cm.doc
4026    if (doc.highlightFrontier >= cm.display.viewTo) { return }
4027    var end = +new Date + cm.options.workTime
4028    var context = getContextBefore(cm, doc.highlightFrontier)
4029    var changedLines = []
4030  
4031    doc.iter(context.line, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function (line) {
4032      if (context.line >= cm.display.viewFrom) { // Visible
4033        var oldStyles = line.styles
4034        var resetState = line.text.length > cm.options.maxHighlightLength ? copyState(doc.mode, context.state) : null
4035        var highlighted = highlightLine(cm, line, context, true)
4036        if (resetState) { context.state = resetState }
4037        line.styles = highlighted.styles
4038        var oldCls = line.styleClasses, newCls = highlighted.classes
4039        if (newCls) { line.styleClasses = newCls }
4040        else if (oldCls) { line.styleClasses = null }
4041        var ischange = !oldStyles || oldStyles.length != line.styles.length ||
4042          oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass)
4043        for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldStyles[i] != line.styles[i] }
4044        if (ischange) { changedLines.push(context.line) }
4045        line.stateAfter = context.save()
4046        context.nextLine()
4047      } else {
4048        if (line.text.length <= cm.options.maxHighlightLength)
4049          { processLine(cm, line.text, context) }
4050        line.stateAfter = context.line % 5 == 0 ? context.save() : null
4051        context.nextLine()
4052      }
4053      if (+new Date > end) {
4054        startWorker(cm, cm.options.workDelay)
4055        return true
4056      }
4057    })
4058    doc.highlightFrontier = context.line
4059    doc.modeFrontier = Math.max(doc.modeFrontier, context.line)
4060    if (changedLines.length) { runInOp(cm, function () {
4061      for (var i = 0; i < changedLines.length; i++)
4062        { regLineChange(cm, changedLines[i], "text") }
4063    }) }
4064  }
4065  
4066  // DISPLAY DRAWING
4067  
4068  var DisplayUpdate = function(cm, viewport, force) {
4069    var display = cm.display
4070  
4071    this.viewport = viewport
4072    // Store some values that we'll need later (but don't want to force a relayout for)
4073    this.visible = visibleLines(display, cm.doc, viewport)
4074    this.editorIsHidden = !display.wrapper.offsetWidth
4075    this.wrapperHeight = display.wrapper.clientHeight
4076    this.wrapperWidth = display.wrapper.clientWidth
4077    this.oldDisplayWidth = displayWidth(cm)
4078    this.force = force
4079    this.dims = getDimensions(cm)
4080    this.events = []
4081  };
4082  
4083  DisplayUpdate.prototype.signal = function (emitter, type) {
4084    if (hasHandler(emitter, type))
4085      { this.events.push(arguments) }
4086  };
4087  DisplayUpdate.prototype.finish = function () {
4088      var this$1 = this;
4089  
4090    for (var i = 0; i < this.events.length; i++)
4091      { signal.apply(null, this$1.events[i]) }
4092  };
4093  
4094  function maybeClipScrollbars(cm) {
4095    var display = cm.display
4096    if (!display.scrollbarsClipped && display.scroller.offsetWidth) {
4097      display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth
4098      display.heightForcer.style.height = scrollGap(cm) + "px"
4099      display.sizer.style.marginBottom = -display.nativeBarWidth + "px"
4100      display.sizer.style.borderRightWidth = scrollGap(cm) + "px"
4101      display.scrollbarsClipped = true
4102    }
4103  }
4104  
4105  function selectionSnapshot(cm) {
4106    if (cm.hasFocus()) { return null }
4107    var active = activeElt()
4108    if (!active || !contains(cm.display.lineDiv, active)) { return null }
4109    var result = {activeElt: active}
4110    if (window.getSelection) {
4111      var sel = window.getSelection()
4112      if (sel.anchorNode && sel.extend && contains(cm.display.lineDiv, sel.anchorNode)) {
4113        result.anchorNode = sel.anchorNode
4114        result.anchorOffset = sel.anchorOffset
4115        result.focusNode = sel.focusNode
4116        result.focusOffset = sel.focusOffset
4117      }
4118    }
4119    return result
4120  }
4121  
4122  function restoreSelection(snapshot) {
4123    if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt()) { return }
4124    snapshot.activeElt.focus()
4125    if (snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) {
4126      var sel = window.getSelection(), range = document.createRange()
4127      range.setEnd(snapshot.anchorNode, snapshot.anchorOffset)
4128      range.collapse(false)
4129      sel.removeAllRanges()
4130      sel.addRange(range)
4131      sel.extend(snapshot.focusNode, snapshot.focusOffset)
4132    }
4133  }
4134  
4135  // Does the actual updating of the line display. Bails out
4136  // (returning false) when there is nothing to be done and forced is
4137  // false.
4138  function updateDisplayIfNeeded(cm, update) {
4139    var display = cm.display, doc = cm.doc
4140  
4141    if (update.editorIsHidden) {
4142      resetView(cm)
4143      return false
4144    }
4145  
4146    // Bail out if the visible area is already rendered and nothing changed.
4147    if (!update.force &&
4148        update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&
4149        (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&
4150        display.renderedView == display.view && countDirtyView(cm) == 0)
4151      { return false }
4152  
4153    if (maybeUpdateLineNumberWidth(cm)) {
4154      resetView(cm)
4155      update.dims = getDimensions(cm)
4156    }
4157  
4158    // Compute a suitable new viewport (from & to)
4159    var end = doc.first + doc.size
4160    var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first)
4161    var to = Math.min(end, update.visible.to + cm.options.viewportMargin)
4162    if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max(doc.first, display.viewFrom) }
4163    if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, display.viewTo) }
4164    if (sawCollapsedSpans) {
4165      from = visualLineNo(cm.doc, from)
4166      to = visualLineEndNo(cm.doc, to)
4167    }
4168  
4169    var different = from != display.viewFrom || to != display.viewTo ||
4170      display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth
4171    adjustView(cm, from, to)
4172  
4173    display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom))
4174    // Position the mover div to align with the current scroll position
4175    cm.display.mover.style.top = display.viewOffset + "px"
4176  
4177    var toUpdate = countDirtyView(cm)
4178    if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view &&
4179        (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))
4180      { return false }
4181  
4182    // For big changes, we hide the enclosing element during the
4183    // update, since that speeds up the operations on most browsers.
4184    var selSnapshot = selectionSnapshot(cm)
4185    if (toUpdate > 4) { display.lineDiv.style.display = "none" }
4186    patchDisplay(cm, display.updateLineNumbers, update.dims)
4187    if (toUpdate > 4) { display.lineDiv.style.display = "" }
4188    display.renderedView = display.view
4189    // There might have been a widget with a focused element that got
4190    // hidden or updated, if so re-focus it.
4191    restoreSelection(selSnapshot)
4192  
4193    // Prevent selection and cursors from interfering with the scroll
4194    // width and height.
4195    removeChildren(display.cursorDiv)
4196    removeChildren(display.selectionDiv)
4197    display.gutters.style.height = display.sizer.style.minHeight = 0
4198  
4199    if (different) {
4200      display.lastWrapHeight = update.wrapperHeight
4201      display.lastWrapWidth = update.wrapperWidth
4202      startWorker(cm, 400)
4203    }
4204  
4205    display.updateLineNumbers = null
4206  
4207    return true
4208  }
4209  
4210  function postUpdateDisplay(cm, update) {
4211    var viewport = update.viewport
4212  
4213    for (var first = true;; first = false) {
4214      if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) {
4215        // Clip forced viewport to actual scrollable area.
4216        if (viewport && viewport.top != null)
4217          { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)} }
4218        // Updated line heights might result in the drawn area not
4219        // actually covering the viewport. Keep looping until it does.
4220        update.visible = visibleLines(cm.display, cm.doc, viewport)
4221        if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)
4222          { break }
4223      }
4224      if (!updateDisplayIfNeeded(cm, update)) { break }
4225      updateHeightsInViewport(cm)
4226      var barMeasure = measureForScrollbars(cm)
4227      updateSelection(cm)
4228      updateScrollbars(cm, barMeasure)
4229      setDocumentHeight(cm, barMeasure)
4230      update.force = false
4231    }
4232  
4233    update.signal(cm, "update", cm)
4234    if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {
4235      update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo)
4236      cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo
4237    }
4238  }
4239  
4240  function updateDisplaySimple(cm, viewport) {
4241    var update = new DisplayUpdate(cm, viewport)
4242    if (updateDisplayIfNeeded(cm, update)) {
4243      updateHeightsInViewport(cm)
4244      postUpdateDisplay(cm, update)
4245      var barMeasure = measureForScrollbars(cm)
4246      updateSelection(cm)
4247      updateScrollbars(cm, barMeasure)
4248      setDocumentHeight(cm, barMeasure)
4249      update.finish()
4250    }
4251  }
4252  
4253  // Sync the actual display DOM structure with display.view, removing
4254  // nodes for lines that are no longer in view, and creating the ones
4255  // that are not there yet, and updating the ones that are out of
4256  // date.
4257  function patchDisplay(cm, updateNumbersFrom, dims) {
4258    var display = cm.display, lineNumbers = cm.options.lineNumbers
4259    var container = display.lineDiv, cur = container.firstChild
4260  
4261    function rm(node) {
4262      var next = node.nextSibling
4263      // Works around a throw-scroll bug in OS X Webkit
4264      if (webkit && mac && cm.display.currentWheelTarget == node)
4265        { node.style.display = "none" }
4266      else
4267        { node.parentNode.removeChild(node) }
4268      return next
4269    }
4270  
4271    var view = display.view, lineN = display.viewFrom
4272    // Loop over the elements in the view, syncing cur (the DOM nodes
4273    // in display.lineDiv) with the view as we go.
4274    for (var i = 0; i < view.length; i++) {
4275      var lineView = view[i]
4276      if (lineView.hidden) {
4277      } else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet
4278        var node = buildLineElement(cm, lineView, lineN, dims)
4279        container.insertBefore(node, cur)
4280      } else { // Already drawn
4281        while (cur != lineView.node) { cur = rm(cur) }
4282        var updateNumber = lineNumbers && updateNumbersFrom != null &&
4283          updateNumbersFrom <= lineN && lineView.lineNumber
4284        if (lineView.changes) {
4285          if (indexOf(lineView.changes, "gutter") > -1) { updateNumber = false }
4286          updateLineForChanges(cm, lineView, lineN, dims)
4287        }
4288        if (updateNumber) {
4289          removeChildren(lineView.lineNumber)
4290          lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN)))
4291        }
4292        cur = lineView.node.nextSibling
4293      }
4294      lineN += lineView.size
4295    }
4296    while (cur) { cur = rm(cur) }
4297  }
4298  
4299  function updateGutterSpace(cm) {
4300    var width = cm.display.gutters.offsetWidth
4301    cm.display.sizer.style.marginLeft = width + "px"
4302  }
4303  
4304  function setDocumentHeight(cm, measure) {
4305    cm.display.sizer.style.minHeight = measure.docHeight + "px"
4306    cm.display.heightForcer.style.top = measure.docHeight + "px"
4307    cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px"
4308  }
4309  
4310  // Rebuild the gutter elements, ensure the margin to the left of the
4311  // code matches their width.
4312  function updateGutters(cm) {
4313    var gutters = cm.display.gutters, specs = cm.options.gutters
4314    removeChildren(gutters)
4315    var i = 0
4316    for (; i < specs.length; ++i) {
4317      var gutterClass = specs[i]
4318      var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass))
4319      if (gutterClass == "CodeMirror-linenumbers") {
4320        cm.display.lineGutter = gElt
4321        gElt.style.width = (cm.display.lineNumWidth || 1) + "px"
4322      }
4323    }
4324    gutters.style.display = i ? "" : "none"
4325    updateGutterSpace(cm)
4326  }
4327  
4328  // Make sure the gutters options contains the element
4329  // "CodeMirror-linenumbers" when the lineNumbers option is true.
4330  function setGuttersForLineNumbers(options) {
4331    var found = indexOf(options.gutters, "CodeMirror-linenumbers")
4332    if (found == -1 && options.lineNumbers) {
4333      options.gutters = options.gutters.concat(["CodeMirror-linenumbers"])
4334    } else if (found > -1 && !options.lineNumbers) {
4335      options.gutters = options.gutters.slice(0)
4336      options.gutters.splice(found, 1)
4337    }
4338  }
4339  
4340  var wheelSamples = 0;
4341  var wheelPixelsPerUnit = null;
4342  // Fill in a browser-detected starting value on browsers where we
4343  // know one. These don't have to be accurate -- the result of them
4344  // being wrong would just be a slight flicker on the first wheel
4345  // scroll (if it is large enough).
4346  if (ie) { wheelPixelsPerUnit = -.53 }
4347  else if (gecko) { wheelPixelsPerUnit = 15 }
4348  else if (chrome) { wheelPixelsPerUnit = -.7 }
4349  else if (safari) { wheelPixelsPerUnit = -1/3 }
4350  
4351  function wheelEventDelta(e) {
4352    var dx = e.wheelDeltaX, dy = e.wheelDeltaY
4353    if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail }
4354    if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail }
4355    else if (dy == null) { dy = e.wheelDelta }
4356    return {x: dx, y: dy}
4357  }
4358  function wheelEventPixels(e) {
4359    var delta = wheelEventDelta(e)
4360    delta.x *= wheelPixelsPerUnit
4361    delta.y *= wheelPixelsPerUnit
4362    return delta
4363  }
4364  
4365  function onScrollWheel(cm, e) {
4366    var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y
4367  
4368    var display = cm.display, scroll = display.scroller
4369    // Quit if there's nothing to scroll here
4370    var canScrollX = scroll.scrollWidth > scroll.clientWidth
4371    var canScrollY = scroll.scrollHeight > scroll.clientHeight
4372    if (!(dx && canScrollX || dy && canScrollY)) { return }
4373  
4374    // Webkit browsers on OS X abort momentum scrolls when the target
4375    // of the scroll event is removed from the scrollable element.
4376    // This hack (see related code in patchDisplay) makes sure the
4377    // element is kept around.
4378    if (dy && mac && webkit) {
4379      outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {
4380        for (var i = 0; i < view.length; i++) {
4381          if (view[i].node == cur) {
4382            cm.display.currentWheelTarget = cur
4383            break outer
4384          }
4385        }
4386      }
4387    }
4388  
4389    // On some browsers, horizontal scrolling will cause redraws to
4390    // happen before the gutter has been realigned, causing it to
4391    // wriggle around in a most unseemly way. When we have an
4392    // estimated pixels/delta value, we just handle horizontal
4393    // scrolling entirely here. It'll be slightly off from native, but
4394    // better than glitching out.
4395    if (dx && !gecko && !presto && wheelPixelsPerUnit != null) {
4396      if (dy && canScrollY)
4397        { updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * wheelPixelsPerUnit)) }
4398      setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * wheelPixelsPerUnit))
4399      // Only prevent default scrolling if vertical scrolling is
4400      // actually possible. Otherwise, it causes vertical scroll
4401      // jitter on OSX trackpads when deltaX is small and deltaY
4402      // is large (issue #3579)
4403      if (!dy || (dy && canScrollY))
4404        { e_preventDefault(e) }
4405      display.wheelStartX = null // Abort measurement, if in progress
4406      return
4407    }
4408  
4409    // 'Project' the visible viewport to cover the area that is being
4410    // scrolled into view (if we know enough to estimate it).
4411    if (dy && wheelPixelsPerUnit != null) {
4412      var pixels = dy * wheelPixelsPerUnit
4413      var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight
4414      if (pixels < 0) { top = Math.max(0, top + pixels - 50) }
4415      else { bot = Math.min(cm.doc.height, bot + pixels + 50) }
4416      updateDisplaySimple(cm, {top: top, bottom: bot})
4417    }
4418  
4419    if (wheelSamples < 20) {
4420      if (display.wheelStartX == null) {
4421        display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop
4422        display.wheelDX = dx; display.wheelDY = dy
4423        setTimeout(function () {
4424          if (display.wheelStartX == null) { return }
4425          var movedX = scroll.scrollLeft - display.wheelStartX
4426          var movedY = scroll.scrollTop - display.wheelStartY
4427          var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
4428            (movedX && display.wheelDX && movedX / display.wheelDX)
4429          display.wheelStartX = display.wheelStartY = null
4430          if (!sample) { return }
4431          wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1)
4432          ++wheelSamples
4433        }, 200)
4434      } else {
4435        display.wheelDX += dx; display.wheelDY += dy
4436      }
4437    }
4438  }
4439  
4440  // Selection objects are immutable. A new one is created every time
4441  // the selection changes. A selection is one or more non-overlapping
4442  // (and non-touching) ranges, sorted, and an integer that indicates
4443  // which one is the primary selection (the one that's scrolled into
4444  // view, that getCursor returns, etc).
4445  var Selection = function(ranges, primIndex) {
4446    this.ranges = ranges
4447    this.primIndex = primIndex
4448  };
4449  
4450  Selection.prototype.primary = function () { return this.ranges[this.primIndex] };
4451  
4452  Selection.prototype.equals = function (other) {
4453      var this$1 = this;
4454  
4455    if (other == this) { return true }
4456    if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false }
4457    for (var i = 0; i < this.ranges.length; i++) {
4458      var here = this$1.ranges[i], there = other.ranges[i]
4459      if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) { return false }
4460    }
4461    return true
4462  };
4463  
4464  Selection.prototype.deepCopy = function () {
4465      var this$1 = this;
4466  
4467    var out = []
4468    for (var i = 0; i < this.ranges.length; i++)
4469      { out[i] = new Range(copyPos(this$1.ranges[i].anchor), copyPos(this$1.ranges[i].head)) }
4470    return new Selection(out, this.primIndex)
4471  };
4472  
4473  Selection.prototype.somethingSelected = function () {
4474      var this$1 = this;
4475  
4476    for (var i = 0; i < this.ranges.length; i++)
4477      { if (!this$1.ranges[i].empty()) { return true } }
4478    return false
4479  };
4480  
4481  Selection.prototype.contains = function (pos, end) {
4482      var this$1 = this;
4483  
4484    if (!end) { end = pos }
4485    for (var i = 0; i < this.ranges.length; i++) {
4486      var range = this$1.ranges[i]
4487      if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)
4488        { return i }
4489    }
4490    return -1
4491  };
4492  
4493  var Range = function(anchor, head) {
4494    this.anchor = anchor; this.head = head
4495  };
4496  
4497  Range.prototype.from = function () { return minPos(this.anchor, this.head) };
4498  Range.prototype.to = function () { return maxPos(this.anchor, this.head) };
4499  Range.prototype.empty = function () { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch };
4500  
4501  // Take an unsorted, potentially overlapping set of ranges, and
4502  // build a selection out of it. 'Consumes' ranges array (modifying
4503  // it).
4504  function normalizeSelection(ranges, primIndex) {
4505    var prim = ranges[primIndex]
4506    ranges.sort(function (a, b) { return cmp(a.from(), b.from()); })
4507    primIndex = indexOf(ranges, prim)
4508    for (var i = 1; i < ranges.length; i++) {
4509      var cur = ranges[i], prev = ranges[i - 1]
4510      if (cmp(prev.to(), cur.from()) >= 0) {
4511        var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to())
4512        var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head
4513        if (i <= primIndex) { --primIndex }
4514        ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to))
4515      }
4516    }
4517    return new Selection(ranges, primIndex)
4518  }
4519  
4520  function simpleSelection(anchor, head) {
4521    return new Selection([new Range(anchor, head || anchor)], 0)
4522  }
4523  
4524  // Compute the position of the end of a change (its 'to' property
4525  // refers to the pre-change end).
4526  function changeEnd(change) {
4527    if (!change.text) { return change.to }
4528    return Pos(change.from.line + change.text.length - 1,
4529               lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0))
4530  }
4531  
4532  // Adjust a position to refer to the post-change position of the
4533  // same text, or the end of the change if the change covers it.
4534  function adjustForChange(pos, change) {
4535    if (cmp(pos, change.from) < 0) { return pos }
4536    if (cmp(pos, change.to) <= 0) { return changeEnd(change) }
4537  
4538    var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch
4539    if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch }
4540    return Pos(line, ch)
4541  }
4542  
4543  function computeSelAfterChange(doc, change) {
4544    var out = []
4545    for (var i = 0; i < doc.sel.ranges.length; i++) {
4546      var range = doc.sel.ranges[i]
4547      out.push(new Range(adjustForChange(range.anchor, change),
4548                         adjustForChange(range.head, change)))
4549    }
4550    return normalizeSelection(out, doc.sel.primIndex)
4551  }
4552  
4553  function offsetPos(pos, old, nw) {
4554    if (pos.line == old.line)
4555      { return Pos(nw.line, pos.ch - old.ch + nw.ch) }
4556    else
4557      { return Pos(nw.line + (pos.line - old.line), pos.ch) }
4558  }
4559  
4560  // Used by replaceSelections to allow moving the selection to the
4561  // start or around the replaced test. Hint may be "start" or "around".
4562  function computeReplacedSel(doc, changes, hint) {
4563    var out = []
4564    var oldPrev = Pos(doc.first, 0), newPrev = oldPrev
4565    for (var i = 0; i < changes.length; i++) {
4566      var change = changes[i]
4567      var from = offsetPos(change.from, oldPrev, newPrev)
4568      var to = offsetPos(changeEnd(change), oldPrev, newPrev)
4569      oldPrev = change.to
4570      newPrev = to
4571      if (hint == "around") {
4572        var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0
4573        out[i] = new Range(inv ? to : from, inv ? from : to)
4574      } else {
4575        out[i] = new Range(from, from)
4576      }
4577    }
4578    return new Selection(out, doc.sel.primIndex)
4579  }
4580  
4581  // Used to get the editor into a consistent state again when options change.
4582  
4583  function loadMode(cm) {
4584    cm.doc.mode = getMode(cm.options, cm.doc.modeOption)
4585    resetModeState(cm)
4586  }
4587  
4588  function resetModeState(cm) {
4589    cm.doc.iter(function (line) {
4590      if (line.stateAfter) { line.stateAfter = null }
4591      if (line.styles) { line.styles = null }
4592    })
4593    cm.doc.modeFrontier = cm.doc.highlightFrontier = cm.doc.first
4594    startWorker(cm, 100)
4595    cm.state.modeGen++
4596    if (cm.curOp) { regChange(cm) }
4597  }
4598  
4599  // DOCUMENT DATA STRUCTURE
4600  
4601  // By default, updates that start and end at the beginning of a line
4602  // are treated specially, in order to make the association of line
4603  // widgets and marker elements with the text behave more intuitive.
4604  function isWholeLineUpdate(doc, change) {
4605    return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" &&
4606      (!doc.cm || doc.cm.options.wholeLineUpdateBefore)
4607  }
4608  
4609  // Perform a change on the document data structure.
4610  function updateDoc(doc, change, markedSpans, estimateHeight) {
4611    function spansFor(n) {return markedSpans ? markedSpans[n] : null}
4612    function update(line, text, spans) {
4613      updateLine(line, text, spans, estimateHeight)
4614      signalLater(line, "change", line, change)
4615    }
4616    function linesFor(start, end) {
4617      var result = []
4618      for (var i = start; i < end; ++i)
4619        { result.push(new Line(text[i], spansFor(i), estimateHeight)) }
4620      return result
4621    }
4622  
4623    var from = change.from, to = change.to, text = change.text
4624    var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line)
4625    var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line
4626  
4627    // Adjust the line structure
4628    if (change.full) {
4629      doc.insert(0, linesFor(0, text.length))
4630      doc.remove(text.length, doc.size - text.length)
4631    } else if (isWholeLineUpdate(doc, change)) {
4632      // This is a whole-line replace. Treated specially to make
4633      // sure line objects move the way they are supposed to.
4634      var added = linesFor(0, text.length - 1)
4635      update(lastLine, lastLine.text, lastSpans)
4636      if (nlines) { doc.remove(from.line, nlines) }
4637      if (added.length) { doc.insert(from.line, added) }
4638    } else if (firstLine == lastLine) {
4639      if (text.length == 1) {
4640        update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans)
4641      } else {
4642        var added$1 = linesFor(1, text.length - 1)
4643        added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight))
4644        update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0))
4645        doc.insert(from.line + 1, added$1)
4646      }
4647    } else if (text.length == 1) {
4648      update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0))
4649      doc.remove(from.line + 1, nlines)
4650    } else {
4651      update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0))
4652      update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans)
4653      var added$2 = linesFor(1, text.length - 1)
4654      if (nlines > 1) { doc.remove(from.line + 1, nlines - 1) }
4655      doc.insert(from.line + 1, added$2)
4656    }
4657  
4658    signalLater(doc, "change", doc, change)
4659  }
4660  
4661  // Call f for all linked documents.
4662  function linkedDocs(doc, f, sharedHistOnly) {
4663    function propagate(doc, skip, sharedHist) {
4664      if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) {
4665        var rel = doc.linked[i]
4666        if (rel.doc == skip) { continue }
4667        var shared = sharedHist && rel.sharedHist
4668        if (sharedHistOnly && !shared) { continue }
4669        f(rel.doc, shared)
4670        propagate(rel.doc, doc, shared)
4671      } }
4672    }
4673    propagate(doc, null, true)
4674  }
4675  
4676  // Attach a document to an editor.
4677  function attachDoc(cm, doc) {
4678    if (doc.cm) { throw new Error("This document is already in use.") }
4679    cm.doc = doc
4680    doc.cm = cm
4681    estimateLineHeights(cm)
4682    loadMode(cm)
4683    setDirectionClass(cm)
4684    if (!cm.options.lineWrapping) { findMaxLine(cm) }
4685    cm.options.mode = doc.modeOption
4686    regChange(cm)
4687  }
4688  
4689  function setDirectionClass(cm) {
4690    ;(cm.doc.direction == "rtl" ? addClass : rmClass)(cm.display.lineDiv, "CodeMirror-rtl")
4691  }
4692  
4693  function directionChanged(cm) {
4694    runInOp(cm, function () {
4695      setDirectionClass(cm)
4696      regChange(cm)
4697    })
4698  }
4699  
4700  function History(startGen) {
4701    // Arrays of change events and selections. Doing something adds an
4702    // event to done and clears undo. Undoing moves events from done
4703    // to undone, redoing moves them in the other direction.
4704    this.done = []; this.undone = []
4705    this.undoDepth = Infinity
4706    // Used to track when changes can be merged into a single undo
4707    // event
4708    this.lastModTime = this.lastSelTime = 0
4709    this.lastOp = this.lastSelOp = null
4710    this.lastOrigin = this.lastSelOrigin = null
4711    // Used by the isClean() method
4712    this.generation = this.maxGeneration = startGen || 1
4713  }
4714  
4715  // Create a history change event from an updateDoc-style change
4716  // object.
4717  function historyChangeFromChange(doc, change) {
4718    var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)}
4719    attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1)
4720    linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); }, true)
4721    return histChange
4722  }
4723  
4724  // Pop all selection events off the end of a history array. Stop at
4725  // a change event.
4726  function clearSelectionEvents(array) {
4727    while (array.length) {
4728      var last = lst(array)
4729      if (last.ranges) { array.pop() }
4730      else { break }
4731    }
4732  }
4733  
4734  // Find the top change event in the history. Pop off selection
4735  // events that are in the way.
4736  function lastChangeEvent(hist, force) {
4737    if (force) {
4738      clearSelectionEvents(hist.done)
4739      return lst(hist.done)
4740    } else if (hist.done.length && !lst(hist.done).ranges) {
4741      return lst(hist.done)
4742    } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) {
4743      hist.done.pop()
4744      return lst(hist.done)
4745    }
4746  }
4747  
4748  // Register a change in the history. Merges changes that are within
4749  // a single operation, or are close together with an origin that
4750  // allows merging (starting with "+") into a single event.
4751  function addChangeToHistory(doc, change, selAfter, opId) {
4752    var hist = doc.history
4753    hist.undone.length = 0
4754    var time = +new Date, cur
4755    var last
4756  
4757    if ((hist.lastOp == opId ||
4758         hist.lastOrigin == change.origin && change.origin &&
4759         ((change.origin.charAt(0) == "+" && doc.cm && hist.lastModTime > time - doc.cm.options.historyEventDelay) ||
4760          change.origin.charAt(0) == "*")) &&
4761        (cur = lastChangeEvent(hist, hist.lastOp == opId))) {
4762      // Merge this change into the last event
4763      last = lst(cur.changes)
4764      if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) {
4765        // Optimized case for simple insertion -- don't want to add
4766        // new changesets for every character typed
4767        last.to = changeEnd(change)
4768      } else {
4769        // Add new sub-event
4770        cur.changes.push(historyChangeFromChange(doc, change))
4771      }
4772    } else {
4773      // Can not be merged, start a new event.
4774      var before = lst(hist.done)
4775      if (!before || !before.ranges)
4776        { pushSelectionToHistory(doc.sel, hist.done) }
4777      cur = {changes: [historyChangeFromChange(doc, change)],
4778             generation: hist.generation}
4779      hist.done.push(cur)
4780      while (hist.done.length > hist.undoDepth) {
4781        hist.done.shift()
4782        if (!hist.done[0].ranges) { hist.done.shift() }
4783      }
4784    }
4785    hist.done.push(selAfter)
4786    hist.generation = ++hist.maxGeneration
4787    hist.lastModTime = hist.lastSelTime = time
4788    hist.lastOp = hist.lastSelOp = opId
4789    hist.lastOrigin = hist.lastSelOrigin = change.origin
4790  
4791    if (!last) { signal(doc, "historyAdded") }
4792  }
4793  
4794  function selectionEventCanBeMerged(doc, origin, prev, sel) {
4795    var ch = origin.charAt(0)
4796    return ch == "*" ||
4797      ch == "+" &&
4798      prev.ranges.length == sel.ranges.length &&
4799      prev.somethingSelected() == sel.somethingSelected() &&
4800      new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500)
4801  }
4802  
4803  // Called whenever the selection changes, sets the new selection as
4804  // the pending selection in the history, and pushes the old pending
4805  // selection into the 'done' array when it was significantly
4806  // different (in number of selected ranges, emptiness, or time).
4807  function addSelectionToHistory(doc, sel, opId, options) {
4808    var hist = doc.history, origin = options && options.origin
4809  
4810    // A new event is started when the previous origin does not match
4811    // the current, or the origins don't allow matching. Origins
4812    // starting with * are always merged, those starting with + are
4813    // merged when similar and close together in time.
4814    if (opId == hist.lastSelOp ||
4815        (origin && hist.lastSelOrigin == origin &&
4816         (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||
4817          selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))
4818      { hist.done[hist.done.length - 1] = sel }
4819    else
4820      { pushSelectionToHistory(sel, hist.done) }
4821  
4822    hist.lastSelTime = +new Date
4823    hist.lastSelOrigin = origin
4824    hist.lastSelOp = opId
4825    if (options && options.clearRedo !== false)
4826      { clearSelectionEvents(hist.undone) }
4827  }
4828  
4829  function pushSelectionToHistory(sel, dest) {
4830    var top = lst(dest)
4831    if (!(top && top.ranges && top.equals(sel)))
4832      { dest.push(sel) }
4833  }
4834  
4835  // Used to store marked span information in the history.
4836  function attachLocalSpans(doc, change, from, to) {
4837    var existing = change["spans_" + doc.id], n = 0
4838    doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function (line) {
4839      if (line.markedSpans)
4840        { (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans }
4841      ++n
4842    })
4843  }
4844  
4845  // When un/re-doing restores text containing marked spans, those
4846  // that have been explicitly cleared should not be restored.
4847  function removeClearedSpans(spans) {
4848    if (!spans) { return null }
4849    var out
4850    for (var i = 0; i < spans.length; ++i) {
4851      if (spans[i].marker.explicitlyCleared) { if (!out) { out = spans.slice(0, i) } }
4852      else if (out) { out.push(spans[i]) }
4853    }
4854    return !out ? spans : out.length ? out : null
4855  }
4856  
4857  // Retrieve and filter the old marked spans stored in a change event.
4858  function getOldSpans(doc, change) {
4859    var found = change["spans_" + doc.id]
4860    if (!found) { return null }
4861    var nw = []
4862    for (var i = 0; i < change.text.length; ++i)
4863      { nw.push(removeClearedSpans(found[i])) }
4864    return nw
4865  }
4866  
4867  // Used for un/re-doing changes from the history. Combines the
4868  // result of computing the existing spans with the set of spans that
4869  // existed in the history (so that deleting around a span and then
4870  // undoing brings back the span).
4871  function mergeOldSpans(doc, change) {
4872    var old = getOldSpans(doc, change)
4873    var stretched = stretchSpansOverChange(doc, change)
4874    if (!old) { return stretched }
4875    if (!stretched) { return old }
4876  
4877    for (var i = 0; i < old.length; ++i) {
4878      var oldCur = old[i], stretchCur = stretched[i]
4879      if (oldCur && stretchCur) {
4880        spans: for (var j = 0; j < stretchCur.length; ++j) {
4881          var span = stretchCur[j]
4882          for (var k = 0; k < oldCur.length; ++k)
4883            { if (oldCur[k].marker == span.marker) { continue spans } }
4884          oldCur.push(span)
4885        }
4886      } else if (stretchCur) {
4887        old[i] = stretchCur
4888      }
4889    }
4890    return old
4891  }
4892  
4893  // Used both to provide a JSON-safe object in .getHistory, and, when
4894  // detaching a document, to split the history in two
4895  function copyHistoryArray(events, newGroup, instantiateSel) {
4896    var copy = []
4897    for (var i = 0; i < events.length; ++i) {
4898      var event = events[i]
4899      if (event.ranges) {
4900        copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event)
4901        continue
4902      }
4903      var changes = event.changes, newChanges = []
4904      copy.push({changes: newChanges})
4905      for (var j = 0; j < changes.length; ++j) {
4906        var change = changes[j], m = (void 0)
4907        newChanges.push({from: change.from, to: change.to, text: change.text})
4908        if (newGroup) { for (var prop in change) { if (m = prop.match(/^spans_(\d+)$/)) {
4909          if (indexOf(newGroup, Number(m[1])) > -1) {
4910            lst(newChanges)[prop] = change[prop]
4911            delete change[prop]
4912          }
4913        } } }
4914      }
4915    }
4916    return copy
4917  }
4918  
4919  // The 'scroll' parameter given to many of these indicated whether
4920  // the new cursor position should be scrolled into view after
4921  // modifying the selection.
4922  
4923  // If shift is held or the extend flag is set, extends a range to
4924  // include a given position (and optionally a second position).
4925  // Otherwise, simply returns the range between the given positions.
4926  // Used for cursor motion and such.
4927  function extendRange(range, head, other, extend) {
4928    if (extend) {
4929      var anchor = range.anchor
4930      if (other) {
4931        var posBefore = cmp(head, anchor) < 0
4932        if (posBefore != (cmp(other, anchor) < 0)) {
4933          anchor = head
4934          head = other
4935        } else if (posBefore != (cmp(head, other) < 0)) {
4936          head = other
4937        }
4938      }
4939      return new Range(anchor, head)
4940    } else {
4941      return new Range(other || head, head)
4942    }
4943  }
4944  
4945  // Extend the primary selection range, discard the rest.
4946  function extendSelection(doc, head, other, options, extend) {
4947    if (extend == null) { extend = doc.cm && (doc.cm.display.shift || doc.extend) }
4948    setSelection(doc, new Selection([extendRange(doc.sel.primary(), head, other, extend)], 0), options)
4949  }
4950  
4951  // Extend all selections (pos is an array of selections with length
4952  // equal the number of selections)
4953  function extendSelections(doc, heads, options) {
4954    var out = []
4955    var extend = doc.cm && (doc.cm.display.shift || doc.extend)
4956    for (var i = 0; i < doc.sel.ranges.length; i++)
4957      { out[i] = extendRange(doc.sel.ranges[i], heads[i], null, extend) }
4958    var newSel = normalizeSelection(out, doc.sel.primIndex)
4959    setSelection(doc, newSel, options)
4960  }
4961  
4962  // Updates a single range in the selection.
4963  function replaceOneSelection(doc, i, range, options) {
4964    var ranges = doc.sel.ranges.slice(0)
4965    ranges[i] = range
4966    setSelection(doc, normalizeSelection(ranges, doc.sel.primIndex), options)
4967  }
4968  
4969  // Reset the selection to a single range.
4970  function setSimpleSelection(doc, anchor, head, options) {
4971    setSelection(doc, simpleSelection(anchor, head), options)
4972  }
4973  
4974  // Give beforeSelectionChange handlers a change to influence a
4975  // selection update.
4976  function filterSelectionChange(doc, sel, options) {
4977    var obj = {
4978      ranges: sel.ranges,
4979      update: function(ranges) {
4980        var this$1 = this;
4981  
4982        this.ranges = []
4983        for (var i = 0; i < ranges.length; i++)
4984          { this$1.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),
4985                                     clipPos(doc, ranges[i].head)) }
4986      },
4987      origin: options && options.origin
4988    }
4989    signal(doc, "beforeSelectionChange", doc, obj)
4990    if (doc.cm) { signal(doc.cm, "beforeSelectionChange", doc.cm, obj) }
4991    if (obj.ranges != sel.ranges) { return normalizeSelection(obj.ranges, obj.ranges.length - 1) }
4992    else { return sel }
4993  }
4994  
4995  function setSelectionReplaceHistory(doc, sel, options) {
4996    var done = doc.history.done, last = lst(done)
4997    if (last && last.ranges) {
4998      done[done.length - 1] = sel
4999      setSelectionNoUndo(doc, sel, options)
5000    } else {
5001      setSelection(doc, sel, options)
5002    }
5003  }
5004  
5005  // Set a new selection.
5006  function setSelection(doc, sel, options) {
5007    setSelectionNoUndo(doc, sel, options)
5008    addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options)
5009  }
5010  
5011  function setSelectionNoUndo(doc, sel, options) {
5012    if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange"))
5013      { sel = filterSelectionChange(doc, sel, options) }
5014  
5015    var bias = options && options.bias ||
5016      (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1)
5017    setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true))
5018  
5019    if (!(options && options.scroll === false) && doc.cm)
5020      { ensureCursorVisible(doc.cm) }
5021  }
5022  
5023  function setSelectionInner(doc, sel) {
5024    if (sel.equals(doc.sel)) { return }
5025  
5026    doc.sel = sel
5027  
5028    if (doc.cm) {
5029      doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true
5030      signalCursorActivity(doc.cm)
5031    }
5032    signalLater(doc, "cursorActivity", doc)
5033  }
5034  
5035  // Verify that the selection does not partially select any atomic
5036  // marked ranges.
5037  function reCheckSelection(doc) {
5038    setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false))
5039  }
5040  
5041  // Return a selection that does not partially select any atomic
5042  // ranges.
5043  function skipAtomicInSelection(doc, sel, bias, mayClear) {
5044    var out
5045    for (var i = 0; i < sel.ranges.length; i++) {
5046      var range = sel.ranges[i]
5047      var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i]
5048      var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear)
5049      var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear)
5050      if (out || newAnchor != range.anchor || newHead != range.head) {
5051        if (!out) { out = sel.ranges.slice(0, i) }
5052        out[i] = new Range(newAnchor, newHead)
5053      }
5054    }
5055    return out ? normalizeSelection(out, sel.primIndex) : sel
5056  }
5057  
5058  function skipAtomicInner(doc, pos, oldPos, dir, mayClear) {
5059    var line = getLine(doc, pos.line)
5060    if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
5061      var sp = line.markedSpans[i], m = sp.marker
5062      if ((sp.from == null || (m.inclusiveLeft ? sp.from <= pos.ch : sp.from < pos.ch)) &&
5063          (sp.to == null || (m.inclusiveRight ? sp.to >= pos.ch : sp.to > pos.ch))) {
5064        if (mayClear) {
5065          signal(m, "beforeCursorEnter")
5066          if (m.explicitlyCleared) {
5067            if (!line.markedSpans) { break }
5068            else {--i; continue}
5069          }
5070        }
5071        if (!m.atomic) { continue }
5072  
5073        if (oldPos) {
5074          var near = m.find(dir < 0 ? 1 : -1), diff = (void 0)
5075          if (dir < 0 ? m.inclusiveRight : m.inclusiveLeft)
5076            { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null) }
5077          if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0))
5078            { return skipAtomicInner(doc, near, pos, dir, mayClear) }
5079        }
5080  
5081        var far = m.find(dir < 0 ? -1 : 1)
5082        if (dir < 0 ? m.inclusiveLeft : m.inclusiveRight)
5083          { far = movePos(doc, far, dir, far.line == pos.line ? line : null) }
5084        return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null
5085      }
5086    } }
5087    return pos
5088  }
5089  
5090  // Ensure a given position is not inside an atomic range.
5091  function skipAtomic(doc, pos, oldPos, bias, mayClear) {
5092    var dir = bias || 1
5093    var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) ||
5094        (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) ||
5095        skipAtomicInner(doc, pos, oldPos, -dir, mayClear) ||
5096        (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true))
5097    if (!found) {
5098      doc.cantEdit = true
5099      return Pos(doc.first, 0)
5100    }
5101    return found
5102  }
5103  
5104  function movePos(doc, pos, dir, line) {
5105    if (dir < 0 && pos.ch == 0) {
5106      if (pos.line > doc.first) { return clipPos(doc, Pos(pos.line - 1)) }
5107      else { return null }
5108    } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) {
5109      if (pos.line < doc.first + doc.size - 1) { return Pos(pos.line + 1, 0) }
5110      else { return null }
5111    } else {
5112      return new Pos(pos.line, pos.ch + dir)
5113    }
5114  }
5115  
5116  function selectAll(cm) {
5117    cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll)
5118  }
5119  
5120  // UPDATING
5121  
5122  // Allow "beforeChange" event handlers to influence a change
5123  function filterChange(doc, change, update) {
5124    var obj = {
5125      canceled: false,
5126      from: change.from,
5127      to: change.to,
5128      text: change.text,
5129      origin: change.origin,
5130      cancel: function () { return obj.canceled = true; }
5131    }
5132    if (update) { obj.update = function (from, to, text, origin) {
5133      if (from) { obj.from = clipPos(doc, from) }
5134      if (to) { obj.to = clipPos(doc, to) }
5135      if (text) { obj.text = text }
5136      if (origin !== undefined) { obj.origin = origin }
5137    } }
5138    signal(doc, "beforeChange", doc, obj)
5139    if (doc.cm) { signal(doc.cm, "beforeChange", doc.cm, obj) }
5140  
5141    if (obj.canceled) { return null }
5142    return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin}
5143  }
5144  
5145  // Apply a change to a document, and add it to the document's
5146  // history, and propagating it to all linked documents.
5147  function makeChange(doc, change, ignoreReadOnly) {
5148    if (doc.cm) {
5149      if (!doc.cm.curOp) { return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) }
5150      if (doc.cm.state.suppressEdits) { return }
5151    }
5152  
5153    if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
5154      change = filterChange(doc, change, true)
5155      if (!change) { return }
5156    }
5157  
5158    // Possibly split or suppress the update based on the presence
5159    // of read-only spans in its range.
5160    var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to)
5161    if (split) {
5162      for (var i = split.length - 1; i >= 0; --i)
5163        { makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text, origin: change.origin}) }
5164    } else {
5165      makeChangeInner(doc, change)
5166    }
5167  }
5168  
5169  function makeChangeInner(doc, change) {
5170    if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) { return }
5171    var selAfter = computeSelAfterChange(doc, change)
5172    addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN)
5173  
5174    makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change))
5175    var rebased = []
5176  
5177    linkedDocs(doc, function (doc, sharedHist) {
5178      if (!sharedHist && indexOf(rebased, doc.history) == -1) {
5179        rebaseHist(doc.history, change)
5180        rebased.push(doc.history)
5181      }
5182      makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change))
5183    })
5184  }
5185  
5186  // Revert a change stored in a document's history.
5187  function makeChangeFromHistory(doc, type, allowSelectionOnly) {
5188    if (doc.cm && doc.cm.state.suppressEdits && !allowSelectionOnly) { return }
5189  
5190    var hist = doc.history, event, selAfter = doc.sel
5191    var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done
5192  
5193    // Verify that there is a useable event (so that ctrl-z won't
5194    // needlessly clear selection events)
5195    var i = 0
5196    for (; i < source.length; i++) {
5197      event = source[i]
5198      if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges)
5199        { break }
5200    }
5201    if (i == source.length) { return }
5202    hist.lastOrigin = hist.lastSelOrigin = null
5203  
5204    for (;;) {
5205      event = source.pop()
5206      if (event.ranges) {
5207        pushSelectionToHistory(event, dest)
5208        if (allowSelectionOnly && !event.equals(doc.sel)) {
5209          setSelection(doc, event, {clearRedo: false})
5210          return
5211        }
5212        selAfter = event
5213      }
5214      else { break }
5215    }
5216  
5217    // Build up a reverse change object to add to the opposite history
5218    // stack (redo when undoing, and vice versa).
5219    var antiChanges = []
5220    pushSelectionToHistory(selAfter, dest)
5221    dest.push({changes: antiChanges, generation: hist.generation})
5222    hist.generation = event.generation || ++hist.maxGeneration
5223  
5224    var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")
5225  
5226    var loop = function ( i ) {
5227      var change = event.changes[i]
5228      change.origin = type
5229      if (filter && !filterChange(doc, change, false)) {
5230        source.length = 0
5231        return {}
5232      }
5233  
5234      antiChanges.push(historyChangeFromChange(doc, change))
5235  
5236      var after = i ? computeSelAfterChange(doc, change) : lst(source)
5237      makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change))
5238      if (!i && doc.cm) { doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}) }
5239      var rebased = []
5240  
5241      // Propagate to the linked documents
5242      linkedDocs(doc, function (doc, sharedHist) {
5243        if (!sharedHist && indexOf(rebased, doc.history) == -1) {
5244          rebaseHist(doc.history, change)
5245          rebased.push(doc.history)
5246        }
5247        makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change))
5248      })
5249    };
5250  
5251    for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) {
5252      var returned = loop( i$1 );
5253  
5254      if ( returned ) return returned.v;
5255    }
5256  }
5257  
5258  // Sub-views need their line numbers shifted when text is added
5259  // above or below them in the parent document.
5260  function shiftDoc(doc, distance) {
5261    if (distance == 0) { return }
5262    doc.first += distance
5263    doc.sel = new Selection(map(doc.sel.ranges, function (range) { return new Range(
5264      Pos(range.anchor.line + distance, range.anchor.ch),
5265      Pos(range.head.line + distance, range.head.ch)
5266    ); }), doc.sel.primIndex)
5267    if (doc.cm) {
5268      regChange(doc.cm, doc.first, doc.first - distance, distance)
5269      for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++)
5270        { regLineChange(doc.cm, l, "gutter") }
5271    }
5272  }
5273  
5274  // More lower-level change function, handling only a single document
5275  // (not linked ones).
5276  function makeChangeSingleDoc(doc, change, selAfter, spans) {
5277    if (doc.cm && !doc.cm.curOp)
5278      { return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) }
5279  
5280    if (change.to.line < doc.first) {
5281      shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line))
5282      return
5283    }
5284    if (change.from.line > doc.lastLine()) { return }
5285  
5286    // Clip the change to the size of this doc
5287    if (change.from.line < doc.first) {
5288      var shift = change.text.length - 1 - (doc.first - change.from.line)
5289      shiftDoc(doc, shift)
5290      change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
5291                text: [lst(change.text)], origin: change.origin}
5292    }
5293    var last = doc.lastLine()
5294    if (change.to.line > last) {
5295      change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
5296                text: [change.text[0]], origin: change.origin}
5297    }
5298  
5299    change.removed = getBetween(doc, change.from, change.to)
5300  
5301    if (!selAfter) { selAfter = computeSelAfterChange(doc, change) }
5302    if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans) }
5303    else { updateDoc(doc, change, spans) }
5304    setSelectionNoUndo(doc, selAfter, sel_dontScroll)
5305  }
5306  
5307  // Handle the interaction of a change to a document with the editor
5308  // that this document is part of.
5309  function makeChangeSingleDocInEditor(cm, change, spans) {
5310    var doc = cm.doc, display = cm.display, from = change.from, to = change.to
5311  
5312    var recomputeMaxLength = false, checkWidthStart = from.line
5313    if (!cm.options.lineWrapping) {
5314      checkWidthStart = lineNo(visualLine(getLine(doc, from.line)))
5315      doc.iter(checkWidthStart, to.line + 1, function (line) {
5316        if (line == display.maxLine) {
5317          recomputeMaxLength = true
5318          return true
5319        }
5320      })
5321    }
5322  
5323    if (doc.sel.contains(change.from, change.to) > -1)
5324      { signalCursorActivity(cm) }
5325  
5326    updateDoc(doc, change, spans, estimateHeight(cm))
5327  
5328    if (!cm.options.lineWrapping) {
5329      doc.iter(checkWidthStart, from.line + change.text.length, function (line) {
5330        var len = lineLength(line)
5331        if (len > display.maxLineLength) {
5332          display.maxLine = line
5333          display.maxLineLength = len
5334          display.maxLineChanged = true
5335          recomputeMaxLength = false
5336        }
5337      })
5338      if (recomputeMaxLength) { cm.curOp.updateMaxLine = true }
5339    }
5340  
5341    retreatFrontier(doc, from.line)
5342    startWorker(cm, 400)
5343  
5344    var lendiff = change.text.length - (to.line - from.line) - 1
5345    // Remember that these lines changed, for updating the display
5346    if (change.full)
5347      { regChange(cm) }
5348    else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change))
5349      { regLineChange(cm, from.line, "text") }
5350    else
5351      { regChange(cm, from.line, to.line + 1, lendiff) }
5352  
5353    var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change")
5354    if (changeHandler || changesHandler) {
5355      var obj = {
5356        from: from, to: to,
5357        text: change.text,
5358        removed: change.removed,
5359        origin: change.origin
5360      }
5361      if (changeHandler) { signalLater(cm, "change", cm, obj) }
5362      if (changesHandler) { (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj) }
5363    }
5364    cm.display.selForContextMenu = null
5365  }
5366  
5367  function replaceRange(doc, code, from, to, origin) {
5368    if (!to) { to = from }
5369    if (cmp(to, from) < 0) { var assign;
5370      (assign = [to, from], from = assign[0], to = assign[1], assign) }
5371    if (typeof code == "string") { code = doc.splitLines(code) }
5372    makeChange(doc, {from: from, to: to, text: code, origin: origin})
5373  }
5374  
5375  // Rebasing/resetting history to deal with externally-sourced changes
5376  
5377  function rebaseHistSelSingle(pos, from, to, diff) {
5378    if (to < pos.line) {
5379      pos.line += diff
5380    } else if (from < pos.line) {
5381      pos.line = from
5382      pos.ch = 0
5383    }
5384  }
5385  
5386  // Tries to rebase an array of history events given a change in the
5387  // document. If the change touches the same lines as the event, the
5388  // event, and everything 'behind' it, is discarded. If the change is
5389  // before the event, the event's positions are updated. Uses a
5390  // copy-on-write scheme for the positions, to avoid having to
5391  // reallocate them all on every rebase, but also avoid problems with
5392  // shared position objects being unsafely updated.
5393  function rebaseHistArray(array, from, to, diff) {
5394    for (var i = 0; i < array.length; ++i) {
5395      var sub = array[i], ok = true
5396      if (sub.ranges) {
5397        if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true }
5398        for (var j = 0; j < sub.ranges.length; j++) {
5399          rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff)
5400          rebaseHistSelSingle(sub.ranges[j].head, from, to, diff)
5401        }
5402        continue
5403      }
5404      for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) {
5405        var cur = sub.changes[j$1]
5406        if (to < cur.from.line) {
5407          cur.from = Pos(cur.from.line + diff, cur.from.ch)
5408          cur.to = Pos(cur.to.line + diff, cur.to.ch)
5409        } else if (from <= cur.to.line) {
5410          ok = false
5411          break
5412        }
5413      }
5414      if (!ok) {
5415        array.splice(0, i + 1)
5416        i = 0
5417      }
5418    }
5419  }
5420  
5421  function rebaseHist(hist, change) {
5422    var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1
5423    rebaseHistArray(hist.done, from, to, diff)
5424    rebaseHistArray(hist.undone, from, to, diff)
5425  }
5426  
5427  // Utility for applying a change to a line by handle or number,
5428  // returning the number and optionally registering the line as
5429  // changed.
5430  function changeLine(doc, handle, changeType, op) {
5431    var no = handle, line = handle
5432    if (typeof handle == "number") { line = getLine(doc, clipLine(doc, handle)) }
5433    else { no = lineNo(handle) }
5434    if (no == null) { return null }
5435    if (op(line, no) && doc.cm) { regLineChange(doc.cm, no, changeType) }
5436    return line
5437  }
5438  
5439  // The document is represented as a BTree consisting of leaves, with
5440  // chunk of lines in them, and branches, with up to ten leaves or
5441  // other branch nodes below them. The top node is always a branch
5442  // node, and is the document object itself (meaning it has
5443  // additional methods and properties).
5444  //
5445  // All nodes have parent links. The tree is used both to go from
5446  // line numbers to line objects, and to go from objects to numbers.
5447  // It also indexes by height, and is used to convert between height
5448  // and line object, and to find the total height of the document.
5449  //
5450  // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html
5451  
5452  function LeafChunk(lines) {
5453    var this$1 = this;
5454  
5455    this.lines = lines
5456    this.parent = null
5457    var height = 0
5458    for (var i = 0; i < lines.length; ++i) {
5459      lines[i].parent = this$1
5460      height += lines[i].height
5461    }
5462    this.height = height
5463  }
5464  
5465  LeafChunk.prototype = {
5466    chunkSize: function chunkSize() { return this.lines.length },
5467  
5468    // Remove the n lines at offset 'at'.
5469    removeInner: function removeInner(at, n) {
5470      var this$1 = this;
5471  
5472      for (var i = at, e = at + n; i < e; ++i) {
5473        var line = this$1.lines[i]
5474        this$1.height -= line.height
5475        cleanUpLine(line)
5476        signalLater(line, "delete")
5477      }
5478      this.lines.splice(at, n)
5479    },
5480  
5481    // Helper used to collapse a small branch into a single leaf.
5482    collapse: function collapse(lines) {
5483      lines.push.apply(lines, this.lines)
5484    },
5485  
5486    // Insert the given array of lines at offset 'at', count them as
5487    // having the given height.
5488    insertInner: function insertInner(at, lines, height) {
5489      var this$1 = this;
5490  
5491      this.height += height
5492      this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at))
5493      for (var i = 0; i < lines.length; ++i) { lines[i].parent = this$1 }
5494    },
5495  
5496    // Used to iterate over a part of the tree.
5497    iterN: function iterN(at, n, op) {
5498      var this$1 = this;
5499  
5500      for (var e = at + n; at < e; ++at)
5501        { if (op(this$1.lines[at])) { return true } }
5502    }
5503  }
5504  
5505  function BranchChunk(children) {
5506    var this$1 = this;
5507  
5508    this.children = children
5509    var size = 0, height = 0
5510    for (var i = 0; i < children.length; ++i) {
5511      var ch = children[i]
5512      size += ch.chunkSize(); height += ch.height
5513      ch.parent = this$1
5514    }
5515    this.size = size
5516    this.height = height
5517    this.parent = null
5518  }
5519  
5520  BranchChunk.prototype = {
5521    chunkSize: function chunkSize() { return this.size },
5522  
5523    removeInner: function removeInner(at, n) {
5524      var this$1 = this;
5525  
5526      this.size -= n
5527      for (var i = 0; i < this.children.length; ++i) {
5528        var child = this$1.children[i], sz = child.chunkSize()
5529        if (at < sz) {
5530          var rm = Math.min(n, sz - at), oldHeight = child.height
5531          child.removeInner(at, rm)
5532          this$1.height -= oldHeight - child.height
5533          if (sz == rm) { this$1.children.splice(i--, 1); child.parent = null }
5534          if ((n -= rm) == 0) { break }
5535          at = 0
5536        } else { at -= sz }
5537      }
5538      // If the result is smaller than 25 lines, ensure that it is a
5539      // single leaf node.
5540      if (this.size - n < 25 &&
5541          (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) {
5542        var lines = []
5543        this.collapse(lines)
5544        this.children = [new LeafChunk(lines)]
5545        this.children[0].parent = this
5546      }
5547    },
5548  
5549    collapse: function collapse(lines) {
5550      var this$1 = this;
5551  
5552      for (var i = 0; i < this.children.length; ++i) { this$1.children[i].collapse(lines) }
5553    },
5554  
5555    insertInner: function insertInner(at, lines, height) {
5556      var this$1 = this;
5557  
5558      this.size += lines.length
5559      this.height += height
5560      for (var i = 0; i < this.children.length; ++i) {
5561        var child = this$1.children[i], sz = child.chunkSize()
5562        if (at <= sz) {
5563          child.insertInner(at, lines, height)
5564          if (child.lines && child.lines.length > 50) {
5565            // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced.
5566            // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest.
5567            var remaining = child.lines.length % 25 + 25
5568            for (var pos = remaining; pos < child.lines.length;) {
5569              var leaf = new LeafChunk(child.lines.slice(pos, pos += 25))
5570              child.height -= leaf.height
5571              this$1.children.splice(++i, 0, leaf)
5572              leaf.parent = this$1
5573            }
5574            child.lines = child.lines.slice(0, remaining)
5575            this$1.maybeSpill()
5576          }
5577          break
5578        }
5579        at -= sz
5580      }
5581    },
5582  
5583    // When a node has grown, check whether it should be split.
5584    maybeSpill: function maybeSpill() {
5585      if (this.children.length <= 10) { return }
5586      var me = this
5587      do {
5588        var spilled = me.children.splice(me.children.length - 5, 5)
5589        var sibling = new BranchChunk(spilled)
5590        if (!me.parent) { // Become the parent node
5591          var copy = new BranchChunk(me.children)
5592          copy.parent = me
5593          me.children = [copy, sibling]
5594          me = copy
5595       } else {
5596          me.size -= sibling.size
5597          me.height -= sibling.height
5598          var myIndex = indexOf(me.parent.children, me)
5599          me.parent.children.splice(myIndex + 1, 0, sibling)
5600        }
5601        sibling.parent = me.parent
5602      } while (me.children.length > 10)
5603      me.parent.maybeSpill()
5604    },
5605  
5606    iterN: function iterN(at, n, op) {
5607      var this$1 = this;
5608  
5609      for (var i = 0; i < this.children.length; ++i) {
5610        var child = this$1.children[i], sz = child.chunkSize()
5611        if (at < sz) {
5612          var used = Math.min(n, sz - at)
5613          if (child.iterN(at, used, op)) { return true }
5614          if ((n -= used) == 0) { break }
5615          at = 0
5616        } else { at -= sz }
5617      }
5618    }
5619  }
5620  
5621  // Line widgets are block elements displayed above or below a line.
5622  
5623  var LineWidget = function(doc, node, options) {
5624    var this$1 = this;
5625  
5626    if (options) { for (var opt in options) { if (options.hasOwnProperty(opt))
5627      { this$1[opt] = options[opt] } } }
5628    this.doc = doc
5629    this.node = node
5630  };
5631  
5632  LineWidget.prototype.clear = function () {
5633      var this$1 = this;
5634  
5635    var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line)
5636    if (no == null || !ws) { return }
5637    for (var i = 0; i < ws.length; ++i) { if (ws[i] == this$1) { ws.splice(i--, 1) } }
5638    if (!ws.length) { line.widgets = null }
5639    var height = widgetHeight(this)
5640    updateLineHeight(line, Math.max(0, line.height - height))
5641    if (cm) {
5642      runInOp(cm, function () {
5643        adjustScrollWhenAboveVisible(cm, line, -height)
5644        regLineChange(cm, no, "widget")
5645      })
5646      signalLater(cm, "lineWidgetCleared", cm, this, no)
5647    }
5648  };
5649  
5650  LineWidget.prototype.changed = function () {
5651      var this$1 = this;
5652  
5653    var oldH = this.height, cm = this.doc.cm, line = this.line
5654    this.height = null
5655    var diff = widgetHeight(this) - oldH
5656    if (!diff) { return }
5657    updateLineHeight(line, line.height + diff)
5658    if (cm) {
5659      runInOp(cm, function () {
5660        cm.curOp.forceUpdate = true
5661        adjustScrollWhenAboveVisible(cm, line, diff)
5662        signalLater(cm, "lineWidgetChanged", cm, this$1, lineNo(line))
5663      })
5664    }
5665  };
5666  eventMixin(LineWidget)
5667  
5668  function adjustScrollWhenAboveVisible(cm, line, diff) {
5669    if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))
5670      { addToScrollTop(cm, diff) }
5671  }
5672  
5673  function addLineWidget(doc, handle, node, options) {
5674    var widget = new LineWidget(doc, node, options)
5675    var cm = doc.cm
5676    if (cm && widget.noHScroll) { cm.display.alignWidgets = true }
5677    changeLine(doc, handle, "widget", function (line) {
5678      var widgets = line.widgets || (line.widgets = [])
5679      if (widget.insertAt == null) { widgets.push(widget) }
5680      else { widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget) }
5681      widget.line = line
5682      if (cm && !lineIsHidden(doc, line)) {
5683        var aboveVisible = heightAtLine(line) < doc.scrollTop
5684        updateLineHeight(line, line.height + widgetHeight(widget))
5685        if (aboveVisible) { addToScrollTop(cm, widget.height) }
5686        cm.curOp.forceUpdate = true
5687      }
5688      return true
5689    })
5690    signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle))
5691    return widget
5692  }
5693  
5694  // TEXTMARKERS
5695  
5696  // Created with markText and setBookmark methods. A TextMarker is a
5697  // handle that can be used to clear or find a marked position in the
5698  // document. Line objects hold arrays (markedSpans) containing
5699  // {from, to, marker} object pointing to such marker objects, and
5700  // indicating that such a marker is present on that line. Multiple
5701  // lines may point to the same marker when it spans across lines.
5702  // The spans will have null for their from/to properties when the
5703  // marker continues beyond the start/end of the line. Markers have
5704  // links back to the lines they currently touch.
5705  
5706  // Collapsed markers have unique ids, in order to be able to order
5707  // them, which is needed for uniquely determining an outer marker
5708  // when they overlap (they may nest, but not partially overlap).
5709  var nextMarkerId = 0
5710  
5711  var TextMarker = function(doc, type) {
5712    this.lines = []
5713    this.type = type
5714    this.doc = doc
5715    this.id = ++nextMarkerId
5716  };
5717  
5718  // Clear the marker.
5719  TextMarker.prototype.clear = function () {
5720      var this$1 = this;
5721  
5722    if (this.explicitlyCleared) { return }
5723    var cm = this.doc.cm, withOp = cm && !cm.curOp
5724    if (withOp) { startOperation(cm) }
5725    if (hasHandler(this, "clear")) {
5726      var found = this.find()
5727      if (found) { signalLater(this, "clear", found.from, found.to) }
5728    }
5729    var min = null, max = null
5730    for (var i = 0; i < this.lines.length; ++i) {
5731      var line = this$1.lines[i]
5732      var span = getMarkedSpanFor(line.markedSpans, this$1)
5733      if (cm && !this$1.collapsed) { regLineChange(cm, lineNo(line), "text") }
5734      else if (cm) {
5735        if (span.to != null) { max = lineNo(line) }
5736        if (span.from != null) { min = lineNo(line) }
5737      }
5738      line.markedSpans = removeMarkedSpan(line.markedSpans, span)
5739      if (span.from == null && this$1.collapsed && !lineIsHidden(this$1.doc, line) && cm)
5740        { updateLineHeight(line, textHeight(cm.display)) }
5741    }
5742    if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) {
5743      var visual = visualLine(this$1.lines[i$1]), len = lineLength(visual)
5744      if (len > cm.display.maxLineLength) {
5745        cm.display.maxLine = visual
5746        cm.display.maxLineLength = len
5747        cm.display.maxLineChanged = true
5748      }
5749    } }
5750  
5751    if (min != null && cm && this.collapsed) { regChange(cm, min, max + 1) }
5752    this.lines.length = 0
5753    this.explicitlyCleared = true
5754    if (this.atomic && this.doc.cantEdit) {
5755      this.doc.cantEdit = false
5756      if (cm) { reCheckSelection(cm.doc) }
5757    }
5758    if (cm) { signalLater(cm, "markerCleared", cm, this, min, max) }
5759    if (withOp) { endOperation(cm) }
5760    if (this.parent) { this.parent.clear() }
5761  };
5762  
5763  // Find the position of the marker in the document. Returns a {from,
5764  // to} object by default. Side can be passed to get a specific side
5765  // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the
5766  // Pos objects returned contain a line object, rather than a line
5767  // number (used to prevent looking up the same line twice).
5768  TextMarker.prototype.find = function (side, lineObj) {
5769      var this$1 = this;
5770  
5771    if (side == null && this.type == "bookmark") { side = 1 }
5772    var from, to
5773    for (var i = 0; i < this.lines.length; ++i) {
5774      var line = this$1.lines[i]
5775      var span = getMarkedSpanFor(line.markedSpans, this$1)
5776      if (span.from != null) {
5777        from = Pos(lineObj ? line : lineNo(line), span.from)
5778        if (side == -1) { return from }
5779      }
5780      if (span.to != null) {
5781        to = Pos(lineObj ? line : lineNo(line), span.to)
5782        if (side == 1) { return to }
5783      }
5784    }
5785    return from && {from: from, to: to}
5786  };
5787  
5788  // Signals that the marker's widget changed, and surrounding layout
5789  // should be recomputed.
5790  TextMarker.prototype.changed = function () {
5791      var this$1 = this;
5792  
5793    var pos = this.find(-1, true), widget = this, cm = this.doc.cm
5794    if (!pos || !cm) { return }
5795    runInOp(cm, function () {
5796      var line = pos.line, lineN = lineNo(pos.line)
5797      var view = findViewForLine(cm, lineN)
5798      if (view) {
5799        clearLineMeasurementCacheFor(view)
5800        cm.curOp.selectionChanged = cm.curOp.forceUpdate = true
5801      }
5802      cm.curOp.updateMaxLine = true
5803      if (!lineIsHidden(widget.doc, line) && widget.height != null) {
5804        var oldHeight = widget.height
5805        widget.height = null
5806        var dHeight = widgetHeight(widget) - oldHeight
5807        if (dHeight)
5808          { updateLineHeight(line, line.height + dHeight) }
5809      }
5810      signalLater(cm, "markerChanged", cm, this$1)
5811    })
5812  };
5813  
5814  TextMarker.prototype.attachLine = function (line) {
5815    if (!this.lines.length && this.doc.cm) {
5816      var op = this.doc.cm.curOp
5817      if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
5818        { (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this) }
5819    }
5820    this.lines.push(line)
5821  };
5822  
5823  TextMarker.prototype.detachLine = function (line) {
5824    this.lines.splice(indexOf(this.lines, line), 1)
5825    if (!this.lines.length && this.doc.cm) {
5826      var op = this.doc.cm.curOp
5827      ;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this)
5828    }
5829  };
5830  eventMixin(TextMarker)
5831  
5832  // Create a marker, wire it up to the right lines, and
5833  function markText(doc, from, to, options, type) {
5834    // Shared markers (across linked documents) are handled separately
5835    // (markTextShared will call out to this again, once per
5836    // document).
5837    if (options && options.shared) { return markTextShared(doc, from, to, options, type) }
5838    // Ensure we are in an operation.
5839    if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, markText)(doc, from, to, options, type) }
5840  
5841    var marker = new TextMarker(doc, type), diff = cmp(from, to)
5842    if (options) { copyObj(options, marker, false) }
5843    // Don't connect empty markers unless clearWhenEmpty is false
5844    if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false)
5845      { return marker }
5846    if (marker.replacedWith) {
5847      // Showing up as a widget implies collapsed (widget replaces text)
5848      marker.collapsed = true
5849      marker.widgetNode = eltP("span", [marker.replacedWith], "CodeMirror-widget")
5850      if (!options.handleMouseEvents) { marker.widgetNode.setAttribute("cm-ignore-events", "true") }
5851      if (options.insertLeft) { marker.widgetNode.insertLeft = true }
5852    }
5853    if (marker.collapsed) {
5854      if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||
5855          from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker))
5856        { throw new Error("Inserting collapsed marker partially overlapping an existing one") }
5857      seeCollapsedSpans()
5858    }
5859  
5860    if (marker.addToHistory)
5861      { addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN) }
5862  
5863    var curLine = from.line, cm = doc.cm, updateMaxLine
5864    doc.iter(curLine, to.line + 1, function (line) {
5865      if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine)
5866        { updateMaxLine = true }
5867      if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0) }
5868      addMarkedSpan(line, new MarkedSpan(marker,
5869                                         curLine == from.line ? from.ch : null,
5870                                         curLine == to.line ? to.ch : null))
5871      ++curLine
5872    })
5873    // lineIsHidden depends on the presence of the spans, so needs a second pass
5874    if (marker.collapsed) { doc.iter(from.line, to.line + 1, function (line) {
5875      if (lineIsHidden(doc, line)) { updateLineHeight(line, 0) }
5876    }) }
5877  
5878    if (marker.clearOnEnter) { on(marker, "beforeCursorEnter", function () { return marker.clear(); }) }
5879  
5880    if (marker.readOnly) {
5881      seeReadOnlySpans()
5882      if (doc.history.done.length || doc.history.undone.length)
5883        { doc.clearHistory() }
5884    }
5885    if (marker.collapsed) {
5886      marker.id = ++nextMarkerId
5887      marker.atomic = true
5888    }
5889    if (cm) {
5890      // Sync editor state
5891      if (updateMaxLine) { cm.curOp.updateMaxLine = true }
5892      if (marker.collapsed)
5893        { regChange(cm, from.line, to.line + 1) }
5894      else if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.css)
5895        { for (var i = from.line; i <= to.line; i++) { regLineChange(cm, i, "text") } }
5896      if (marker.atomic) { reCheckSelection(cm.doc) }
5897      signalLater(cm, "markerAdded", cm, marker)
5898    }
5899    return marker
5900  }
5901  
5902  // SHARED TEXTMARKERS
5903  
5904  // A shared marker spans multiple linked documents. It is
5905  // implemented as a meta-marker-object controlling multiple normal
5906  // markers.
5907  var SharedTextMarker = function(markers, primary) {
5908    var this$1 = this;
5909  
5910    this.markers = markers
5911    this.primary = primary
5912    for (var i = 0; i < markers.length; ++i)
5913      { markers[i].parent = this$1 }
5914  };
5915  
5916  SharedTextMarker.prototype.clear = function () {
5917      var this$1 = this;
5918  
5919    if (this.explicitlyCleared) { return }
5920    this.explicitlyCleared = true
5921    for (var i = 0; i < this.markers.length; ++i)
5922      { this$1.markers[i].clear() }
5923    signalLater(this, "clear")
5924  };
5925  
5926  SharedTextMarker.prototype.find = function (side, lineObj) {
5927    return this.primary.find(side, lineObj)
5928  };
5929  eventMixin(SharedTextMarker)
5930  
5931  function markTextShared(doc, from, to, options, type) {
5932    options = copyObj(options)
5933    options.shared = false
5934    var markers = [markText(doc, from, to, options, type)], primary = markers[0]
5935    var widget = options.widgetNode
5936    linkedDocs(doc, function (doc) {
5937      if (widget) { options.widgetNode = widget.cloneNode(true) }
5938      markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type))
5939      for (var i = 0; i < doc.linked.length; ++i)
5940        { if (doc.linked[i].isParent) { return } }
5941      primary = lst(markers)
5942    })
5943    return new SharedTextMarker(markers, primary)
5944  }
5945  
5946  function findSharedMarkers(doc) {
5947    return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function (m) { return m.parent; })
5948  }
5949  
5950  function copySharedMarkers(doc, markers) {
5951    for (var i = 0; i < markers.length; i++) {
5952      var marker = markers[i], pos = marker.find()
5953      var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to)
5954      if (cmp(mFrom, mTo)) {
5955        var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type)
5956        marker.markers.push(subMark)
5957        subMark.parent = marker
5958      }
5959    }
5960  }
5961  
5962  function detachSharedMarkers(markers) {
5963    var loop = function ( i ) {
5964      var marker = markers[i], linked = [marker.primary.doc]
5965      linkedDocs(marker.primary.doc, function (d) { return linked.push(d); })
5966      for (var j = 0; j < marker.markers.length; j++) {
5967        var subMarker = marker.markers[j]
5968        if (indexOf(linked, subMarker.doc) == -1) {
5969          subMarker.parent = null
5970          marker.markers.splice(j--, 1)
5971        }
5972      }
5973    };
5974  
5975    for (var i = 0; i < markers.length; i++) loop( i );
5976  }
5977  
5978  var nextDocId = 0
5979  var Doc = function(text, mode, firstLine, lineSep, direction) {
5980    if (!(this instanceof Doc)) { return new Doc(text, mode, firstLine, lineSep, direction) }
5981    if (firstLine == null) { firstLine = 0 }
5982  
5983    BranchChunk.call(this, [new LeafChunk([new Line("", null)])])
5984    this.first = firstLine
5985    this.scrollTop = this.scrollLeft = 0
5986    this.cantEdit = false
5987    this.cleanGeneration = 1
5988    this.modeFrontier = this.highlightFrontier = firstLine
5989    var start = Pos(firstLine, 0)
5990    this.sel = simpleSelection(start)
5991    this.history = new History(null)
5992    this.id = ++nextDocId
5993    this.modeOption = mode
5994    this.lineSep = lineSep
5995    this.direction = (direction == "rtl") ? "rtl" : "ltr"
5996    this.extend = false
5997  
5998    if (typeof text == "string") { text = this.splitLines(text) }
5999    updateDoc(this, {from: start, to: start, text: text})
6000    setSelection(this, simpleSelection(start), sel_dontScroll)
6001  }
6002  
6003  Doc.prototype = createObj(BranchChunk.prototype, {
6004    constructor: Doc,
6005    // Iterate over the document. Supports two forms -- with only one
6006    // argument, it calls that for each line in the document. With
6007    // three, it iterates over the range given by the first two (with
6008    // the second being non-inclusive).
6009    iter: function(from, to, op) {
6010      if (op) { this.iterN(from - this.first, to - from, op) }
6011      else { this.iterN(this.first, this.first + this.size, from) }
6012    },
6013  
6014    // Non-public interface for adding and removing lines.
6015    insert: function(at, lines) {
6016      var height = 0
6017      for (var i = 0; i < lines.length; ++i) { height += lines[i].height }
6018      this.insertInner(at - this.first, lines, height)
6019    },
6020    remove: function(at, n) { this.removeInner(at - this.first, n) },
6021  
6022    // From here, the methods are part of the public interface. Most
6023    // are also available from CodeMirror (editor) instances.
6024  
6025    getValue: function(lineSep) {
6026      var lines = getLines(this, this.first, this.first + this.size)
6027      if (lineSep === false) { return lines }
6028      return lines.join(lineSep || this.lineSeparator())
6029    },
6030    setValue: docMethodOp(function(code) {
6031      var top = Pos(this.first, 0), last = this.first + this.size - 1
6032      makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
6033                        text: this.splitLines(code), origin: "setValue", full: true}, true)
6034      if (this.cm) { scrollToCoords(this.cm, 0, 0) }
6035      setSelection(this, simpleSelection(top), sel_dontScroll)
6036    }),
6037    replaceRange: function(code, from, to, origin) {
6038      from = clipPos(this, from)
6039      to = to ? clipPos(this, to) : from
6040      replaceRange(this, code, from, to, origin)
6041    },
6042    getRange: function(from, to, lineSep) {
6043      var lines = getBetween(this, clipPos(this, from), clipPos(this, to))
6044      if (lineSep === false) { return lines }
6045      return lines.join(lineSep || this.lineSeparator())
6046    },
6047  
6048    getLine: function(line) {var l = this.getLineHandle(line); return l && l.text},
6049  
6050    getLineHandle: function(line) {if (isLine(this, line)) { return getLine(this, line) }},
6051    getLineNumber: function(line) {return lineNo(line)},
6052  
6053    getLineHandleVisualStart: function(line) {
6054      if (typeof line == "number") { line = getLine(this, line) }
6055      return visualLine(line)
6056    },
6057  
6058    lineCount: function() {return this.size},
6059    firstLine: function() {return this.first},
6060    lastLine: function() {return this.first + this.size - 1},
6061  
6062    clipPos: function(pos) {return clipPos(this, pos)},
6063  
6064    getCursor: function(start) {
6065      var range = this.sel.primary(), pos
6066      if (start == null || start == "head") { pos = range.head }
6067      else if (start == "anchor") { pos = range.anchor }
6068      else if (start == "end" || start == "to" || start === false) { pos = range.to() }
6069      else { pos = range.from() }
6070      return pos
6071    },
6072    listSelections: function() { return this.sel.ranges },
6073    somethingSelected: function() {return this.sel.somethingSelected()},
6074  
6075    setCursor: docMethodOp(function(line, ch, options) {
6076      setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options)
6077    }),
6078    setSelection: docMethodOp(function(anchor, head, options) {
6079      setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options)
6080    }),
6081    extendSelection: docMethodOp(function(head, other, options) {
6082      extendSelection(this, clipPos(this, head), other && clipPos(this, other), options)
6083    }),
6084    extendSelections: docMethodOp(function(heads, options) {
6085      extendSelections(this, clipPosArray(this, heads), options)
6086    }),
6087    extendSelectionsBy: docMethodOp(function(f, options) {
6088      var heads = map(this.sel.ranges, f)
6089      extendSelections(this, clipPosArray(this, heads), options)
6090    }),
6091    setSelections: docMethodOp(function(ranges, primary, options) {
6092      var this$1 = this;
6093  
6094      if (!ranges.length) { return }
6095      var out = []
6096      for (var i = 0; i < ranges.length; i++)
6097        { out[i] = new Range(clipPos(this$1, ranges[i].anchor),
6098                           clipPos(this$1, ranges[i].head)) }
6099      if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex) }
6100      setSelection(this, normalizeSelection(out, primary), options)
6101    }),
6102    addSelection: docMethodOp(function(anchor, head, options) {
6103      var ranges = this.sel.ranges.slice(0)
6104      ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)))
6105      setSelection(this, normalizeSelection(ranges, ranges.length - 1), options)
6106    }),
6107  
6108    getSelection: function(lineSep) {
6109      var this$1 = this;
6110  
6111      var ranges = this.sel.ranges, lines
6112      for (var i = 0; i < ranges.length; i++) {
6113        var sel = getBetween(this$1, ranges[i].from(), ranges[i].to())
6114        lines = lines ? lines.concat(sel) : sel
6115      }
6116      if (lineSep === false) { return lines }
6117      else { return lines.join(lineSep || this.lineSeparator()) }
6118    },
6119    getSelections: function(lineSep) {
6120      var this$1 = this;
6121  
6122      var parts = [], ranges = this.sel.ranges
6123      for (var i = 0; i < ranges.length; i++) {
6124        var sel = getBetween(this$1, ranges[i].from(), ranges[i].to())
6125        if (lineSep !== false) { sel = sel.join(lineSep || this$1.lineSeparator()) }
6126        parts[i] = sel
6127      }
6128      return parts
6129    },
6130    replaceSelection: function(code, collapse, origin) {
6131      var dup = []
6132      for (var i = 0; i < this.sel.ranges.length; i++)
6133        { dup[i] = code }
6134      this.replaceSelections(dup, collapse, origin || "+input")
6135    },
6136    replaceSelections: docMethodOp(function(code, collapse, origin) {
6137      var this$1 = this;
6138  
6139      var changes = [], sel = this.sel
6140      for (var i = 0; i < sel.ranges.length; i++) {
6141        var range = sel.ranges[i]
6142        changes[i] = {from: range.from(), to: range.to(), text: this$1.splitLines(code[i]), origin: origin}
6143      }
6144      var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse)
6145      for (var i$1 = changes.length - 1; i$1 >= 0; i$1--)
6146        { makeChange(this$1, changes[i$1]) }
6147      if (newSel) { setSelectionReplaceHistory(this, newSel) }
6148      else if (this.cm) { ensureCursorVisible(this.cm) }
6149    }),
6150    undo: docMethodOp(function() {makeChangeFromHistory(this, "undo")}),
6151    redo: docMethodOp(function() {makeChangeFromHistory(this, "redo")}),
6152    undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true)}),
6153    redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true)}),
6154  
6155    setExtending: function(val) {this.extend = val},
6156    getExtending: function() {return this.extend},
6157  
6158    historySize: function() {
6159      var hist = this.history, done = 0, undone = 0
6160      for (var i = 0; i < hist.done.length; i++) { if (!hist.done[i].ranges) { ++done } }
6161      for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].ranges) { ++undone } }
6162      return {undo: done, redo: undone}
6163    },
6164    clearHistory: function() {this.history = new History(this.history.maxGeneration)},
6165  
6166    markClean: function() {
6167      this.cleanGeneration = this.changeGeneration(true)
6168    },
6169    changeGeneration: function(forceSplit) {
6170      if (forceSplit)
6171        { this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null }
6172      return this.history.generation
6173    },
6174    isClean: function (gen) {
6175      return this.history.generation == (gen || this.cleanGeneration)
6176    },
6177  
6178    getHistory: function() {
6179      return {done: copyHistoryArray(this.history.done),
6180              undone: copyHistoryArray(this.history.undone)}
6181    },
6182    setHistory: function(histData) {
6183      var hist = this.history = new History(this.history.maxGeneration)
6184      hist.done = copyHistoryArray(histData.done.slice(0), null, true)
6185      hist.undone = copyHistoryArray(histData.undone.slice(0), null, true)
6186    },
6187  
6188    setGutterMarker: docMethodOp(function(line, gutterID, value) {
6189      return changeLine(this, line, "gutter", function (line) {
6190        var markers = line.gutterMarkers || (line.gutterMarkers = {})
6191        markers[gutterID] = value
6192        if (!value && isEmpty(markers)) { line.gutterMarkers = null }
6193        return true
6194      })
6195    }),
6196  
6197    clearGutter: docMethodOp(function(gutterID) {
6198      var this$1 = this;
6199  
6200      this.iter(function (line) {
6201        if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
6202          changeLine(this$1, line, "gutter", function () {
6203            line.gutterMarkers[gutterID] = null
6204            if (isEmpty(line.gutterMarkers)) { line.gutterMarkers = null }
6205            return true
6206          })
6207        }
6208      })
6209    }),
6210  
6211    lineInfo: function(line) {
6212      var n
6213      if (typeof line == "number") {
6214        if (!isLine(this, line)) { return null }
6215        n = line
6216        line = getLine(this, line)
6217        if (!line) { return null }
6218      } else {
6219        n = lineNo(line)
6220        if (n == null) { return null }
6221      }
6222      return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
6223              textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
6224              widgets: line.widgets}
6225    },
6226  
6227    addLineClass: docMethodOp(function(handle, where, cls) {
6228      return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
6229        var prop = where == "text" ? "textClass"
6230                 : where == "background" ? "bgClass"
6231                 : where == "gutter" ? "gutterClass" : "wrapClass"
6232        if (!line[prop]) { line[prop] = cls }
6233        else if (classTest(cls).test(line[prop])) { return false }
6234        else { line[prop] += " " + cls }
6235        return true
6236      })
6237    }),
6238    removeLineClass: docMethodOp(function(handle, where, cls) {
6239      return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
6240        var prop = where == "text" ? "textClass"
6241                 : where == "background" ? "bgClass"
6242                 : where == "gutter" ? "gutterClass" : "wrapClass"
6243        var cur = line[prop]
6244        if (!cur) { return false }
6245        else if (cls == null) { line[prop] = null }
6246        else {
6247          var found = cur.match(classTest(cls))
6248          if (!found) { return false }
6249          var end = found.index + found[0].length
6250          line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null
6251        }
6252        return true
6253      })
6254    }),
6255  
6256    addLineWidget: docMethodOp(function(handle, node, options) {
6257      return addLineWidget(this, handle, node, options)
6258    }),
6259    removeLineWidget: function(widget) { widget.clear() },
6260  
6261    markText: function(from, to, options) {
6262      return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range")
6263    },
6264    setBookmark: function(pos, options) {
6265      var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
6266                      insertLeft: options && options.insertLeft,
6267                      clearWhenEmpty: false, shared: options && options.shared,
6268                      handleMouseEvents: options && options.handleMouseEvents}
6269      pos = clipPos(this, pos)
6270      return markText(this, pos, pos, realOpts, "bookmark")
6271    },
6272    findMarksAt: function(pos) {
6273      pos = clipPos(this, pos)
6274      var markers = [], spans = getLine(this, pos.line).markedSpans
6275      if (spans) { for (var i = 0; i < spans.length; ++i) {
6276        var span = spans[i]
6277        if ((span.from == null || span.from <= pos.ch) &&
6278            (span.to == null || span.to >= pos.ch))
6279          { markers.push(span.marker.parent || span.marker) }
6280      } }
6281      return markers
6282    },
6283    findMarks: function(from, to, filter) {
6284      from = clipPos(this, from); to = clipPos(this, to)
6285      var found = [], lineNo = from.line
6286      this.iter(from.line, to.line + 1, function (line) {
6287        var spans = line.markedSpans
6288        if (spans) { for (var i = 0; i < spans.length; i++) {
6289          var span = spans[i]
6290          if (!(span.to != null && lineNo == from.line && from.ch >= span.to ||
6291                span.from == null && lineNo != from.line ||
6292                span.from != null && lineNo == to.line && span.from >= to.ch) &&
6293              (!filter || filter(span.marker)))
6294            { found.push(span.marker.parent || span.marker) }
6295        } }
6296        ++lineNo
6297      })
6298      return found
6299    },
6300    getAllMarks: function() {
6301      var markers = []
6302      this.iter(function (line) {
6303        var sps = line.markedSpans
6304        if (sps) { for (var i = 0; i < sps.length; ++i)
6305          { if (sps[i].from != null) { markers.push(sps[i].marker) } } }
6306      })
6307      return markers
6308    },
6309  
6310    posFromIndex: function(off) {
6311      var ch, lineNo = this.first, sepSize = this.lineSeparator().length
6312      this.iter(function (line) {
6313        var sz = line.text.length + sepSize
6314        if (sz > off) { ch = off; return true }
6315        off -= sz
6316        ++lineNo
6317      })
6318      return clipPos(this, Pos(lineNo, ch))
6319    },
6320    indexFromPos: function (coords) {
6321      coords = clipPos(this, coords)
6322      var index = coords.ch
6323      if (coords.line < this.first || coords.ch < 0) { return 0 }
6324      var sepSize = this.lineSeparator().length
6325      this.iter(this.first, coords.line, function (line) { // iter aborts when callback returns a truthy value
6326        index += line.text.length + sepSize
6327      })
6328      return index
6329    },
6330  
6331    copy: function(copyHistory) {
6332      var doc = new Doc(getLines(this, this.first, this.first + this.size),
6333                        this.modeOption, this.first, this.lineSep, this.direction)
6334      doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft
6335      doc.sel = this.sel
6336      doc.extend = false
6337      if (copyHistory) {
6338        doc.history.undoDepth = this.history.undoDepth
6339        doc.setHistory(this.getHistory())
6340      }
6341      return doc
6342    },
6343  
6344    linkedDoc: function(options) {
6345      if (!options) { options = {} }
6346      var from = this.first, to = this.first + this.size
6347      if (options.from != null && options.from > from) { from = options.from }
6348      if (options.to != null && options.to < to) { to = options.to }
6349      var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep, this.direction)
6350      if (options.sharedHist) { copy.history = this.history
6351      ; }(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist})
6352      copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}]
6353      copySharedMarkers(copy, findSharedMarkers(this))
6354      return copy
6355    },
6356    unlinkDoc: function(other) {
6357      var this$1 = this;
6358  
6359      if (other instanceof CodeMirror) { other = other.doc }
6360      if (this.linked) { for (var i = 0; i < this.linked.length; ++i) {
6361        var link = this$1.linked[i]
6362        if (link.doc != other) { continue }
6363        this$1.linked.splice(i, 1)
6364        other.unlinkDoc(this$1)
6365        detachSharedMarkers(findSharedMarkers(this$1))
6366        break
6367      } }
6368      // If the histories were shared, split them again
6369      if (other.history == this.history) {
6370        var splitIds = [other.id]
6371        linkedDocs(other, function (doc) { return splitIds.push(doc.id); }, true)
6372        other.history = new History(null)
6373        other.history.done = copyHistoryArray(this.history.done, splitIds)
6374        other.history.undone = copyHistoryArray(this.history.undone, splitIds)
6375      }
6376    },
6377    iterLinkedDocs: function(f) {linkedDocs(this, f)},
6378  
6379    getMode: function() {return this.mode},
6380    getEditor: function() {return this.cm},
6381  
6382    splitLines: function(str) {
6383      if (this.lineSep) { return str.split(this.lineSep) }
6384      return splitLinesAuto(str)
6385    },
6386    lineSeparator: function() { return this.lineSep || "\n" },
6387  
6388    setDirection: docMethodOp(function (dir) {
6389      if (dir != "rtl") { dir = "ltr" }
6390      if (dir == this.direction) { return }
6391      this.direction = dir
6392      this.iter(function (line) { return line.order = null; })
6393      if (this.cm) { directionChanged(this.cm) }
6394    })
6395  })
6396  
6397  // Public alias.
6398  Doc.prototype.eachLine = Doc.prototype.iter
6399  
6400  // Kludge to work around strange IE behavior where it'll sometimes
6401  // re-fire a series of drag-related events right after the drop (#1551)
6402  var lastDrop = 0
6403  
6404  function onDrop(e) {
6405    var cm = this
6406    clearDragCursor(cm)
6407    if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))
6408      { return }
6409    e_preventDefault(e)
6410    if (ie) { lastDrop = +new Date }
6411    var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files
6412    if (!pos || cm.isReadOnly()) { return }
6413    // Might be a file drop, in which case we simply extract the text
6414    // and insert it.
6415    if (files && files.length && window.FileReader && window.File) {
6416      var n = files.length, text = Array(n), read = 0
6417      var loadFile = function (file, i) {
6418        if (cm.options.allowDropFileTypes &&
6419            indexOf(cm.options.allowDropFileTypes, file.type) == -1)
6420          { return }
6421  
6422        var reader = new FileReader
6423        reader.onload = operation(cm, function () {
6424          var content = reader.result
6425          if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) { content = "" }
6426          text[i] = content
6427          if (++read == n) {
6428            pos = clipPos(cm.doc, pos)
6429            var change = {from: pos, to: pos,
6430                          text: cm.doc.splitLines(text.join(cm.doc.lineSeparator())),
6431                          origin: "paste"}
6432            makeChange(cm.doc, change)
6433            setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change)))
6434          }
6435        })
6436        reader.readAsText(file)
6437      }
6438      for (var i = 0; i < n; ++i) { loadFile(files[i], i) }
6439    } else { // Normal drop
6440      // Don't do a replace if the drop happened inside of the selected text.
6441      if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {
6442        cm.state.draggingText(e)
6443        // Ensure the editor is re-focused
6444        setTimeout(function () { return cm.display.input.focus(); }, 20)
6445        return
6446      }
6447      try {
6448        var text$1 = e.dataTransfer.getData("Text")
6449        if (text$1) {
6450          var selected
6451          if (cm.state.draggingText && !cm.state.draggingText.copy)
6452            { selected = cm.listSelections() }
6453          setSelectionNoUndo(cm.doc, simpleSelection(pos, pos))
6454          if (selected) { for (var i$1 = 0; i$1 < selected.length; ++i$1)
6455            { replaceRange(cm.doc, "", selected[i$1].anchor, selected[i$1].head, "drag") } }
6456          cm.replaceSelection(text$1, "around", "paste")
6457          cm.display.input.focus()
6458        }
6459      }
6460      catch(e){}
6461    }
6462  }
6463  
6464  function onDragStart(cm, e) {
6465    if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return }
6466    if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return }
6467  
6468    e.dataTransfer.setData("Text", cm.getSelection())
6469    e.dataTransfer.effectAllowed = "copyMove"
6470  
6471    // Use dummy image instead of default browsers image.
6472    // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
6473    if (e.dataTransfer.setDragImage && !safari) {
6474      var img = elt("img", null, null, "position: fixed; left: 0; top: 0;")
6475      img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
6476      if (presto) {
6477        img.width = img.height = 1
6478        cm.display.wrapper.appendChild(img)
6479        // Force a relayout, or Opera won't use our image for some obscure reason
6480        img._top = img.offsetTop
6481      }
6482      e.dataTransfer.setDragImage(img, 0, 0)
6483      if (presto) { img.parentNode.removeChild(img) }
6484    }
6485  }
6486  
6487  function onDragOver(cm, e) {
6488    var pos = posFromMouse(cm, e)
6489    if (!pos) { return }
6490    var frag = document.createDocumentFragment()
6491    drawSelectionCursor(cm, pos, frag)
6492    if (!cm.display.dragCursor) {
6493      cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors")
6494      cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv)
6495    }
6496    removeChildrenAndAdd(cm.display.dragCursor, frag)
6497  }
6498  
6499  function clearDragCursor(cm) {
6500    if (cm.display.dragCursor) {
6501      cm.display.lineSpace.removeChild(cm.display.dragCursor)
6502      cm.display.dragCursor = null
6503    }
6504  }
6505  
6506  // These must be handled carefully, because naively registering a
6507  // handler for each editor will cause the editors to never be
6508  // garbage collected.
6509  
6510  function forEachCodeMirror(f) {
6511    if (!document.getElementsByClassName) { return }
6512    var byClass = document.getElementsByClassName("CodeMirror")
6513    for (var i = 0; i < byClass.length; i++) {
6514      var cm = byClass[i].CodeMirror
6515      if (cm) { f(cm) }
6516    }
6517  }
6518  
6519  var globalsRegistered = false
6520  function ensureGlobalHandlers() {
6521    if (globalsRegistered) { return }
6522    registerGlobalHandlers()
6523    globalsRegistered = true
6524  }
6525  function registerGlobalHandlers() {
6526    // When the window resizes, we need to refresh active editors.
6527    var resizeTimer
6528    on(window, "resize", function () {
6529      if (resizeTimer == null) { resizeTimer = setTimeout(function () {
6530        resizeTimer = null
6531        forEachCodeMirror(onResize)
6532      }, 100) }
6533    })
6534    // When the window loses focus, we want to show the editor as blurred
6535    on(window, "blur", function () { return forEachCodeMirror(onBlur); })
6536  }
6537  // Called when the window resizes
6538  function onResize(cm) {
6539    var d = cm.display
6540    if (d.lastWrapHeight == d.wrapper.clientHeight && d.lastWrapWidth == d.wrapper.clientWidth)
6541      { return }
6542    // Might be a text scaling operation, clear size caches.
6543    d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null
6544    d.scrollbarsClipped = false
6545    cm.setSize()
6546  }
6547  
6548  var keyNames = {
6549    3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
6550    19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
6551    36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
6552    46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod",
6553    106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 127: "Delete",
6554    173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
6555    221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete",
6556    63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert"
6557  }
6558  
6559  // Number keys
6560  for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i) }
6561  // Alphabetic keys
6562  for (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1) }
6563  // Function keys
6564  for (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235] = "F" + i$2 }
6565  
6566  var keyMap = {}
6567  
6568  keyMap.basic = {
6569    "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
6570    "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
6571    "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore",
6572    "Tab": "defaultTab", "Shift-Tab": "indentAuto",
6573    "Enter": "newlineAndIndent", "Insert": "toggleOverwrite",
6574    "Esc": "singleSelection"
6575  }
6576  // Note that the save and find-related commands aren't defined by
6577  // default. User code or addons can define them. Unknown commands
6578  // are simply ignored.
6579  keyMap.pcDefault = {
6580    "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
6581    "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown",
6582    "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
6583    "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
6584    "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
6585    "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
6586    "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection",
6587    fallthrough: "basic"
6588  }
6589  // Very basic readline/emacs-style bindings, which are standard on Mac.
6590  keyMap.emacsy = {
6591    "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
6592    "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
6593    "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
6594    "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars",
6595    "Ctrl-O": "openLine"
6596  }
6597  keyMap.macDefault = {
6598    "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
6599    "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
6600    "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore",
6601    "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
6602    "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
6603    "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight",
6604    "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd",
6605    fallthrough: ["basic", "emacsy"]
6606  }
6607  keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault
6608  
6609  // KEYMAP DISPATCH
6610  
6611  function normalizeKeyName(name) {
6612    var parts = name.split(/-(?!$)/)
6613    name = parts[parts.length - 1]
6614    var alt, ctrl, shift, cmd
6615    for (var i = 0; i < parts.length - 1; i++) {
6616      var mod = parts[i]
6617      if (/^(cmd|meta|m)$/i.test(mod)) { cmd = true }
6618      else if (/^a(lt)?$/i.test(mod)) { alt = true }
6619      else if (/^(c|ctrl|control)$/i.test(mod)) { ctrl = true }
6620      else if (/^s(hift)?$/i.test(mod)) { shift = true }
6621      else { throw new Error("Unrecognized modifier name: " + mod) }
6622    }
6623    if (alt) { name = "Alt-" + name }
6624    if (ctrl) { name = "Ctrl-" + name }
6625    if (cmd) { name = "Cmd-" + name }
6626    if (shift) { name = "Shift-" + name }
6627    return name
6628  }
6629  
6630  // This is a kludge to keep keymaps mostly working as raw objects
6631  // (backwards compatibility) while at the same time support features
6632  // like normalization and multi-stroke key bindings. It compiles a
6633  // new normalized keymap, and then updates the old object to reflect
6634  // this.
6635  function normalizeKeyMap(keymap) {
6636    var copy = {}
6637    for (var keyname in keymap) { if (keymap.hasOwnProperty(keyname)) {
6638      var value = keymap[keyname]
6639      if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) { continue }
6640      if (value == "...") { delete keymap[keyname]; continue }
6641  
6642      var keys = map(keyname.split(" "), normalizeKeyName)
6643      for (var i = 0; i < keys.length; i++) {
6644        var val = (void 0), name = (void 0)
6645        if (i == keys.length - 1) {
6646          name = keys.join(" ")
6647          val = value
6648        } else {
6649          name = keys.slice(0, i + 1).join(" ")
6650          val = "..."
6651        }
6652        var prev = copy[name]
6653        if (!prev) { copy[name] = val }
6654        else if (prev != val) { throw new Error("Inconsistent bindings for " + name) }
6655      }
6656      delete keymap[keyname]
6657    } }
6658    for (var prop in copy) { keymap[prop] = copy[prop] }
6659    return keymap
6660  }
6661  
6662  function lookupKey(key, map, handle, context) {
6663    map = getKeyMap(map)
6664    var found = map.call ? map.call(key, context) : map[key]
6665    if (found === false) { return "nothing" }
6666    if (found === "...") { return "multi" }
6667    if (found != null && handle(found)) { return "handled" }
6668  
6669    if (map.fallthrough) {
6670      if (Object.prototype.toString.call(map.fallthrough) != "[object Array]")
6671        { return lookupKey(key, map.fallthrough, handle, context) }
6672      for (var i = 0; i < map.fallthrough.length; i++) {
6673        var result = lookupKey(key, map.fallthrough[i], handle, context)
6674        if (result) { return result }
6675      }
6676    }
6677  }
6678  
6679  // Modifier key presses don't count as 'real' key presses for the
6680  // purpose of keymap fallthrough.
6681  function isModifierKey(value) {
6682    var name = typeof value == "string" ? value : keyNames[value.keyCode]
6683    return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod"
6684  }
6685  
6686  function addModifierNames(name, event, noShift) {
6687    var base = name
6688    if (event.altKey && base != "Alt") { name = "Alt-" + name }
6689    if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") { name = "Ctrl-" + name }
6690    if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") { name = "Cmd-" + name }
6691    if (!noShift && event.shiftKey && base != "Shift") { name = "Shift-" + name }
6692    return name
6693  }
6694  
6695  // Look up the name of a key as indicated by an event object.
6696  function keyName(event, noShift) {
6697    if (presto && event.keyCode == 34 && event["char"]) { return false }
6698    var name = keyNames[event.keyCode]
6699    if (name == null || event.altGraphKey) { return false }
6700    return addModifierNames(name, event, noShift)
6701  }
6702  
6703  function getKeyMap(val) {
6704    return typeof val == "string" ? keyMap[val] : val
6705  }
6706  
6707  // Helper for deleting text near the selection(s), used to implement
6708  // backspace, delete, and similar functionality.
6709  function deleteNearSelection(cm, compute) {
6710    var ranges = cm.doc.sel.ranges, kill = []
6711    // Build up a set of ranges to kill first, merging overlapping
6712    // ranges.
6713    for (var i = 0; i < ranges.length; i++) {
6714      var toKill = compute(ranges[i])
6715      while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {
6716        var replaced = kill.pop()
6717        if (cmp(replaced.from, toKill.from) < 0) {
6718          toKill.from = replaced.from
6719          break
6720        }
6721      }
6722      kill.push(toKill)
6723    }
6724    // Next, remove those actual ranges.
6725    runInOp(cm, function () {
6726      for (var i = kill.length - 1; i >= 0; i--)
6727        { replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete") }
6728      ensureCursorVisible(cm)
6729    })
6730  }
6731  
6732  function moveCharLogically(line, ch, dir) {
6733    var target = skipExtendingChars(line.text, ch + dir, dir)
6734    return target < 0 || target > line.text.length ? null : target
6735  }
6736  
6737  function moveLogically(line, start, dir) {
6738    var ch = moveCharLogically(line, start.ch, dir)
6739    return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before")
6740  }
6741  
6742  function endOfLine(visually, cm, lineObj, lineNo, dir) {
6743    if (visually) {
6744      var order = getOrder(lineObj, cm.doc.direction)
6745      if (order) {
6746        var part = dir < 0 ? lst(order) : order[0]
6747        var moveInStorageOrder = (dir < 0) == (part.level == 1)
6748        var sticky = moveInStorageOrder ? "after" : "before"
6749        var ch
6750        // With a wrapped rtl chunk (possibly spanning multiple bidi parts),
6751        // it could be that the last bidi part is not on the last visual line,
6752        // since visual lines contain content order-consecutive chunks.
6753        // Thus, in rtl, we are looking for the first (content-order) character
6754        // in the rtl chunk that is on the last line (that is, the same line
6755        // as the last (content-order) character).
6756        if (part.level > 0) {
6757          var prep = prepareMeasureForLine(cm, lineObj)
6758          ch = dir < 0 ? lineObj.text.length - 1 : 0
6759          var targetTop = measureCharPrepared(cm, prep, ch).top
6760          ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch)
6761          if (sticky == "before") { ch = moveCharLogically(lineObj, ch, 1) }
6762        } else { ch = dir < 0 ? part.to : part.from }
6763        return new Pos(lineNo, ch, sticky)
6764      }
6765    }
6766    return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after")
6767  }
6768  
6769  function moveVisually(cm, line, start, dir) {
6770    var bidi = getOrder(line, cm.doc.direction)
6771    if (!bidi) { return moveLogically(line, start, dir) }
6772    if (start.ch >= line.text.length) {
6773      start.ch = line.text.length
6774      start.sticky = "before"
6775    } else if (start.ch <= 0) {
6776      start.ch = 0
6777      start.sticky = "after"
6778    }
6779    var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos]
6780    if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) {
6781      // Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines,
6782      // nothing interesting happens.
6783      return moveLogically(line, start, dir)
6784    }
6785  
6786    var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); }
6787    var prep
6788    var getWrappedLineExtent = function (ch) {
6789      if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} }
6790      prep = prep || prepareMeasureForLine(cm, line)
6791      return wrappedLineExtentChar(cm, line, prep, ch)
6792    }
6793    var wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch)
6794  
6795    if (cm.doc.direction == "rtl" || part.level == 1) {
6796      var moveInStorageOrder = (part.level == 1) == (dir < 0)
6797      var ch = mv(start, moveInStorageOrder ? 1 : -1)
6798      if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) {
6799        // Case 2: We move within an rtl part or in an rtl editor on the same visual line
6800        var sticky = moveInStorageOrder ? "before" : "after"
6801        return new Pos(start.line, ch, sticky)
6802      }
6803    }
6804  
6805    // Case 3: Could not move within this bidi part in this visual line, so leave
6806    // the current bidi part
6807  
6808    var searchInVisualLine = function (partPos, dir, wrappedLineExtent) {
6809      var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder
6810        ? new Pos(start.line, mv(ch, 1), "before")
6811        : new Pos(start.line, ch, "after"); }
6812  
6813      for (; partPos >= 0 && partPos < bidi.length; partPos += dir) {
6814        var part = bidi[partPos]
6815        var moveInStorageOrder = (dir > 0) == (part.level != 1)
6816        var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1)
6817        if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) }
6818        ch = moveInStorageOrder ? part.from : mv(part.to, -1)
6819        if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) }
6820      }
6821    }
6822  
6823    // Case 3a: Look for other bidi parts on the same visual line
6824    var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent)
6825    if (res) { return res }
6826  
6827    // Case 3b: Look for other bidi parts on the next visual line
6828    var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1)
6829    if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) {
6830      res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh))
6831      if (res) { return res }
6832    }
6833  
6834    // Case 4: Nowhere to move
6835    return null
6836  }
6837  
6838  // Commands are parameter-less actions that can be performed on an
6839  // editor, mostly used for keybindings.
6840  var commands = {
6841    selectAll: selectAll,
6842    singleSelection: function (cm) { return cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll); },
6843    killLine: function (cm) { return deleteNearSelection(cm, function (range) {
6844      if (range.empty()) {
6845        var len = getLine(cm.doc, range.head.line).text.length
6846        if (range.head.ch == len && range.head.line < cm.lastLine())
6847          { return {from: range.head, to: Pos(range.head.line + 1, 0)} }
6848        else
6849          { return {from: range.head, to: Pos(range.head.line, len)} }
6850      } else {
6851        return {from: range.from(), to: range.to()}
6852      }
6853    }); },
6854    deleteLine: function (cm) { return deleteNearSelection(cm, function (range) { return ({
6855      from: Pos(range.from().line, 0),
6856      to: clipPos(cm.doc, Pos(range.to().line + 1, 0))
6857    }); }); },
6858    delLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { return ({
6859      from: Pos(range.from().line, 0), to: range.from()
6860    }); }); },
6861    delWrappedLineLeft: function (cm) { return deleteNearSelection(cm, function (range) {
6862      var top = cm.charCoords(range.head, "div").top + 5
6863      var leftPos = cm.coordsChar({left: 0, top: top}, "div")
6864      return {from: leftPos, to: range.from()}
6865    }); },
6866    delWrappedLineRight: function (cm) { return deleteNearSelection(cm, function (range) {
6867      var top = cm.charCoords(range.head, "div").top + 5
6868      var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div")
6869      return {from: range.from(), to: rightPos }
6870    }); },
6871    undo: function (cm) { return cm.undo(); },
6872    redo: function (cm) { return cm.redo(); },
6873    undoSelection: function (cm) { return cm.undoSelection(); },
6874    redoSelection: function (cm) { return cm.redoSelection(); },
6875    goDocStart: function (cm) { return cm.extendSelection(Pos(cm.firstLine(), 0)); },
6876    goDocEnd: function (cm) { return cm.extendSelection(Pos(cm.lastLine())); },
6877    goLineStart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStart(cm, range.head.line); },
6878      {origin: "+move", bias: 1}
6879    ); },
6880    goLineStartSmart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStartSmart(cm, range.head); },
6881      {origin: "+move", bias: 1}
6882    ); },
6883    goLineEnd: function (cm) { return cm.extendSelectionsBy(function (range) { return lineEnd(cm, range.head.line); },
6884      {origin: "+move", bias: -1}
6885    ); },
6886    goLineRight: function (cm) { return cm.extendSelectionsBy(function (range) {
6887      var top = cm.cursorCoords(range.head, "div").top + 5
6888      return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div")
6889    }, sel_move); },
6890    goLineLeft: function (cm) { return cm.extendSelectionsBy(function (range) {
6891      var top = cm.cursorCoords(range.head, "div").top + 5
6892      return cm.coordsChar({left: 0, top: top}, "div")
6893    }, sel_move); },
6894    goLineLeftSmart: function (cm) { return cm.extendSelectionsBy(function (range) {
6895      var top = cm.cursorCoords(range.head, "div").top + 5
6896      var pos = cm.coordsChar({left: 0, top: top}, "div")
6897      if (pos.ch < cm.getLine(pos.line).search(/\S/)) { return lineStartSmart(cm, range.head) }
6898      return pos
6899    }, sel_move); },
6900    goLineUp: function (cm) { return cm.moveV(-1, "line"); },
6901    goLineDown: function (cm) { return cm.moveV(1, "line"); },
6902    goPageUp: function (cm) { return cm.moveV(-1, "page"); },
6903    goPageDown: function (cm) { return cm.moveV(1, "page"); },
6904    goCharLeft: function (cm) { return cm.moveH(-1, "char"); },
6905    goCharRight: function (cm) { return cm.moveH(1, "char"); },
6906    goColumnLeft: function (cm) { return cm.moveH(-1, "column"); },
6907    goColumnRight: function (cm) { return cm.moveH(1, "column"); },
6908    goWordLeft: function (cm) { return cm.moveH(-1, "word"); },
6909    goGroupRight: function (cm) { return cm.moveH(1, "group"); },
6910    goGroupLeft: function (cm) { return cm.moveH(-1, "group"); },
6911    goWordRight: function (cm) { return cm.moveH(1, "word"); },
6912    delCharBefore: function (cm) { return cm.deleteH(-1, "char"); },
6913    delCharAfter: function (cm) { return cm.deleteH(1, "char"); },
6914    delWordBefore: function (cm) { return cm.deleteH(-1, "word"); },
6915    delWordAfter: function (cm) { return cm.deleteH(1, "word"); },
6916    delGroupBefore: function (cm) { return cm.deleteH(-1, "group"); },
6917    delGroupAfter: function (cm) { return cm.deleteH(1, "group"); },
6918    indentAuto: function (cm) { return cm.indentSelection("smart"); },
6919    indentMore: function (cm) { return cm.indentSelection("add"); },
6920    indentLess: function (cm) { return cm.indentSelection("subtract"); },
6921    insertTab: function (cm) { return cm.replaceSelection("\t"); },
6922    insertSoftTab: function (cm) {
6923      var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize
6924      for (var i = 0; i < ranges.length; i++) {
6925        var pos = ranges[i].from()
6926        var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize)
6927        spaces.push(spaceStr(tabSize - col % tabSize))
6928      }
6929      cm.replaceSelections(spaces)
6930    },
6931    defaultTab: function (cm) {
6932      if (cm.somethingSelected()) { cm.indentSelection("add") }
6933      else { cm.execCommand("insertTab") }
6934    },
6935    // Swap the two chars left and right of each selection's head.
6936    // Move cursor behind the two swapped characters afterwards.
6937    //
6938    // Doesn't consider line feeds a character.
6939    // Doesn't scan more than one line above to find a character.
6940    // Doesn't do anything on an empty line.
6941    // Doesn't do anything with non-empty selections.
6942    transposeChars: function (cm) { return runInOp(cm, function () {
6943      var ranges = cm.listSelections(), newSel = []
6944      for (var i = 0; i < ranges.length; i++) {
6945        if (!ranges[i].empty()) { continue }
6946        var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text
6947        if (line) {
6948          if (cur.ch == line.length) { cur = new Pos(cur.line, cur.ch - 1) }
6949          if (cur.ch > 0) {
6950            cur = new Pos(cur.line, cur.ch + 1)
6951            cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),
6952                            Pos(cur.line, cur.ch - 2), cur, "+transpose")
6953          } else if (cur.line > cm.doc.first) {
6954            var prev = getLine(cm.doc, cur.line - 1).text
6955            if (prev) {
6956              cur = new Pos(cur.line, 1)
6957              cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() +
6958                              prev.charAt(prev.length - 1),
6959                              Pos(cur.line - 1, prev.length - 1), cur, "+transpose")
6960            }
6961          }
6962        }
6963        newSel.push(new Range(cur, cur))
6964      }
6965      cm.setSelections(newSel)
6966    }); },
6967    newlineAndIndent: function (cm) { return runInOp(cm, function () {
6968      var sels = cm.listSelections()
6969      for (var i = sels.length - 1; i >= 0; i--)
6970        { cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input") }
6971      sels = cm.listSelections()
6972      for (var i$1 = 0; i$1 < sels.length; i$1++)
6973        { cm.indentLine(sels[i$1].from().line, null, true) }
6974      ensureCursorVisible(cm)
6975    }); },
6976    openLine: function (cm) { return cm.replaceSelection("\n", "start"); },
6977    toggleOverwrite: function (cm) { return cm.toggleOverwrite(); }
6978  }
6979  
6980  
6981  function lineStart(cm, lineN) {
6982    var line = getLine(cm.doc, lineN)
6983    var visual = visualLine(line)
6984    if (visual != line) { lineN = lineNo(visual) }
6985    return endOfLine(true, cm, visual, lineN, 1)
6986  }
6987  function lineEnd(cm, lineN) {
6988    var line = getLine(cm.doc, lineN)
6989    var visual = visualLineEnd(line)
6990    if (visual != line) { lineN = lineNo(visual) }
6991    return endOfLine(true, cm, line, lineN, -1)
6992  }
6993  function lineStartSmart(cm, pos) {
6994    var start = lineStart(cm, pos.line)
6995    var line = getLine(cm.doc, start.line)
6996    var order = getOrder(line, cm.doc.direction)
6997    if (!order || order[0].level == 0) {
6998      var firstNonWS = Math.max(0, line.text.search(/\S/))
6999      var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch
7000      return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky)
7001    }
7002    return start
7003  }
7004  
7005  // Run a handler that was bound to a key.
7006  function doHandleBinding(cm, bound, dropShift) {
7007    if (typeof bound == "string") {
7008      bound = commands[bound]
7009      if (!bound) { return false }
7010    }
7011    // Ensure previous input has been read, so that the handler sees a
7012    // consistent view of the document
7013    cm.display.input.ensurePolled()
7014    var prevShift = cm.display.shift, done = false
7015    try {
7016      if (cm.isReadOnly()) { cm.state.suppressEdits = true }
7017      if (dropShift) { cm.display.shift = false }
7018      done = bound(cm) != Pass
7019    } finally {
7020      cm.display.shift = prevShift
7021      cm.state.suppressEdits = false
7022    }
7023    return done
7024  }
7025  
7026  function lookupKeyForEditor(cm, name, handle) {
7027    for (var i = 0; i < cm.state.keyMaps.length; i++) {
7028      var result = lookupKey(name, cm.state.keyMaps[i], handle, cm)
7029      if (result) { return result }
7030    }
7031    return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm))
7032      || lookupKey(name, cm.options.keyMap, handle, cm)
7033  }
7034  
7035  // Note that, despite the name, this function is also used to check
7036  // for bound mouse clicks.
7037  
7038  var stopSeq = new Delayed
7039  function dispatchKey(cm, name, e, handle) {
7040    var seq = cm.state.keySeq
7041    if (seq) {
7042      if (isModifierKey(name)) { return "handled" }
7043      stopSeq.set(50, function () {
7044        if (cm.state.keySeq == seq) {
7045          cm.state.keySeq = null
7046          cm.display.input.reset()
7047        }
7048      })
7049      name = seq + " " + name
7050    }
7051    var result = lookupKeyForEditor(cm, name, handle)
7052  
7053    if (result == "multi")
7054      { cm.state.keySeq = name }
7055    if (result == "handled")
7056      { signalLater(cm, "keyHandled", cm, name, e) }
7057  
7058    if (result == "handled" || result == "multi") {
7059      e_preventDefault(e)
7060      restartBlink(cm)
7061    }
7062  
7063    if (seq && !result && /\'$/.test(name)) {
7064      e_preventDefault(e)
7065      return true
7066    }
7067    return !!result
7068  }
7069  
7070  // Handle a key from the keydown event.
7071  function handleKeyBinding(cm, e) {
7072    var name = keyName(e, true)
7073    if (!name) { return false }
7074  
7075    if (e.shiftKey && !cm.state.keySeq) {
7076      // First try to resolve full name (including 'Shift-'). Failing
7077      // that, see if there is a cursor-motion command (starting with
7078      // 'go') bound to the keyname without 'Shift-'.
7079      return dispatchKey(cm, "Shift-" + name, e, function (b) { return doHandleBinding(cm, b, true); })
7080          || dispatchKey(cm, name, e, function (b) {
7081               if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
7082                 { return doHandleBinding(cm, b) }
7083             })
7084    } else {
7085      return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b); })
7086    }
7087  }
7088  
7089  // Handle a key from the keypress event
7090  function handleCharBinding(cm, e, ch) {
7091    return dispatchKey(cm, "'" + ch + "'", e, function (b) { return doHandleBinding(cm, b, true); })
7092  }
7093  
7094  var lastStoppedKey = null
7095  function onKeyDown(e) {
7096    var cm = this
7097    cm.curOp.focus = activeElt()
7098    if (signalDOMEvent(cm, e)) { return }
7099    // IE does strange things with escape.
7100    if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false }
7101    var code = e.keyCode
7102    cm.display.shift = code == 16 || e.shiftKey
7103    var handled = handleKeyBinding(cm, e)
7104    if (presto) {
7105      lastStoppedKey = handled ? code : null
7106      // Opera has no cut event... we try to at least catch the key combo
7107      if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
7108        { cm.replaceSelection("", null, "cut") }
7109    }
7110  
7111    // Turn mouse into crosshair when Alt is held on Mac.
7112    if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className))
7113      { showCrossHair(cm) }
7114  }
7115  
7116  function showCrossHair(cm) {
7117    var lineDiv = cm.display.lineDiv
7118    addClass(lineDiv, "CodeMirror-crosshair")
7119  
7120    function up(e) {
7121      if (e.keyCode == 18 || !e.altKey) {
7122        rmClass(lineDiv, "CodeMirror-crosshair")
7123        off(document, "keyup", up)
7124        off(document, "mouseover", up)
7125      }
7126    }
7127    on(document, "keyup", up)
7128    on(document, "mouseover", up)
7129  }
7130  
7131  function onKeyUp(e) {
7132    if (e.keyCode == 16) { this.doc.sel.shift = false }
7133    signalDOMEvent(this, e)
7134  }
7135  
7136  function onKeyPress(e) {
7137    var cm = this
7138    if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return }
7139    var keyCode = e.keyCode, charCode = e.charCode
7140    if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return}
7141    if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) { return }
7142    var ch = String.fromCharCode(charCode == null ? keyCode : charCode)
7143    // Some browsers fire keypress events for backspace
7144    if (ch == "\x08") { return }
7145    if (handleCharBinding(cm, e, ch)) { return }
7146    cm.display.input.onKeyPress(e)
7147  }
7148  
7149  var DOUBLECLICK_DELAY = 400
7150  
7151  var PastClick = function(time, pos, button) {
7152    this.time = time
7153    this.pos = pos
7154    this.button = button
7155  };
7156  
7157  PastClick.prototype.compare = function (time, pos, button) {
7158    return this.time + DOUBLECLICK_DELAY > time &&
7159      cmp(pos, this.pos) == 0 && button == this.button
7160  };
7161  
7162  var lastClick;
7163  var lastDoubleClick;
7164  function clickRepeat(pos, button) {
7165    var now = +new Date
7166    if (lastDoubleClick && lastDoubleClick.compare(now, pos, button)) {
7167      lastClick = lastDoubleClick = null
7168      return "triple"
7169    } else if (lastClick && lastClick.compare(now, pos, button)) {
7170      lastDoubleClick = new PastClick(now, pos, button)
7171      lastClick = null
7172      return "double"
7173    } else {
7174      lastClick = new PastClick(now, pos, button)
7175      lastDoubleClick = null
7176      return "single"
7177    }
7178  }
7179  
7180  // A mouse down can be a single click, double click, triple click,
7181  // start of selection drag, start of text drag, new cursor
7182  // (ctrl-click), rectangle drag (alt-drag), or xwin
7183  // middle-click-paste. Or it might be a click on something we should
7184  // not interfere with, such as a scrollbar or widget.
7185  function onMouseDown(e) {
7186    var cm = this, display = cm.display
7187    if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) { return }
7188    display.input.ensurePolled()
7189    display.shift = e.shiftKey
7190  
7191    if (eventInWidget(display, e)) {
7192      if (!webkit) {
7193        // Briefly turn off draggability, to allow widgets to do
7194        // normal dragging things.
7195        display.scroller.draggable = false
7196        setTimeout(function () { return display.scroller.draggable = true; }, 100)
7197      }
7198      return
7199    }
7200    if (clickInGutter(cm, e)) { return }
7201    var pos = posFromMouse(cm, e), button = e_button(e), repeat = pos ? clickRepeat(pos, button) : "single"
7202    window.focus()
7203  
7204    // #3261: make sure, that we're not starting a second selection
7205    if (button == 1 && cm.state.selectingText)
7206      { cm.state.selectingText(e) }
7207  
7208    if (pos && handleMappedButton(cm, button, pos, repeat, e)) { return }
7209  
7210    if (button == 1) {
7211      if (pos) { leftButtonDown(cm, pos, repeat, e) }
7212      else if (e_target(e) == display.scroller) { e_preventDefault(e) }
7213    } else if (button == 2) {
7214      if (pos) { extendSelection(cm.doc, pos) }
7215      setTimeout(function () { return display.input.focus(); }, 20)
7216    } else if (button == 3) {
7217      if (captureRightClick) { onContextMenu(cm, e) }
7218      else { delayBlurEvent(cm) }
7219    }
7220  }
7221  
7222  function handleMappedButton(cm, button, pos, repeat, event) {
7223    var name = "Click"
7224    if (repeat == "double") { name = "Double" + name }
7225    else if (repeat == "triple") { name = "Triple" + name }
7226    name = (button == 1 ? "Left" : button == 2 ? "Middle" : "Right") + name
7227  
7228    return dispatchKey(cm,  addModifierNames(name, event), event, function (bound) {
7229      if (typeof bound == "string") { bound = commands[bound] }
7230      if (!bound) { return false }
7231      var done = false
7232      try {
7233        if (cm.isReadOnly()) { cm.state.suppressEdits = true }
7234        done = bound(cm, pos) != Pass
7235      } finally {
7236        cm.state.suppressEdits = false
7237      }
7238      return done
7239    })
7240  }
7241  
7242  function configureMouse(cm, repeat, event) {
7243    var option = cm.getOption("configureMouse")
7244    var value = option ? option(cm, repeat, event) : {}
7245    if (value.unit == null) {
7246      var rect = chromeOS ? event.shiftKey && event.metaKey : event.altKey
7247      value.unit = rect ? "rectangle" : repeat == "single" ? "char" : repeat == "double" ? "word" : "line"
7248    }
7249    if (value.extend == null || cm.doc.extend) { value.extend = cm.doc.extend || event.shiftKey }
7250    if (value.addNew == null) { value.addNew = mac ? event.metaKey : event.ctrlKey }
7251    if (value.moveOnDrag == null) { value.moveOnDrag = !(mac ? event.altKey : event.ctrlKey) }
7252    return value
7253  }
7254  
7255  function leftButtonDown(cm, pos, repeat, event) {
7256    if (ie) { setTimeout(bind(ensureFocus, cm), 0) }
7257    else { cm.curOp.focus = activeElt() }
7258  
7259    var behavior = configureMouse(cm, repeat, event)
7260  
7261    var sel = cm.doc.sel, contained
7262    if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() &&
7263        repeat == "single" && (contained = sel.contains(pos)) > -1 &&
7264        (cmp((contained = sel.ranges[contained]).from(), pos) < 0 || pos.xRel > 0) &&
7265        (cmp(contained.to(), pos) > 0 || pos.xRel < 0))
7266      { leftButtonStartDrag(cm, event, pos, behavior) }
7267    else
7268      {