openrat-cms

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

codemirror.js (368086B)


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