[ Index ]

PHP Cross Reference of MyBB 1.8.37

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      { leftButtonSelect(cm, event, pos, behavior) }
7269  }
7270  
7271  // Start a text drag. When it ends, see if any dragging actually
7272  // happen, and treat as a click if it didn't.
7273  function leftButtonStartDrag(cm, event, pos, behavior) {
7274    var display = cm.display, moved = false
7275    var dragEnd = operation(cm, function (e) {
7276      if (webkit) { display.scroller.draggable = false }
7277      cm.state.draggingText = false
7278      off(document, "mouseup", dragEnd)
7279      off(document, "mousemove", mouseMove)
7280      off(display.scroller, "dragstart", dragStart)
7281      off(display.scroller, "drop", dragEnd)
7282      if (!moved) {
7283        e_preventDefault(e)
7284        if (!behavior.addNew)
7285          { extendSelection(cm.doc, pos, null, null, behavior.extend) }
7286        // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)
7287        if (webkit || ie && ie_version == 9)
7288          { setTimeout(function () {document.body.focus(); display.input.focus()}, 20) }
7289        else
7290          { display.input.focus() }
7291      }
7292    })
7293    var mouseMove = function(e2) {
7294      moved = moved || Math.abs(event.clientX - e2.clientX) + Math.abs(event.clientY - e2.clientY) >= 10
7295    }
7296    var dragStart = function () { return moved = true; }
7297    // Let the drag handler handle this.
7298    if (webkit) { display.scroller.draggable = true }
7299    cm.state.draggingText = dragEnd
7300    dragEnd.copy = !behavior.moveOnDrag
7301    // IE's approach to draggable
7302    if (display.scroller.dragDrop) { display.scroller.dragDrop() }
7303    on(document, "mouseup", dragEnd)
7304    on(document, "mousemove", mouseMove)
7305    on(display.scroller, "dragstart", dragStart)
7306    on(display.scroller, "drop", dragEnd)
7307  
7308    delayBlurEvent(cm)
7309    setTimeout(function () { return display.input.focus(); }, 20)
7310  }
7311  
7312  function rangeForUnit(cm, pos, unit) {
7313    if (unit == "char") { return new Range(pos, pos) }
7314    if (unit == "word") { return cm.findWordAt(pos) }
7315    if (unit == "line") { return new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) }
7316    var result = unit(cm, pos)
7317    return new Range(result.from, result.to)
7318  }
7319  
7320  // Normal selection, as opposed to text dragging.
7321  function leftButtonSelect(cm, event, start, behavior) {
7322    var display = cm.display, doc = cm.doc
7323    e_preventDefault(event)
7324  
7325    var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges
7326    if (behavior.addNew && !behavior.extend) {
7327      ourIndex = doc.sel.contains(start)
7328      if (ourIndex > -1)
7329        { ourRange = ranges[ourIndex] }
7330      else
7331        { ourRange = new Range(start, start) }
7332    } else {
7333      ourRange = doc.sel.primary()
7334      ourIndex = doc.sel.primIndex
7335    }
7336  
7337    if (behavior.unit == "rectangle") {
7338      if (!behavior.addNew) { ourRange = new Range(start, start) }
7339      start = posFromMouse(cm, event, true, true)
7340      ourIndex = -1
7341    } else {
7342      var range = rangeForUnit(cm, start, behavior.unit)
7343      if (behavior.extend)
7344        { ourRange = extendRange(ourRange, range.anchor, range.head, behavior.extend) }
7345      else
7346        { ourRange = range }
7347    }
7348  
7349    if (!behavior.addNew) {
7350      ourIndex = 0
7351      setSelection(doc, new Selection([ourRange], 0), sel_mouse)
7352      startSel = doc.sel
7353    } else if (ourIndex == -1) {
7354      ourIndex = ranges.length
7355      setSelection(doc, normalizeSelection(ranges.concat([ourRange]), ourIndex),
7356                   {scroll: false, origin: "*mouse"})
7357    } else if (ranges.length > 1 && ranges[ourIndex].empty() && behavior.unit == "char" && !behavior.extend) {
7358      setSelection(doc, normalizeSelection(ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0),
7359                   {scroll: false, origin: "*mouse"})
7360      startSel = doc.sel
7361    } else {
7362      replaceOneSelection(doc, ourIndex, ourRange, sel_mouse)
7363    }
7364  
7365    var lastPos = start
7366    function extendTo(pos) {
7367      if (cmp(lastPos, pos) == 0) { return }
7368      lastPos = pos
7369  
7370      if (behavior.unit == "rectangle") {
7371        var ranges = [], tabSize = cm.options.tabSize
7372        var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize)
7373        var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize)
7374        var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol)
7375        for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line));
7376             line <= end; line++) {
7377          var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize)
7378          if (left == right)
7379            { ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))) }
7380          else if (text.length > leftPos)
7381            { ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))) }
7382        }
7383        if (!ranges.length) { ranges.push(new Range(start, start)) }
7384        setSelection(doc, normalizeSelection(startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex),
7385                     {origin: "*mouse", scroll: false})
7386        cm.scrollIntoView(pos)
7387      } else {
7388        var oldRange = ourRange
7389        var range = rangeForUnit(cm, pos, behavior.unit)
7390        var anchor = oldRange.anchor, head
7391        if (cmp(range.anchor, anchor) > 0) {
7392          head = range.head
7393          anchor = minPos(oldRange.from(), range.anchor)
7394        } else {
7395          head = range.anchor
7396          anchor = maxPos(oldRange.to(), range.head)
7397        }
7398        var ranges$1 = startSel.ranges.slice(0)
7399        ranges$1[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc, anchor), head))
7400        setSelection(doc, normalizeSelection(ranges$1, ourIndex), sel_mouse)
7401      }
7402    }
7403  
7404    var editorSize = display.wrapper.getBoundingClientRect()
7405    // Used to ensure timeout re-tries don't fire when another extend
7406    // happened in the meantime (clearTimeout isn't reliable -- at
7407    // least on Chrome, the timeouts still happen even when cleared,
7408    // if the clear happens after their scheduled firing time).
7409    var counter = 0
7410  
7411    function extend(e) {
7412      var curCount = ++counter
7413      var cur = posFromMouse(cm, e, true, behavior.unit == "rectangle")
7414      if (!cur) { return }
7415      if (cmp(cur, lastPos) != 0) {
7416        cm.curOp.focus = activeElt()
7417        extendTo(cur)
7418        var visible = visibleLines(display, doc)
7419        if (cur.line >= visible.to || cur.line < visible.from)
7420          { setTimeout(operation(cm, function () {if (counter == curCount) { extend(e) }}), 150) }
7421      } else {
7422        var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0
7423        if (outside) { setTimeout(operation(cm, function () {
7424          if (counter != curCount) { return }
7425          display.scroller.scrollTop += outside
7426          extend(e)
7427        }), 50) }
7428      }
7429    }
7430  
7431    function done(e) {
7432      cm.state.selectingText = false
7433      counter = Infinity
7434      e_preventDefault(e)
7435      display.input.focus()
7436      off(document, "mousemove", move)
7437      off(document, "mouseup", up)
7438      doc.history.lastSelOrigin = null
7439    }
7440  
7441    var move = operation(cm, function (e) {
7442      if (!e_button(e)) { done(e) }
7443      else { extend(e) }
7444    })
7445    var up = operation(cm, done)
7446    cm.state.selectingText = up
7447    on(document, "mousemove", move)
7448    on(document, "mouseup", up)
7449  }
7450  
7451  // Used when mouse-selecting to adjust the anchor to the proper side
7452  // of a bidi jump depending on the visual position of the head.
7453  function bidiSimplify(cm, range) {
7454    var anchor = range.anchor;
7455    var head = range.head;
7456    var anchorLine = getLine(cm.doc, anchor.line)
7457    if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) { return range }
7458    var order = getOrder(anchorLine)
7459    if (!order) { return range }
7460    var index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index]
7461    if (part.from != anchor.ch && part.to != anchor.ch) { return range }
7462    var boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1)
7463    if (boundary == 0 || boundary == order.length) { return range }
7464  
7465    // Compute the relative visual position of the head compared to the
7466    // anchor (<0 is to the left, >0 to the right)
7467    var leftSide
7468    if (head.line != anchor.line) {
7469      leftSide = (head.line - anchor.line) * (cm.doc.direction == "ltr" ? 1 : -1) > 0
7470    } else {
7471      var headIndex = getBidiPartAt(order, head.ch, head.sticky)
7472      var dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1)
7473      if (headIndex == boundary - 1 || headIndex == boundary)
7474        { leftSide = dir < 0 }
7475      else
7476        { leftSide = dir > 0 }
7477    }
7478  
7479    var usePart = order[boundary + (leftSide ? -1 : 0)]
7480    var from = leftSide == (usePart.level == 1)
7481    var ch = from ? usePart.from : usePart.to, sticky = from ? "after" : "before"
7482    return anchor.ch == ch && anchor.sticky == sticky ? range : new Range(new Pos(anchor.line, ch, sticky), head)
7483  }
7484  
7485  
7486  // Determines whether an event happened in the gutter, and fires the
7487  // handlers for the corresponding event.
7488  function gutterEvent(cm, e, type, prevent) {
7489    var mX, mY
7490    if (e.touches) {
7491      mX = e.touches[0].clientX
7492      mY = e.touches[0].clientY
7493    } else {
7494      try { mX = e.clientX; mY = e.clientY }
7495      catch(e) { return false }
7496    }
7497    if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false }
7498    if (prevent) { e_preventDefault(e) }
7499  
7500    var display = cm.display
7501    var lineBox = display.lineDiv.getBoundingClientRect()
7502  
7503    if (mY > lineBox.bottom || !hasHandler(cm, type)) { return e_defaultPrevented(e) }
7504    mY -= lineBox.top - display.viewOffset
7505  
7506    for (var i = 0; i < cm.options.gutters.length; ++i) {
7507      var g = display.gutters.childNodes[i]
7508      if (g && g.getBoundingClientRect().right >= mX) {
7509        var line = lineAtHeight(cm.doc, mY)
7510        var gutter = cm.options.gutters[i]
7511        signal(cm, type, cm, line, gutter, e)
7512        return e_defaultPrevented(e)
7513      }
7514    }
7515  }
7516  
7517  function clickInGutter(cm, e) {
7518    return gutterEvent(cm, e, "gutterClick", true)
7519  }
7520  
7521  // CONTEXT MENU HANDLING
7522  
7523  // To make the context menu work, we need to briefly unhide the
7524  // textarea (making it as unobtrusive as possible) to let the
7525  // right-click take effect on it.
7526  function onContextMenu(cm, e) {
7527    if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) { return }
7528    if (signalDOMEvent(cm, e, "contextmenu")) { return }
7529    cm.display.input.onContextMenu(e)
7530  }
7531  
7532  function contextMenuInGutter(cm, e) {
7533    if (!hasHandler(cm, "gutterContextMenu")) { return false }
7534    return gutterEvent(cm, e, "gutterContextMenu", false)
7535  }
7536  
7537  function themeChanged(cm) {
7538    cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
7539      cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-")
7540    clearCaches(cm)
7541  }
7542  
7543  var Init = {toString: function(){return "CodeMirror.Init"}}
7544  
7545  var defaults = {}
7546  var optionHandlers = {}
7547  
7548  function defineOptions(CodeMirror) {
7549    var optionHandlers = CodeMirror.optionHandlers
7550  
7551    function option(name, deflt, handle, notOnInit) {
7552      CodeMirror.defaults[name] = deflt
7553      if (handle) { optionHandlers[name] =
7554        notOnInit ? function (cm, val, old) {if (old != Init) { handle(cm, val, old) }} : handle }
7555    }
7556  
7557    CodeMirror.defineOption = option
7558  
7559    // Passed to option handlers when there is no old value.
7560    CodeMirror.Init = Init
7561  
7562    // These two are, on init, called from the constructor because they
7563    // have to be initialized before the editor can start at all.
7564    option("value", "", function (cm, val) { return cm.setValue(val); }, true)
7565    option("mode", null, function (cm, val) {
7566      cm.doc.modeOption = val
7567      loadMode(cm)
7568    }, true)
7569  
7570    option("indentUnit", 2, loadMode, true)
7571    option("indentWithTabs", false)
7572    option("smartIndent", true)
7573    option("tabSize", 4, function (cm) {
7574      resetModeState(cm)
7575      clearCaches(cm)
7576      regChange(cm)
7577    }, true)
7578    option("lineSeparator", null, function (cm, val) {
7579      cm.doc.lineSep = val
7580      if (!val) { return }
7581      var newBreaks = [], lineNo = cm.doc.first
7582      cm.doc.iter(function (line) {
7583        for (var pos = 0;;) {
7584          var found = line.text.indexOf(val, pos)
7585          if (found == -1) { break }
7586          pos = found + val.length
7587          newBreaks.push(Pos(lineNo, found))
7588        }
7589        lineNo++
7590      })
7591      for (var i = newBreaks.length - 1; i >= 0; i--)
7592        { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)) }
7593    })
7594    option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff]/g, function (cm, val, old) {
7595      cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g")
7596      if (old != Init) { cm.refresh() }
7597    })
7598    option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function (cm) { return cm.refresh(); }, true)
7599    option("electricChars", true)
7600    option("inputStyle", mobile ? "contenteditable" : "textarea", function () {
7601      throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME
7602    }, true)
7603    option("spellcheck", false, function (cm, val) { return cm.getInputField().spellcheck = val; }, true)
7604    option("rtlMoveVisually", !windows)
7605    option("wholeLineUpdateBefore", true)
7606  
7607    option("theme", "default", function (cm) {
7608      themeChanged(cm)
7609      guttersChanged(cm)
7610    }, true)
7611    option("keyMap", "default", function (cm, val, old) {
7612      var next = getKeyMap(val)
7613      var prev = old != Init && getKeyMap(old)
7614      if (prev && prev.detach) { prev.detach(cm, next) }
7615      if (next.attach) { next.attach(cm, prev || null) }
7616    })
7617    option("extraKeys", null)
7618    option("configureMouse", null)
7619  
7620    option("lineWrapping", false, wrappingChanged, true)
7621    option("gutters", [], function (cm) {
7622      setGuttersForLineNumbers(cm.options)
7623      guttersChanged(cm)
7624    }, true)
7625    option("fixedGutter", true, function (cm, val) {
7626      cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0"
7627      cm.refresh()
7628    }, true)
7629    option("coverGutterNextToScrollbar", false, function (cm) { return updateScrollbars(cm); }, true)
7630    option("scrollbarStyle", "native", function (cm) {
7631      initScrollbars(cm)
7632      updateScrollbars(cm)
7633      cm.display.scrollbars.setScrollTop(cm.doc.scrollTop)
7634      cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft)
7635    }, true)
7636    option("lineNumbers", false, function (cm) {
7637      setGuttersForLineNumbers(cm.options)
7638      guttersChanged(cm)
7639    }, true)
7640    option("firstLineNumber", 1, guttersChanged, true)
7641    option("lineNumberFormatter", function (integer) { return integer; }, guttersChanged, true)
7642    option("showCursorWhenSelecting", false, updateSelection, true)
7643  
7644    option("resetSelectionOnContextMenu", true)
7645    option("lineWiseCopyCut", true)
7646    option("pasteLinesPerSelection", true)
7647  
7648    option("readOnly", false, function (cm, val) {
7649      if (val == "nocursor") {
7650        onBlur(cm)
7651        cm.display.input.blur()
7652      }
7653      cm.display.input.readOnlyChanged(val)
7654    })
7655    option("disableInput", false, function (cm, val) {if (!val) { cm.display.input.reset() }}, true)
7656    option("dragDrop", true, dragDropChanged)
7657    option("allowDropFileTypes", null)
7658  
7659    option("cursorBlinkRate", 530)
7660    option("cursorScrollMargin", 0)
7661    option("cursorHeight", 1, updateSelection, true)
7662    option("singleCursorHeightPerLine", true, updateSelection, true)
7663    option("workTime", 100)
7664    option("workDelay", 100)
7665    option("flattenSpans", true, resetModeState, true)
7666    option("addModeClass", false, resetModeState, true)
7667    option("pollInterval", 100)
7668    option("undoDepth", 200, function (cm, val) { return cm.doc.history.undoDepth = val; })
7669    option("historyEventDelay", 1250)
7670    option("viewportMargin", 10, function (cm) { return cm.refresh(); }, true)
7671    option("maxHighlightLength", 10000, resetModeState, true)
7672    option("moveInputWithCursor", true, function (cm, val) {
7673      if (!val) { cm.display.input.resetPosition() }
7674    })
7675  
7676    option("tabindex", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || ""; })
7677    option("autofocus", null)
7678    option("direction", "ltr", function (cm, val) { return cm.doc.setDirection(val); }, true)
7679  }
7680  
7681  function guttersChanged(cm) {
7682    updateGutters(cm)
7683    regChange(cm)
7684    alignHorizontally(cm)
7685  }
7686  
7687  function dragDropChanged(cm, value, old) {
7688    var wasOn = old && old != Init
7689    if (!value != !wasOn) {
7690      var funcs = cm.display.dragFunctions
7691      var toggle = value ? on : off
7692      toggle(cm.display.scroller, "dragstart", funcs.start)
7693      toggle(cm.display.scroller, "dragenter", funcs.enter)
7694      toggle(cm.display.scroller, "dragover", funcs.over)
7695      toggle(cm.display.scroller, "dragleave", funcs.leave)
7696      toggle(cm.display.scroller, "drop", funcs.drop)
7697    }
7698  }
7699  
7700  function wrappingChanged(cm) {
7701    if (cm.options.lineWrapping) {
7702      addClass(cm.display.wrapper, "CodeMirror-wrap")
7703      cm.display.sizer.style.minWidth = ""
7704      cm.display.sizerWidth = null
7705    } else {
7706      rmClass(cm.display.wrapper, "CodeMirror-wrap")
7707      findMaxLine(cm)
7708    }
7709    estimateLineHeights(cm)
7710    regChange(cm)
7711    clearCaches(cm)
7712    setTimeout(function () { return updateScrollbars(cm); }, 100)
7713  }
7714  
7715  // A CodeMirror instance represents an editor. This is the object
7716  // that user code is usually dealing with.
7717  
7718  function CodeMirror(place, options) {
7719    var this$1 = this;
7720  
7721    if (!(this instanceof CodeMirror)) { return new CodeMirror(place, options) }
7722  
7723    this.options = options = options ? copyObj(options) : {}
7724    // Determine effective options based on given values and defaults.
7725    copyObj(defaults, options, false)
7726    setGuttersForLineNumbers(options)
7727  
7728    var doc = options.value
7729    if (typeof doc == "string") { doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction) }
7730    this.doc = doc
7731  
7732    var input = new CodeMirror.inputStyles[options.inputStyle](this)
7733    var display = this.display = new Display(place, doc, input)
7734    display.wrapper.CodeMirror = this
7735    updateGutters(this)
7736    themeChanged(this)
7737    if (options.lineWrapping)
7738      { this.display.wrapper.className += " CodeMirror-wrap" }
7739    initScrollbars(this)
7740  
7741    this.state = {
7742      keyMaps: [],  // stores maps added by addKeyMap
7743      overlays: [], // highlighting overlays, as added by addOverlay
7744      modeGen: 0,   // bumped when mode/overlay changes, used to invalidate highlighting info
7745      overwrite: false,
7746      delayingBlurEvent: false,
7747      focused: false,
7748      suppressEdits: false, // used to disable editing during key handlers when in readOnly mode
7749      pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edits in input.poll
7750      selectingText: false,
7751      draggingText: false,
7752      highlight: new Delayed(), // stores highlight worker timeout
7753      keySeq: null,  // Unfinished key sequence
7754      specialChars: null
7755    }
7756  
7757    if (options.autofocus && !mobile) { display.input.focus() }
7758  
7759    // Override magic textarea content restore that IE sometimes does
7760    // on our hidden textarea on reload
7761    if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20) }
7762  
7763    registerEventHandlers(this)
7764    ensureGlobalHandlers()
7765  
7766    startOperation(this)
7767    this.curOp.forceUpdate = true
7768    attachDoc(this, doc)
7769  
7770    if ((options.autofocus && !mobile) || this.hasFocus())
7771      { setTimeout(bind(onFocus, this), 20) }
7772    else
7773      { onBlur(this) }
7774  
7775    for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt))
7776      { optionHandlers[opt](this$1, options[opt], Init) } }
7777    maybeUpdateLineNumberWidth(this)
7778    if (options.finishInit) { options.finishInit(this) }
7779    for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this$1) }
7780    endOperation(this)
7781    // Suppress optimizelegibility in Webkit, since it breaks text
7782    // measuring on line wrapping boundaries.
7783    if (webkit && options.lineWrapping &&
7784        getComputedStyle(display.lineDiv).textRendering == "optimizelegibility")
7785      { display.lineDiv.style.textRendering = "auto" }
7786  }
7787  
7788  // The default configuration options.
7789  CodeMirror.defaults = defaults
7790  // Functions to run when options are changed.
7791  CodeMirror.optionHandlers = optionHandlers
7792  
7793  // Attach the necessary event handlers when initializing the editor
7794  function registerEventHandlers(cm) {
7795    var d = cm.display
7796    on(d.scroller, "mousedown", operation(cm, onMouseDown))
7797    // Older IE's will not fire a second mousedown for a double click
7798    if (ie && ie_version < 11)
7799      { on(d.scroller, "dblclick", operation(cm, function (e) {
7800        if (signalDOMEvent(cm, e)) { return }
7801        var pos = posFromMouse(cm, e)
7802        if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) { return }
7803        e_preventDefault(e)
7804        var word = cm.findWordAt(pos)
7805        extendSelection(cm.doc, word.anchor, word.head)
7806      })) }
7807    else
7808      { on(d.scroller, "dblclick", function (e) { return signalDOMEvent(cm, e) || e_preventDefault(e); }) }
7809    // Some browsers fire contextmenu *after* opening the menu, at
7810    // which point we can't mess with it anymore. Context menu is
7811    // handled in onMouseDown for these browsers.
7812    if (!captureRightClick) { on(d.scroller, "contextmenu", function (e) { return onContextMenu(cm, e); }) }
7813  
7814    // Used to suppress mouse event handling when a touch happens
7815    var touchFinished, prevTouch = {end: 0}
7816    function finishTouch() {
7817      if (d.activeTouch) {
7818        touchFinished = setTimeout(function () { return d.activeTouch = null; }, 1000)
7819        prevTouch = d.activeTouch
7820        prevTouch.end = +new Date
7821      }
7822    }
7823    function isMouseLikeTouchEvent(e) {
7824      if (e.touches.length != 1) { return false }
7825      var touch = e.touches[0]
7826      return touch.radiusX <= 1 && touch.radiusY <= 1
7827    }
7828    function farAway(touch, other) {
7829      if (other.left == null) { return true }
7830      var dx = other.left - touch.left, dy = other.top - touch.top
7831      return dx * dx + dy * dy > 20 * 20
7832    }
7833    on(d.scroller, "touchstart", function (e) {
7834      if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e) && !clickInGutter(cm, e)) {
7835        d.input.ensurePolled()
7836        clearTimeout(touchFinished)
7837        var now = +new Date
7838        d.activeTouch = {start: now, moved: false,
7839                         prev: now - prevTouch.end <= 300 ? prevTouch : null}
7840        if (e.touches.length == 1) {
7841          d.activeTouch.left = e.touches[0].pageX
7842          d.activeTouch.top = e.touches[0].pageY
7843        }
7844      }
7845    })
7846    on(d.scroller, "touchmove", function () {
7847      if (d.activeTouch) { d.activeTouch.moved = true }
7848    })
7849    on(d.scroller, "touchend", function (e) {
7850      var touch = d.activeTouch
7851      if (touch && !eventInWidget(d, e) && touch.left != null &&
7852          !touch.moved && new Date - touch.start < 300) {
7853        var pos = cm.coordsChar(d.activeTouch, "page"), range
7854        if (!touch.prev || farAway(touch, touch.prev)) // Single tap
7855          { range = new Range(pos, pos) }
7856        else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap
7857          { range = cm.findWordAt(pos) }
7858        else // Triple tap
7859          { range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) }
7860        cm.setSelection(range.anchor, range.head)
7861        cm.focus()
7862        e_preventDefault(e)
7863      }
7864      finishTouch()
7865    })
7866    on(d.scroller, "touchcancel", finishTouch)
7867  
7868    // Sync scrolling between fake scrollbars and real scrollable
7869    // area, ensure viewport is updated when scrolling.
7870    on(d.scroller, "scroll", function () {
7871      if (d.scroller.clientHeight) {
7872        updateScrollTop(cm, d.scroller.scrollTop)
7873        setScrollLeft(cm, d.scroller.scrollLeft, true)
7874        signal(cm, "scroll", cm)
7875      }
7876    })
7877  
7878    // Listen to wheel events in order to try and update the viewport on time.
7879    on(d.scroller, "mousewheel", function (e) { return onScrollWheel(cm, e); })
7880    on(d.scroller, "DOMMouseScroll", function (e) { return onScrollWheel(cm, e); })
7881  
7882    // Prevent wrapper from ever scrolling
7883    on(d.wrapper, "scroll", function () { return d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; })
7884  
7885    d.dragFunctions = {
7886      enter: function (e) {if (!signalDOMEvent(cm, e)) { e_stop(e) }},
7887      over: function (e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e) }},
7888      start: function (e) { return onDragStart(cm, e); },
7889      drop: operation(cm, onDrop),
7890      leave: function (e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm) }}
7891    }
7892  
7893    var inp = d.input.getField()
7894    on(inp, "keyup", function (e) { return onKeyUp.call(cm, e); })
7895    on(inp, "keydown", operation(cm, onKeyDown))
7896    on(inp, "keypress", operation(cm, onKeyPress))
7897    on(inp, "focus", function (e) { return onFocus(cm, e); })
7898    on(inp, "blur", function (e) { return onBlur(cm, e); })
7899  }
7900  
7901  var initHooks = []
7902  CodeMirror.defineInitHook = function (f) { return initHooks.push(f); }
7903  
7904  // Indent the given line. The how parameter can be "smart",
7905  // "add"/null, "subtract", or "prev". When aggressive is false
7906  // (typically set to true for forced single-line indents), empty
7907  // lines are not indented, and places where the mode returns Pass
7908  // are left alone.
7909  function indentLine(cm, n, how, aggressive) {
7910    var doc = cm.doc, state
7911    if (how == null) { how = "add" }
7912    if (how == "smart") {
7913      // Fall back to "prev" when the mode doesn't have an indentation
7914      // method.
7915      if (!doc.mode.indent) { how = "prev" }
7916      else { state = getContextBefore(cm, n).state }
7917    }
7918  
7919    var tabSize = cm.options.tabSize
7920    var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize)
7921    if (line.stateAfter) { line.stateAfter = null }
7922    var curSpaceString = line.text.match(/^\s*/)[0], indentation
7923    if (!aggressive && !/\S/.test(line.text)) {
7924      indentation = 0
7925      how = "not"
7926    } else if (how == "smart") {
7927      indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text)
7928      if (indentation == Pass || indentation > 150) {
7929        if (!aggressive) { return }
7930        how = "prev"
7931      }
7932    }
7933    if (how == "prev") {
7934      if (n > doc.first) { indentation = countColumn(getLine(doc, n-1).text, null, tabSize) }
7935      else { indentation = 0 }
7936    } else if (how == "add") {
7937      indentation = curSpace + cm.options.indentUnit
7938    } else if (how == "subtract") {
7939      indentation = curSpace - cm.options.indentUnit
7940    } else if (typeof how == "number") {
7941      indentation = curSpace + how
7942    }
7943    indentation = Math.max(0, indentation)
7944  
7945    var indentString = "", pos = 0
7946    if (cm.options.indentWithTabs)
7947      { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t"} }
7948    if (pos < indentation) { indentString += spaceStr(indentation - pos) }
7949  
7950    if (indentString != curSpaceString) {
7951      replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input")
7952      line.stateAfter = null
7953      return true
7954    } else {
7955      // Ensure that, if the cursor was in the whitespace at the start
7956      // of the line, it is moved to the end of that space.
7957      for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) {
7958        var range = doc.sel.ranges[i$1]
7959        if (range.head.line == n && range.head.ch < curSpaceString.length) {
7960          var pos$1 = Pos(n, curSpaceString.length)
7961          replaceOneSelection(doc, i$1, new Range(pos$1, pos$1))
7962          break
7963        }
7964      }
7965    }
7966  }
7967  
7968  // This will be set to a {lineWise: bool, text: [string]} object, so
7969  // that, when pasting, we know what kind of selections the copied
7970  // text was made out of.
7971  var lastCopied = null
7972  
7973  function setLastCopied(newLastCopied) {
7974    lastCopied = newLastCopied
7975  }
7976  
7977  function applyTextInput(cm, inserted, deleted, sel, origin) {
7978    var doc = cm.doc
7979    cm.display.shift = false
7980    if (!sel) { sel = doc.sel }
7981  
7982    var paste = cm.state.pasteIncoming || origin == "paste"
7983    var textLines = splitLinesAuto(inserted), multiPaste = null
7984    // When pasing N lines into N selections, insert one line per selection
7985    if (paste && sel.ranges.length > 1) {
7986      if (lastCopied && lastCopied.text.join("\n") == inserted) {
7987        if (sel.ranges.length % lastCopied.text.length == 0) {
7988          multiPaste = []
7989          for (var i = 0; i < lastCopied.text.length; i++)
7990            { multiPaste.push(doc.splitLines(lastCopied.text[i])) }
7991        }
7992      } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) {
7993        multiPaste = map(textLines, function (l) { return [l]; })
7994      }
7995    }
7996  
7997    var updateInput
7998    // Normal behavior is to insert the new text into every selection
7999    for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) {
8000      var range = sel.ranges[i$1]
8001      var from = range.from(), to = range.to()
8002      if (range.empty()) {
8003        if (deleted && deleted > 0) // Handle deletion
8004          { from = Pos(from.line, from.ch - deleted) }
8005        else if (cm.state.overwrite && !paste) // Handle overwrite
8006          { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)) }
8007        else if (lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == inserted)
8008          { from = to = Pos(from.line, 0) }
8009      }
8010      updateInput = cm.curOp.updateInput
8011      var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines,
8012                         origin: origin || (paste ? "paste" : cm.state.cutIncoming ? "cut" : "+input")}
8013      makeChange(cm.doc, changeEvent)
8014      signalLater(cm, "inputRead", cm, changeEvent)
8015    }
8016    if (inserted && !paste)
8017      { triggerElectric(cm, inserted) }
8018  
8019    ensureCursorVisible(cm)
8020    cm.curOp.updateInput = updateInput
8021    cm.curOp.typing = true
8022    cm.state.pasteIncoming = cm.state.cutIncoming = false
8023  }
8024  
8025  function handlePaste(e, cm) {
8026    var pasted = e.clipboardData && e.clipboardData.getData("Text")
8027    if (pasted) {
8028      e.preventDefault()
8029      if (!cm.isReadOnly() && !cm.options.disableInput)
8030        { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, "paste"); }) }
8031      return true
8032    }
8033  }
8034  
8035  function triggerElectric(cm, inserted) {
8036    // When an 'electric' character is inserted, immediately trigger a reindent
8037    if (!cm.options.electricChars || !cm.options.smartIndent) { return }
8038    var sel = cm.doc.sel
8039  
8040    for (var i = sel.ranges.length - 1; i >= 0; i--) {
8041      var range = sel.ranges[i]
8042      if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head.line)) { continue }
8043      var mode = cm.getModeAt(range.head)
8044      var indented = false
8045      if (mode.electricChars) {
8046        for (var j = 0; j < mode.electricChars.length; j++)
8047          { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
8048            indented = indentLine(cm, range.head.line, "smart")
8049            break
8050          } }
8051      } else if (mode.electricInput) {
8052        if (mode.electricInput.test(getLine(cm.doc, range.head.line).text.slice(0, range.head.ch)))
8053          { indented = indentLine(cm, range.head.line, "smart") }
8054      }
8055      if (indented) { signalLater(cm, "electricInput", cm, range.head.line) }
8056    }
8057  }
8058  
8059  function copyableRanges(cm) {
8060    var text = [], ranges = []
8061    for (var i = 0; i < cm.doc.sel.ranges.length; i++) {
8062      var line = cm.doc.sel.ranges[i].head.line
8063      var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)}
8064      ranges.push(lineRange)
8065      text.push(cm.getRange(lineRange.anchor, lineRange.head))
8066    }
8067    return {text: text, ranges: ranges}
8068  }
8069  
8070  function disableBrowserMagic(field, spellcheck) {
8071    field.setAttribute("autocorrect", "off")
8072    field.setAttribute("autocapitalize", "off")
8073    field.setAttribute("spellcheck", !!spellcheck)
8074  }
8075  
8076  function hiddenTextarea() {
8077    var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none")
8078    var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;")
8079    // The textarea is kept positioned near the cursor to prevent the
8080    // fact that it'll be scrolled into view on input from scrolling
8081    // our fake cursor out of view. On webkit, when wrap=off, paste is
8082    // very slow. So make the area wide instead.
8083    if (webkit) { te.style.width = "1000px" }
8084    else { te.setAttribute("wrap", "off") }
8085    // If border: 0; -- iOS fails to open keyboard (issue #1287)
8086    if (ios) { te.style.border = "1px solid black" }
8087    disableBrowserMagic(te)
8088    return div
8089  }
8090  
8091  // The publicly visible API. Note that methodOp(f) means
8092  // 'wrap f in an operation, performed on its `this` parameter'.
8093  
8094  // This is not the complete set of editor methods. Most of the
8095  // methods defined on the Doc type are also injected into
8096  // CodeMirror.prototype, for backwards compatibility and
8097  // convenience.
8098  
8099  function addEditorMethods(CodeMirror) {
8100    var optionHandlers = CodeMirror.optionHandlers
8101  
8102    var helpers = CodeMirror.helpers = {}
8103  
8104    CodeMirror.prototype = {
8105      constructor: CodeMirror,
8106      focus: function(){window.focus(); this.display.input.focus()},
8107  
8108      setOption: function(option, value) {
8109        var options = this.options, old = options[option]
8110        if (options[option] == value && option != "mode") { return }
8111        options[option] = value
8112        if (optionHandlers.hasOwnProperty(option))
8113          { operation(this, optionHandlers[option])(this, value, old) }
8114        signal(this, "optionChange", this, option)
8115      },
8116  
8117      getOption: function(option) {return this.options[option]},
8118      getDoc: function() {return this.doc},
8119  
8120      addKeyMap: function(map, bottom) {
8121        this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map))
8122      },
8123      removeKeyMap: function(map) {
8124        var maps = this.state.keyMaps
8125        for (var i = 0; i < maps.length; ++i)
8126          { if (maps[i] == map || maps[i].name == map) {
8127            maps.splice(i, 1)
8128            return true
8129          } }
8130      },
8131  
8132      addOverlay: methodOp(function(spec, options) {
8133        var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec)
8134        if (mode.startState) { throw new Error("Overlays may not be stateful.") }
8135        insertSorted(this.state.overlays,
8136                     {mode: mode, modeSpec: spec, opaque: options && options.opaque,
8137                      priority: (options && options.priority) || 0},
8138                     function (overlay) { return overlay.priority; })
8139        this.state.modeGen++
8140        regChange(this)
8141      }),
8142      removeOverlay: methodOp(function(spec) {
8143        var this$1 = this;
8144  
8145        var overlays = this.state.overlays
8146        for (var i = 0; i < overlays.length; ++i) {
8147          var cur = overlays[i].modeSpec
8148          if (cur == spec || typeof spec == "string" && cur.name == spec) {
8149            overlays.splice(i, 1)
8150            this$1.state.modeGen++
8151            regChange(this$1)
8152            return
8153          }
8154        }
8155      }),
8156  
8157      indentLine: methodOp(function(n, dir, aggressive) {
8158        if (typeof dir != "string" && typeof dir != "number") {
8159          if (dir == null) { dir = this.options.smartIndent ? "smart" : "prev" }
8160          else { dir = dir ? "add" : "subtract" }
8161        }
8162        if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive) }
8163      }),
8164      indentSelection: methodOp(function(how) {
8165        var this$1 = this;
8166  
8167        var ranges = this.doc.sel.ranges, end = -1
8168        for (var i = 0; i < ranges.length; i++) {
8169          var range = ranges[i]
8170          if (!range.empty()) {
8171            var from = range.from(), to = range.to()
8172            var start = Math.max(end, from.line)
8173            end = Math.min(this$1.lastLine(), to.line - (to.ch ? 0 : 1)) + 1
8174            for (var j = start; j < end; ++j)
8175              { indentLine(this$1, j, how) }
8176            var newRanges = this$1.doc.sel.ranges
8177            if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0)
8178              { replaceOneSelection(this$1.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll) }
8179          } else if (range.head.line > end) {
8180            indentLine(this$1, range.head.line, how, true)
8181            end = range.head.line
8182            if (i == this$1.doc.sel.primIndex) { ensureCursorVisible(this$1) }
8183          }
8184        }
8185      }),
8186  
8187      // Fetch the parser token for a given character. Useful for hacks
8188      // that want to inspect the mode state (say, for completion).
8189      getTokenAt: function(pos, precise) {
8190        return takeToken(this, pos, precise)
8191      },
8192  
8193      getLineTokens: function(line, precise) {
8194        return takeToken(this, Pos(line), precise, true)
8195      },
8196  
8197      getTokenTypeAt: function(pos) {
8198        pos = clipPos(this.doc, pos)
8199        var styles = getLineStyles(this, getLine(this.doc, pos.line))
8200        var before = 0, after = (styles.length - 1) / 2, ch = pos.ch
8201        var type
8202        if (ch == 0) { type = styles[2] }
8203        else { for (;;) {
8204          var mid = (before + after) >> 1
8205          if ((mid ? styles[mid * 2 - 1] : 0) >= ch) { after = mid }
8206          else if (styles[mid * 2 + 1] < ch) { before = mid + 1 }
8207          else { type = styles[mid * 2 + 2]; break }
8208        } }
8209        var cut = type ? type.indexOf("overlay ") : -1
8210        return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1)
8211      },
8212  
8213      getModeAt: function(pos) {
8214        var mode = this.doc.mode
8215        if (!mode.innerMode) { return mode }
8216        return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode
8217      },
8218  
8219      getHelper: function(pos, type) {
8220        return this.getHelpers(pos, type)[0]
8221      },
8222  
8223      getHelpers: function(pos, type) {
8224        var this$1 = this;
8225  
8226        var found = []
8227        if (!helpers.hasOwnProperty(type)) { return found }
8228        var help = helpers[type], mode = this.getModeAt(pos)
8229        if (typeof mode[type] == "string") {
8230          if (help[mode[type]]) { found.push(help[mode[type]]) }
8231        } else if (mode[type]) {
8232          for (var i = 0; i < mode[type].length; i++) {
8233            var val = help[mode[type][i]]
8234            if (val) { found.push(val) }
8235          }
8236        } else if (mode.helperType && help[mode.helperType]) {
8237          found.push(help[mode.helperType])
8238        } else if (help[mode.name]) {
8239          found.push(help[mode.name])
8240        }
8241        for (var i$1 = 0; i$1 < help._global.length; i$1++) {
8242          var cur = help._global[i$1]
8243          if (cur.pred(mode, this$1) && indexOf(found, cur.val) == -1)
8244            { found.push(cur.val) }
8245        }
8246        return found
8247      },
8248  
8249      getStateAfter: function(line, precise) {
8250        var doc = this.doc
8251        line = clipLine(doc, line == null ? doc.first + doc.size - 1: line)
8252        return getContextBefore(this, line + 1, precise).state
8253      },
8254  
8255      cursorCoords: function(start, mode) {
8256        var pos, range = this.doc.sel.primary()
8257        if (start == null) { pos = range.head }
8258        else if (typeof start == "object") { pos = clipPos(this.doc, start) }
8259        else { pos = start ? range.from() : range.to() }
8260        return cursorCoords(this, pos, mode || "page")
8261      },
8262  
8263      charCoords: function(pos, mode) {
8264        return charCoords(this, clipPos(this.doc, pos), mode || "page")
8265      },
8266  
8267      coordsChar: function(coords, mode) {
8268        coords = fromCoordSystem(this, coords, mode || "page")
8269        return coordsChar(this, coords.left, coords.top)
8270      },
8271  
8272      lineAtHeight: function(height, mode) {
8273        height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top
8274        return lineAtHeight(this.doc, height + this.display.viewOffset)
8275      },
8276      heightAtLine: function(line, mode, includeWidgets) {
8277        var end = false, lineObj
8278        if (typeof line == "number") {
8279          var last = this.doc.first + this.doc.size - 1
8280          if (line < this.doc.first) { line = this.doc.first }
8281          else if (line > last) { line = last; end = true }
8282          lineObj = getLine(this.doc, line)
8283        } else {
8284          lineObj = line
8285        }
8286        return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page", includeWidgets || end).top +
8287          (end ? this.doc.height - heightAtLine(lineObj) : 0)
8288      },
8289  
8290      defaultTextHeight: function() { return textHeight(this.display) },
8291      defaultCharWidth: function() { return charWidth(this.display) },
8292  
8293      getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}},
8294  
8295      addWidget: function(pos, node, scroll, vert, horiz) {
8296        var display = this.display
8297        pos = cursorCoords(this, clipPos(this.doc, pos))
8298        var top = pos.bottom, left = pos.left
8299        node.style.position = "absolute"
8300        node.setAttribute("cm-ignore-events", "true")
8301        this.display.input.setUneditable(node)
8302        display.sizer.appendChild(node)
8303        if (vert == "over") {
8304          top = pos.top
8305        } else if (vert == "above" || vert == "near") {
8306          var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
8307          hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth)
8308          // Default to positioning above (if specified and possible); otherwise default to positioning below
8309          if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
8310            { top = pos.top - node.offsetHeight }
8311          else if (pos.bottom + node.offsetHeight <= vspace)
8312            { top = pos.bottom }
8313          if (left + node.offsetWidth > hspace)
8314            { left = hspace - node.offsetWidth }
8315        }
8316        node.style.top = top + "px"
8317        node.style.left = node.style.right = ""
8318        if (horiz == "right") {
8319          left = display.sizer.clientWidth - node.offsetWidth
8320          node.style.right = "0px"
8321        } else {
8322          if (horiz == "left") { left = 0 }
8323          else if (horiz == "middle") { left = (display.sizer.clientWidth - node.offsetWidth) / 2 }
8324          node.style.left = left + "px"
8325        }
8326        if (scroll)
8327          { scrollIntoView(this, {left: left, top: top, right: left + node.offsetWidth, bottom: top + node.offsetHeight}) }
8328      },
8329  
8330      triggerOnKeyDown: methodOp(onKeyDown),
8331      triggerOnKeyPress: methodOp(onKeyPress),
8332      triggerOnKeyUp: onKeyUp,
8333      triggerOnMouseDown: methodOp(onMouseDown),
8334  
8335      execCommand: function(cmd) {
8336        if (commands.hasOwnProperty(cmd))
8337          { return commands[cmd].call(null, this) }
8338      },
8339  
8340      triggerElectric: methodOp(function(text) { triggerElectric(this, text) }),
8341  
8342      findPosH: function(from, amount, unit, visually) {
8343        var this$1 = this;
8344  
8345        var dir = 1
8346        if (amount < 0) { dir = -1; amount = -amount }
8347        var cur = clipPos(this.doc, from)
8348        for (var i = 0; i < amount; ++i) {
8349          cur = findPosH(this$1.doc, cur, dir, unit, visually)
8350          if (cur.hitSide) { break }
8351        }
8352        return cur
8353      },
8354  
8355      moveH: methodOp(function(dir, unit) {
8356        var this$1 = this;
8357  
8358        this.extendSelectionsBy(function (range) {
8359          if (this$1.display.shift || this$1.doc.extend || range.empty())
8360            { return findPosH(this$1.doc, range.head, dir, unit, this$1.options.rtlMoveVisually) }
8361          else
8362            { return dir < 0 ? range.from() : range.to() }
8363        }, sel_move)
8364      }),
8365  
8366      deleteH: methodOp(function(dir, unit) {
8367        var sel = this.doc.sel, doc = this.doc
8368        if (sel.somethingSelected())
8369          { doc.replaceSelection("", null, "+delete") }
8370        else
8371          { deleteNearSelection(this, function (range) {
8372            var other = findPosH(doc, range.head, dir, unit, false)
8373            return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other}
8374          }) }
8375      }),
8376  
8377      findPosV: function(from, amount, unit, goalColumn) {
8378        var this$1 = this;
8379  
8380        var dir = 1, x = goalColumn
8381        if (amount < 0) { dir = -1; amount = -amount }
8382        var cur = clipPos(this.doc, from)
8383        for (var i = 0; i < amount; ++i) {
8384          var coords = cursorCoords(this$1, cur, "div")
8385          if (x == null) { x = coords.left }
8386          else { coords.left = x }
8387          cur = findPosV(this$1, coords, dir, unit)
8388          if (cur.hitSide) { break }
8389        }
8390        return cur
8391      },
8392  
8393      moveV: methodOp(function(dir, unit) {
8394        var this$1 = this;
8395  
8396        var doc = this.doc, goals = []
8397        var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected()
8398        doc.extendSelectionsBy(function (range) {
8399          if (collapse)
8400            { return dir < 0 ? range.from() : range.to() }
8401          var headPos = cursorCoords(this$1, range.head, "div")
8402          if (range.goalColumn != null) { headPos.left = range.goalColumn }
8403          goals.push(headPos.left)
8404          var pos = findPosV(this$1, headPos, dir, unit)
8405          if (unit == "page" && range == doc.sel.primary())
8406            { addToScrollTop(this$1, charCoords(this$1, pos, "div").top - headPos.top) }
8407          return pos
8408        }, sel_move)
8409        if (goals.length) { for (var i = 0; i < doc.sel.ranges.length; i++)
8410          { doc.sel.ranges[i].goalColumn = goals[i] } }
8411      }),
8412  
8413      // Find the word at the given position (as returned by coordsChar).
8414      findWordAt: function(pos) {
8415        var doc = this.doc, line = getLine(doc, pos.line).text
8416        var start = pos.ch, end = pos.ch
8417        if (line) {
8418          var helper = this.getHelper(pos, "wordChars")
8419          if ((pos.sticky == "before" || end == line.length) && start) { --start; } else { ++end }
8420          var startChar = line.charAt(start)
8421          var check = isWordChar(startChar, helper)
8422            ? function (ch) { return isWordChar(ch, helper); }
8423            : /\s/.test(startChar) ? function (ch) { return /\s/.test(ch); }
8424            : function (ch) { return (!/\s/.test(ch) && !isWordChar(ch)); }
8425          while (start > 0 && check(line.charAt(start - 1))) { --start }
8426          while (end < line.length && check(line.charAt(end))) { ++end }
8427        }
8428        return new Range(Pos(pos.line, start), Pos(pos.line, end))
8429      },
8430  
8431      toggleOverwrite: function(value) {
8432        if (value != null && value == this.state.overwrite) { return }
8433        if (this.state.overwrite = !this.state.overwrite)
8434          { addClass(this.display.cursorDiv, "CodeMirror-overwrite") }
8435        else
8436          { rmClass(this.display.cursorDiv, "CodeMirror-overwrite") }
8437  
8438        signal(this, "overwriteToggle", this, this.state.overwrite)
8439      },
8440      hasFocus: function() { return this.display.input.getField() == activeElt() },
8441      isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) },
8442  
8443      scrollTo: methodOp(function (x, y) { scrollToCoords(this, x, y) }),
8444      getScrollInfo: function() {
8445        var scroller = this.display.scroller
8446        return {left: scroller.scrollLeft, top: scroller.scrollTop,
8447                height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight,
8448                width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth,
8449                clientHeight: displayHeight(this), clientWidth: displayWidth(this)}
8450      },
8451  
8452      scrollIntoView: methodOp(function(range, margin) {
8453        if (range == null) {
8454          range = {from: this.doc.sel.primary().head, to: null}
8455          if (margin == null) { margin = this.options.cursorScrollMargin }
8456        } else if (typeof range == "number") {
8457          range = {from: Pos(range, 0), to: null}
8458        } else if (range.from == null) {
8459          range = {from: range, to: null}
8460        }
8461        if (!range.to) { range.to = range.from }
8462        range.margin = margin || 0
8463  
8464        if (range.from.line != null) {
8465          scrollToRange(this, range)
8466        } else {
8467          scrollToCoordsRange(this, range.from, range.to, range.margin)
8468        }
8469      }),
8470  
8471      setSize: methodOp(function(width, height) {
8472        var this$1 = this;
8473  
8474        var interpret = function (val) { return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; }
8475        if (width != null) { this.display.wrapper.style.width = interpret(width) }
8476        if (height != null) { this.display.wrapper.style.height = interpret(height) }
8477        if (this.options.lineWrapping) { clearLineMeasurementCache(this) }
8478        var lineNo = this.display.viewFrom
8479        this.doc.iter(lineNo, this.display.viewTo, function (line) {
8480          if (line.widgets) { for (var i = 0; i < line.widgets.length; i++)
8481            { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo, "widget"); break } } }
8482          ++lineNo
8483        })
8484        this.curOp.forceUpdate = true
8485        signal(this, "refresh", this)
8486      }),
8487  
8488      operation: function(f){return runInOp(this, f)},
8489      startOperation: function(){return startOperation(this)},
8490      endOperation: function(){return endOperation(this)},
8491  
8492      refresh: methodOp(function() {
8493        var oldHeight = this.display.cachedTextHeight
8494        regChange(this)
8495        this.curOp.forceUpdate = true
8496        clearCaches(this)
8497        scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop)
8498        updateGutterSpace(this)
8499        if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5)
8500          { estimateLineHeights(this) }
8501        signal(this, "refresh", this)
8502      }),
8503  
8504      swapDoc: methodOp(function(doc) {
8505        var old = this.doc
8506        old.cm = null
8507        attachDoc(this, doc)
8508        clearCaches(this)
8509        this.display.input.reset()
8510        scrollToCoords(this, doc.scrollLeft, doc.scrollTop)
8511        this.curOp.forceScroll = true
8512        signalLater(this, "swapDoc", this, old)
8513        return old
8514      }),
8515  
8516      getInputField: function(){return this.display.input.getField()},
8517      getWrapperElement: function(){return this.display.wrapper},
8518      getScrollerElement: function(){return this.display.scroller},
8519      getGutterElement: function(){return this.display.gutters}
8520    }
8521    eventMixin(CodeMirror)
8522  
8523    CodeMirror.registerHelper = function(type, name, value) {
8524      if (!helpers.hasOwnProperty(type)) { helpers[type] = CodeMirror[type] = {_global: []} }
8525      helpers[type][name] = value
8526    }
8527    CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {
8528      CodeMirror.registerHelper(type, name, value)
8529      helpers[type]._global.push({pred: predicate, val: value})
8530    }
8531  }
8532  
8533  // Used for horizontal relative motion. Dir is -1 or 1 (left or
8534  // right), unit can be "char", "column" (like char, but doesn't
8535  // cross line boundaries), "word" (across next word), or "group" (to
8536  // the start of next group of word or non-word-non-whitespace
8537  // chars). The visually param controls whether, in right-to-left
8538  // text, direction 1 means to move towards the next index in the
8539  // string, or towards the character to the right of the current
8540  // position. The resulting position will have a hitSide=true
8541  // property if it reached the end of the document.
8542  function findPosH(doc, pos, dir, unit, visually) {
8543    var oldPos = pos
8544    var origDir = dir
8545    var lineObj = getLine(doc, pos.line)
8546    function findNextLine() {
8547      var l = pos.line + dir
8548      if (l < doc.first || l >= doc.first + doc.size) { return false }
8549      pos = new Pos(l, pos.ch, pos.sticky)
8550      return lineObj = getLine(doc, l)
8551    }
8552    function moveOnce(boundToLine) {
8553      var next
8554      if (visually) {
8555        next = moveVisually(doc.cm, lineObj, pos, dir)
8556      } else {
8557        next = moveLogically(lineObj, pos, dir)
8558      }
8559      if (next == null) {
8560        if (!boundToLine && findNextLine())
8561          { pos = endOfLine(visually, doc.cm, lineObj, pos.line, dir) }
8562        else
8563          { return false }
8564      } else {
8565        pos = next
8566      }
8567      return true
8568    }
8569  
8570    if (unit == "char") {
8571      moveOnce()
8572    } else if (unit == "column") {
8573      moveOnce(true)
8574    } else if (unit == "word" || unit == "group") {
8575      var sawType = null, group = unit == "group"
8576      var helper = doc.cm && doc.cm.getHelper(pos, "wordChars")
8577      for (var first = true;; first = false) {
8578        if (dir < 0 && !moveOnce(!first)) { break }
8579        var cur = lineObj.text.charAt(pos.ch) || "\n"
8580        var type = isWordChar(cur, helper) ? "w"
8581          : group && cur == "\n" ? "n"
8582          : !group || /\s/.test(cur) ? null
8583          : "p"
8584        if (group && !first && !type) { type = "s" }
8585        if (sawType && sawType != type) {
8586          if (dir < 0) {dir = 1; moveOnce(); pos.sticky = "after"}
8587          break
8588        }
8589  
8590        if (type) { sawType = type }
8591        if (dir > 0 && !moveOnce(!first)) { break }
8592      }
8593    }
8594    var result = skipAtomic(doc, pos, oldPos, origDir, true)
8595    if (equalCursorPos(oldPos, result)) { result.hitSide = true }
8596    return result
8597  }
8598  
8599  // For relative vertical movement. Dir may be -1 or 1. Unit can be
8600  // "page" or "line". The resulting position will have a hitSide=true
8601  // property if it reached the end of the document.
8602  function findPosV(cm, pos, dir, unit) {
8603    var doc = cm.doc, x = pos.left, y
8604    if (unit == "page") {
8605      var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight)
8606      var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3)
8607      y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount
8608  
8609    } else if (unit == "line") {
8610      y = dir > 0 ? pos.bottom + 3 : pos.top - 3
8611    }
8612    var target
8613    for (;;) {
8614      target = coordsChar(cm, x, y)
8615      if (!target.outside) { break }
8616      if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break }
8617      y += dir * 5
8618    }
8619    return target
8620  }
8621  
8622  // CONTENTEDITABLE INPUT STYLE
8623  
8624  var ContentEditableInput = function(cm) {
8625    this.cm = cm
8626    this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null
8627    this.polling = new Delayed()
8628    this.composing = null
8629    this.gracePeriod = false
8630    this.readDOMTimeout = null
8631  };
8632  
8633  ContentEditableInput.prototype.init = function (display) {
8634      var this$1 = this;
8635  
8636    var input = this, cm = input.cm
8637    var div = input.div = display.lineDiv
8638    disableBrowserMagic(div, cm.options.spellcheck)
8639  
8640    on(div, "paste", function (e) {
8641      if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
8642      // IE doesn't fire input events, so we schedule a read for the pasted content in this way
8643      if (ie_version <= 11) { setTimeout(operation(cm, function () { return this$1.updateFromDOM(); }), 20) }
8644    })
8645  
8646    on(div, "compositionstart", function (e) {
8647      this$1.composing = {data: e.data, done: false}
8648    })
8649    on(div, "compositionupdate", function (e) {
8650      if (!this$1.composing) { this$1.composing = {data: e.data, done: false} }
8651    })
8652    on(div, "compositionend", function (e) {
8653      if (this$1.composing) {
8654        if (e.data != this$1.composing.data) { this$1.readFromDOMSoon() }
8655        this$1.composing.done = true
8656      }
8657    })
8658  
8659    on(div, "touchstart", function () { return input.forceCompositionEnd(); })
8660  
8661    on(div, "input", function () {
8662      if (!this$1.composing) { this$1.readFromDOMSoon() }
8663    })
8664  
8665    function onCopyCut(e) {
8666      if (signalDOMEvent(cm, e)) { return }
8667      if (cm.somethingSelected()) {
8668        setLastCopied({lineWise: false, text: cm.getSelections()})
8669        if (e.type == "cut") { cm.replaceSelection("", null, "cut") }
8670      } else if (!cm.options.lineWiseCopyCut) {
8671        return
8672      } else {
8673        var ranges = copyableRanges(cm)
8674        setLastCopied({lineWise: true, text: ranges.text})
8675        if (e.type == "cut") {
8676          cm.operation(function () {
8677            cm.setSelections(ranges.ranges, 0, sel_dontScroll)
8678            cm.replaceSelection("", null, "cut")
8679          })
8680        }
8681      }
8682      if (e.clipboardData) {
8683        e.clipboardData.clearData()
8684        var content = lastCopied.text.join("\n")
8685        // iOS exposes the clipboard API, but seems to discard content inserted into it
8686        e.clipboardData.setData("Text", content)
8687        if (e.clipboardData.getData("Text") == content) {
8688          e.preventDefault()
8689          return
8690        }
8691      }
8692      // Old-fashioned briefly-focus-a-textarea hack
8693      var kludge = hiddenTextarea(), te = kludge.firstChild
8694      cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild)
8695      te.value = lastCopied.text.join("\n")
8696      var hadFocus = document.activeElement
8697      selectInput(te)
8698      setTimeout(function () {
8699        cm.display.lineSpace.removeChild(kludge)
8700        hadFocus.focus()
8701        if (hadFocus == div) { input.showPrimarySelection() }
8702      }, 50)
8703    }
8704    on(div, "copy", onCopyCut)
8705    on(div, "cut", onCopyCut)
8706  };
8707  
8708  ContentEditableInput.prototype.prepareSelection = function () {
8709    var result = prepareSelection(this.cm, false)
8710    result.focus = this.cm.state.focused
8711    return result
8712  };
8713  
8714  ContentEditableInput.prototype.showSelection = function (info, takeFocus) {
8715    if (!info || !this.cm.display.view.length) { return }
8716    if (info.focus || takeFocus) { this.showPrimarySelection() }
8717    this.showMultipleSelections(info)
8718  };
8719  
8720  ContentEditableInput.prototype.showPrimarySelection = function () {
8721    var sel = window.getSelection(), cm = this.cm, prim = cm.doc.sel.primary()
8722    var from = prim.from(), to = prim.to()
8723  
8724    if (cm.display.viewTo == cm.display.viewFrom || from.line >= cm.display.viewTo || to.line < cm.display.viewFrom) {
8725      sel.removeAllRanges()
8726      return
8727    }
8728  
8729    var curAnchor = domToPos(cm, sel.anchorNode, sel.anchorOffset)
8730    var curFocus = domToPos(cm, sel.focusNode, sel.focusOffset)
8731    if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad &&
8732        cmp(minPos(curAnchor, curFocus), from) == 0 &&
8733        cmp(maxPos(curAnchor, curFocus), to) == 0)
8734      { return }
8735  
8736    var view = cm.display.view
8737    var start = (from.line >= cm.display.viewFrom && posToDOM(cm, from)) ||
8738        {node: view[0].measure.map[2], offset: 0}
8739    var end = to.line < cm.display.viewTo && posToDOM(cm, to)
8740    if (!end) {
8741      var measure = view[view.length - 1].measure
8742      var map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map
8743      end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map.length - 3]}
8744    }
8745  
8746    if (!start || !end) {
8747      sel.removeAllRanges()
8748      return
8749    }
8750  
8751    var old = sel.rangeCount && sel.getRangeAt(0), rng
8752    try { rng = range(start.node, start.offset, end.offset, end.node) }
8753    catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible
8754    if (rng) {
8755      if (!gecko && cm.state.focused) {
8756        sel.collapse(start.node, start.offset)
8757        if (!rng.collapsed) {
8758          sel.removeAllRanges()
8759          sel.addRange(rng)
8760        }
8761      } else {
8762        sel.removeAllRanges()
8763        sel.addRange(rng)
8764      }
8765      if (old && sel.anchorNode == null) { sel.addRange(old) }
8766      else if (gecko) { this.startGracePeriod() }
8767    }
8768    this.rememberSelection()
8769  };
8770  
8771  ContentEditableInput.prototype.startGracePeriod = function () {
8772      var this$1 = this;
8773  
8774    clearTimeout(this.gracePeriod)
8775    this.gracePeriod = setTimeout(function () {
8776      this$1.gracePeriod = false
8777      if (this$1.selectionChanged())
8778        { this$1.cm.operation(function () { return this$1.cm.curOp.selectionChanged = true; }) }
8779    }, 20)
8780  };
8781  
8782  ContentEditableInput.prototype.showMultipleSelections = function (info) {
8783    removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors)
8784    removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection)
8785  };
8786  
8787  ContentEditableInput.prototype.rememberSelection = function () {
8788    var sel = window.getSelection()
8789    this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset
8790    this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset
8791  };
8792  
8793  ContentEditableInput.prototype.selectionInEditor = function () {
8794    var sel = window.getSelection()
8795    if (!sel.rangeCount) { return false }
8796    var node = sel.getRangeAt(0).commonAncestorContainer
8797    return contains(this.div, node)
8798  };
8799  
8800  ContentEditableInput.prototype.focus = function () {
8801    if (this.cm.options.readOnly != "nocursor") {
8802      if (!this.selectionInEditor())
8803        { this.showSelection(this.prepareSelection(), true) }
8804      this.div.focus()
8805    }
8806  };
8807  ContentEditableInput.prototype.blur = function () { this.div.blur() };
8808  ContentEditableInput.prototype.getField = function () { return this.div };
8809  
8810  ContentEditableInput.prototype.supportsTouch = function () { return true };
8811  
8812  ContentEditableInput.prototype.receivedFocus = function () {
8813    var input = this
8814    if (this.selectionInEditor())
8815      { this.pollSelection() }
8816    else
8817      { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }) }
8818  
8819    function poll() {
8820      if (input.cm.state.focused) {
8821        input.pollSelection()
8822        input.polling.set(input.cm.options.pollInterval, poll)
8823      }
8824    }
8825    this.polling.set(this.cm.options.pollInterval, poll)
8826  };
8827  
8828  ContentEditableInput.prototype.selectionChanged = function () {
8829    var sel = window.getSelection()
8830    return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||
8831      sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset
8832  };
8833  
8834  ContentEditableInput.prototype.pollSelection = function () {
8835    if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) { return }
8836    var sel = window.getSelection(), cm = this.cm
8837    // On Android Chrome (version 56, at least), backspacing into an
8838    // uneditable block element will put the cursor in that element,
8839    // and then, because it's not editable, hide the virtual keyboard.
8840    // Because Android doesn't allow us to actually detect backspace
8841    // presses in a sane way, this code checks for when that happens
8842    // and simulates a backspace press in this case.
8843    if (android && chrome && this.cm.options.gutters.length && isInGutter(sel.anchorNode)) {
8844      this.cm.triggerOnKeyDown({type: "keydown", keyCode: 8, preventDefault: Math.abs})
8845      this.blur()
8846      this.focus()
8847      return
8848    }
8849    if (this.composing) { return }
8850    this.rememberSelection()
8851    var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset)
8852    var head = domToPos(cm, sel.focusNode, sel.focusOffset)
8853    if (anchor && head) { runInOp(cm, function () {
8854      setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll)
8855      if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true }
8856    }) }
8857  };
8858  
8859  ContentEditableInput.prototype.pollContent = function () {
8860    if (this.readDOMTimeout != null) {
8861      clearTimeout(this.readDOMTimeout)
8862      this.readDOMTimeout = null
8863    }
8864  
8865    var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary()
8866    var from = sel.from(), to = sel.to()
8867    if (from.ch == 0 && from.line > cm.firstLine())
8868      { from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length) }
8869    if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine())
8870      { to = Pos(to.line + 1, 0) }
8871    if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return false }
8872  
8873    var fromIndex, fromLine, fromNode
8874    if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) {
8875      fromLine = lineNo(display.view[0].line)
8876      fromNode = display.view[0].node
8877    } else {
8878      fromLine = lineNo(display.view[fromIndex].line)
8879      fromNode = display.view[fromIndex - 1].node.nextSibling
8880    }
8881    var toIndex = findViewIndex(cm, to.line)
8882    var toLine, toNode
8883    if (toIndex == display.view.length - 1) {
8884      toLine = display.viewTo - 1
8885      toNode = display.lineDiv.lastChild
8886    } else {
8887      toLine = lineNo(display.view[toIndex + 1].line) - 1
8888      toNode = display.view[toIndex + 1].node.previousSibling
8889    }
8890  
8891    if (!fromNode) { return false }
8892    var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine))
8893    var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length))
8894    while (newText.length > 1 && oldText.length > 1) {
8895      if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine-- }
8896      else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++ }
8897      else { break }
8898    }
8899  
8900    var cutFront = 0, cutEnd = 0
8901    var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length)
8902    while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront))
8903      { ++cutFront }
8904    var newBot = lst(newText), oldBot = lst(oldText)
8905    var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0),
8906                             oldBot.length - (oldText.length == 1 ? cutFront : 0))
8907    while (cutEnd < maxCutEnd &&
8908           newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1))
8909      { ++cutEnd }
8910    // Try to move start of change to start of selection if ambiguous
8911    if (newText.length == 1 && oldText.length == 1 && fromLine == from.line) {
8912      while (cutFront && cutFront > from.ch &&
8913             newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) {
8914        cutFront--
8915        cutEnd++
8916      }
8917    }
8918  
8919    newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, "")
8920    newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, "")
8921  
8922    var chFrom = Pos(fromLine, cutFront)
8923    var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0)
8924    if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {
8925      replaceRange(cm.doc, newText, chFrom, chTo, "+input")
8926      return true
8927    }
8928  };
8929  
8930  ContentEditableInput.prototype.ensurePolled = function () {
8931    this.forceCompositionEnd()
8932  };
8933  ContentEditableInput.prototype.reset = function () {
8934    this.forceCompositionEnd()
8935  };
8936  ContentEditableInput.prototype.forceCompositionEnd = function () {
8937    if (!this.composing) { return }
8938    clearTimeout(this.readDOMTimeout)
8939    this.composing = null
8940    this.updateFromDOM()
8941    this.div.blur()
8942    this.div.focus()
8943  };
8944  ContentEditableInput.prototype.readFromDOMSoon = function () {
8945      var this$1 = this;
8946  
8947    if (this.readDOMTimeout != null) { return }
8948    this.readDOMTimeout = setTimeout(function () {
8949      this$1.readDOMTimeout = null
8950      if (this$1.composing) {
8951        if (this$1.composing.done) { this$1.composing = null }
8952        else { return }
8953      }
8954      this$1.updateFromDOM()
8955    }, 80)
8956  };
8957  
8958  ContentEditableInput.prototype.updateFromDOM = function () {
8959      var this$1 = this;
8960  
8961    if (this.cm.isReadOnly() || !this.pollContent())
8962      { runInOp(this.cm, function () { return regChange(this$1.cm); }) }
8963  };
8964  
8965  ContentEditableInput.prototype.setUneditable = function (node) {
8966    node.contentEditable = "false"
8967  };
8968  
8969  ContentEditableInput.prototype.onKeyPress = function (e) {
8970    if (e.charCode == 0) { return }
8971    e.preventDefault()
8972    if (!this.cm.isReadOnly())
8973      { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0) }
8974  };
8975  
8976  ContentEditableInput.prototype.readOnlyChanged = function (val) {
8977    this.div.contentEditable = String(val != "nocursor")
8978  };
8979  
8980  ContentEditableInput.prototype.onContextMenu = function () {};
8981  ContentEditableInput.prototype.resetPosition = function () {};
8982  
8983  ContentEditableInput.prototype.needsContentAttribute = true
8984  
8985  function posToDOM(cm, pos) {
8986    var view = findViewForLine(cm, pos.line)
8987    if (!view || view.hidden) { return null }
8988    var line = getLine(cm.doc, pos.line)
8989    var info = mapFromLineView(view, line, pos.line)
8990  
8991    var order = getOrder(line, cm.doc.direction), side = "left"
8992    if (order) {
8993      var partPos = getBidiPartAt(order, pos.ch)
8994      side = partPos % 2 ? "right" : "left"
8995    }
8996    var result = nodeAndOffsetInLineMap(info.map, pos.ch, side)
8997    result.offset = result.collapse == "right" ? result.end : result.start
8998    return result
8999  }
9000  
9001  function isInGutter(node) {
9002    for (var scan = node; scan; scan = scan.parentNode)
9003      { if (/CodeMirror-gutter-wrapper/.test(scan.className)) { return true } }
9004    return false
9005  }
9006  
9007  function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos }
9008  
9009  function domTextBetween(cm, from, to, fromLine, toLine) {
9010    var text = "", closing = false, lineSep = cm.doc.lineSeparator()
9011    function recognizeMarker(id) { return function (marker) { return marker.id == id; } }
9012    function close() {
9013      if (closing) {
9014        text += lineSep
9015        closing = false
9016      }
9017    }
9018    function addText(str) {
9019      if (str) {
9020        close()
9021        text += str
9022      }
9023    }
9024    function walk(node) {
9025      if (node.nodeType == 1) {
9026        var cmText = node.getAttribute("cm-text")
9027        if (cmText != null) {
9028          addText(cmText || node.textContent.replace(/\u200b/g, ""))
9029          return
9030        }
9031        var markerID = node.getAttribute("cm-marker"), range
9032        if (markerID) {
9033          var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID))
9034          if (found.length && (range = found[0].find(0)))
9035            { addText(getBetween(cm.doc, range.from, range.to).join(lineSep)) }
9036          return
9037        }
9038        if (node.getAttribute("contenteditable") == "false") { return }
9039        var isBlock = /^(pre|div|p)$/i.test(node.nodeName)
9040        if (isBlock) { close() }
9041        for (var i = 0; i < node.childNodes.length; i++)
9042          { walk(node.childNodes[i]) }
9043        if (isBlock) { closing = true }
9044      } else if (node.nodeType == 3) {
9045        addText(node.nodeValue)
9046      }
9047    }
9048    for (;;) {
9049      walk(from)
9050      if (from == to) { break }
9051      from = from.nextSibling
9052    }
9053    return text
9054  }
9055  
9056  function domToPos(cm, node, offset) {
9057    var lineNode
9058    if (node == cm.display.lineDiv) {
9059      lineNode = cm.display.lineDiv.childNodes[offset]
9060      if (!lineNode) { return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) }
9061      node = null; offset = 0
9062    } else {
9063      for (lineNode = node;; lineNode = lineNode.parentNode) {
9064        if (!lineNode || lineNode == cm.display.lineDiv) { return null }
9065        if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) { break }
9066      }
9067    }
9068    for (var i = 0; i < cm.display.view.length; i++) {
9069      var lineView = cm.display.view[i]
9070      if (lineView.node == lineNode)
9071        { return locateNodeInLineView(lineView, node, offset) }
9072    }
9073  }
9074  
9075  function locateNodeInLineView(lineView, node, offset) {
9076    var wrapper = lineView.text.firstChild, bad = false
9077    if (!node || !contains(wrapper, node)) { return badPos(Pos(lineNo(lineView.line), 0), true) }
9078    if (node == wrapper) {
9079      bad = true
9080      node = wrapper.childNodes[offset]
9081      offset = 0
9082      if (!node) {
9083        var line = lineView.rest ? lst(lineView.rest) : lineView.line
9084        return badPos(Pos(lineNo(line), line.text.length), bad)
9085      }
9086    }
9087  
9088    var textNode = node.nodeType == 3 ? node : null, topNode = node
9089    if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {
9090      textNode = node.firstChild
9091      if (offset) { offset = textNode.nodeValue.length }
9092    }
9093    while (topNode.parentNode != wrapper) { topNode = topNode.parentNode }
9094    var measure = lineView.measure, maps = measure.maps
9095  
9096    function find(textNode, topNode, offset) {
9097      for (var i = -1; i < (maps ? maps.length : 0); i++) {
9098        var map = i < 0 ? measure.map : maps[i]
9099        for (var j = 0; j < map.length; j += 3) {
9100          var curNode = map[j + 2]
9101          if (curNode == textNode || curNode == topNode) {
9102            var line = lineNo(i < 0 ? lineView.line : lineView.rest[i])
9103            var ch = map[j] + offset
9104            if (offset < 0 || curNode != textNode) { ch = map[j + (offset ? 1 : 0)] }
9105            return Pos(line, ch)
9106          }
9107        }
9108      }
9109    }
9110    var found = find(textNode, topNode, offset)
9111    if (found) { return badPos(found, bad) }
9112  
9113    // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems
9114    for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) {
9115      found = find(after, after.firstChild, 0)
9116      if (found)
9117        { return badPos(Pos(found.line, found.ch - dist), bad) }
9118      else
9119        { dist += after.textContent.length }
9120    }
9121    for (var before = topNode.previousSibling, dist$1 = offset; before; before = before.previousSibling) {
9122      found = find(before, before.firstChild, -1)
9123      if (found)
9124        { return badPos(Pos(found.line, found.ch + dist$1), bad) }
9125      else
9126        { dist$1 += before.textContent.length }
9127    }
9128  }
9129  
9130  // TEXTAREA INPUT STYLE
9131  
9132  var TextareaInput = function(cm) {
9133    this.cm = cm
9134    // See input.poll and input.reset
9135    this.prevInput = ""
9136  
9137    // Flag that indicates whether we expect input to appear real soon
9138    // now (after some event like 'keypress' or 'input') and are
9139    // polling intensively.
9140    this.pollingFast = false
9141    // Self-resetting timeout for the poller
9142    this.polling = new Delayed()
9143    // Used to work around IE issue with selection being forgotten when focus moves away from textarea
9144    this.hasSelection = false
9145    this.composing = null
9146  };
9147  
9148  TextareaInput.prototype.init = function (display) {
9149      var this$1 = this;
9150  
9151    var input = this, cm = this.cm
9152  
9153    // Wraps and hides input textarea
9154    var div = this.wrapper = hiddenTextarea()
9155    // The semihidden textarea that is focused when the editor is
9156    // focused, and receives input.
9157    var te = this.textarea = div.firstChild
9158    display.wrapper.insertBefore(div, display.wrapper.firstChild)
9159  
9160    // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)
9161    if (ios) { te.style.width = "0px" }
9162  
9163    on(te, "input", function () {
9164      if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = null }
9165      input.poll()
9166    })
9167  
9168    on(te, "paste", function (e) {
9169      if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
9170  
9171      cm.state.pasteIncoming = true
9172      input.fastPoll()
9173    })
9174  
9175    function prepareCopyCut(e) {
9176      if (signalDOMEvent(cm, e)) { return }
9177      if (cm.somethingSelected()) {
9178        setLastCopied({lineWise: false, text: cm.getSelections()})
9179      } else if (!cm.options.lineWiseCopyCut) {
9180        return
9181      } else {
9182        var ranges = copyableRanges(cm)
9183        setLastCopied({lineWise: true, text: ranges.text})
9184        if (e.type == "cut") {
9185          cm.setSelections(ranges.ranges, null, sel_dontScroll)
9186        } else {
9187          input.prevInput = ""
9188          te.value = ranges.text.join("\n")
9189          selectInput(te)
9190        }
9191      }
9192      if (e.type == "cut") { cm.state.cutIncoming = true }
9193    }
9194    on(te, "cut", prepareCopyCut)
9195    on(te, "copy", prepareCopyCut)
9196  
9197    on(display.scroller, "paste", function (e) {
9198      if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return }
9199      cm.state.pasteIncoming = true
9200      input.focus()
9201    })
9202  
9203    // Prevent normal selection in the editor (we handle our own)
9204    on(display.lineSpace, "selectstart", function (e) {
9205      if (!eventInWidget(display, e)) { e_preventDefault(e) }
9206    })
9207  
9208    on(te, "compositionstart", function () {
9209      var start = cm.getCursor("from")
9210      if (input.composing) { input.composing.range.clear() }
9211      input.composing = {
9212        start: start,
9213        range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"})
9214      }
9215    })
9216    on(te, "compositionend", function () {
9217      if (input.composing) {
9218        input.poll()
9219        input.composing.range.clear()
9220        input.composing = null
9221      }
9222    })
9223  };
9224  
9225  TextareaInput.prototype.prepareSelection = function () {
9226    // Redraw the selection and/or cursor
9227    var cm = this.cm, display = cm.display, doc = cm.doc
9228    var result = prepareSelection(cm)
9229  
9230    // Move the hidden textarea near the cursor to prevent scrolling artifacts
9231    if (cm.options.moveInputWithCursor) {
9232      var headPos = cursorCoords(cm, doc.sel.primary().head, "div")
9233      var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect()
9234      result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
9235                                          headPos.top + lineOff.top - wrapOff.top))
9236      result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
9237                                           headPos.left + lineOff.left - wrapOff.left))
9238    }
9239  
9240    return result
9241  };
9242  
9243  TextareaInput.prototype.showSelection = function (drawn) {
9244    var cm = this.cm, display = cm.display
9245    removeChildrenAndAdd(display.cursorDiv, drawn.cursors)
9246    removeChildrenAndAdd(display.selectionDiv, drawn.selection)
9247    if (drawn.teTop != null) {
9248      this.wrapper.style.top = drawn.teTop + "px"
9249      this.wrapper.style.left = drawn.teLeft + "px"
9250    }
9251  };
9252  
9253  // Reset the input to correspond to the selection (or to be empty,
9254  // when not typing and nothing is selected)
9255  TextareaInput.prototype.reset = function (typing) {
9256    if (this.contextMenuPending || this.composing) { return }
9257    var cm = this.cm
9258    if (cm.somethingSelected()) {
9259      this.prevInput = ""
9260      var content = cm.getSelection()
9261      this.textarea.value = content
9262      if (cm.state.focused) { selectInput(this.textarea) }
9263      if (ie && ie_version >= 9) { this.hasSelection = content }
9264    } else if (!typing) {
9265      this.prevInput = this.textarea.value = ""
9266      if (ie && ie_version >= 9) { this.hasSelection = null }
9267    }
9268  };
9269  
9270  TextareaInput.prototype.getField = function () { return this.textarea };
9271  
9272  TextareaInput.prototype.supportsTouch = function () { return false };
9273  
9274  TextareaInput.prototype.focus = function () {
9275    if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) {
9276      try { this.textarea.focus() }
9277      catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM
9278    }
9279  };
9280  
9281  TextareaInput.prototype.blur = function () { this.textarea.blur() };
9282  
9283  TextareaInput.prototype.resetPosition = function () {
9284    this.wrapper.style.top = this.wrapper.style.left = 0
9285  };
9286  
9287  TextareaInput.prototype.receivedFocus = function () { this.slowPoll() };
9288  
9289  // Poll for input changes, using the normal rate of polling. This
9290  // runs as long as the editor is focused.
9291  TextareaInput.prototype.slowPoll = function () {
9292      var this$1 = this;
9293  
9294    if (this.pollingFast) { return }
9295    this.polling.set(this.cm.options.pollInterval, function () {
9296      this$1.poll()
9297      if (this$1.cm.state.focused) { this$1.slowPoll() }
9298    })
9299  };
9300  
9301  // When an event has just come in that is likely to add or change
9302  // something in the input textarea, we poll faster, to ensure that
9303  // the change appears on the screen quickly.
9304  TextareaInput.prototype.fastPoll = function () {
9305    var missed = false, input = this
9306    input.pollingFast = true
9307    function p() {
9308      var changed = input.poll()
9309      if (!changed && !missed) {missed = true; input.polling.set(60, p)}
9310      else {input.pollingFast = false; input.slowPoll()}
9311    }
9312    input.polling.set(20, p)
9313  };
9314  
9315  // Read input from the textarea, and update the document to match.
9316  // When something is selected, it is present in the textarea, and
9317  // selected (unless it is huge, in which case a placeholder is
9318  // used). When nothing is selected, the cursor sits after previously
9319  // seen text (can be empty), which is stored in prevInput (we must
9320  // not reset the textarea when typing, because that breaks IME).
9321  TextareaInput.prototype.poll = function () {
9322      var this$1 = this;
9323  
9324    var cm = this.cm, input = this.textarea, prevInput = this.prevInput
9325    // Since this is called a *lot*, try to bail out as cheaply as
9326    // possible when it is clear that nothing happened. hasSelection
9327    // will be the case when there is a lot of text in the textarea,
9328    // in which case reading its value would be expensive.
9329    if (this.contextMenuPending || !cm.state.focused ||
9330        (hasSelection(input) && !prevInput && !this.composing) ||
9331        cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq)
9332      { return false }
9333  
9334    var text = input.value
9335    // If nothing changed, bail.
9336    if (text == prevInput && !cm.somethingSelected()) { return false }
9337    // Work around nonsensical selection resetting in IE9/10, and
9338    // inexplicable appearance of private area unicode characters on
9339    // some key combos in Mac (#2689).
9340    if (ie && ie_version >= 9 && this.hasSelection === text ||
9341        mac && /[\uf700-\uf7ff]/.test(text)) {
9342      cm.display.input.reset()
9343      return false
9344    }
9345  
9346    if (cm.doc.sel == cm.display.selForContextMenu) {
9347      var first = text.charCodeAt(0)
9348      if (first == 0x200b && !prevInput) { prevInput = "\u200b" }
9349      if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") }
9350    }
9351    // Find the part of the input that is actually new
9352    var same = 0, l = Math.min(prevInput.length, text.length)
9353    while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same }
9354  
9355    runInOp(cm, function () {
9356      applyTextInput(cm, text.slice(same), prevInput.length - same,
9357                     null, this$1.composing ? "*compose" : null)
9358  
9359      // Don't leave long text in the textarea, since it makes further polling slow
9360      if (text.length > 1000 || text.indexOf("\n") > -1) { input.value = this$1.prevInput = "" }
9361      else { this$1.prevInput = text }
9362  
9363      if (this$1.composing) {
9364        this$1.composing.range.clear()
9365        this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor("to"),
9366                                           {className: "CodeMirror-composing"})
9367      }
9368    })
9369    return true
9370  };
9371  
9372  TextareaInput.prototype.ensurePolled = function () {
9373    if (this.pollingFast && this.poll()) { this.pollingFast = false }
9374  };
9375  
9376  TextareaInput.prototype.onKeyPress = function () {
9377    if (ie && ie_version >= 9) { this.hasSelection = null }
9378    this.fastPoll()
9379  };
9380  
9381  TextareaInput.prototype.onContextMenu = function (e) {
9382    var input = this, cm = input.cm, display = cm.display, te = input.textarea
9383    var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop
9384    if (!pos || presto) { return } // Opera is difficult.
9385  
9386    // Reset the current text selection only if the click is done outside of the selection
9387    // and 'resetSelectionOnContextMenu' option is true.
9388    var reset = cm.options.resetSelectionOnContextMenu
9389    if (reset && cm.doc.sel.contains(pos) == -1)
9390      { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll) }
9391  
9392    var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText
9393    input.wrapper.style.cssText = "position: absolute"
9394    var wrapperBox = input.wrapper.getBoundingClientRect()
9395    te.style.cssText = "position: absolute; width: 30px; height: 30px;\n      top: " + (e.clientY - wrapperBox.top - 5) + "px; left: " + (e.clientX - wrapperBox.left - 5) + "px;\n      z-index: 1000; background: " + (ie ? "rgba(255, 255, 255, .05)" : "transparent") + ";\n      outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);"
9396    var oldScrollY
9397    if (webkit) { oldScrollY = window.scrollY } // Work around Chrome issue (#2712)
9398    display.input.focus()
9399    if (webkit) { window.scrollTo(null, oldScrollY) }
9400    display.input.reset()
9401    // Adds "Select all" to context menu in FF
9402    if (!cm.somethingSelected()) { te.value = input.prevInput = " " }
9403    input.contextMenuPending = true
9404    display.selForContextMenu = cm.doc.sel
9405    clearTimeout(display.detectingSelectAll)
9406  
9407    // Select-all will be greyed out if there's nothing to select, so
9408    // this adds a zero-width space so that we can later check whether
9409    // it got selected.
9410    function prepareSelectAllHack() {
9411      if (te.selectionStart != null) {
9412        var selected = cm.somethingSelected()
9413        var extval = "\u200b" + (selected ? te.value : "")
9414        te.value = "\u21da" // Used to catch context-menu undo
9415        te.value = extval
9416        input.prevInput = selected ? "" : "\u200b"
9417        te.selectionStart = 1; te.selectionEnd = extval.length
9418        // Re-set this, in case some other handler touched the
9419        // selection in the meantime.
9420        display.selForContextMenu = cm.doc.sel
9421      }
9422    }
9423    function rehide() {
9424      input.contextMenuPending = false
9425      input.wrapper.style.cssText = oldWrapperCSS
9426      te.style.cssText = oldCSS
9427      if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos) }
9428  
9429      // Try to detect the user choosing select-all
9430      if (te.selectionStart != null) {
9431        if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack() }
9432        var i = 0, poll = function () {
9433          if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 &&
9434              te.selectionEnd > 0 && input.prevInput == "\u200b") {
9435            operation(cm, selectAll)(cm)
9436          } else if (i++ < 10) {
9437            display.detectingSelectAll = setTimeout(poll, 500)
9438          } else {
9439            display.selForContextMenu = null
9440            display.input.reset()
9441          }
9442        }
9443        display.detectingSelectAll = setTimeout(poll, 200)
9444      }
9445    }
9446  
9447    if (ie && ie_version >= 9) { prepareSelectAllHack() }
9448    if (captureRightClick) {
9449      e_stop(e)
9450      var mouseup = function () {
9451        off(window, "mouseup", mouseup)
9452        setTimeout(rehide, 20)
9453      }
9454      on(window, "mouseup", mouseup)
9455    } else {
9456      setTimeout(rehide, 50)
9457    }
9458  };
9459  
9460  TextareaInput.prototype.readOnlyChanged = function (val) {
9461    if (!val) { this.reset() }
9462    this.textarea.disabled = val == "nocursor"
9463  };
9464  
9465  TextareaInput.prototype.setUneditable = function () {};
9466  
9467  TextareaInput.prototype.needsContentAttribute = false
9468  
9469  function fromTextArea(textarea, options) {
9470    options = options ? copyObj(options) : {}
9471    options.value = textarea.value
9472    if (!options.tabindex && textarea.tabIndex)
9473      { options.tabindex = textarea.tabIndex }
9474    if (!options.placeholder && textarea.placeholder)
9475      { options.placeholder = textarea.placeholder }
9476    // Set autofocus to true if this textarea is focused, or if it has
9477    // autofocus and no other element is focused.
9478    if (options.autofocus == null) {
9479      var hasFocus = activeElt()
9480      options.autofocus = hasFocus == textarea ||
9481        textarea.getAttribute("autofocus") != null && hasFocus == document.body
9482    }
9483  
9484    function save() {textarea.value = cm.getValue()}
9485  
9486    var realSubmit
9487    if (textarea.form) {
9488      on(textarea.form, "submit", save)
9489      // Deplorable hack to make the submit method do the right thing.
9490      if (!options.leaveSubmitMethodAlone) {
9491        var form = textarea.form
9492        realSubmit = form.submit
9493        try {
9494          var wrappedSubmit = form.submit = function () {
9495            save()
9496            form.submit = realSubmit
9497            form.submit()
9498            form.submit = wrappedSubmit
9499          }
9500        } catch(e) {}
9501      }
9502    }
9503  
9504    options.finishInit = function (cm) {
9505      cm.save = save
9506      cm.getTextArea = function () { return textarea; }
9507      cm.toTextArea = function () {
9508        cm.toTextArea = isNaN // Prevent this from being ran twice
9509        save()
9510        textarea.parentNode.removeChild(cm.getWrapperElement())
9511        textarea.style.display = ""
9512        if (textarea.form) {
9513          off(textarea.form, "submit", save)
9514          if (typeof textarea.form.submit == "function")
9515            { textarea.form.submit = realSubmit }
9516        }
9517      }
9518    }
9519  
9520    textarea.style.display = "none"
9521    var cm = CodeMirror(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); },
9522      options)
9523    return cm
9524  }
9525  
9526  function addLegacyProps(CodeMirror) {
9527    CodeMirror.off = off
9528    CodeMirror.on = on
9529    CodeMirror.wheelEventPixels = wheelEventPixels
9530    CodeMirror.Doc = Doc
9531    CodeMirror.splitLines = splitLinesAuto
9532    CodeMirror.countColumn = countColumn
9533    CodeMirror.findColumn = findColumn
9534    CodeMirror.isWordChar = isWordCharBasic
9535    CodeMirror.Pass = Pass
9536    CodeMirror.signal = signal
9537    CodeMirror.Line = Line
9538    CodeMirror.changeEnd = changeEnd
9539    CodeMirror.scrollbarModel = scrollbarModel
9540    CodeMirror.Pos = Pos
9541    CodeMirror.cmpPos = cmp
9542    CodeMirror.modes = modes
9543    CodeMirror.mimeModes = mimeModes
9544    CodeMirror.resolveMode = resolveMode
9545    CodeMirror.getMode = getMode
9546    CodeMirror.modeExtensions = modeExtensions
9547    CodeMirror.extendMode = extendMode
9548    CodeMirror.copyState = copyState
9549    CodeMirror.startState = startState
9550    CodeMirror.innerMode = innerMode
9551    CodeMirror.commands = commands
9552    CodeMirror.keyMap = keyMap
9553    CodeMirror.keyName = keyName
9554    CodeMirror.isModifierKey = isModifierKey
9555    CodeMirror.lookupKey = lookupKey
9556    CodeMirror.normalizeKeyMap = normalizeKeyMap
9557    CodeMirror.StringStream = StringStream
9558    CodeMirror.SharedTextMarker = SharedTextMarker
9559    CodeMirror.TextMarker = TextMarker
9560    CodeMirror.LineWidget = LineWidget
9561    CodeMirror.e_preventDefault = e_preventDefault
9562    CodeMirror.e_stopPropagation = e_stopPropagation
9563    CodeMirror.e_stop = e_stop
9564    CodeMirror.addClass = addClass
9565    CodeMirror.contains = contains
9566    CodeMirror.rmClass = rmClass
9567    CodeMirror.keyNames = keyNames
9568  }
9569  
9570  // EDITOR CONSTRUCTOR
9571  
9572  defineOptions(CodeMirror)
9573  
9574  addEditorMethods(CodeMirror)
9575  
9576  // Set up methods on CodeMirror's prototype to redirect to the editor's document.
9577  var dontDelegate = "iter insert remove copy getEditor constructor".split(" ")
9578  for (var prop in Doc.prototype) { if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
9579    { CodeMirror.prototype[prop] = (function(method) {
9580      return function() {return method.apply(this.doc, arguments)}
9581    })(Doc.prototype[prop]) } }
9582  
9583  eventMixin(Doc)
9584  
9585  // INPUT HANDLING
9586  
9587  CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput}
9588  
9589  // MODE DEFINITION AND QUERYING
9590  
9591  // Extra arguments are stored as the mode's dependencies, which is
9592  // used by (legacy) mechanisms like loadmode.js to automatically
9593  // load a mode. (Preferred mechanism is the require/define calls.)
9594  CodeMirror.defineMode = function(name/*, mode, …*/) {
9595    if (!CodeMirror.defaults.mode && name != "null") { CodeMirror.defaults.mode = name }
9596    defineMode.apply(this, arguments)
9597  }
9598  
9599  CodeMirror.defineMIME = defineMIME
9600  
9601  // Minimal default mode.
9602  CodeMirror.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); })
9603  CodeMirror.defineMIME("text/plain", "null")
9604  
9605  // EXTENSIONS
9606  
9607  CodeMirror.defineExtension = function (name, func) {
9608    CodeMirror.prototype[name] = func
9609  }
9610  CodeMirror.defineDocExtension = function (name, func) {
9611    Doc.prototype[name] = func
9612  }
9613  
9614  CodeMirror.fromTextArea = fromTextArea
9615  
9616  addLegacyProps(CodeMirror)
9617  
9618  CodeMirror.version = "5.30.0"
9619  
9620  return CodeMirror;
9621  
9622  })));


2005 - 2021 © MyBB.de | Alle Rechte vorbehalten! | Sponsor: netcup Cross-referenced by PHPXref