Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
YUI.add('moodle-atto_html-codemirror', function (Y, NAME) {
2
 
3
var _codeMirror = window.codeMirror;
4
var define = null; // Remove require.js support in this context.
5
// CodeMirror, copyright (c) by Marijn Haverbeke and others
6
// Distributed under an MIT license: https://codemirror.net/5/LICENSE
7
 
8
// This is CodeMirror (https://codemirror.net/5), a code editor
9
// implemented in JavaScript on top of the browser's DOM.
10
//
11
// You can find some technical background for some of the code below
12
// at http://marijnhaverbeke.nl/blog/#cm-internals .
13
 
14
(function (global, factory) {
15
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
16
  typeof define === 'function' && define.amd ? define(factory) :
17
  (global = global || self, global.CodeMirror = factory());
18
}(this, (function () { 'use strict';
19
 
20
  // Kludges for bugs and behavior differences that can't be feature
21
  // detected are enabled based on userAgent etc sniffing.
22
  var userAgent = navigator.userAgent;
23
  var platform = navigator.platform;
24
 
25
  var gecko = /gecko\/\d/i.test(userAgent);
26
  var ie_upto10 = /MSIE \d/.test(userAgent);
27
  var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent);
28
  var edge = /Edge\/(\d+)/.exec(userAgent);
29
  var ie = ie_upto10 || ie_11up || edge;
30
  var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1]);
31
  var webkit = !edge && /WebKit\//.test(userAgent);
32
  var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent);
33
  var chrome = !edge && /Chrome\/(\d+)/.exec(userAgent);
34
  var chrome_version = chrome && +chrome[1];
35
  var presto = /Opera\//.test(userAgent);
36
  var safari = /Apple Computer/.test(navigator.vendor);
37
  var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent);
38
  var phantom = /PhantomJS/.test(userAgent);
39
 
40
  var ios = safari && (/Mobile\/\w+/.test(userAgent) || navigator.maxTouchPoints > 2);
41
  var android = /Android/.test(userAgent);
42
  // This is woefully incomplete. Suggestions for alternative methods welcome.
43
  var mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent);
44
  var mac = ios || /Mac/.test(platform);
45
  var chromeOS = /\bCrOS\b/.test(userAgent);
46
  var windows = /win/i.test(platform);
47
 
48
  var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/);
49
  if (presto_version) { presto_version = Number(presto_version[1]); }
50
  if (presto_version && presto_version >= 15) { presto = false; webkit = true; }
51
  // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
52
  var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11));
53
  var captureRightClick = gecko || (ie && ie_version >= 9);
54
 
55
  function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") }
56
 
57
  var rmClass = function(node, cls) {
58
    var current = node.className;
59
    var match = classTest(cls).exec(current);
60
    if (match) {
61
      var after = current.slice(match.index + match[0].length);
62
      node.className = current.slice(0, match.index) + (after ? match[1] + after : "");
63
    }
64
  };
65
 
66
  function removeChildren(e) {
67
    for (var count = e.childNodes.length; count > 0; --count)
68
      { e.removeChild(e.firstChild); }
69
    return e
70
  }
71
 
72
  function removeChildrenAndAdd(parent, e) {
73
    return removeChildren(parent).appendChild(e)
74
  }
75
 
76
  function elt(tag, content, className, style) {
77
    var e = document.createElement(tag);
78
    if (className) { e.className = className; }
79
    if (style) { e.style.cssText = style; }
80
    if (typeof content == "string") { e.appendChild(document.createTextNode(content)); }
81
    else if (content) { for (var i = 0; i < content.length; ++i) { e.appendChild(content[i]); } }
82
    return e
83
  }
84
  // wrapper for elt, which removes the elt from the accessibility tree
85
  function eltP(tag, content, className, style) {
86
    var e = elt(tag, content, className, style);
87
    e.setAttribute("role", "presentation");
88
    return e
89
  }
90
 
91
  var range;
92
  if (document.createRange) { range = function(node, start, end, endNode) {
93
    var r = document.createRange();
94
    r.setEnd(endNode || node, end);
95
    r.setStart(node, start);
96
    return r
97
  }; }
98
  else { range = function(node, start, end) {
99
    var r = document.body.createTextRange();
100
    try { r.moveToElementText(node.parentNode); }
101
    catch(e) { return r }
102
    r.collapse(true);
103
    r.moveEnd("character", end);
104
    r.moveStart("character", start);
105
    return r
106
  }; }
107
 
108
  function contains(parent, child) {
109
    if (child.nodeType == 3) // Android browser always returns false when child is a textnode
110
      { child = child.parentNode; }
111
    if (parent.contains)
112
      { return parent.contains(child) }
113
    do {
114
      if (child.nodeType == 11) { child = child.host; }
115
      if (child == parent) { return true }
116
    } while (child = child.parentNode)
117
  }
118
 
119
  function activeElt(rootNode) {
120
    // IE and Edge may throw an "Unspecified Error" when accessing document.activeElement.
121
    // IE < 10 will throw when accessed while the page is loading or in an iframe.
122
    // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable.
123
    var doc = rootNode.ownerDocument || rootNode;
124
    var activeElement;
125
    try {
126
      activeElement = rootNode.activeElement;
127
    } catch(e) {
128
      activeElement = doc.body || null;
129
    }
130
    while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement)
131
      { activeElement = activeElement.shadowRoot.activeElement; }
132
    return activeElement
133
  }
134
 
135
  function addClass(node, cls) {
136
    var current = node.className;
137
    if (!classTest(cls).test(current)) { node.className += (current ? " " : "") + cls; }
138
  }
139
  function joinClasses(a, b) {
140
    var as = a.split(" ");
141
    for (var i = 0; i < as.length; i++)
142
      { if (as[i] && !classTest(as[i]).test(b)) { b += " " + as[i]; } }
143
    return b
144
  }
145
 
146
  var selectInput = function(node) { node.select(); };
147
  if (ios) // Mobile Safari apparently has a bug where select() is broken.
148
    { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; }; }
149
  else if (ie) // Suppress mysterious IE10 errors
150
    { selectInput = function(node) { try { node.select(); } catch(_e) {} }; }
151
 
152
  function doc(cm) { return cm.display.wrapper.ownerDocument }
153
 
154
  function root(cm) {
155
    return rootNode(cm.display.wrapper)
156
  }
157
 
158
  function rootNode(element) {
159
    // Detect modern browsers (2017+).
160
    return element.getRootNode ? element.getRootNode() : element.ownerDocument
161
  }
162
 
163
  function win(cm) { return doc(cm).defaultView }
164
 
165
  function bind(f) {
166
    var args = Array.prototype.slice.call(arguments, 1);
167
    return function(){return f.apply(null, args)}
168
  }
169
 
170
  function copyObj(obj, target, overwrite) {
171
    if (!target) { target = {}; }
172
    for (var prop in obj)
173
      { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
174
        { target[prop] = obj[prop]; } }
175
    return target
176
  }
177
 
178
  // Counts the column offset in a string, taking tabs into account.
179
  // Used mostly to find indentation.
180
  function countColumn(string, end, tabSize, startIndex, startValue) {
181
    if (end == null) {
182
      end = string.search(/[^\s\u00a0]/);
183
      if (end == -1) { end = string.length; }
184
    }
185
    for (var i = startIndex || 0, n = startValue || 0;;) {
186
      var nextTab = string.indexOf("\t", i);
187
      if (nextTab < 0 || nextTab >= end)
188
        { return n + (end - i) }
189
      n += nextTab - i;
190
      n += tabSize - (n % tabSize);
191
      i = nextTab + 1;
192
    }
193
  }
194
 
195
  var Delayed = function() {
196
    this.id = null;
197
    this.f = null;
198
    this.time = 0;
199
    this.handler = bind(this.onTimeout, this);
200
  };
201
  Delayed.prototype.onTimeout = function (self) {
202
    self.id = 0;
203
    if (self.time <= +new Date) {
204
      self.f();
205
    } else {
206
      setTimeout(self.handler, self.time - +new Date);
207
    }
208
  };
209
  Delayed.prototype.set = function (ms, f) {
210
    this.f = f;
211
    var time = +new Date + ms;
212
    if (!this.id || time < this.time) {
213
      clearTimeout(this.id);
214
      this.id = setTimeout(this.handler, ms);
215
      this.time = time;
216
    }
217
  };
218
 
219
  function indexOf(array, elt) {
220
    for (var i = 0; i < array.length; ++i)
221
      { if (array[i] == elt) { return i } }
222
    return -1
223
  }
224
 
225
  // Number of pixels added to scroller and sizer to hide scrollbar
226
  var scrollerGap = 50;
227
 
228
  // Returned or thrown by various protocols to signal 'I'm not
229
  // handling this'.
230
  var Pass = {toString: function(){return "CodeMirror.Pass"}};
231
 
232
  // Reused option objects for setSelection & friends
233
  var sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"};
234
 
235
  // The inverse of countColumn -- find the offset that corresponds to
236
  // a particular column.
237
  function findColumn(string, goal, tabSize) {
238
    for (var pos = 0, col = 0;;) {
239
      var nextTab = string.indexOf("\t", pos);
240
      if (nextTab == -1) { nextTab = string.length; }
241
      var skipped = nextTab - pos;
242
      if (nextTab == string.length || col + skipped >= goal)
243
        { return pos + Math.min(skipped, goal - col) }
244
      col += nextTab - pos;
245
      col += tabSize - (col % tabSize);
246
      pos = nextTab + 1;
247
      if (col >= goal) { return pos }
248
    }
249
  }
250
 
251
  var spaceStrs = [""];
252
  function spaceStr(n) {
253
    while (spaceStrs.length <= n)
254
      { spaceStrs.push(lst(spaceStrs) + " "); }
255
    return spaceStrs[n]
256
  }
257
 
258
  function lst(arr) { return arr[arr.length-1] }
259
 
260
  function map(array, f) {
261
    var out = [];
262
    for (var i = 0; i < array.length; i++) { out[i] = f(array[i], i); }
263
    return out
264
  }
265
 
266
  function insertSorted(array, value, score) {
267
    var pos = 0, priority = score(value);
268
    while (pos < array.length && score(array[pos]) <= priority) { pos++; }
269
    array.splice(pos, 0, value);
270
  }
271
 
272
  function nothing() {}
273
 
274
  function createObj(base, props) {
275
    var inst;
276
    if (Object.create) {
277
      inst = Object.create(base);
278
    } else {
279
      nothing.prototype = base;
280
      inst = new nothing();
281
    }
282
    if (props) { copyObj(props, inst); }
283
    return inst
284
  }
285
 
286
  var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
287
  function isWordCharBasic(ch) {
288
    return /\w/.test(ch) || ch > "\x80" &&
289
      (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch))
290
  }
291
  function isWordChar(ch, helper) {
292
    if (!helper) { return isWordCharBasic(ch) }
293
    if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) { return true }
294
    return helper.test(ch)
295
  }
296
 
297
  function isEmpty(obj) {
298
    for (var n in obj) { if (obj.hasOwnProperty(n) && obj[n]) { return false } }
299
    return true
300
  }
301
 
302
  // Extending unicode characters. A series of a non-extending char +
303
  // any number of extending chars is treated as a single unit as far
304
  // as editing and measuring is concerned. This is not fully correct,
305
  // since some scripts/fonts/browsers also treat other configurations
306
  // of code points as a group.
307
  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]/;
308
  function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) }
309
 
310
  // Returns a number from the range [`0`; `str.length`] unless `pos` is outside that range.
311
  function skipExtendingChars(str, pos, dir) {
312
    while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(pos))) { pos += dir; }
313
    return pos
314
  }
315
 
316
  // Returns the value from the range [`from`; `to`] that satisfies
317
  // `pred` and is closest to `from`. Assumes that at least `to`
318
  // satisfies `pred`. Supports `from` being greater than `to`.
319
  function findFirst(pred, from, to) {
320
    // At any point we are certain `to` satisfies `pred`, don't know
321
    // whether `from` does.
322
    var dir = from > to ? -1 : 1;
323
    for (;;) {
324
      if (from == to) { return from }
325
      var midF = (from + to) / 2, mid = dir < 0 ? Math.ceil(midF) : Math.floor(midF);
326
      if (mid == from) { return pred(mid) ? from : to }
327
      if (pred(mid)) { to = mid; }
328
      else { from = mid + dir; }
329
    }
330
  }
331
 
332
  // BIDI HELPERS
333
 
334
  function iterateBidiSections(order, from, to, f) {
335
    if (!order) { return f(from, to, "ltr", 0) }
336
    var found = false;
337
    for (var i = 0; i < order.length; ++i) {
338
      var part = order[i];
339
      if (part.from < to && part.to > from || from == to && part.to == from) {
340
        f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr", i);
341
        found = true;
342
      }
343
    }
344
    if (!found) { f(from, to, "ltr"); }
345
  }
346
 
347
  var bidiOther = null;
348
  function getBidiPartAt(order, ch, sticky) {
349
    var found;
350
    bidiOther = null;
351
    for (var i = 0; i < order.length; ++i) {
352
      var cur = order[i];
353
      if (cur.from < ch && cur.to > ch) { return i }
354
      if (cur.to == ch) {
355
        if (cur.from != cur.to && sticky == "before") { found = i; }
356
        else { bidiOther = i; }
357
      }
358
      if (cur.from == ch) {
359
        if (cur.from != cur.to && sticky != "before") { found = i; }
360
        else { bidiOther = i; }
361
      }
362
    }
363
    return found != null ? found : bidiOther
364
  }
365
 
366
  // Bidirectional ordering algorithm
367
  // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
368
  // that this (partially) implements.
369
 
370
  // One-char codes used for character types:
371
  // L (L):   Left-to-Right
372
  // R (R):   Right-to-Left
373
  // r (AL):  Right-to-Left Arabic
374
  // 1 (EN):  European Number
375
  // + (ES):  European Number Separator
376
  // % (ET):  European Number Terminator
377
  // n (AN):  Arabic Number
378
  // , (CS):  Common Number Separator
379
  // m (NSM): Non-Spacing Mark
380
  // b (BN):  Boundary Neutral
381
  // s (B):   Paragraph Separator
382
  // t (S):   Segment Separator
383
  // w (WS):  Whitespace
384
  // N (ON):  Other Neutrals
385
 
386
  // Returns null if characters are ordered as they appear
387
  // (left-to-right), or an array of sections ({from, to, level}
388
  // objects) in the order in which they occur visually.
389
  var bidiOrdering = (function() {
390
    // Character types for codepoints 0 to 0xff
391
    var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN";
392
    // Character types for codepoints 0x600 to 0x6f9
393
    var arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111";
394
    function charType(code) {
395
      if (code <= 0xf7) { return lowTypes.charAt(code) }
396
      else if (0x590 <= code && code <= 0x5f4) { return "R" }
397
      else if (0x600 <= code && code <= 0x6f9) { return arabicTypes.charAt(code - 0x600) }
398
      else if (0x6ee <= code && code <= 0x8ac) { return "r" }
399
      else if (0x2000 <= code && code <= 0x200b) { return "w" }
400
      else if (code == 0x200c) { return "b" }
401
      else { return "L" }
402
    }
403
 
404
    var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
405
    var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
406
 
407
    function BidiSpan(level, from, to) {
408
      this.level = level;
409
      this.from = from; this.to = to;
410
    }
411
 
412
    return function(str, direction) {
413
      var outerType = direction == "ltr" ? "L" : "R";
414
 
415
      if (str.length == 0 || direction == "ltr" && !bidiRE.test(str)) { return false }
416
      var len = str.length, types = [];
417
      for (var i = 0; i < len; ++i)
418
        { types.push(charType(str.charCodeAt(i))); }
419
 
420
      // W1. Examine each non-spacing mark (NSM) in the level run, and
421
      // change the type of the NSM to the type of the previous
422
      // character. If the NSM is at the start of the level run, it will
423
      // get the type of sor.
424
      for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) {
425
        var type = types[i$1];
426
        if (type == "m") { types[i$1] = prev; }
427
        else { prev = type; }
428
      }
429
 
430
      // W2. Search backwards from each instance of a European number
431
      // until the first strong type (R, L, AL, or sor) is found. If an
432
      // AL is found, change the type of the European number to Arabic
433
      // number.
434
      // W3. Change all ALs to R.
435
      for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) {
436
        var type$1 = types[i$2];
437
        if (type$1 == "1" && cur == "r") { types[i$2] = "n"; }
438
        else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == "r") { types[i$2] = "R"; } }
439
      }
440
 
441
      // W4. A single European separator between two European numbers
442
      // changes to a European number. A single common separator between
443
      // two numbers of the same type changes to that type.
444
      for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) {
445
        var type$2 = types[i$3];
446
        if (type$2 == "+" && prev$1 == "1" && types[i$3+1] == "1") { types[i$3] = "1"; }
447
        else if (type$2 == "," && prev$1 == types[i$3+1] &&
448
                 (prev$1 == "1" || prev$1 == "n")) { types[i$3] = prev$1; }
449
        prev$1 = type$2;
450
      }
451
 
452
      // W5. A sequence of European terminators adjacent to European
453
      // numbers changes to all European numbers.
454
      // W6. Otherwise, separators and terminators change to Other
455
      // Neutral.
456
      for (var i$4 = 0; i$4 < len; ++i$4) {
457
        var type$3 = types[i$4];
458
        if (type$3 == ",") { types[i$4] = "N"; }
459
        else if (type$3 == "%") {
460
          var end = (void 0);
461
          for (end = i$4 + 1; end < len && types[end] == "%"; ++end) {}
462
          var replace = (i$4 && types[i$4-1] == "!") || (end < len && types[end] == "1") ? "1" : "N";
463
          for (var j = i$4; j < end; ++j) { types[j] = replace; }
464
          i$4 = end - 1;
465
        }
466
      }
467
 
468
      // W7. Search backwards from each instance of a European number
469
      // until the first strong type (R, L, or sor) is found. If an L is
470
      // found, then change the type of the European number to L.
471
      for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) {
472
        var type$4 = types[i$5];
473
        if (cur$1 == "L" && type$4 == "1") { types[i$5] = "L"; }
474
        else if (isStrong.test(type$4)) { cur$1 = type$4; }
475
      }
476
 
477
      // N1. A sequence of neutrals takes the direction of the
478
      // surrounding strong text if the text on both sides has the same
479
      // direction. European and Arabic numbers act as if they were R in
480
      // terms of their influence on neutrals. Start-of-level-run (sor)
481
      // and end-of-level-run (eor) are used at level run boundaries.
482
      // N2. Any remaining neutrals take the embedding direction.
483
      for (var i$6 = 0; i$6 < len; ++i$6) {
484
        if (isNeutral.test(types[i$6])) {
485
          var end$1 = (void 0);
486
          for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {}
487
          var before = (i$6 ? types[i$6-1] : outerType) == "L";
488
          var after = (end$1 < len ? types[end$1] : outerType) == "L";
489
          var replace$1 = before == after ? (before ? "L" : "R") : outerType;
490
          for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1; }
491
          i$6 = end$1 - 1;
492
        }
493
      }
494
 
495
      // Here we depart from the documented algorithm, in order to avoid
496
      // building up an actual levels array. Since there are only three
497
      // levels (0, 1, 2) in an implementation that doesn't take
498
      // explicit embedding into account, we can build up the order on
499
      // the fly, without following the level-based algorithm.
500
      var order = [], m;
501
      for (var i$7 = 0; i$7 < len;) {
502
        if (countsAsLeft.test(types[i$7])) {
503
          var start = i$7;
504
          for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {}
505
          order.push(new BidiSpan(0, start, i$7));
506
        } else {
507
          var pos = i$7, at = order.length, isRTL = direction == "rtl" ? 1 : 0;
508
          for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {}
509
          for (var j$2 = pos; j$2 < i$7;) {
510
            if (countsAsNum.test(types[j$2])) {
511
              if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)); at += isRTL; }
512
              var nstart = j$2;
513
              for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {}
514
              order.splice(at, 0, new BidiSpan(2, nstart, j$2));
515
              at += isRTL;
516
              pos = j$2;
517
            } else { ++j$2; }
518
          }
519
          if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)); }
520
        }
521
      }
522
      if (direction == "ltr") {
523
        if (order[0].level == 1 && (m = str.match(/^\s+/))) {
524
          order[0].from = m[0].length;
525
          order.unshift(new BidiSpan(0, 0, m[0].length));
526
        }
527
        if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
528
          lst(order).to -= m[0].length;
529
          order.push(new BidiSpan(0, len - m[0].length, len));
530
        }
531
      }
532
 
533
      return direction == "rtl" ? order.reverse() : order
534
    }
535
  })();
536
 
537
  // Get the bidi ordering for the given line (and cache it). Returns
538
  // false for lines that are fully left-to-right, and an array of
539
  // BidiSpan objects otherwise.
540
  function getOrder(line, direction) {
541
    var order = line.order;
542
    if (order == null) { order = line.order = bidiOrdering(line.text, direction); }
543
    return order
544
  }
545
 
546
  // EVENT HANDLING
547
 
548
  // Lightweight event framework. on/off also work on DOM nodes,
549
  // registering native DOM handlers.
550
 
551
  var noHandlers = [];
552
 
553
  var on = function(emitter, type, f) {
554
    if (emitter.addEventListener) {
555
      emitter.addEventListener(type, f, false);
556
    } else if (emitter.attachEvent) {
557
      emitter.attachEvent("on" + type, f);
558
    } else {
559
      var map = emitter._handlers || (emitter._handlers = {});
560
      map[type] = (map[type] || noHandlers).concat(f);
561
    }
562
  };
563
 
564
  function getHandlers(emitter, type) {
565
    return emitter._handlers && emitter._handlers[type] || noHandlers
566
  }
567
 
568
  function off(emitter, type, f) {
569
    if (emitter.removeEventListener) {
570
      emitter.removeEventListener(type, f, false);
571
    } else if (emitter.detachEvent) {
572
      emitter.detachEvent("on" + type, f);
573
    } else {
574
      var map = emitter._handlers, arr = map && map[type];
575
      if (arr) {
576
        var index = indexOf(arr, f);
577
        if (index > -1)
578
          { map[type] = arr.slice(0, index).concat(arr.slice(index + 1)); }
579
      }
580
    }
581
  }
582
 
583
  function signal(emitter, type /*, values...*/) {
584
    var handlers = getHandlers(emitter, type);
585
    if (!handlers.length) { return }
586
    var args = Array.prototype.slice.call(arguments, 2);
587
    for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args); }
588
  }
589
 
590
  // The DOM events that CodeMirror handles can be overridden by
591
  // registering a (non-DOM) handler on the editor for the event name,
592
  // and preventDefault-ing the event in that handler.
593
  function signalDOMEvent(cm, e, override) {
594
    if (typeof e == "string")
595
      { e = {type: e, preventDefault: function() { this.defaultPrevented = true; }}; }
596
    signal(cm, override || e.type, cm, e);
597
    return e_defaultPrevented(e) || e.codemirrorIgnore
598
  }
599
 
600
  function signalCursorActivity(cm) {
601
    var arr = cm._handlers && cm._handlers.cursorActivity;
602
    if (!arr) { return }
603
    var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []);
604
    for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1)
605
      { set.push(arr[i]); } }
606
  }
607
 
608
  function hasHandler(emitter, type) {
609
    return getHandlers(emitter, type).length > 0
610
  }
611
 
612
  // Add on and off methods to a constructor's prototype, to make
613
  // registering events on such objects more convenient.
614
  function eventMixin(ctor) {
615
    ctor.prototype.on = function(type, f) {on(this, type, f);};
616
    ctor.prototype.off = function(type, f) {off(this, type, f);};
617
  }
618
 
619
  // Due to the fact that we still support jurassic IE versions, some
620
  // compatibility wrappers are needed.
621
 
622
  function e_preventDefault(e) {
623
    if (e.preventDefault) { e.preventDefault(); }
624
    else { e.returnValue = false; }
625
  }
626
  function e_stopPropagation(e) {
627
    if (e.stopPropagation) { e.stopPropagation(); }
628
    else { e.cancelBubble = true; }
629
  }
630
  function e_defaultPrevented(e) {
631
    return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false
632
  }
633
  function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
634
 
635
  function e_target(e) {return e.target || e.srcElement}
636
  function e_button(e) {
637
    var b = e.which;
638
    if (b == null) {
639
      if (e.button & 1) { b = 1; }
640
      else if (e.button & 2) { b = 3; }
641
      else if (e.button & 4) { b = 2; }
642
    }
643
    if (mac && e.ctrlKey && b == 1) { b = 3; }
644
    return b
645
  }
646
 
647
  // Detect drag-and-drop
648
  var dragAndDrop = function() {
649
    // There is *some* kind of drag-and-drop support in IE6-8, but I
650
    // couldn't get it to work yet.
651
    if (ie && ie_version < 9) { return false }
652
    var div = elt('div');
653
    return "draggable" in div || "dragDrop" in div
654
  }();
655
 
656
  var zwspSupported;
657
  function zeroWidthElement(measure) {
658
    if (zwspSupported == null) {
659
      var test = elt("span", "\u200b");
660
      removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]));
661
      if (measure.firstChild.offsetHeight != 0)
662
        { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8); }
663
    }
664
    var node = zwspSupported ? elt("span", "\u200b") :
665
      elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
666
    node.setAttribute("cm-text", "");
667
    return node
668
  }
669
 
670
  // Feature-detect IE's crummy client rect reporting for bidi text
671
  var badBidiRects;
672
  function hasBadBidiRects(measure) {
673
    if (badBidiRects != null) { return badBidiRects }
674
    var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA"));
675
    var r0 = range(txt, 0, 1).getBoundingClientRect();
676
    var r1 = range(txt, 1, 2).getBoundingClientRect();
677
    removeChildren(measure);
678
    if (!r0 || r0.left == r0.right) { return false } // Safari returns null in some cases (#2780)
679
    return badBidiRects = (r1.right - r0.right < 3)
680
  }
681
 
682
  // See if "".split is the broken IE version, if so, provide an
683
  // alternative way to split lines.
684
  var splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function (string) {
685
    var pos = 0, result = [], l = string.length;
686
    while (pos <= l) {
687
      var nl = string.indexOf("\n", pos);
688
      if (nl == -1) { nl = string.length; }
689
      var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
690
      var rt = line.indexOf("\r");
691
      if (rt != -1) {
692
        result.push(line.slice(0, rt));
693
        pos += rt + 1;
694
      } else {
695
        result.push(line);
696
        pos = nl + 1;
697
      }
698
    }
699
    return result
700
  } : function (string) { return string.split(/\r\n?|\n/); };
701
 
702
  var hasSelection = window.getSelection ? function (te) {
703
    try { return te.selectionStart != te.selectionEnd }
704
    catch(e) { return false }
705
  } : function (te) {
706
    var range;
707
    try {range = te.ownerDocument.selection.createRange();}
708
    catch(e) {}
709
    if (!range || range.parentElement() != te) { return false }
710
    return range.compareEndPoints("StartToEnd", range) != 0
711
  };
712
 
713
  var hasCopyEvent = (function () {
714
    var e = elt("div");
715
    if ("oncopy" in e) { return true }
716
    e.setAttribute("oncopy", "return;");
717
    return typeof e.oncopy == "function"
718
  })();
719
 
720
  var badZoomedRects = null;
721
  function hasBadZoomedRects(measure) {
722
    if (badZoomedRects != null) { return badZoomedRects }
723
    var node = removeChildrenAndAdd(measure, elt("span", "x"));
724
    var normal = node.getBoundingClientRect();
725
    var fromRange = range(node, 0, 1).getBoundingClientRect();
726
    return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1
727
  }
728
 
729
  // Known modes, by name and by MIME
730
  var modes = {}, mimeModes = {};
731
 
732
  // Extra arguments are stored as the mode's dependencies, which is
733
  // used by (legacy) mechanisms like loadmode.js to automatically
734
  // load a mode. (Preferred mechanism is the require/define calls.)
735
  function defineMode(name, mode) {
736
    if (arguments.length > 2)
737
      { mode.dependencies = Array.prototype.slice.call(arguments, 2); }
738
    modes[name] = mode;
739
  }
740
 
741
  function defineMIME(mime, spec) {
742
    mimeModes[mime] = spec;
743
  }
744
 
745
  // Given a MIME type, a {name, ...options} config object, or a name
746
  // string, return a mode config object.
747
  function resolveMode(spec) {
748
    if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
749
      spec = mimeModes[spec];
750
    } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
751
      var found = mimeModes[spec.name];
752
      if (typeof found == "string") { found = {name: found}; }
753
      spec = createObj(found, spec);
754
      spec.name = found.name;
755
    } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
756
      return resolveMode("application/xml")
757
    } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) {
758
      return resolveMode("application/json")
759
    }
760
    if (typeof spec == "string") { return {name: spec} }
761
    else { return spec || {name: "null"} }
762
  }
763
 
764
  // Given a mode spec (anything that resolveMode accepts), find and
765
  // initialize an actual mode object.
766
  function getMode(options, spec) {
767
    spec = resolveMode(spec);
768
    var mfactory = modes[spec.name];
769
    if (!mfactory) { return getMode(options, "text/plain") }
770
    var modeObj = mfactory(options, spec);
771
    if (modeExtensions.hasOwnProperty(spec.name)) {
772
      var exts = modeExtensions[spec.name];
773
      for (var prop in exts) {
774
        if (!exts.hasOwnProperty(prop)) { continue }
775
        if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop]; }
776
        modeObj[prop] = exts[prop];
777
      }
778
    }
779
    modeObj.name = spec.name;
780
    if (spec.helperType) { modeObj.helperType = spec.helperType; }
781
    if (spec.modeProps) { for (var prop$1 in spec.modeProps)
782
      { modeObj[prop$1] = spec.modeProps[prop$1]; } }
783
 
784
    return modeObj
785
  }
786
 
787
  // This can be used to attach properties to mode objects from
788
  // outside the actual mode definition.
789
  var modeExtensions = {};
790
  function extendMode(mode, properties) {
791
    var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
792
    copyObj(properties, exts);
793
  }
794
 
795
  function copyState(mode, state) {
796
    if (state === true) { return state }
797
    if (mode.copyState) { return mode.copyState(state) }
798
    var nstate = {};
799
    for (var n in state) {
800
      var val = state[n];
801
      if (val instanceof Array) { val = val.concat([]); }
802
      nstate[n] = val;
803
    }
804
    return nstate
805
  }
806
 
807
  // Given a mode and a state (for that mode), find the inner mode and
808
  // state at the position that the state refers to.
809
  function innerMode(mode, state) {
810
    var info;
811
    while (mode.innerMode) {
812
      info = mode.innerMode(state);
813
      if (!info || info.mode == mode) { break }
814
      state = info.state;
815
      mode = info.mode;
816
    }
817
    return info || {mode: mode, state: state}
818
  }
819
 
820
  function startState(mode, a1, a2) {
821
    return mode.startState ? mode.startState(a1, a2) : true
822
  }
823
 
824
  // STRING STREAM
825
 
826
  // Fed to the mode parsers, provides helper functions to make
827
  // parsers more succinct.
828
 
829
  var StringStream = function(string, tabSize, lineOracle) {
830
    this.pos = this.start = 0;
831
    this.string = string;
832
    this.tabSize = tabSize || 8;
833
    this.lastColumnPos = this.lastColumnValue = 0;
834
    this.lineStart = 0;
835
    this.lineOracle = lineOracle;
836
  };
837
 
838
  StringStream.prototype.eol = function () {return this.pos >= this.string.length};
839
  StringStream.prototype.sol = function () {return this.pos == this.lineStart};
840
  StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined};
841
  StringStream.prototype.next = function () {
842
    if (this.pos < this.string.length)
843
      { return this.string.charAt(this.pos++) }
844
  };
845
  StringStream.prototype.eat = function (match) {
846
    var ch = this.string.charAt(this.pos);
847
    var ok;
848
    if (typeof match == "string") { ok = ch == match; }
849
    else { ok = ch && (match.test ? match.test(ch) : match(ch)); }
850
    if (ok) {++this.pos; return ch}
851
  };
852
  StringStream.prototype.eatWhile = function (match) {
853
    var start = this.pos;
854
    while (this.eat(match)){}
855
    return this.pos > start
856
  };
857
  StringStream.prototype.eatSpace = function () {
858
    var start = this.pos;
859
    while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this.pos; }
860
    return this.pos > start
861
  };
862
  StringStream.prototype.skipToEnd = function () {this.pos = this.string.length;};
863
  StringStream.prototype.skipTo = function (ch) {
864
    var found = this.string.indexOf(ch, this.pos);
865
    if (found > -1) {this.pos = found; return true}
866
  };
867
  StringStream.prototype.backUp = function (n) {this.pos -= n;};
868
  StringStream.prototype.column = function () {
869
    if (this.lastColumnPos < this.start) {
870
      this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
871
      this.lastColumnPos = this.start;
872
    }
873
    return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
874
  };
875
  StringStream.prototype.indentation = function () {
876
    return countColumn(this.string, null, this.tabSize) -
877
      (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
878
  };
879
  StringStream.prototype.match = function (pattern, consume, caseInsensitive) {
880
    if (typeof pattern == "string") {
881
      var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; };
882
      var substr = this.string.substr(this.pos, pattern.length);
883
      if (cased(substr) == cased(pattern)) {
884
        if (consume !== false) { this.pos += pattern.length; }
885
        return true
886
      }
887
    } else {
888
      var match = this.string.slice(this.pos).match(pattern);
889
      if (match && match.index > 0) { return null }
890
      if (match && consume !== false) { this.pos += match[0].length; }
891
      return match
892
    }
893
  };
894
  StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)};
895
  StringStream.prototype.hideFirstChars = function (n, inner) {
896
    this.lineStart += n;
897
    try { return inner() }
898
    finally { this.lineStart -= n; }
899
  };
900
  StringStream.prototype.lookAhead = function (n) {
901
    var oracle = this.lineOracle;
902
    return oracle && oracle.lookAhead(n)
903
  };
904
  StringStream.prototype.baseToken = function () {
905
    var oracle = this.lineOracle;
906
    return oracle && oracle.baseToken(this.pos)
907
  };
908
 
909
  // Find the line object corresponding to the given line number.
910
  function getLine(doc, n) {
911
    n -= doc.first;
912
    if (n < 0 || n >= doc.size) { throw new Error("There is no line " + (n + doc.first) + " in the document.") }
913
    var chunk = doc;
914
    while (!chunk.lines) {
915
      for (var i = 0;; ++i) {
916
        var child = chunk.children[i], sz = child.chunkSize();
917
        if (n < sz) { chunk = child; break }
918
        n -= sz;
919
      }
920
    }
921
    return chunk.lines[n]
922
  }
923
 
924
  // Get the part of a document between two positions, as an array of
925
  // strings.
926
  function getBetween(doc, start, end) {
927
    var out = [], n = start.line;
928
    doc.iter(start.line, end.line + 1, function (line) {
929
      var text = line.text;
930
      if (n == end.line) { text = text.slice(0, end.ch); }
931
      if (n == start.line) { text = text.slice(start.ch); }
932
      out.push(text);
933
      ++n;
934
    });
935
    return out
936
  }
937
  // Get the lines between from and to, as array of strings.
938
  function getLines(doc, from, to) {
939
    var out = [];
940
    doc.iter(from, to, function (line) { out.push(line.text); }); // iter aborts when callback returns truthy value
941
    return out
942
  }
943
 
944
  // Update the height of a line, propagating the height change
945
  // upwards to parent nodes.
946
  function updateLineHeight(line, height) {
947
    var diff = height - line.height;
948
    if (diff) { for (var n = line; n; n = n.parent) { n.height += diff; } }
949
  }
950
 
951
  // Given a line object, find its line number by walking up through
952
  // its parent links.
953
  function lineNo(line) {
954
    if (line.parent == null) { return null }
955
    var cur = line.parent, no = indexOf(cur.lines, line);
956
    for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
957
      for (var i = 0;; ++i) {
958
        if (chunk.children[i] == cur) { break }
959
        no += chunk.children[i].chunkSize();
960
      }
961
    }
962
    return no + cur.first
963
  }
964
 
965
  // Find the line at the given vertical position, using the height
966
  // information in the document tree.
967
  function lineAtHeight(chunk, h) {
968
    var n = chunk.first;
969
    outer: do {
970
      for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) {
971
        var child = chunk.children[i$1], ch = child.height;
972
        if (h < ch) { chunk = child; continue outer }
973
        h -= ch;
974
        n += child.chunkSize();
975
      }
976
      return n
977
    } while (!chunk.lines)
978
    var i = 0;
979
    for (; i < chunk.lines.length; ++i) {
980
      var line = chunk.lines[i], lh = line.height;
981
      if (h < lh) { break }
982
      h -= lh;
983
    }
984
    return n + i
985
  }
986
 
987
  function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size}
988
 
989
  function lineNumberFor(options, i) {
990
    return String(options.lineNumberFormatter(i + options.firstLineNumber))
991
  }
992
 
993
  // A Pos instance represents a position within the text.
994
  function Pos(line, ch, sticky) {
995
    if ( sticky === void 0 ) sticky = null;
996
 
997
    if (!(this instanceof Pos)) { return new Pos(line, ch, sticky) }
998
    this.line = line;
999
    this.ch = ch;
1000
    this.sticky = sticky;
1001
  }
1002
 
1003
  // Compare two positions, return 0 if they are the same, a negative
1004
  // number when a is less, and a positive number otherwise.
1005
  function cmp(a, b) { return a.line - b.line || a.ch - b.ch }
1006
 
1007
  function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 }
1008
 
1009
  function copyPos(x) {return Pos(x.line, x.ch)}
1010
  function maxPos(a, b) { return cmp(a, b) < 0 ? b : a }
1011
  function minPos(a, b) { return cmp(a, b) < 0 ? a : b }
1012
 
1013
  // Most of the external API clips given positions to make sure they
1014
  // actually exist within the document.
1015
  function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))}
1016
  function clipPos(doc, pos) {
1017
    if (pos.line < doc.first) { return Pos(doc.first, 0) }
1018
    var last = doc.first + doc.size - 1;
1019
    if (pos.line > last) { return Pos(last, getLine(doc, last).text.length) }
1020
    return clipToLen(pos, getLine(doc, pos.line).text.length)
1021
  }
1022
  function clipToLen(pos, linelen) {
1023
    var ch = pos.ch;
1024
    if (ch == null || ch > linelen) { return Pos(pos.line, linelen) }
1025
    else if (ch < 0) { return Pos(pos.line, 0) }
1026
    else { return pos }
1027
  }
1028
  function clipPosArray(doc, array) {
1029
    var out = [];
1030
    for (var i = 0; i < array.length; i++) { out[i] = clipPos(doc, array[i]); }
1031
    return out
1032
  }
1033
 
1034
  var SavedContext = function(state, lookAhead) {
1035
    this.state = state;
1036
    this.lookAhead = lookAhead;
1037
  };
1038
 
1039
  var Context = function(doc, state, line, lookAhead) {
1040
    this.state = state;
1041
    this.doc = doc;
1042
    this.line = line;
1043
    this.maxLookAhead = lookAhead || 0;
1044
    this.baseTokens = null;
1045
    this.baseTokenPos = 1;
1046
  };
1047
 
1048
  Context.prototype.lookAhead = function (n) {
1049
    var line = this.doc.getLine(this.line + n);
1050
    if (line != null && n > this.maxLookAhead) { this.maxLookAhead = n; }
1051
    return line
1052
  };
1053
 
1054
  Context.prototype.baseToken = function (n) {
1055
    if (!this.baseTokens) { return null }
1056
    while (this.baseTokens[this.baseTokenPos] <= n)
1057
      { this.baseTokenPos += 2; }
1058
    var type = this.baseTokens[this.baseTokenPos + 1];
1059
    return {type: type && type.replace(/( |^)overlay .*/, ""),
1060
            size: this.baseTokens[this.baseTokenPos] - n}
1061
  };
1062
 
1063
  Context.prototype.nextLine = function () {
1064
    this.line++;
1065
    if (this.maxLookAhead > 0) { this.maxLookAhead--; }
1066
  };
1067
 
1068
  Context.fromSaved = function (doc, saved, line) {
1069
    if (saved instanceof SavedContext)
1070
      { return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead) }
1071
    else
1072
      { return new Context(doc, copyState(doc.mode, saved), line) }
1073
  };
1074
 
1075
  Context.prototype.save = function (copy) {
1076
    var state = copy !== false ? copyState(this.doc.mode, this.state) : this.state;
1077
    return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state
1078
  };
1079
 
1080
 
1081
  // Compute a style array (an array starting with a mode generation
1082
  // -- for invalidation -- followed by pairs of end positions and
1083
  // style strings), which is used to highlight the tokens on the
1084
  // line.
1085
  function highlightLine(cm, line, context, forceToEnd) {
1086
    // A styles array always starts with a number identifying the
1087
    // mode/overlays that it is based on (for easy invalidation).
1088
    var st = [cm.state.modeGen], lineClasses = {};
1089
    // Compute the base array of styles
1090
    runMode(cm, line.text, cm.doc.mode, context, function (end, style) { return st.push(end, style); },
1091
            lineClasses, forceToEnd);
1092
    var state = context.state;
1093
 
1094
    // Run overlays, adjust style array.
1095
    var loop = function ( o ) {
1096
      context.baseTokens = st;
1097
      var overlay = cm.state.overlays[o], i = 1, at = 0;
1098
      context.state = true;
1099
      runMode(cm, line.text, overlay.mode, context, function (end, style) {
1100
        var start = i;
1101
        // Ensure there's a token end at the current position, and that i points at it
1102
        while (at < end) {
1103
          var i_end = st[i];
1104
          if (i_end > end)
1105
            { st.splice(i, 1, end, st[i+1], i_end); }
1106
          i += 2;
1107
          at = Math.min(end, i_end);
1108
        }
1109
        if (!style) { return }
1110
        if (overlay.opaque) {
1111
          st.splice(start, i - start, end, "overlay " + style);
1112
          i = start + 2;
1113
        } else {
1114
          for (; start < i; start += 2) {
1115
            var cur = st[start+1];
1116
            st[start+1] = (cur ? cur + " " : "") + "overlay " + style;
1117
          }
1118
        }
1119
      }, lineClasses);
1120
      context.state = state;
1121
      context.baseTokens = null;
1122
      context.baseTokenPos = 1;
1123
    };
1124
 
1125
    for (var o = 0; o < cm.state.overlays.length; ++o) loop( o );
1126
 
1127
    return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null}
1128
  }
1129
 
1130
  function getLineStyles(cm, line, updateFrontier) {
1131
    if (!line.styles || line.styles[0] != cm.state.modeGen) {
1132
      var context = getContextBefore(cm, lineNo(line));
1133
      var resetState = line.text.length > cm.options.maxHighlightLength && copyState(cm.doc.mode, context.state);
1134
      var result = highlightLine(cm, line, context);
1135
      if (resetState) { context.state = resetState; }
1136
      line.stateAfter = context.save(!resetState);
1137
      line.styles = result.styles;
1138
      if (result.classes) { line.styleClasses = result.classes; }
1139
      else if (line.styleClasses) { line.styleClasses = null; }
1140
      if (updateFrontier === cm.doc.highlightFrontier)
1141
        { cm.doc.modeFrontier = Math.max(cm.doc.modeFrontier, ++cm.doc.highlightFrontier); }
1142
    }
1143
    return line.styles
1144
  }
1145
 
1146
  function getContextBefore(cm, n, precise) {
1147
    var doc = cm.doc, display = cm.display;
1148
    if (!doc.mode.startState) { return new Context(doc, true, n) }
1149
    var start = findStartLine(cm, n, precise);
1150
    var saved = start > doc.first && getLine(doc, start - 1).stateAfter;
1151
    var context = saved ? Context.fromSaved(doc, saved, start) : new Context(doc, startState(doc.mode), start);
1152
 
1153
    doc.iter(start, n, function (line) {
1154
      processLine(cm, line.text, context);
1155
      var pos = context.line;
1156
      line.stateAfter = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo ? context.save() : null;
1157
      context.nextLine();
1158
    });
1159
    if (precise) { doc.modeFrontier = context.line; }
1160
    return context
1161
  }
1162
 
1163
  // Lightweight form of highlight -- proceed over this line and
1164
  // update state, but don't save a style array. Used for lines that
1165
  // aren't currently visible.
1166
  function processLine(cm, text, context, startAt) {
1167
    var mode = cm.doc.mode;
1168
    var stream = new StringStream(text, cm.options.tabSize, context);
1169
    stream.start = stream.pos = startAt || 0;
1170
    if (text == "") { callBlankLine(mode, context.state); }
1171
    while (!stream.eol()) {
1172
      readToken(mode, stream, context.state);
1173
      stream.start = stream.pos;
1174
    }
1175
  }
1176
 
1177
  function callBlankLine(mode, state) {
1178
    if (mode.blankLine) { return mode.blankLine(state) }
1179
    if (!mode.innerMode) { return }
1180
    var inner = innerMode(mode, state);
1181
    if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) }
1182
  }
1183
 
1184
  function readToken(mode, stream, state, inner) {
1185
    for (var i = 0; i < 10; i++) {
1186
      if (inner) { inner[0] = innerMode(mode, state).mode; }
1187
      var style = mode.token(stream, state);
1188
      if (stream.pos > stream.start) { return style }
1189
    }
1190
    throw new Error("Mode " + mode.name + " failed to advance stream.")
1191
  }
1192
 
1193
  var Token = function(stream, type, state) {
1194
    this.start = stream.start; this.end = stream.pos;
1195
    this.string = stream.current();
1196
    this.type = type || null;
1197
    this.state = state;
1198
  };
1199
 
1200
  // Utility for getTokenAt and getLineTokens
1201
  function takeToken(cm, pos, precise, asArray) {
1202
    var doc = cm.doc, mode = doc.mode, style;
1203
    pos = clipPos(doc, pos);
1204
    var line = getLine(doc, pos.line), context = getContextBefore(cm, pos.line, precise);
1205
    var stream = new StringStream(line.text, cm.options.tabSize, context), tokens;
1206
    if (asArray) { tokens = []; }
1207
    while ((asArray || stream.pos < pos.ch) && !stream.eol()) {
1208
      stream.start = stream.pos;
1209
      style = readToken(mode, stream, context.state);
1210
      if (asArray) { tokens.push(new Token(stream, style, copyState(doc.mode, context.state))); }
1211
    }
1212
    return asArray ? tokens : new Token(stream, style, context.state)
1213
  }
1214
 
1215
  function extractLineClasses(type, output) {
1216
    if (type) { for (;;) {
1217
      var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/);
1218
      if (!lineClass) { break }
1219
      type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length);
1220
      var prop = lineClass[1] ? "bgClass" : "textClass";
1221
      if (output[prop] == null)
1222
        { output[prop] = lineClass[2]; }
1223
      else if (!(new RegExp("(?:^|\\s)" + lineClass[2] + "(?:$|\\s)")).test(output[prop]))
1224
        { output[prop] += " " + lineClass[2]; }
1225
    } }
1226
    return type
1227
  }
1228
 
1229
  // Run the given mode's parser over a line, calling f for each token.
1230
  function runMode(cm, text, mode, context, f, lineClasses, forceToEnd) {
1231
    var flattenSpans = mode.flattenSpans;
1232
    if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans; }
1233
    var curStart = 0, curStyle = null;
1234
    var stream = new StringStream(text, cm.options.tabSize, context), style;
1235
    var inner = cm.options.addModeClass && [null];
1236
    if (text == "") { extractLineClasses(callBlankLine(mode, context.state), lineClasses); }
1237
    while (!stream.eol()) {
1238
      if (stream.pos > cm.options.maxHighlightLength) {
1239
        flattenSpans = false;
1240
        if (forceToEnd) { processLine(cm, text, context, stream.pos); }
1241
        stream.pos = text.length;
1242
        style = null;
1243
      } else {
1244
        style = extractLineClasses(readToken(mode, stream, context.state, inner), lineClasses);
1245
      }
1246
      if (inner) {
1247
        var mName = inner[0].name;
1248
        if (mName) { style = "m-" + (style ? mName + " " + style : mName); }
1249
      }
1250
      if (!flattenSpans || curStyle != style) {
1251
        while (curStart < stream.start) {
1252
          curStart = Math.min(stream.start, curStart + 5000);
1253
          f(curStart, curStyle);
1254
        }
1255
        curStyle = style;
1256
      }
1257
      stream.start = stream.pos;
1258
    }
1259
    while (curStart < stream.pos) {
1260
      // Webkit seems to refuse to render text nodes longer than 57444
1261
      // characters, and returns inaccurate measurements in nodes
1262
      // starting around 5000 chars.
1263
      var pos = Math.min(stream.pos, curStart + 5000);
1264
      f(pos, curStyle);
1265
      curStart = pos;
1266
    }
1267
  }
1268
 
1269
  // Finds the line to start with when starting a parse. Tries to
1270
  // find a line with a stateAfter, so that it can start with a
1271
  // valid state. If that fails, it returns the line with the
1272
  // smallest indentation, which tends to need the least context to
1273
  // parse correctly.
1274
  function findStartLine(cm, n, precise) {
1275
    var minindent, minline, doc = cm.doc;
1276
    var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100);
1277
    for (var search = n; search > lim; --search) {
1278
      if (search <= doc.first) { return doc.first }
1279
      var line = getLine(doc, search - 1), after = line.stateAfter;
1280
      if (after && (!precise || search + (after instanceof SavedContext ? after.lookAhead : 0) <= doc.modeFrontier))
1281
        { return search }
1282
      var indented = countColumn(line.text, null, cm.options.tabSize);
1283
      if (minline == null || minindent > indented) {
1284
        minline = search - 1;
1285
        minindent = indented;
1286
      }
1287
    }
1288
    return minline
1289
  }
1290
 
1291
  function retreatFrontier(doc, n) {
1292
    doc.modeFrontier = Math.min(doc.modeFrontier, n);
1293
    if (doc.highlightFrontier < n - 10) { return }
1294
    var start = doc.first;
1295
    for (var line = n - 1; line > start; line--) {
1296
      var saved = getLine(doc, line).stateAfter;
1297
      // change is on 3
1298
      // state on line 1 looked ahead 2 -- so saw 3
1299
      // test 1 + 2 < 3 should cover this
1300
      if (saved && (!(saved instanceof SavedContext) || line + saved.lookAhead < n)) {
1301
        start = line + 1;
1302
        break
1303
      }
1304
    }
1305
    doc.highlightFrontier = Math.min(doc.highlightFrontier, start);
1306
  }
1307
 
1308
  // Optimize some code when these features are not used.
1309
  var sawReadOnlySpans = false, sawCollapsedSpans = false;
1310
 
1311
  function seeReadOnlySpans() {
1312
    sawReadOnlySpans = true;
1313
  }
1314
 
1315
  function seeCollapsedSpans() {
1316
    sawCollapsedSpans = true;
1317
  }
1318
 
1319
  // TEXTMARKER SPANS
1320
 
1321
  function MarkedSpan(marker, from, to) {
1322
    this.marker = marker;
1323
    this.from = from; this.to = to;
1324
  }
1325
 
1326
  // Search an array of spans for a span matching the given marker.
1327
  function getMarkedSpanFor(spans, marker) {
1328
    if (spans) { for (var i = 0; i < spans.length; ++i) {
1329
      var span = spans[i];
1330
      if (span.marker == marker) { return span }
1331
    } }
1332
  }
1333
 
1334
  // Remove a span from an array, returning undefined if no spans are
1335
  // left (we don't store arrays for lines without spans).
1336
  function removeMarkedSpan(spans, span) {
1337
    var r;
1338
    for (var i = 0; i < spans.length; ++i)
1339
      { if (spans[i] != span) { (r || (r = [])).push(spans[i]); } }
1340
    return r
1341
  }
1342
 
1343
  // Add a span to a line.
1344
  function addMarkedSpan(line, span, op) {
1345
    var inThisOp = op && window.WeakSet && (op.markedSpans || (op.markedSpans = new WeakSet));
1346
    if (inThisOp && line.markedSpans && inThisOp.has(line.markedSpans)) {
1347
      line.markedSpans.push(span);
1348
    } else {
1349
      line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
1350
      if (inThisOp) { inThisOp.add(line.markedSpans); }
1351
    }
1352
    span.marker.attachLine(line);
1353
  }
1354
 
1355
  // Used for the algorithm that adjusts markers for a change in the
1356
  // document. These functions cut an array of spans at a given
1357
  // character position, returning an array of remaining chunks (or
1358
  // undefined if nothing remains).
1359
  function markedSpansBefore(old, startCh, isInsert) {
1360
    var nw;
1361
    if (old) { for (var i = 0; i < old.length; ++i) {
1362
      var span = old[i], marker = span.marker;
1363
      var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
1364
      if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) {
1365
        var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh)
1366
        ;(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to));
1367
      }
1368
    } }
1369
    return nw
1370
  }
1371
  function markedSpansAfter(old, endCh, isInsert) {
1372
    var nw;
1373
    if (old) { for (var i = 0; i < old.length; ++i) {
1374
      var span = old[i], marker = span.marker;
1375
      var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
1376
      if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) {
1377
        var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh)
1378
        ;(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh,
1379
                                              span.to == null ? null : span.to - endCh));
1380
      }
1381
    } }
1382
    return nw
1383
  }
1384
 
1385
  // Given a change object, compute the new set of marker spans that
1386
  // cover the line in which the change took place. Removes spans
1387
  // entirely within the change, reconnects spans belonging to the
1388
  // same marker that appear on both sides of the change, and cuts off
1389
  // spans partially within the change. Returns an array of span
1390
  // arrays with one element for each line in (after) the change.
1391
  function stretchSpansOverChange(doc, change) {
1392
    if (change.full) { return null }
1393
    var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans;
1394
    var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans;
1395
    if (!oldFirst && !oldLast) { return null }
1396
 
1397
    var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0;
1398
    // Get the spans that 'stick out' on both sides
1399
    var first = markedSpansBefore(oldFirst, startCh, isInsert);
1400
    var last = markedSpansAfter(oldLast, endCh, isInsert);
1401
 
1402
    // Next, merge those two ends
1403
    var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0);
1404
    if (first) {
1405
      // Fix up .to properties of first
1406
      for (var i = 0; i < first.length; ++i) {
1407
        var span = first[i];
1408
        if (span.to == null) {
1409
          var found = getMarkedSpanFor(last, span.marker);
1410
          if (!found) { span.to = startCh; }
1411
          else if (sameLine) { span.to = found.to == null ? null : found.to + offset; }
1412
        }
1413
      }
1414
    }
1415
    if (last) {
1416
      // Fix up .from in last (or move them into first in case of sameLine)
1417
      for (var i$1 = 0; i$1 < last.length; ++i$1) {
1418
        var span$1 = last[i$1];
1419
        if (span$1.to != null) { span$1.to += offset; }
1420
        if (span$1.from == null) {
1421
          var found$1 = getMarkedSpanFor(first, span$1.marker);
1422
          if (!found$1) {
1423
            span$1.from = offset;
1424
            if (sameLine) { (first || (first = [])).push(span$1); }
1425
          }
1426
        } else {
1427
          span$1.from += offset;
1428
          if (sameLine) { (first || (first = [])).push(span$1); }
1429
        }
1430
      }
1431
    }
1432
    // Make sure we didn't create any zero-length spans
1433
    if (first) { first = clearEmptySpans(first); }
1434
    if (last && last != first) { last = clearEmptySpans(last); }
1435
 
1436
    var newMarkers = [first];
1437
    if (!sameLine) {
1438
      // Fill gap with whole-line-spans
1439
      var gap = change.text.length - 2, gapMarkers;
1440
      if (gap > 0 && first)
1441
        { for (var i$2 = 0; i$2 < first.length; ++i$2)
1442
          { if (first[i$2].to == null)
1443
            { (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i$2].marker, null, null)); } } }
1444
      for (var i$3 = 0; i$3 < gap; ++i$3)
1445
        { newMarkers.push(gapMarkers); }
1446
      newMarkers.push(last);
1447
    }
1448
    return newMarkers
1449
  }
1450
 
1451
  // Remove spans that are empty and don't have a clearWhenEmpty
1452
  // option of false.
1453
  function clearEmptySpans(spans) {
1454
    for (var i = 0; i < spans.length; ++i) {
1455
      var span = spans[i];
1456
      if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)
1457
        { spans.splice(i--, 1); }
1458
    }
1459
    if (!spans.length) { return null }
1460
    return spans
1461
  }
1462
 
1463
  // Used to 'clip' out readOnly ranges when making a change.
1464
  function removeReadOnlyRanges(doc, from, to) {
1465
    var markers = null;
1466
    doc.iter(from.line, to.line + 1, function (line) {
1467
      if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
1468
        var mark = line.markedSpans[i].marker;
1469
        if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
1470
          { (markers || (markers = [])).push(mark); }
1471
      } }
1472
    });
1473
    if (!markers) { return null }
1474
    var parts = [{from: from, to: to}];
1475
    for (var i = 0; i < markers.length; ++i) {
1476
      var mk = markers[i], m = mk.find(0);
1477
      for (var j = 0; j < parts.length; ++j) {
1478
        var p = parts[j];
1479
        if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) { continue }
1480
        var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to);
1481
        if (dfrom < 0 || !mk.inclusiveLeft && !dfrom)
1482
          { newParts.push({from: p.from, to: m.from}); }
1483
        if (dto > 0 || !mk.inclusiveRight && !dto)
1484
          { newParts.push({from: m.to, to: p.to}); }
1485
        parts.splice.apply(parts, newParts);
1486
        j += newParts.length - 3;
1487
      }
1488
    }
1489
    return parts
1490
  }
1491
 
1492
  // Connect or disconnect spans from a line.
1493
  function detachMarkedSpans(line) {
1494
    var spans = line.markedSpans;
1495
    if (!spans) { return }
1496
    for (var i = 0; i < spans.length; ++i)
1497
      { spans[i].marker.detachLine(line); }
1498
    line.markedSpans = null;
1499
  }
1500
  function attachMarkedSpans(line, spans) {
1501
    if (!spans) { return }
1502
    for (var i = 0; i < spans.length; ++i)
1503
      { spans[i].marker.attachLine(line); }
1504
    line.markedSpans = spans;
1505
  }
1506
 
1507
  // Helpers used when computing which overlapping collapsed span
1508
  // counts as the larger one.
1509
  function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 }
1510
  function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 }
1511
 
1512
  // Returns a number indicating which of two overlapping collapsed
1513
  // spans is larger (and thus includes the other). Falls back to
1514
  // comparing ids when the spans cover exactly the same range.
1515
  function compareCollapsedMarkers(a, b) {
1516
    var lenDiff = a.lines.length - b.lines.length;
1517
    if (lenDiff != 0) { return lenDiff }
1518
    var aPos = a.find(), bPos = b.find();
1519
    var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b);
1520
    if (fromCmp) { return -fromCmp }
1521
    var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b);
1522
    if (toCmp) { return toCmp }
1523
    return b.id - a.id
1524
  }
1525
 
1526
  // Find out whether a line ends or starts in a collapsed span. If
1527
  // so, return the marker for that span.
1528
  function collapsedSpanAtSide(line, start) {
1529
    var sps = sawCollapsedSpans && line.markedSpans, found;
1530
    if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
1531
      sp = sps[i];
1532
      if (sp.marker.collapsed && (start ? sp.from : sp.to) == null &&
1533
          (!found || compareCollapsedMarkers(found, sp.marker) < 0))
1534
        { found = sp.marker; }
1535
    } }
1536
    return found
1537
  }
1538
  function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) }
1539
  function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) }
1540
 
1541
  function collapsedSpanAround(line, ch) {
1542
    var sps = sawCollapsedSpans && line.markedSpans, found;
1543
    if (sps) { for (var i = 0; i < sps.length; ++i) {
1544
      var sp = sps[i];
1545
      if (sp.marker.collapsed && (sp.from == null || sp.from < ch) && (sp.to == null || sp.to > ch) &&
1546
          (!found || compareCollapsedMarkers(found, sp.marker) < 0)) { found = sp.marker; }
1547
    } }
1548
    return found
1549
  }
1550
 
1551
  // Test whether there exists a collapsed span that partially
1552
  // overlaps (covers the start or end, but not both) of a new span.
1553
  // Such overlap is not allowed.
1554
  function conflictingCollapsedRange(doc, lineNo, from, to, marker) {
1555
    var line = getLine(doc, lineNo);
1556
    var sps = sawCollapsedSpans && line.markedSpans;
1557
    if (sps) { for (var i = 0; i < sps.length; ++i) {
1558
      var sp = sps[i];
1559
      if (!sp.marker.collapsed) { continue }
1560
      var found = sp.marker.find(0);
1561
      var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker);
1562
      var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker);
1563
      if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue }
1564
      if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) ||
1565
          fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0))
1566
        { return true }
1567
    } }
1568
  }
1569
 
1570
  // A visual line is a line as drawn on the screen. Folding, for
1571
  // example, can cause multiple logical lines to appear on the same
1572
  // visual line. This finds the start of the visual line that the
1573
  // given line is part of (usually that is the line itself).
1574
  function visualLine(line) {
1575
    var merged;
1576
    while (merged = collapsedSpanAtStart(line))
1577
      { line = merged.find(-1, true).line; }
1578
    return line
1579
  }
1580
 
1581
  function visualLineEnd(line) {
1582
    var merged;
1583
    while (merged = collapsedSpanAtEnd(line))
1584
      { line = merged.find(1, true).line; }
1585
    return line
1586
  }
1587
 
1588
  // Returns an array of logical lines that continue the visual line
1589
  // started by the argument, or undefined if there are no such lines.
1590
  function visualLineContinued(line) {
1591
    var merged, lines;
1592
    while (merged = collapsedSpanAtEnd(line)) {
1593
      line = merged.find(1, true).line
1594
      ;(lines || (lines = [])).push(line);
1595
    }
1596
    return lines
1597
  }
1598
 
1599
  // Get the line number of the start of the visual line that the
1600
  // given line number is part of.
1601
  function visualLineNo(doc, lineN) {
1602
    var line = getLine(doc, lineN), vis = visualLine(line);
1603
    if (line == vis) { return lineN }
1604
    return lineNo(vis)
1605
  }
1606
 
1607
  // Get the line number of the start of the next visual line after
1608
  // the given line.
1609
  function visualLineEndNo(doc, lineN) {
1610
    if (lineN > doc.lastLine()) { return lineN }
1611
    var line = getLine(doc, lineN), merged;
1612
    if (!lineIsHidden(doc, line)) { return lineN }
1613
    while (merged = collapsedSpanAtEnd(line))
1614
      { line = merged.find(1, true).line; }
1615
    return lineNo(line) + 1
1616
  }
1617
 
1618
  // Compute whether a line is hidden. Lines count as hidden when they
1619
  // are part of a visual line that starts with another line, or when
1620
  // they are entirely covered by collapsed, non-widget span.
1621
  function lineIsHidden(doc, line) {
1622
    var sps = sawCollapsedSpans && line.markedSpans;
1623
    if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
1624
      sp = sps[i];
1625
      if (!sp.marker.collapsed) { continue }
1626
      if (sp.from == null) { return true }
1627
      if (sp.marker.widgetNode) { continue }
1628
      if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
1629
        { return true }
1630
    } }
1631
  }
1632
  function lineIsHiddenInner(doc, line, span) {
1633
    if (span.to == null) {
1634
      var end = span.marker.find(1, true);
1635
      return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker))
1636
    }
1637
    if (span.marker.inclusiveRight && span.to == line.text.length)
1638
      { return true }
1639
    for (var sp = (void 0), i = 0; i < line.markedSpans.length; ++i) {
1640
      sp = line.markedSpans[i];
1641
      if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to &&
1642
          (sp.to == null || sp.to != span.from) &&
1643
          (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
1644
          lineIsHiddenInner(doc, line, sp)) { return true }
1645
    }
1646
  }
1647
 
1648
  // Find the height above the given line.
1649
  function heightAtLine(lineObj) {
1650
    lineObj = visualLine(lineObj);
1651
 
1652
    var h = 0, chunk = lineObj.parent;
1653
    for (var i = 0; i < chunk.lines.length; ++i) {
1654
      var line = chunk.lines[i];
1655
      if (line == lineObj) { break }
1656
      else { h += line.height; }
1657
    }
1658
    for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
1659
      for (var i$1 = 0; i$1 < p.children.length; ++i$1) {
1660
        var cur = p.children[i$1];
1661
        if (cur == chunk) { break }
1662
        else { h += cur.height; }
1663
      }
1664
    }
1665
    return h
1666
  }
1667
 
1668
  // Compute the character length of a line, taking into account
1669
  // collapsed ranges (see markText) that might hide parts, and join
1670
  // other lines onto it.
1671
  function lineLength(line) {
1672
    if (line.height == 0) { return 0 }
1673
    var len = line.text.length, merged, cur = line;
1674
    while (merged = collapsedSpanAtStart(cur)) {
1675
      var found = merged.find(0, true);
1676
      cur = found.from.line;
1677
      len += found.from.ch - found.to.ch;
1678
    }
1679
    cur = line;
1680
    while (merged = collapsedSpanAtEnd(cur)) {
1681
      var found$1 = merged.find(0, true);
1682
      len -= cur.text.length - found$1.from.ch;
1683
      cur = found$1.to.line;
1684
      len += cur.text.length - found$1.to.ch;
1685
    }
1686
    return len
1687
  }
1688
 
1689
  // Find the longest line in the document.
1690
  function findMaxLine(cm) {
1691
    var d = cm.display, doc = cm.doc;
1692
    d.maxLine = getLine(doc, doc.first);
1693
    d.maxLineLength = lineLength(d.maxLine);
1694
    d.maxLineChanged = true;
1695
    doc.iter(function (line) {
1696
      var len = lineLength(line);
1697
      if (len > d.maxLineLength) {
1698
        d.maxLineLength = len;
1699
        d.maxLine = line;
1700
      }
1701
    });
1702
  }
1703
 
1704
  // LINE DATA STRUCTURE
1705
 
1706
  // Line objects. These hold state related to a line, including
1707
  // highlighting info (the styles array).
1708
  var Line = function(text, markedSpans, estimateHeight) {
1709
    this.text = text;
1710
    attachMarkedSpans(this, markedSpans);
1711
    this.height = estimateHeight ? estimateHeight(this) : 1;
1712
  };
1713
 
1714
  Line.prototype.lineNo = function () { return lineNo(this) };
1715
  eventMixin(Line);
1716
 
1717
  // Change the content (text, markers) of a line. Automatically
1718
  // invalidates cached information and tries to re-estimate the
1719
  // line's height.
1720
  function updateLine(line, text, markedSpans, estimateHeight) {
1721
    line.text = text;
1722
    if (line.stateAfter) { line.stateAfter = null; }
1723
    if (line.styles) { line.styles = null; }
1724
    if (line.order != null) { line.order = null; }
1725
    detachMarkedSpans(line);
1726
    attachMarkedSpans(line, markedSpans);
1727
    var estHeight = estimateHeight ? estimateHeight(line) : 1;
1728
    if (estHeight != line.height) { updateLineHeight(line, estHeight); }
1729
  }
1730
 
1731
  // Detach a line from the document tree and its markers.
1732
  function cleanUpLine(line) {
1733
    line.parent = null;
1734
    detachMarkedSpans(line);
1735
  }
1736
 
1737
  // Convert a style as returned by a mode (either null, or a string
1738
  // containing one or more styles) to a CSS style. This is cached,
1739
  // and also looks for line-wide styles.
1740
  var styleToClassCache = {}, styleToClassCacheWithMode = {};
1741
  function interpretTokenStyle(style, options) {
1742
    if (!style || /^\s*$/.test(style)) { return null }
1743
    var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache;
1744
    return cache[style] ||
1745
      (cache[style] = style.replace(/\S+/g, "cm-$&"))
1746
  }
1747
 
1748
  // Render the DOM representation of the text of a line. Also builds
1749
  // up a 'line map', which points at the DOM nodes that represent
1750
  // specific stretches of text, and is used by the measuring code.
1751
  // The returned object contains the DOM node, this map, and
1752
  // information about line-wide styles that were set by the mode.
1753
  function buildLineContent(cm, lineView) {
1754
    // The padding-right forces the element to have a 'border', which
1755
    // is needed on Webkit to be able to get line-level bounding
1756
    // rectangles for it (in measureChar).
1757
    var content = eltP("span", null, null, webkit ? "padding-right: .1px" : null);
1758
    var builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: content,
1759
                   col: 0, pos: 0, cm: cm,
1760
                   trailingSpace: false,
1761
                   splitSpaces: cm.getOption("lineWrapping")};
1762
    lineView.measure = {};
1763
 
1764
    // Iterate over the logical lines that make up this visual line.
1765
    for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {
1766
      var line = i ? lineView.rest[i - 1] : lineView.line, order = (void 0);
1767
      builder.pos = 0;
1768
      builder.addToken = buildToken;
1769
      // Optionally wire in some hacks into the token-rendering
1770
      // algorithm, to deal with browser quirks.
1771
      if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction)))
1772
        { builder.addToken = buildTokenBadBidi(builder.addToken, order); }
1773
      builder.map = [];
1774
      var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line);
1775
      insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate));
1776
      if (line.styleClasses) {
1777
        if (line.styleClasses.bgClass)
1778
          { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || ""); }
1779
        if (line.styleClasses.textClass)
1780
          { builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || ""); }
1781
      }
1782
 
1783
      // Ensure at least a single node is present, for measuring.
1784
      if (builder.map.length == 0)
1785
        { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))); }
1786
 
1787
      // Store the map and a cache object for the current logical line
1788
      if (i == 0) {
1789
        lineView.measure.map = builder.map;
1790
        lineView.measure.cache = {};
1791
      } else {
1792
  (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map)
1793
        ;(lineView.measure.caches || (lineView.measure.caches = [])).push({});
1794
      }
1795
    }
1796
 
1797
    // See issue #2901
1798
    if (webkit) {
1799
      var last = builder.content.lastChild;
1800
      if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab")))
1801
        { builder.content.className = "cm-tab-wrap-hack"; }
1802
    }
1803
 
1804
    signal(cm, "renderLine", cm, lineView.line, builder.pre);
1805
    if (builder.pre.className)
1806
      { builder.textClass = joinClasses(builder.pre.className, builder.textClass || ""); }
1807
 
1808
    return builder
1809
  }
1810
 
1811
  function defaultSpecialCharPlaceholder(ch) {
1812
    var token = elt("span", "\u2022", "cm-invalidchar");
1813
    token.title = "\\u" + ch.charCodeAt(0).toString(16);
1814
    token.setAttribute("aria-label", token.title);
1815
    return token
1816
  }
1817
 
1818
  // Build up the DOM representation for a single token, and add it to
1819
  // the line map. Takes care to render special characters separately.
1820
  function buildToken(builder, text, style, startStyle, endStyle, css, attributes) {
1821
    if (!text) { return }
1822
    var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text;
1823
    var special = builder.cm.state.specialChars, mustWrap = false;
1824
    var content;
1825
    if (!special.test(text)) {
1826
      builder.col += text.length;
1827
      content = document.createTextNode(displayText);
1828
      builder.map.push(builder.pos, builder.pos + text.length, content);
1829
      if (ie && ie_version < 9) { mustWrap = true; }
1830
      builder.pos += text.length;
1831
    } else {
1832
      content = document.createDocumentFragment();
1833
      var pos = 0;
1834
      while (true) {
1835
        special.lastIndex = pos;
1836
        var m = special.exec(text);
1837
        var skipped = m ? m.index - pos : text.length - pos;
1838
        if (skipped) {
1839
          var txt = document.createTextNode(displayText.slice(pos, pos + skipped));
1840
          if (ie && ie_version < 9) { content.appendChild(elt("span", [txt])); }
1841
          else { content.appendChild(txt); }
1842
          builder.map.push(builder.pos, builder.pos + skipped, txt);
1843
          builder.col += skipped;
1844
          builder.pos += skipped;
1845
        }
1846
        if (!m) { break }
1847
        pos += skipped + 1;
1848
        var txt$1 = (void 0);
1849
        if (m[0] == "\t") {
1850
          var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
1851
          txt$1 = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
1852
          txt$1.setAttribute("role", "presentation");
1853
          txt$1.setAttribute("cm-text", "\t");
1854
          builder.col += tabWidth;
1855
        } else if (m[0] == "\r" || m[0] == "\n") {
1856
          txt$1 = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar"));
1857
          txt$1.setAttribute("cm-text", m[0]);
1858
          builder.col += 1;
1859
        } else {
1860
          txt$1 = builder.cm.options.specialCharPlaceholder(m[0]);
1861
          txt$1.setAttribute("cm-text", m[0]);
1862
          if (ie && ie_version < 9) { content.appendChild(elt("span", [txt$1])); }
1863
          else { content.appendChild(txt$1); }
1864
          builder.col += 1;
1865
        }
1866
        builder.map.push(builder.pos, builder.pos + 1, txt$1);
1867
        builder.pos++;
1868
      }
1869
    }
1870
    builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32;
1871
    if (style || startStyle || endStyle || mustWrap || css || attributes) {
1872
      var fullStyle = style || "";
1873
      if (startStyle) { fullStyle += startStyle; }
1874
      if (endStyle) { fullStyle += endStyle; }
1875
      var token = elt("span", [content], fullStyle, css);
1876
      if (attributes) {
1877
        for (var attr in attributes) { if (attributes.hasOwnProperty(attr) && attr != "style" && attr != "class")
1878
          { token.setAttribute(attr, attributes[attr]); } }
1879
      }
1880
      return builder.content.appendChild(token)
1881
    }
1882
    builder.content.appendChild(content);
1883
  }
1884
 
1885
  // Change some spaces to NBSP to prevent the browser from collapsing
1886
  // trailing spaces at the end of a line when rendering text (issue #1362).
1887
  function splitSpaces(text, trailingBefore) {
1888
    if (text.length > 1 && !/  /.test(text)) { return text }
1889
    var spaceBefore = trailingBefore, result = "";
1890
    for (var i = 0; i < text.length; i++) {
1891
      var ch = text.charAt(i);
1892
      if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32))
1893
        { ch = "\u00a0"; }
1894
      result += ch;
1895
      spaceBefore = ch == " ";
1896
    }
1897
    return result
1898
  }
1899
 
1900
  // Work around nonsense dimensions being reported for stretches of
1901
  // right-to-left text.
1902
  function buildTokenBadBidi(inner, order) {
1903
    return function (builder, text, style, startStyle, endStyle, css, attributes) {
1904
      style = style ? style + " cm-force-border" : "cm-force-border";
1905
      var start = builder.pos, end = start + text.length;
1906
      for (;;) {
1907
        // Find the part that overlaps with the start of this text
1908
        var part = (void 0);
1909
        for (var i = 0; i < order.length; i++) {
1910
          part = order[i];
1911
          if (part.to > start && part.from <= start) { break }
1912
        }
1913
        if (part.to >= end) { return inner(builder, text, style, startStyle, endStyle, css, attributes) }
1914
        inner(builder, text.slice(0, part.to - start), style, startStyle, null, css, attributes);
1915
        startStyle = null;
1916
        text = text.slice(part.to - start);
1917
        start = part.to;
1918
      }
1919
    }
1920
  }
1921
 
1922
  function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
1923
    var widget = !ignoreWidget && marker.widgetNode;
1924
    if (widget) { builder.map.push(builder.pos, builder.pos + size, widget); }
1925
    if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {
1926
      if (!widget)
1927
        { widget = builder.content.appendChild(document.createElement("span")); }
1928
      widget.setAttribute("cm-marker", marker.id);
1929
    }
1930
    if (widget) {
1931
      builder.cm.display.input.setUneditable(widget);
1932
      builder.content.appendChild(widget);
1933
    }
1934
    builder.pos += size;
1935
    builder.trailingSpace = false;
1936
  }
1937
 
1938
  // Outputs a number of spans to make up a line, taking highlighting
1939
  // and marked text into account.
1940
  function insertLineContent(line, builder, styles) {
1941
    var spans = line.markedSpans, allText = line.text, at = 0;
1942
    if (!spans) {
1943
      for (var i$1 = 1; i$1 < styles.length; i$1+=2)
1944
        { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpretTokenStyle(styles[i$1+1], builder.cm.options)); }
1945
      return
1946
    }
1947
 
1948
    var len = allText.length, pos = 0, i = 1, text = "", style, css;
1949
    var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed, attributes;
1950
    for (;;) {
1951
      if (nextChange == pos) { // Update current marker set
1952
        spanStyle = spanEndStyle = spanStartStyle = css = "";
1953
        attributes = null;
1954
        collapsed = null; nextChange = Infinity;
1955
        var foundBookmarks = [], endStyles = (void 0);
1956
        for (var j = 0; j < spans.length; ++j) {
1957
          var sp = spans[j], m = sp.marker;
1958
          if (m.type == "bookmark" && sp.from == pos && m.widgetNode) {
1959
            foundBookmarks.push(m);
1960
          } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) {
1961
            if (sp.to != null && sp.to != pos && nextChange > sp.to) {
1962
              nextChange = sp.to;
1963
              spanEndStyle = "";
1964
            }
1965
            if (m.className) { spanStyle += " " + m.className; }
1966
            if (m.css) { css = (css ? css + ";" : "") + m.css; }
1967
            if (m.startStyle && sp.from == pos) { spanStartStyle += " " + m.startStyle; }
1968
            if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [])).push(m.endStyle, sp.to); }
1969
            // support for the old title property
1970
            // https://github.com/codemirror/CodeMirror/pull/5673
1971
            if (m.title) { (attributes || (attributes = {})).title = m.title; }
1972
            if (m.attributes) {
1973
              for (var attr in m.attributes)
1974
                { (attributes || (attributes = {}))[attr] = m.attributes[attr]; }
1975
            }
1976
            if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))
1977
              { collapsed = sp; }
1978
          } else if (sp.from > pos && nextChange > sp.from) {
1979
            nextChange = sp.from;
1980
          }
1981
        }
1982
        if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2)
1983
          { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += " " + endStyles[j$1]; } } }
1984
 
1985
        if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2)
1986
          { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]); } }
1987
        if (collapsed && (collapsed.from || 0) == pos) {
1988
          buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos,
1989
                             collapsed.marker, collapsed.from == null);
1990
          if (collapsed.to == null) { return }
1991
          if (collapsed.to == pos) { collapsed = false; }
1992
        }
1993
      }
1994
      if (pos >= len) { break }
1995
 
1996
      var upto = Math.min(len, nextChange);
1997
      while (true) {
1998
        if (text) {
1999
          var end = pos + text.length;
2000
          if (!collapsed) {
2001
            var tokenText = end > upto ? text.slice(0, upto - pos) : text;
2002
            builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
2003
                             spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", css, attributes);
2004
          }
2005
          if (end >= upto) {text = text.slice(upto - pos); pos = upto; break}
2006
          pos = end;
2007
          spanStartStyle = "";
2008
        }
2009
        text = allText.slice(at, at = styles[i++]);
2010
        style = interpretTokenStyle(styles[i++], builder.cm.options);
2011
      }
2012
    }
2013
  }
2014
 
2015
 
2016
  // These objects are used to represent the visible (currently drawn)
2017
  // part of the document. A LineView may correspond to multiple
2018
  // logical lines, if those are connected by collapsed ranges.
2019
  function LineView(doc, line, lineN) {
2020
    // The starting line
2021
    this.line = line;
2022
    // Continuing lines, if any
2023
    this.rest = visualLineContinued(line);
2024
    // Number of logical lines in this visual line
2025
    this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1;
2026
    this.node = this.text = null;
2027
    this.hidden = lineIsHidden(doc, line);
2028
  }
2029
 
2030
  // Create a range of LineView objects for the given lines.
2031
  function buildViewArray(cm, from, to) {
2032
    var array = [], nextPos;
2033
    for (var pos = from; pos < to; pos = nextPos) {
2034
      var view = new LineView(cm.doc, getLine(cm.doc, pos), pos);
2035
      nextPos = pos + view.size;
2036
      array.push(view);
2037
    }
2038
    return array
2039
  }
2040
 
2041
  var operationGroup = null;
2042
 
2043
  function pushOperation(op) {
2044
    if (operationGroup) {
2045
      operationGroup.ops.push(op);
2046
    } else {
2047
      op.ownsGroup = operationGroup = {
2048
        ops: [op],
2049
        delayedCallbacks: []
2050
      };
2051
    }
2052
  }
2053
 
2054
  function fireCallbacksForOps(group) {
2055
    // Calls delayed callbacks and cursorActivity handlers until no
2056
    // new ones appear
2057
    var callbacks = group.delayedCallbacks, i = 0;
2058
    do {
2059
      for (; i < callbacks.length; i++)
2060
        { callbacks[i].call(null); }
2061
      for (var j = 0; j < group.ops.length; j++) {
2062
        var op = group.ops[j];
2063
        if (op.cursorActivityHandlers)
2064
          { while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
2065
            { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm); } }
2066
      }
2067
    } while (i < callbacks.length)
2068
  }
2069
 
2070
  function finishOperation(op, endCb) {
2071
    var group = op.ownsGroup;
2072
    if (!group) { return }
2073
 
2074
    try { fireCallbacksForOps(group); }
2075
    finally {
2076
      operationGroup = null;
2077
      endCb(group);
2078
    }
2079
  }
2080
 
2081
  var orphanDelayedCallbacks = null;
2082
 
2083
  // Often, we want to signal events at a point where we are in the
2084
  // middle of some work, but don't want the handler to start calling
2085
  // other methods on the editor, which might be in an inconsistent
2086
  // state or simply not expect any other events to happen.
2087
  // signalLater looks whether there are any handlers, and schedules
2088
  // them to be executed when the last operation ends, or, if no
2089
  // operation is active, when a timeout fires.
2090
  function signalLater(emitter, type /*, values...*/) {
2091
    var arr = getHandlers(emitter, type);
2092
    if (!arr.length) { return }
2093
    var args = Array.prototype.slice.call(arguments, 2), list;
2094
    if (operationGroup) {
2095
      list = operationGroup.delayedCallbacks;
2096
    } else if (orphanDelayedCallbacks) {
2097
      list = orphanDelayedCallbacks;
2098
    } else {
2099
      list = orphanDelayedCallbacks = [];
2100
      setTimeout(fireOrphanDelayed, 0);
2101
    }
2102
    var loop = function ( i ) {
2103
      list.push(function () { return arr[i].apply(null, args); });
2104
    };
2105
 
2106
    for (var i = 0; i < arr.length; ++i)
2107
      loop( i );
2108
  }
2109
 
2110
  function fireOrphanDelayed() {
2111
    var delayed = orphanDelayedCallbacks;
2112
    orphanDelayedCallbacks = null;
2113
    for (var i = 0; i < delayed.length; ++i) { delayed[i](); }
2114
  }
2115
 
2116
  // When an aspect of a line changes, a string is added to
2117
  // lineView.changes. This updates the relevant part of the line's
2118
  // DOM structure.
2119
  function updateLineForChanges(cm, lineView, lineN, dims) {
2120
    for (var j = 0; j < lineView.changes.length; j++) {
2121
      var type = lineView.changes[j];
2122
      if (type == "text") { updateLineText(cm, lineView); }
2123
      else if (type == "gutter") { updateLineGutter(cm, lineView, lineN, dims); }
2124
      else if (type == "class") { updateLineClasses(cm, lineView); }
2125
      else if (type == "widget") { updateLineWidgets(cm, lineView, dims); }
2126
    }
2127
    lineView.changes = null;
2128
  }
2129
 
2130
  // Lines with gutter elements, widgets or a background class need to
2131
  // be wrapped, and have the extra elements added to the wrapper div
2132
  function ensureLineWrapped(lineView) {
2133
    if (lineView.node == lineView.text) {
2134
      lineView.node = elt("div", null, null, "position: relative");
2135
      if (lineView.text.parentNode)
2136
        { lineView.text.parentNode.replaceChild(lineView.node, lineView.text); }
2137
      lineView.node.appendChild(lineView.text);
2138
      if (ie && ie_version < 8) { lineView.node.style.zIndex = 2; }
2139
    }
2140
    return lineView.node
2141
  }
2142
 
2143
  function updateLineBackground(cm, lineView) {
2144
    var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass;
2145
    if (cls) { cls += " CodeMirror-linebackground"; }
2146
    if (lineView.background) {
2147
      if (cls) { lineView.background.className = cls; }
2148
      else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; }
2149
    } else if (cls) {
2150
      var wrap = ensureLineWrapped(lineView);
2151
      lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild);
2152
      cm.display.input.setUneditable(lineView.background);
2153
    }
2154
  }
2155
 
2156
  // Wrapper around buildLineContent which will reuse the structure
2157
  // in display.externalMeasured when possible.
2158
  function getLineContent(cm, lineView) {
2159
    var ext = cm.display.externalMeasured;
2160
    if (ext && ext.line == lineView.line) {
2161
      cm.display.externalMeasured = null;
2162
      lineView.measure = ext.measure;
2163
      return ext.built
2164
    }
2165
    return buildLineContent(cm, lineView)
2166
  }
2167
 
2168
  // Redraw the line's text. Interacts with the background and text
2169
  // classes because the mode may output tokens that influence these
2170
  // classes.
2171
  function updateLineText(cm, lineView) {
2172
    var cls = lineView.text.className;
2173
    var built = getLineContent(cm, lineView);
2174
    if (lineView.text == lineView.node) { lineView.node = built.pre; }
2175
    lineView.text.parentNode.replaceChild(built.pre, lineView.text);
2176
    lineView.text = built.pre;
2177
    if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {
2178
      lineView.bgClass = built.bgClass;
2179
      lineView.textClass = built.textClass;
2180
      updateLineClasses(cm, lineView);
2181
    } else if (cls) {
2182
      lineView.text.className = cls;
2183
    }
2184
  }
2185
 
2186
  function updateLineClasses(cm, lineView) {
2187
    updateLineBackground(cm, lineView);
2188
    if (lineView.line.wrapClass)
2189
      { ensureLineWrapped(lineView).className = lineView.line.wrapClass; }
2190
    else if (lineView.node != lineView.text)
2191
      { lineView.node.className = ""; }
2192
    var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass;
2193
    lineView.text.className = textClass || "";
2194
  }
2195
 
2196
  function updateLineGutter(cm, lineView, lineN, dims) {
2197
    if (lineView.gutter) {
2198
      lineView.node.removeChild(lineView.gutter);
2199
      lineView.gutter = null;
2200
    }
2201
    if (lineView.gutterBackground) {
2202
      lineView.node.removeChild(lineView.gutterBackground);
2203
      lineView.gutterBackground = null;
2204
    }
2205
    if (lineView.line.gutterClass) {
2206
      var wrap = ensureLineWrapped(lineView);
2207
      lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass,
2208
                                      ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px; width: " + (dims.gutterTotalWidth) + "px"));
2209
      cm.display.input.setUneditable(lineView.gutterBackground);
2210
      wrap.insertBefore(lineView.gutterBackground, lineView.text);
2211
    }
2212
    var markers = lineView.line.gutterMarkers;
2213
    if (cm.options.lineNumbers || markers) {
2214
      var wrap$1 = ensureLineWrapped(lineView);
2215
      var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"));
2216
      gutterWrap.setAttribute("aria-hidden", "true");
2217
      cm.display.input.setUneditable(gutterWrap);
2218
      wrap$1.insertBefore(gutterWrap, lineView.text);
2219
      if (lineView.line.gutterClass)
2220
        { gutterWrap.className += " " + lineView.line.gutterClass; }
2221
      if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
2222
        { lineView.lineNumber = gutterWrap.appendChild(
2223
          elt("div", lineNumberFor(cm.options, lineN),
2224
              "CodeMirror-linenumber CodeMirror-gutter-elt",
2225
              ("left: " + (dims.gutterLeft["CodeMirror-linenumbers"]) + "px; width: " + (cm.display.lineNumInnerWidth) + "px"))); }
2226
      if (markers) { for (var k = 0; k < cm.display.gutterSpecs.length; ++k) {
2227
        var id = cm.display.gutterSpecs[k].className, found = markers.hasOwnProperty(id) && markers[id];
2228
        if (found)
2229
          { gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt",
2230
                                     ("left: " + (dims.gutterLeft[id]) + "px; width: " + (dims.gutterWidth[id]) + "px"))); }
2231
      } }
2232
    }
2233
  }
2234
 
2235
  function updateLineWidgets(cm, lineView, dims) {
2236
    if (lineView.alignable) { lineView.alignable = null; }
2237
    var isWidget = classTest("CodeMirror-linewidget");
2238
    for (var node = lineView.node.firstChild, next = (void 0); node; node = next) {
2239
      next = node.nextSibling;
2240
      if (isWidget.test(node.className)) { lineView.node.removeChild(node); }
2241
    }
2242
    insertLineWidgets(cm, lineView, dims);
2243
  }
2244
 
2245
  // Build a line's DOM representation from scratch
2246
  function buildLineElement(cm, lineView, lineN, dims) {
2247
    var built = getLineContent(cm, lineView);
2248
    lineView.text = lineView.node = built.pre;
2249
    if (built.bgClass) { lineView.bgClass = built.bgClass; }
2250
    if (built.textClass) { lineView.textClass = built.textClass; }
2251
 
2252
    updateLineClasses(cm, lineView);
2253
    updateLineGutter(cm, lineView, lineN, dims);
2254
    insertLineWidgets(cm, lineView, dims);
2255
    return lineView.node
2256
  }
2257
 
2258
  // A lineView may contain multiple logical lines (when merged by
2259
  // collapsed spans). The widgets for all of them need to be drawn.
2260
  function insertLineWidgets(cm, lineView, dims) {
2261
    insertLineWidgetsFor(cm, lineView.line, lineView, dims, true);
2262
    if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
2263
      { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false); } }
2264
  }
2265
 
2266
  function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {
2267
    if (!line.widgets) { return }
2268
    var wrap = ensureLineWrapped(lineView);
2269
    for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
2270
      var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget" + (widget.className ? " " + widget.className : ""));
2271
      if (!widget.handleMouseEvents) { node.setAttribute("cm-ignore-events", "true"); }
2272
      positionLineWidget(widget, node, lineView, dims);
2273
      cm.display.input.setUneditable(node);
2274
      if (allowAbove && widget.above)
2275
        { wrap.insertBefore(node, lineView.gutter || lineView.text); }
2276
      else
2277
        { wrap.appendChild(node); }
2278
      signalLater(widget, "redraw");
2279
    }
2280
  }
2281
 
2282
  function positionLineWidget(widget, node, lineView, dims) {
2283
    if (widget.noHScroll) {
2284
  (lineView.alignable || (lineView.alignable = [])).push(node);
2285
      var width = dims.wrapperWidth;
2286
      node.style.left = dims.fixedPos + "px";
2287
      if (!widget.coverGutter) {
2288
        width -= dims.gutterTotalWidth;
2289
        node.style.paddingLeft = dims.gutterTotalWidth + "px";
2290
      }
2291
      node.style.width = width + "px";
2292
    }
2293
    if (widget.coverGutter) {
2294
      node.style.zIndex = 5;
2295
      node.style.position = "relative";
2296
      if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + "px"; }
2297
    }
2298
  }
2299
 
2300
  function widgetHeight(widget) {
2301
    if (widget.height != null) { return widget.height }
2302
    var cm = widget.doc.cm;
2303
    if (!cm) { return 0 }
2304
    if (!contains(document.body, widget.node)) {
2305
      var parentStyle = "position: relative;";
2306
      if (widget.coverGutter)
2307
        { parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;"; }
2308
      if (widget.noHScroll)
2309
        { parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;"; }
2310
      removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle));
2311
    }
2312
    return widget.height = widget.node.parentNode.offsetHeight
2313
  }
2314
 
2315
  // Return true when the given mouse event happened in a widget
2316
  function eventInWidget(display, e) {
2317
    for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
2318
      if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") ||
2319
          (n.parentNode == display.sizer && n != display.mover))
2320
        { return true }
2321
    }
2322
  }
2323
 
2324
  // POSITION MEASUREMENT
2325
 
2326
  function paddingTop(display) {return display.lineSpace.offsetTop}
2327
  function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight}
2328
  function paddingH(display) {
2329
    if (display.cachedPaddingH) { return display.cachedPaddingH }
2330
    var e = removeChildrenAndAdd(display.measure, elt("pre", "x", "CodeMirror-line-like"));
2331
    var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle;
2332
    var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)};
2333
    if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data; }
2334
    return data
2335
  }
2336
 
2337
  function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth }
2338
  function displayWidth(cm) {
2339
    return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth
2340
  }
2341
  function displayHeight(cm) {
2342
    return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight
2343
  }
2344
 
2345
  // Ensure the lineView.wrapping.heights array is populated. This is
2346
  // an array of bottom offsets for the lines that make up a drawn
2347
  // line. When lineWrapping is on, there might be more than one
2348
  // height.
2349
  function ensureLineHeights(cm, lineView, rect) {
2350
    var wrapping = cm.options.lineWrapping;
2351
    var curWidth = wrapping && displayWidth(cm);
2352
    if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {
2353
      var heights = lineView.measure.heights = [];
2354
      if (wrapping) {
2355
        lineView.measure.width = curWidth;
2356
        var rects = lineView.text.firstChild.getClientRects();
2357
        for (var i = 0; i < rects.length - 1; i++) {
2358
          var cur = rects[i], next = rects[i + 1];
2359
          if (Math.abs(cur.bottom - next.bottom) > 2)
2360
            { heights.push((cur.bottom + next.top) / 2 - rect.top); }
2361
        }
2362
      }
2363
      heights.push(rect.bottom - rect.top);
2364
    }
2365
  }
2366
 
2367
  // Find a line map (mapping character offsets to text nodes) and a
2368
  // measurement cache for the given line number. (A line view might
2369
  // contain multiple lines when collapsed ranges are present.)
2370
  function mapFromLineView(lineView, line, lineN) {
2371
    if (lineView.line == line)
2372
      { return {map: lineView.measure.map, cache: lineView.measure.cache} }
2373
    if (lineView.rest) {
2374
      for (var i = 0; i < lineView.rest.length; i++)
2375
        { if (lineView.rest[i] == line)
2376
          { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } }
2377
      for (var i$1 = 0; i$1 < lineView.rest.length; i$1++)
2378
        { if (lineNo(lineView.rest[i$1]) > lineN)
2379
          { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } }
2380
    }
2381
  }
2382
 
2383
  // Render a line into the hidden node display.externalMeasured. Used
2384
  // when measurement is needed for a line that's not in the viewport.
2385
  function updateExternalMeasurement(cm, line) {
2386
    line = visualLine(line);
2387
    var lineN = lineNo(line);
2388
    var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN);
2389
    view.lineN = lineN;
2390
    var built = view.built = buildLineContent(cm, view);
2391
    view.text = built.pre;
2392
    removeChildrenAndAdd(cm.display.lineMeasure, built.pre);
2393
    return view
2394
  }
2395
 
2396
  // Get a {top, bottom, left, right} box (in line-local coordinates)
2397
  // for a given character.
2398
  function measureChar(cm, line, ch, bias) {
2399
    return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias)
2400
  }
2401
 
2402
  // Find a line view that corresponds to the given line number.
2403
  function findViewForLine(cm, lineN) {
2404
    if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo)
2405
      { return cm.display.view[findViewIndex(cm, lineN)] }
2406
    var ext = cm.display.externalMeasured;
2407
    if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size)
2408
      { return ext }
2409
  }
2410
 
2411
  // Measurement can be split in two steps, the set-up work that
2412
  // applies to the whole line, and the measurement of the actual
2413
  // character. Functions like coordsChar, that need to do a lot of
2414
  // measurements in a row, can thus ensure that the set-up work is
2415
  // only done once.
2416
  function prepareMeasureForLine(cm, line) {
2417
    var lineN = lineNo(line);
2418
    var view = findViewForLine(cm, lineN);
2419
    if (view && !view.text) {
2420
      view = null;
2421
    } else if (view && view.changes) {
2422
      updateLineForChanges(cm, view, lineN, getDimensions(cm));
2423
      cm.curOp.forceUpdate = true;
2424
    }
2425
    if (!view)
2426
      { view = updateExternalMeasurement(cm, line); }
2427
 
2428
    var info = mapFromLineView(view, line, lineN);
2429
    return {
2430
      line: line, view: view, rect: null,
2431
      map: info.map, cache: info.cache, before: info.before,
2432
      hasHeights: false
2433
    }
2434
  }
2435
 
2436
  // Given a prepared measurement object, measures the position of an
2437
  // actual character (or fetches it from the cache).
2438
  function measureCharPrepared(cm, prepared, ch, bias, varHeight) {
2439
    if (prepared.before) { ch = -1; }
2440
    var key = ch + (bias || ""), found;
2441
    if (prepared.cache.hasOwnProperty(key)) {
2442
      found = prepared.cache[key];
2443
    } else {
2444
      if (!prepared.rect)
2445
        { prepared.rect = prepared.view.text.getBoundingClientRect(); }
2446
      if (!prepared.hasHeights) {
2447
        ensureLineHeights(cm, prepared.view, prepared.rect);
2448
        prepared.hasHeights = true;
2449
      }
2450
      found = measureCharInner(cm, prepared, ch, bias);
2451
      if (!found.bogus) { prepared.cache[key] = found; }
2452
    }
2453
    return {left: found.left, right: found.right,
2454
            top: varHeight ? found.rtop : found.top,
2455
            bottom: varHeight ? found.rbottom : found.bottom}
2456
  }
2457
 
2458
  var nullRect = {left: 0, right: 0, top: 0, bottom: 0};
2459
 
2460
  function nodeAndOffsetInLineMap(map, ch, bias) {
2461
    var node, start, end, collapse, mStart, mEnd;
2462
    // First, search the line map for the text node corresponding to,
2463
    // or closest to, the target character.
2464
    for (var i = 0; i < map.length; i += 3) {
2465
      mStart = map[i];
2466
      mEnd = map[i + 1];
2467
      if (ch < mStart) {
2468
        start = 0; end = 1;
2469
        collapse = "left";
2470
      } else if (ch < mEnd) {
2471
        start = ch - mStart;
2472
        end = start + 1;
2473
      } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) {
2474
        end = mEnd - mStart;
2475
        start = end - 1;
2476
        if (ch >= mEnd) { collapse = "right"; }
2477
      }
2478
      if (start != null) {
2479
        node = map[i + 2];
2480
        if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right"))
2481
          { collapse = bias; }
2482
        if (bias == "left" && start == 0)
2483
          { while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) {
2484
            node = map[(i -= 3) + 2];
2485
            collapse = "left";
2486
          } }
2487
        if (bias == "right" && start == mEnd - mStart)
2488
          { while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].insertLeft) {
2489
            node = map[(i += 3) + 2];
2490
            collapse = "right";
2491
          } }
2492
        break
2493
      }
2494
    }
2495
    return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd}
2496
  }
2497
 
2498
  function getUsefulRect(rects, bias) {
2499
    var rect = nullRect;
2500
    if (bias == "left") { for (var i = 0; i < rects.length; i++) {
2501
      if ((rect = rects[i]).left != rect.right) { break }
2502
    } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) {
2503
      if ((rect = rects[i$1]).left != rect.right) { break }
2504
    } }
2505
    return rect
2506
  }
2507
 
2508
  function measureCharInner(cm, prepared, ch, bias) {
2509
    var place = nodeAndOffsetInLineMap(prepared.map, ch, bias);
2510
    var node = place.node, start = place.start, end = place.end, collapse = place.collapse;
2511
 
2512
    var rect;
2513
    if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.
2514
      for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonsense rectangles are returned
2515
        while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start; }
2516
        while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) { ++end; }
2517
        if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart)
2518
          { rect = node.parentNode.getBoundingClientRect(); }
2519
        else
2520
          { rect = getUsefulRect(range(node, start, end).getClientRects(), bias); }
2521
        if (rect.left || rect.right || start == 0) { break }
2522
        end = start;
2523
        start = start - 1;
2524
        collapse = "right";
2525
      }
2526
      if (ie && ie_version < 11) { rect = maybeUpdateRectForZooming(cm.display.measure, rect); }
2527
    } else { // If it is a widget, simply get the box for the whole widget.
2528
      if (start > 0) { collapse = bias = "right"; }
2529
      var rects;
2530
      if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)
2531
        { rect = rects[bias == "right" ? rects.length - 1 : 0]; }
2532
      else
2533
        { rect = node.getBoundingClientRect(); }
2534
    }
2535
    if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {
2536
      var rSpan = node.parentNode.getClientRects()[0];
2537
      if (rSpan)
2538
        { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom}; }
2539
      else
2540
        { rect = nullRect; }
2541
    }
2542
 
2543
    var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top;
2544
    var mid = (rtop + rbot) / 2;
2545
    var heights = prepared.view.measure.heights;
2546
    var i = 0;
2547
    for (; i < heights.length - 1; i++)
2548
      { if (mid < heights[i]) { break } }
2549
    var top = i ? heights[i - 1] : 0, bot = heights[i];
2550
    var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left,
2551
                  right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left,
2552
                  top: top, bottom: bot};
2553
    if (!rect.left && !rect.right) { result.bogus = true; }
2554
    if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; }
2555
 
2556
    return result
2557
  }
2558
 
2559
  // Work around problem with bounding client rects on ranges being
2560
  // returned incorrectly when zoomed on IE10 and below.
2561
  function maybeUpdateRectForZooming(measure, rect) {
2562
    if (!window.screen || screen.logicalXDPI == null ||
2563
        screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))
2564
      { return rect }
2565
    var scaleX = screen.logicalXDPI / screen.deviceXDPI;
2566
    var scaleY = screen.logicalYDPI / screen.deviceYDPI;
2567
    return {left: rect.left * scaleX, right: rect.right * scaleX,
2568
            top: rect.top * scaleY, bottom: rect.bottom * scaleY}
2569
  }
2570
 
2571
  function clearLineMeasurementCacheFor(lineView) {
2572
    if (lineView.measure) {
2573
      lineView.measure.cache = {};
2574
      lineView.measure.heights = null;
2575
      if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
2576
        { lineView.measure.caches[i] = {}; } }
2577
    }
2578
  }
2579
 
2580
  function clearLineMeasurementCache(cm) {
2581
    cm.display.externalMeasure = null;
2582
    removeChildren(cm.display.lineMeasure);
2583
    for (var i = 0; i < cm.display.view.length; i++)
2584
      { clearLineMeasurementCacheFor(cm.display.view[i]); }
2585
  }
2586
 
2587
  function clearCaches(cm) {
2588
    clearLineMeasurementCache(cm);
2589
    cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null;
2590
    if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true; }
2591
    cm.display.lineNumChars = null;
2592
  }
2593
 
2594
  function pageScrollX(doc) {
2595
    // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=489206
2596
    // which causes page_Offset and bounding client rects to use
2597
    // different reference viewports and invalidate our calculations.
2598
    if (chrome && android) { return -(doc.body.getBoundingClientRect().left - parseInt(getComputedStyle(doc.body).marginLeft)) }
2599
    return doc.defaultView.pageXOffset || (doc.documentElement || doc.body).scrollLeft
2600
  }
2601
  function pageScrollY(doc) {
2602
    if (chrome && android) { return -(doc.body.getBoundingClientRect().top - parseInt(getComputedStyle(doc.body).marginTop)) }
2603
    return doc.defaultView.pageYOffset || (doc.documentElement || doc.body).scrollTop
2604
  }
2605
 
2606
  function widgetTopHeight(lineObj) {
2607
    var ref = visualLine(lineObj);
2608
    var widgets = ref.widgets;
2609
    var height = 0;
2610
    if (widgets) { for (var i = 0; i < widgets.length; ++i) { if (widgets[i].above)
2611
      { height += widgetHeight(widgets[i]); } } }
2612
    return height
2613
  }
2614
 
2615
  // Converts a {top, bottom, left, right} box from line-local
2616
  // coordinates into another coordinate system. Context may be one of
2617
  // "line", "div" (display.lineDiv), "local"./null (editor), "window",
2618
  // or "page".
2619
  function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) {
2620
    if (!includeWidgets) {
2621
      var height = widgetTopHeight(lineObj);
2622
      rect.top += height; rect.bottom += height;
2623
    }
2624
    if (context == "line") { return rect }
2625
    if (!context) { context = "local"; }
2626
    var yOff = heightAtLine(lineObj);
2627
    if (context == "local") { yOff += paddingTop(cm.display); }
2628
    else { yOff -= cm.display.viewOffset; }
2629
    if (context == "page" || context == "window") {
2630
      var lOff = cm.display.lineSpace.getBoundingClientRect();
2631
      yOff += lOff.top + (context == "window" ? 0 : pageScrollY(doc(cm)));
2632
      var xOff = lOff.left + (context == "window" ? 0 : pageScrollX(doc(cm)));
2633
      rect.left += xOff; rect.right += xOff;
2634
    }
2635
    rect.top += yOff; rect.bottom += yOff;
2636
    return rect
2637
  }
2638
 
2639
  // Coverts a box from "div" coords to another coordinate system.
2640
  // Context may be "window", "page", "div", or "local"./null.
2641
  function fromCoordSystem(cm, coords, context) {
2642
    if (context == "div") { return coords }
2643
    var left = coords.left, top = coords.top;
2644
    // First move into "page" coordinate system
2645
    if (context == "page") {
2646
      left -= pageScrollX(doc(cm));
2647
      top -= pageScrollY(doc(cm));
2648
    } else if (context == "local" || !context) {
2649
      var localBox = cm.display.sizer.getBoundingClientRect();
2650
      left += localBox.left;
2651
      top += localBox.top;
2652
    }
2653
 
2654
    var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect();
2655
    return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top}
2656
  }
2657
 
2658
  function charCoords(cm, pos, context, lineObj, bias) {
2659
    if (!lineObj) { lineObj = getLine(cm.doc, pos.line); }
2660
    return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context)
2661
  }
2662
 
2663
  // Returns a box for a given cursor position, which may have an
2664
  // 'other' property containing the position of the secondary cursor
2665
  // on a bidi boundary.
2666
  // A cursor Pos(line, char, "before") is on the same visual line as `char - 1`
2667
  // and after `char - 1` in writing order of `char - 1`
2668
  // A cursor Pos(line, char, "after") is on the same visual line as `char`
2669
  // and before `char` in writing order of `char`
2670
  // Examples (upper-case letters are RTL, lower-case are LTR):
2671
  //     Pos(0, 1, ...)
2672
  //     before   after
2673
  // ab     a|b     a|b
2674
  // aB     a|B     aB|
2675
  // Ab     |Ab     A|b
2676
  // AB     B|A     B|A
2677
  // Every position after the last character on a line is considered to stick
2678
  // to the last character on the line.
2679
  function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
2680
    lineObj = lineObj || getLine(cm.doc, pos.line);
2681
    if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); }
2682
    function get(ch, right) {
2683
      var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight);
2684
      if (right) { m.left = m.right; } else { m.right = m.left; }
2685
      return intoCoordSystem(cm, lineObj, m, context)
2686
    }
2687
    var order = getOrder(lineObj, cm.doc.direction), ch = pos.ch, sticky = pos.sticky;
2688
    if (ch >= lineObj.text.length) {
2689
      ch = lineObj.text.length;
2690
      sticky = "before";
2691
    } else if (ch <= 0) {
2692
      ch = 0;
2693
      sticky = "after";
2694
    }
2695
    if (!order) { return get(sticky == "before" ? ch - 1 : ch, sticky == "before") }
2696
 
2697
    function getBidi(ch, partPos, invert) {
2698
      var part = order[partPos], right = part.level == 1;
2699
      return get(invert ? ch - 1 : ch, right != invert)
2700
    }
2701
    var partPos = getBidiPartAt(order, ch, sticky);
2702
    var other = bidiOther;
2703
    var val = getBidi(ch, partPos, sticky == "before");
2704
    if (other != null) { val.other = getBidi(ch, other, sticky != "before"); }
2705
    return val
2706
  }
2707
 
2708
  // Used to cheaply estimate the coordinates for a position. Used for
2709
  // intermediate scroll updates.
2710
  function estimateCoords(cm, pos) {
2711
    var left = 0;
2712
    pos = clipPos(cm.doc, pos);
2713
    if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch; }
2714
    var lineObj = getLine(cm.doc, pos.line);
2715
    var top = heightAtLine(lineObj) + paddingTop(cm.display);
2716
    return {left: left, right: left, top: top, bottom: top + lineObj.height}
2717
  }
2718
 
2719
  // Positions returned by coordsChar contain some extra information.
2720
  // xRel is the relative x position of the input coordinates compared
2721
  // to the found position (so xRel > 0 means the coordinates are to
2722
  // the right of the character position, for example). When outside
2723
  // is true, that means the coordinates lie outside the line's
2724
  // vertical range.
2725
  function PosWithInfo(line, ch, sticky, outside, xRel) {
2726
    var pos = Pos(line, ch, sticky);
2727
    pos.xRel = xRel;
2728
    if (outside) { pos.outside = outside; }
2729
    return pos
2730
  }
2731
 
2732
  // Compute the character position closest to the given coordinates.
2733
  // Input must be lineSpace-local ("div" coordinate system).
2734
  function coordsChar(cm, x, y) {
2735
    var doc = cm.doc;
2736
    y += cm.display.viewOffset;
2737
    if (y < 0) { return PosWithInfo(doc.first, 0, null, -1, -1) }
2738
    var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
2739
    if (lineN > last)
2740
      { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, 1, 1) }
2741
    if (x < 0) { x = 0; }
2742
 
2743
    var lineObj = getLine(doc, lineN);
2744
    for (;;) {
2745
      var found = coordsCharInner(cm, lineObj, lineN, x, y);
2746
      var collapsed = collapsedSpanAround(lineObj, found.ch + (found.xRel > 0 || found.outside > 0 ? 1 : 0));
2747
      if (!collapsed) { return found }
2748
      var rangeEnd = collapsed.find(1);
2749
      if (rangeEnd.line == lineN) { return rangeEnd }
2750
      lineObj = getLine(doc, lineN = rangeEnd.line);
2751
    }
2752
  }
2753
 
2754
  function wrappedLineExtent(cm, lineObj, preparedMeasure, y) {
2755
    y -= widgetTopHeight(lineObj);
2756
    var end = lineObj.text.length;
2757
    var begin = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch - 1).bottom <= y; }, end, 0);
2758
    end = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch).top > y; }, begin, end);
2759
    return {begin: begin, end: end}
2760
  }
2761
 
2762
  function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) {
2763
    if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); }
2764
    var targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), "line").top;
2765
    return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop)
2766
  }
2767
 
2768
  // Returns true if the given side of a box is after the given
2769
  // coordinates, in top-to-bottom, left-to-right order.
2770
  function boxIsAfter(box, x, y, left) {
2771
    return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x
2772
  }
2773
 
2774
  function coordsCharInner(cm, lineObj, lineNo, x, y) {
2775
    // Move y into line-local coordinate space
2776
    y -= heightAtLine(lineObj);
2777
    var preparedMeasure = prepareMeasureForLine(cm, lineObj);
2778
    // When directly calling `measureCharPrepared`, we have to adjust
2779
    // for the widgets at this line.
2780
    var widgetHeight = widgetTopHeight(lineObj);
2781
    var begin = 0, end = lineObj.text.length, ltr = true;
2782
 
2783
    var order = getOrder(lineObj, cm.doc.direction);
2784
    // If the line isn't plain left-to-right text, first figure out
2785
    // which bidi section the coordinates fall into.
2786
    if (order) {
2787
      var part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart)
2788
                   (cm, lineObj, lineNo, preparedMeasure, order, x, y);
2789
      ltr = part.level != 1;
2790
      // The awkward -1 offsets are needed because findFirst (called
2791
      // on these below) will treat its first bound as inclusive,
2792
      // second as exclusive, but we want to actually address the
2793
      // characters in the part's range
2794
      begin = ltr ? part.from : part.to - 1;
2795
      end = ltr ? part.to : part.from - 1;
2796
    }
2797
 
2798
    // A binary search to find the first character whose bounding box
2799
    // starts after the coordinates. If we run across any whose box wrap
2800
    // the coordinates, store that.
2801
    var chAround = null, boxAround = null;
2802
    var ch = findFirst(function (ch) {
2803
      var box = measureCharPrepared(cm, preparedMeasure, ch);
2804
      box.top += widgetHeight; box.bottom += widgetHeight;
2805
      if (!boxIsAfter(box, x, y, false)) { return false }
2806
      if (box.top <= y && box.left <= x) {
2807
        chAround = ch;
2808
        boxAround = box;
2809
      }
2810
      return true
2811
    }, begin, end);
2812
 
2813
    var baseX, sticky, outside = false;
2814
    // If a box around the coordinates was found, use that
2815
    if (boxAround) {
2816
      // Distinguish coordinates nearer to the left or right side of the box
2817
      var atLeft = x - boxAround.left < boxAround.right - x, atStart = atLeft == ltr;
2818
      ch = chAround + (atStart ? 0 : 1);
2819
      sticky = atStart ? "after" : "before";
2820
      baseX = atLeft ? boxAround.left : boxAround.right;
2821
    } else {
2822
      // (Adjust for extended bound, if necessary.)
2823
      if (!ltr && (ch == end || ch == begin)) { ch++; }
2824
      // To determine which side to associate with, get the box to the
2825
      // left of the character and compare it's vertical position to the
2826
      // coordinates
2827
      sticky = ch == 0 ? "after" : ch == lineObj.text.length ? "before" :
2828
        (measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight <= y) == ltr ?
2829
        "after" : "before";
2830
      // Now get accurate coordinates for this place, in order to get a
2831
      // base X position
2832
      var coords = cursorCoords(cm, Pos(lineNo, ch, sticky), "line", lineObj, preparedMeasure);
2833
      baseX = coords.left;
2834
      outside = y < coords.top ? -1 : y >= coords.bottom ? 1 : 0;
2835
    }
2836
 
2837
    ch = skipExtendingChars(lineObj.text, ch, 1);
2838
    return PosWithInfo(lineNo, ch, sticky, outside, x - baseX)
2839
  }
2840
 
2841
  function coordsBidiPart(cm, lineObj, lineNo, preparedMeasure, order, x, y) {
2842
    // Bidi parts are sorted left-to-right, and in a non-line-wrapping
2843
    // situation, we can take this ordering to correspond to the visual
2844
    // ordering. This finds the first part whose end is after the given
2845
    // coordinates.
2846
    var index = findFirst(function (i) {
2847
      var part = order[i], ltr = part.level != 1;
2848
      return boxIsAfter(cursorCoords(cm, Pos(lineNo, ltr ? part.to : part.from, ltr ? "before" : "after"),
2849
                                     "line", lineObj, preparedMeasure), x, y, true)
2850
    }, 0, order.length - 1);
2851
    var part = order[index];
2852
    // If this isn't the first part, the part's start is also after
2853
    // the coordinates, and the coordinates aren't on the same line as
2854
    // that start, move one part back.
2855
    if (index > 0) {
2856
      var ltr = part.level != 1;
2857
      var start = cursorCoords(cm, Pos(lineNo, ltr ? part.from : part.to, ltr ? "after" : "before"),
2858
                               "line", lineObj, preparedMeasure);
2859
      if (boxIsAfter(start, x, y, true) && start.top > y)
2860
        { part = order[index - 1]; }
2861
    }
2862
    return part
2863
  }
2864
 
2865
  function coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x, y) {
2866
    // In a wrapped line, rtl text on wrapping boundaries can do things
2867
    // that don't correspond to the ordering in our `order` array at
2868
    // all, so a binary search doesn't work, and we want to return a
2869
    // part that only spans one line so that the binary search in
2870
    // coordsCharInner is safe. As such, we first find the extent of the
2871
    // wrapped line, and then do a flat search in which we discard any
2872
    // spans that aren't on the line.
2873
    var ref = wrappedLineExtent(cm, lineObj, preparedMeasure, y);
2874
    var begin = ref.begin;
2875
    var end = ref.end;
2876
    if (/\s/.test(lineObj.text.charAt(end - 1))) { end--; }
2877
    var part = null, closestDist = null;
2878
    for (var i = 0; i < order.length; i++) {
2879
      var p = order[i];
2880
      if (p.from >= end || p.to <= begin) { continue }
2881
      var ltr = p.level != 1;
2882
      var endX = measureCharPrepared(cm, preparedMeasure, ltr ? Math.min(end, p.to) - 1 : Math.max(begin, p.from)).right;
2883
      // Weigh against spans ending before this, so that they are only
2884
      // picked if nothing ends after
2885
      var dist = endX < x ? x - endX + 1e9 : endX - x;
2886
      if (!part || closestDist > dist) {
2887
        part = p;
2888
        closestDist = dist;
2889
      }
2890
    }
2891
    if (!part) { part = order[order.length - 1]; }
2892
    // Clip the part to the wrapped line.
2893
    if (part.from < begin) { part = {from: begin, to: part.to, level: part.level}; }
2894
    if (part.to > end) { part = {from: part.from, to: end, level: part.level}; }
2895
    return part
2896
  }
2897
 
2898
  var measureText;
2899
  // Compute the default text height.
2900
  function textHeight(display) {
2901
    if (display.cachedTextHeight != null) { return display.cachedTextHeight }
2902
    if (measureText == null) {
2903
      measureText = elt("pre", null, "CodeMirror-line-like");
2904
      // Measure a bunch of lines, for browsers that compute
2905
      // fractional heights.
2906
      for (var i = 0; i < 49; ++i) {
2907
        measureText.appendChild(document.createTextNode("x"));
2908
        measureText.appendChild(elt("br"));
2909
      }
2910
      measureText.appendChild(document.createTextNode("x"));
2911
    }
2912
    removeChildrenAndAdd(display.measure, measureText);
2913
    var height = measureText.offsetHeight / 50;
2914
    if (height > 3) { display.cachedTextHeight = height; }
2915
    removeChildren(display.measure);
2916
    return height || 1
2917
  }
2918
 
2919
  // Compute the default character width.
2920
  function charWidth(display) {
2921
    if (display.cachedCharWidth != null) { return display.cachedCharWidth }
2922
    var anchor = elt("span", "xxxxxxxxxx");
2923
    var pre = elt("pre", [anchor], "CodeMirror-line-like");
2924
    removeChildrenAndAdd(display.measure, pre);
2925
    var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10;
2926
    if (width > 2) { display.cachedCharWidth = width; }
2927
    return width || 10
2928
  }
2929
 
2930
  // Do a bulk-read of the DOM positions and sizes needed to draw the
2931
  // view, so that we don't interleave reading and writing to the DOM.
2932
  function getDimensions(cm) {
2933
    var d = cm.display, left = {}, width = {};
2934
    var gutterLeft = d.gutters.clientLeft;
2935
    for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
2936
      var id = cm.display.gutterSpecs[i].className;
2937
      left[id] = n.offsetLeft + n.clientLeft + gutterLeft;
2938
      width[id] = n.clientWidth;
2939
    }
2940
    return {fixedPos: compensateForHScroll(d),
2941
            gutterTotalWidth: d.gutters.offsetWidth,
2942
            gutterLeft: left,
2943
            gutterWidth: width,
2944
            wrapperWidth: d.wrapper.clientWidth}
2945
  }
2946
 
2947
  // Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
2948
  // but using getBoundingClientRect to get a sub-pixel-accurate
2949
  // result.
2950
  function compensateForHScroll(display) {
2951
    return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left
2952
  }
2953
 
2954
  // Returns a function that estimates the height of a line, to use as
2955
  // first approximation until the line becomes visible (and is thus
2956
  // properly measurable).
2957
  function estimateHeight(cm) {
2958
    var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
2959
    var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
2960
    return function (line) {
2961
      if (lineIsHidden(cm.doc, line)) { return 0 }
2962
 
2963
      var widgetsHeight = 0;
2964
      if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) {
2965
        if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height; }
2966
      } }
2967
 
2968
      if (wrapping)
2969
        { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th }
2970
      else
2971
        { return widgetsHeight + th }
2972
    }
2973
  }
2974
 
2975
  function estimateLineHeights(cm) {
2976
    var doc = cm.doc, est = estimateHeight(cm);
2977
    doc.iter(function (line) {
2978
      var estHeight = est(line);
2979
      if (estHeight != line.height) { updateLineHeight(line, estHeight); }
2980
    });
2981
  }
2982
 
2983
  // Given a mouse event, find the corresponding position. If liberal
2984
  // is false, it checks whether a gutter or scrollbar was clicked,
2985
  // and returns null if it was. forRect is used by rectangular
2986
  // selections, and tries to estimate a character position even for
2987
  // coordinates beyond the right of the text.
2988
  function posFromMouse(cm, e, liberal, forRect) {
2989
    var display = cm.display;
2990
    if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") { return null }
2991
 
2992
    var x, y, space = display.lineSpace.getBoundingClientRect();
2993
    // Fails unpredictably on IE[67] when mouse is dragged around quickly.
2994
    try { x = e.clientX - space.left; y = e.clientY - space.top; }
2995
    catch (e$1) { return null }
2996
    var coords = coordsChar(cm, x, y), line;
2997
    if (forRect && coords.xRel > 0 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) {
2998
      var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length;
2999
      coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff));
3000
    }
3001
    return coords
3002
  }
3003
 
3004
  // Find the view element corresponding to a given line. Return null
3005
  // when the line isn't visible.
3006
  function findViewIndex(cm, n) {
3007
    if (n >= cm.display.viewTo) { return null }
3008
    n -= cm.display.viewFrom;
3009
    if (n < 0) { return null }
3010
    var view = cm.display.view;
3011
    for (var i = 0; i < view.length; i++) {
3012
      n -= view[i].size;
3013
      if (n < 0) { return i }
3014
    }
3015
  }
3016
 
3017
  // Updates the display.view data structure for a given change to the
3018
  // document. From and to are in pre-change coordinates. Lendiff is
3019
  // the amount of lines added or subtracted by the change. This is
3020
  // used for changes that span multiple lines, or change the way
3021
  // lines are divided into visual lines. regLineChange (below)
3022
  // registers single-line changes.
3023
  function regChange(cm, from, to, lendiff) {
3024
    if (from == null) { from = cm.doc.first; }
3025
    if (to == null) { to = cm.doc.first + cm.doc.size; }
3026
    if (!lendiff) { lendiff = 0; }
3027
 
3028
    var display = cm.display;
3029
    if (lendiff && to < display.viewTo &&
3030
        (display.updateLineNumbers == null || display.updateLineNumbers > from))
3031
      { display.updateLineNumbers = from; }
3032
 
3033
    cm.curOp.viewChanged = true;
3034
 
3035
    if (from >= display.viewTo) { // Change after
3036
      if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)
3037
        { resetView(cm); }
3038
    } else if (to <= display.viewFrom) { // Change before
3039
      if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {
3040
        resetView(cm);
3041
      } else {
3042
        display.viewFrom += lendiff;
3043
        display.viewTo += lendiff;
3044
      }
3045
    } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap
3046
      resetView(cm);
3047
    } else if (from <= display.viewFrom) { // Top overlap
3048
      var cut = viewCuttingPoint(cm, to, to + lendiff, 1);
3049
      if (cut) {
3050
        display.view = display.view.slice(cut.index);
3051
        display.viewFrom = cut.lineN;
3052
        display.viewTo += lendiff;
3053
      } else {
3054
        resetView(cm);
3055
      }
3056
    } else if (to >= display.viewTo) { // Bottom overlap
3057
      var cut$1 = viewCuttingPoint(cm, from, from, -1);
3058
      if (cut$1) {
3059
        display.view = display.view.slice(0, cut$1.index);
3060
        display.viewTo = cut$1.lineN;
3061
      } else {
3062
        resetView(cm);
3063
      }
3064
    } else { // Gap in the middle
3065
      var cutTop = viewCuttingPoint(cm, from, from, -1);
3066
      var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1);
3067
      if (cutTop && cutBot) {
3068
        display.view = display.view.slice(0, cutTop.index)
3069
          .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))
3070
          .concat(display.view.slice(cutBot.index));
3071
        display.viewTo += lendiff;
3072
      } else {
3073
        resetView(cm);
3074
      }
3075
    }
3076
 
3077
    var ext = display.externalMeasured;
3078
    if (ext) {
3079
      if (to < ext.lineN)
3080
        { ext.lineN += lendiff; }
3081
      else if (from < ext.lineN + ext.size)
3082
        { display.externalMeasured = null; }
3083
    }
3084
  }
3085
 
3086
  // Register a change to a single line. Type must be one of "text",
3087
  // "gutter", "class", "widget"
3088
  function regLineChange(cm, line, type) {
3089
    cm.curOp.viewChanged = true;
3090
    var display = cm.display, ext = cm.display.externalMeasured;
3091
    if (ext && line >= ext.lineN && line < ext.lineN + ext.size)
3092
      { display.externalMeasured = null; }
3093
 
3094
    if (line < display.viewFrom || line >= display.viewTo) { return }
3095
    var lineView = display.view[findViewIndex(cm, line)];
3096
    if (lineView.node == null) { return }
3097
    var arr = lineView.changes || (lineView.changes = []);
3098
    if (indexOf(arr, type) == -1) { arr.push(type); }
3099
  }
3100
 
3101
  // Clear the view.
3102
  function resetView(cm) {
3103
    cm.display.viewFrom = cm.display.viewTo = cm.doc.first;
3104
    cm.display.view = [];
3105
    cm.display.viewOffset = 0;
3106
  }
3107
 
3108
  function viewCuttingPoint(cm, oldN, newN, dir) {
3109
    var index = findViewIndex(cm, oldN), diff, view = cm.display.view;
3110
    if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)
3111
      { return {index: index, lineN: newN} }
3112
    var n = cm.display.viewFrom;
3113
    for (var i = 0; i < index; i++)
3114
      { n += view[i].size; }
3115
    if (n != oldN) {
3116
      if (dir > 0) {
3117
        if (index == view.length - 1) { return null }
3118
        diff = (n + view[index].size) - oldN;
3119
        index++;
3120
      } else {
3121
        diff = n - oldN;
3122
      }
3123
      oldN += diff; newN += diff;
3124
    }
3125
    while (visualLineNo(cm.doc, newN) != newN) {
3126
      if (index == (dir < 0 ? 0 : view.length - 1)) { return null }
3127
      newN += dir * view[index - (dir < 0 ? 1 : 0)].size;
3128
      index += dir;
3129
    }
3130
    return {index: index, lineN: newN}
3131
  }
3132
 
3133
  // Force the view to cover a given range, adding empty view element
3134
  // or clipping off existing ones as needed.
3135
  function adjustView(cm, from, to) {
3136
    var display = cm.display, view = display.view;
3137
    if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {
3138
      display.view = buildViewArray(cm, from, to);
3139
      display.viewFrom = from;
3140
    } else {
3141
      if (display.viewFrom > from)
3142
        { display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view); }
3143
      else if (display.viewFrom < from)
3144
        { display.view = display.view.slice(findViewIndex(cm, from)); }
3145
      display.viewFrom = from;
3146
      if (display.viewTo < to)
3147
        { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)); }
3148
      else if (display.viewTo > to)
3149
        { display.view = display.view.slice(0, findViewIndex(cm, to)); }
3150
    }
3151
    display.viewTo = to;
3152
  }
3153
 
3154
  // Count the number of lines in the view whose DOM representation is
3155
  // out of date (or nonexistent).
3156
  function countDirtyView(cm) {
3157
    var view = cm.display.view, dirty = 0;
3158
    for (var i = 0; i < view.length; i++) {
3159
      var lineView = view[i];
3160
      if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty; }
3161
    }
3162
    return dirty
3163
  }
3164
 
3165
  function updateSelection(cm) {
3166
    cm.display.input.showSelection(cm.display.input.prepareSelection());
3167
  }
3168
 
3169
  function prepareSelection(cm, primary) {
3170
    if ( primary === void 0 ) primary = true;
3171
 
3172
    var doc = cm.doc, result = {};
3173
    var curFragment = result.cursors = document.createDocumentFragment();
3174
    var selFragment = result.selection = document.createDocumentFragment();
3175
 
3176
    var customCursor = cm.options.$customCursor;
3177
    if (customCursor) { primary = true; }
3178
    for (var i = 0; i < doc.sel.ranges.length; i++) {
3179
      if (!primary && i == doc.sel.primIndex) { continue }
3180
      var range = doc.sel.ranges[i];
3181
      if (range.from().line >= cm.display.viewTo || range.to().line < cm.display.viewFrom) { continue }
3182
      var collapsed = range.empty();
3183
      if (customCursor) {
3184
        var head = customCursor(cm, range);
3185
        if (head) { drawSelectionCursor(cm, head, curFragment); }
3186
      } else if (collapsed || cm.options.showCursorWhenSelecting) {
3187
        drawSelectionCursor(cm, range.head, curFragment);
3188
      }
3189
      if (!collapsed)
3190
        { drawSelectionRange(cm, range, selFragment); }
3191
    }
3192
    return result
3193
  }
3194
 
3195
  // Draws a cursor for the given range
3196
  function drawSelectionCursor(cm, head, output) {
3197
    var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine);
3198
 
3199
    var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor"));
3200
    cursor.style.left = pos.left + "px";
3201
    cursor.style.top = pos.top + "px";
3202
    cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
3203
 
3204
    if (/\bcm-fat-cursor\b/.test(cm.getWrapperElement().className)) {
3205
      var charPos = charCoords(cm, head, "div", null, null);
3206
      var width = charPos.right - charPos.left;
3207
      cursor.style.width = (width > 0 ? width : cm.defaultCharWidth()) + "px";
3208
    }
3209
 
3210
    if (pos.other) {
3211
      // Secondary cursor, shown when on a 'jump' in bi-directional text
3212
      var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"));
3213
      otherCursor.style.display = "";
3214
      otherCursor.style.left = pos.other.left + "px";
3215
      otherCursor.style.top = pos.other.top + "px";
3216
      otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
3217
    }
3218
  }
3219
 
3220
  function cmpCoords(a, b) { return a.top - b.top || a.left - b.left }
3221
 
3222
  // Draws the given range as a highlighted selection
3223
  function drawSelectionRange(cm, range, output) {
3224
    var display = cm.display, doc = cm.doc;
3225
    var fragment = document.createDocumentFragment();
3226
    var padding = paddingH(cm.display), leftSide = padding.left;
3227
    var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right;
3228
    var docLTR = doc.direction == "ltr";
3229
 
3230
    function add(left, top, width, bottom) {
3231
      if (top < 0) { top = 0; }
3232
      top = Math.round(top);
3233
      bottom = Math.round(bottom);
3234
      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")));
3235
    }
3236
 
3237
    function drawForLine(line, fromArg, toArg) {
3238
      var lineObj = getLine(doc, line);
3239
      var lineLen = lineObj.text.length;
3240
      var start, end;
3241
      function coords(ch, bias) {
3242
        return charCoords(cm, Pos(line, ch), "div", lineObj, bias)
3243
      }
3244
 
3245
      function wrapX(pos, dir, side) {
3246
        var extent = wrappedLineExtentChar(cm, lineObj, null, pos);
3247
        var prop = (dir == "ltr") == (side == "after") ? "left" : "right";
3248
        var ch = side == "after" ? extent.begin : extent.end - (/\s/.test(lineObj.text.charAt(extent.end - 1)) ? 2 : 1);
3249
        return coords(ch, prop)[prop]
3250
      }
3251
 
3252
      var order = getOrder(lineObj, doc.direction);
3253
      iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir, i) {
3254
        var ltr = dir == "ltr";
3255
        var fromPos = coords(from, ltr ? "left" : "right");
3256
        var toPos = coords(to - 1, ltr ? "right" : "left");
3257
 
3258
        var openStart = fromArg == null && from == 0, openEnd = toArg == null && to == lineLen;
3259
        var first = i == 0, last = !order || i == order.length - 1;
3260
        if (toPos.top - fromPos.top <= 3) { // Single line
3261
          var openLeft = (docLTR ? openStart : openEnd) && first;
3262
          var openRight = (docLTR ? openEnd : openStart) && last;
3263
          var left = openLeft ? leftSide : (ltr ? fromPos : toPos).left;
3264
          var right = openRight ? rightSide : (ltr ? toPos : fromPos).right;
3265
          add(left, fromPos.top, right - left, fromPos.bottom);
3266
        } else { // Multiple lines
3267
          var topLeft, topRight, botLeft, botRight;
3268
          if (ltr) {
3269
            topLeft = docLTR && openStart && first ? leftSide : fromPos.left;
3270
            topRight = docLTR ? rightSide : wrapX(from, dir, "before");
3271
            botLeft = docLTR ? leftSide : wrapX(to, dir, "after");
3272
            botRight = docLTR && openEnd && last ? rightSide : toPos.right;
3273
          } else {
3274
            topLeft = !docLTR ? leftSide : wrapX(from, dir, "before");
3275
            topRight = !docLTR && openStart && first ? rightSide : fromPos.right;
3276
            botLeft = !docLTR && openEnd && last ? leftSide : toPos.left;
3277
            botRight = !docLTR ? rightSide : wrapX(to, dir, "after");
3278
          }
3279
          add(topLeft, fromPos.top, topRight - topLeft, fromPos.bottom);
3280
          if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top); }
3281
          add(botLeft, toPos.top, botRight - botLeft, toPos.bottom);
3282
        }
3283
 
3284
        if (!start || cmpCoords(fromPos, start) < 0) { start = fromPos; }
3285
        if (cmpCoords(toPos, start) < 0) { start = toPos; }
3286
        if (!end || cmpCoords(fromPos, end) < 0) { end = fromPos; }
3287
        if (cmpCoords(toPos, end) < 0) { end = toPos; }
3288
      });
3289
      return {start: start, end: end}
3290
    }
3291
 
3292
    var sFrom = range.from(), sTo = range.to();
3293
    if (sFrom.line == sTo.line) {
3294
      drawForLine(sFrom.line, sFrom.ch, sTo.ch);
3295
    } else {
3296
      var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line);
3297
      var singleVLine = visualLine(fromLine) == visualLine(toLine);
3298
      var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end;
3299
      var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start;
3300
      if (singleVLine) {
3301
        if (leftEnd.top < rightStart.top - 2) {
3302
          add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);
3303
          add(leftSide, rightStart.top, rightStart.left, rightStart.bottom);
3304
        } else {
3305
          add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);
3306
        }
3307
      }
3308
      if (leftEnd.bottom < rightStart.top)
3309
        { add(leftSide, leftEnd.bottom, null, rightStart.top); }
3310
    }
3311
 
3312
    output.appendChild(fragment);
3313
  }
3314
 
3315
  // Cursor-blinking
3316
  function restartBlink(cm) {
3317
    if (!cm.state.focused) { return }
3318
    var display = cm.display;
3319
    clearInterval(display.blinker);
3320
    var on = true;
3321
    display.cursorDiv.style.visibility = "";
3322
    if (cm.options.cursorBlinkRate > 0)
3323
      { display.blinker = setInterval(function () {
3324
        if (!cm.hasFocus()) { onBlur(cm); }
3325
        display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden";
3326
      }, cm.options.cursorBlinkRate); }
3327
    else if (cm.options.cursorBlinkRate < 0)
3328
      { display.cursorDiv.style.visibility = "hidden"; }
3329
  }
3330
 
3331
  function ensureFocus(cm) {
3332
    if (!cm.hasFocus()) {
3333
      cm.display.input.focus();
3334
      if (!cm.state.focused) { onFocus(cm); }
3335
    }
3336
  }
3337
 
3338
  function delayBlurEvent(cm) {
3339
    cm.state.delayingBlurEvent = true;
3340
    setTimeout(function () { if (cm.state.delayingBlurEvent) {
3341
      cm.state.delayingBlurEvent = false;
3342
      if (cm.state.focused) { onBlur(cm); }
3343
    } }, 100);
3344
  }
3345
 
3346
  function onFocus(cm, e) {
3347
    if (cm.state.delayingBlurEvent && !cm.state.draggingText) { cm.state.delayingBlurEvent = false; }
3348
 
3349
    if (cm.options.readOnly == "nocursor") { return }
3350
    if (!cm.state.focused) {
3351
      signal(cm, "focus", cm, e);
3352
      cm.state.focused = true;
3353
      addClass(cm.display.wrapper, "CodeMirror-focused");
3354
      // This test prevents this from firing when a context
3355
      // menu is closed (since the input reset would kill the
3356
      // select-all detection hack)
3357
      if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {
3358
        cm.display.input.reset();
3359
        if (webkit) { setTimeout(function () { return cm.display.input.reset(true); }, 20); } // Issue #1730
3360
      }
3361
      cm.display.input.receivedFocus();
3362
    }
3363
    restartBlink(cm);
3364
  }
3365
  function onBlur(cm, e) {
3366
    if (cm.state.delayingBlurEvent) { return }
3367
 
3368
    if (cm.state.focused) {
3369
      signal(cm, "blur", cm, e);
3370
      cm.state.focused = false;
3371
      rmClass(cm.display.wrapper, "CodeMirror-focused");
3372
    }
3373
    clearInterval(cm.display.blinker);
3374
    setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false; } }, 150);
3375
  }
3376
 
3377
  // Read the actual heights of the rendered lines, and update their
3378
  // stored heights to match.
3379
  function updateHeightsInViewport(cm) {
3380
    var display = cm.display;
3381
    var prevBottom = display.lineDiv.offsetTop;
3382
    var viewTop = Math.max(0, display.scroller.getBoundingClientRect().top);
3383
    var oldHeight = display.lineDiv.getBoundingClientRect().top;
3384
    var mustScroll = 0;
3385
    for (var i = 0; i < display.view.length; i++) {
3386
      var cur = display.view[i], wrapping = cm.options.lineWrapping;
3387
      var height = (void 0), width = 0;
3388
      if (cur.hidden) { continue }
3389
      oldHeight += cur.line.height;
3390
      if (ie && ie_version < 8) {
3391
        var bot = cur.node.offsetTop + cur.node.offsetHeight;
3392
        height = bot - prevBottom;
3393
        prevBottom = bot;
3394
      } else {
3395
        var box = cur.node.getBoundingClientRect();
3396
        height = box.bottom - box.top;
3397
        // Check that lines don't extend past the right of the current
3398
        // editor width
3399
        if (!wrapping && cur.text.firstChild)
3400
          { width = cur.text.firstChild.getBoundingClientRect().right - box.left - 1; }
3401
      }
3402
      var diff = cur.line.height - height;
3403
      if (diff > .005 || diff < -.005) {
3404
        if (oldHeight < viewTop) { mustScroll -= diff; }
3405
        updateLineHeight(cur.line, height);
3406
        updateWidgetHeight(cur.line);
3407
        if (cur.rest) { for (var j = 0; j < cur.rest.length; j++)
3408
          { updateWidgetHeight(cur.rest[j]); } }
3409
      }
3410
      if (width > cm.display.sizerWidth) {
3411
        var chWidth = Math.ceil(width / charWidth(cm.display));
3412
        if (chWidth > cm.display.maxLineLength) {
3413
          cm.display.maxLineLength = chWidth;
3414
          cm.display.maxLine = cur.line;
3415
          cm.display.maxLineChanged = true;
3416
        }
3417
      }
3418
    }
3419
    if (Math.abs(mustScroll) > 2) { display.scroller.scrollTop += mustScroll; }
3420
  }
3421
 
3422
  // Read and store the height of line widgets associated with the
3423
  // given line.
3424
  function updateWidgetHeight(line) {
3425
    if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i) {
3426
      var w = line.widgets[i], parent = w.node.parentNode;
3427
      if (parent) { w.height = parent.offsetHeight; }
3428
    } }
3429
  }
3430
 
3431
  // Compute the lines that are visible in a given viewport (defaults
3432
  // the the current scroll position). viewport may contain top,
3433
  // height, and ensure (see op.scrollToPos) properties.
3434
  function visibleLines(display, doc, viewport) {
3435
    var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop;
3436
    top = Math.floor(top - paddingTop(display));
3437
    var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight;
3438
 
3439
    var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom);
3440
    // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
3441
    // forces those lines into the viewport (if possible).
3442
    if (viewport && viewport.ensure) {
3443
      var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line;
3444
      if (ensureFrom < from) {
3445
        from = ensureFrom;
3446
        to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight);
3447
      } else if (Math.min(ensureTo, doc.lastLine()) >= to) {
3448
        from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight);
3449
        to = ensureTo;
3450
      }
3451
    }
3452
    return {from: from, to: Math.max(to, from + 1)}
3453
  }
3454
 
3455
  // SCROLLING THINGS INTO VIEW
3456
 
3457
  // If an editor sits on the top or bottom of the window, partially
3458
  // scrolled out of view, this ensures that the cursor is visible.
3459
  function maybeScrollWindow(cm, rect) {
3460
    if (signalDOMEvent(cm, "scrollCursorIntoView")) { return }
3461
 
3462
    var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;
3463
    var doc = display.wrapper.ownerDocument;
3464
    if (rect.top + box.top < 0) { doScroll = true; }
3465
    else if (rect.bottom + box.top > (doc.defaultView.innerHeight || doc.documentElement.clientHeight)) { doScroll = false; }
3466
    if (doScroll != null && !phantom) {
3467
      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;"));
3468
      cm.display.lineSpace.appendChild(scrollNode);
3469
      scrollNode.scrollIntoView(doScroll);
3470
      cm.display.lineSpace.removeChild(scrollNode);
3471
    }
3472
  }
3473
 
3474
  // Scroll a given position into view (immediately), verifying that
3475
  // it actually became visible (as line heights are accurately
3476
  // measured, the position of something may 'drift' during drawing).
3477
  function scrollPosIntoView(cm, pos, end, margin) {
3478
    if (margin == null) { margin = 0; }
3479
    var rect;
3480
    if (!cm.options.lineWrapping && pos == end) {
3481
      // Set pos and end to the cursor positions around the character pos sticks to
3482
      // If pos.sticky == "before", that is around pos.ch - 1, otherwise around pos.ch
3483
      // If pos == Pos(_, 0, "before"), pos and end are unchanged
3484
      end = pos.sticky == "before" ? Pos(pos.line, pos.ch + 1, "before") : pos;
3485
      pos = pos.ch ? Pos(pos.line, pos.sticky == "before" ? pos.ch - 1 : pos.ch, "after") : pos;
3486
    }
3487
    for (var limit = 0; limit < 5; limit++) {
3488
      var changed = false;
3489
      var coords = cursorCoords(cm, pos);
3490
      var endCoords = !end || end == pos ? coords : cursorCoords(cm, end);
3491
      rect = {left: Math.min(coords.left, endCoords.left),
3492
              top: Math.min(coords.top, endCoords.top) - margin,
3493
              right: Math.max(coords.left, endCoords.left),
3494
              bottom: Math.max(coords.bottom, endCoords.bottom) + margin};
3495
      var scrollPos = calculateScrollPos(cm, rect);
3496
      var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
3497
      if (scrollPos.scrollTop != null) {
3498
        updateScrollTop(cm, scrollPos.scrollTop);
3499
        if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true; }
3500
      }
3501
      if (scrollPos.scrollLeft != null) {
3502
        setScrollLeft(cm, scrollPos.scrollLeft);
3503
        if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true; }
3504
      }
3505
      if (!changed) { break }
3506
    }
3507
    return rect
3508
  }
3509
 
3510
  // Scroll a given set of coordinates into view (immediately).
3511
  function scrollIntoView(cm, rect) {
3512
    var scrollPos = calculateScrollPos(cm, rect);
3513
    if (scrollPos.scrollTop != null) { updateScrollTop(cm, scrollPos.scrollTop); }
3514
    if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft); }
3515
  }
3516
 
3517
  // Calculate a new scroll position needed to scroll the given
3518
  // rectangle into view. Returns an object with scrollTop and
3519
  // scrollLeft properties. When these are undefined, the
3520
  // vertical/horizontal position does not need to be adjusted.
3521
  function calculateScrollPos(cm, rect) {
3522
    var display = cm.display, snapMargin = textHeight(cm.display);
3523
    if (rect.top < 0) { rect.top = 0; }
3524
    var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop;
3525
    var screen = displayHeight(cm), result = {};
3526
    if (rect.bottom - rect.top > screen) { rect.bottom = rect.top + screen; }
3527
    var docBottom = cm.doc.height + paddingVert(display);
3528
    var atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMargin;
3529
    if (rect.top < screentop) {
3530
      result.scrollTop = atTop ? 0 : rect.top;
3531
    } else if (rect.bottom > screentop + screen) {
3532
      var newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen);
3533
      if (newTop != screentop) { result.scrollTop = newTop; }
3534
    }
3535
 
3536
    var gutterSpace = cm.options.fixedGutter ? 0 : display.gutters.offsetWidth;
3537
    var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft - gutterSpace;
3538
    var screenw = displayWidth(cm) - display.gutters.offsetWidth;
3539
    var tooWide = rect.right - rect.left > screenw;
3540
    if (tooWide) { rect.right = rect.left + screenw; }
3541
    if (rect.left < 10)
3542
      { result.scrollLeft = 0; }
3543
    else if (rect.left < screenleft)
3544
      { result.scrollLeft = Math.max(0, rect.left + gutterSpace - (tooWide ? 0 : 10)); }
3545
    else if (rect.right > screenw + screenleft - 3)
3546
      { result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw; }
3547
    return result
3548
  }
3549
 
3550
  // Store a relative adjustment to the scroll position in the current
3551
  // operation (to be applied when the operation finishes).
3552
  function addToScrollTop(cm, top) {
3553
    if (top == null) { return }
3554
    resolveScrollToPos(cm);
3555
    cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top;
3556
  }
3557
 
3558
  // Make sure that at the end of the operation the current cursor is
3559
  // shown.
3560
  function ensureCursorVisible(cm) {
3561
    resolveScrollToPos(cm);
3562
    var cur = cm.getCursor();
3563
    cm.curOp.scrollToPos = {from: cur, to: cur, margin: cm.options.cursorScrollMargin};
3564
  }
3565
 
3566
  function scrollToCoords(cm, x, y) {
3567
    if (x != null || y != null) { resolveScrollToPos(cm); }
3568
    if (x != null) { cm.curOp.scrollLeft = x; }
3569
    if (y != null) { cm.curOp.scrollTop = y; }
3570
  }
3571
 
3572
  function scrollToRange(cm, range) {
3573
    resolveScrollToPos(cm);
3574
    cm.curOp.scrollToPos = range;
3575
  }
3576
 
3577
  // When an operation has its scrollToPos property set, and another
3578
  // scroll action is applied before the end of the operation, this
3579
  // 'simulates' scrolling that position into view in a cheap way, so
3580
  // that the effect of intermediate scroll commands is not ignored.
3581
  function resolveScrollToPos(cm) {
3582
    var range = cm.curOp.scrollToPos;
3583
    if (range) {
3584
      cm.curOp.scrollToPos = null;
3585
      var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to);
3586
      scrollToCoordsRange(cm, from, to, range.margin);
3587
    }
3588
  }
3589
 
3590
  function scrollToCoordsRange(cm, from, to, margin) {
3591
    var sPos = calculateScrollPos(cm, {
3592
      left: Math.min(from.left, to.left),
3593
      top: Math.min(from.top, to.top) - margin,
3594
      right: Math.max(from.right, to.right),
3595
      bottom: Math.max(from.bottom, to.bottom) + margin
3596
    });
3597
    scrollToCoords(cm, sPos.scrollLeft, sPos.scrollTop);
3598
  }
3599
 
3600
  // Sync the scrollable area and scrollbars, ensure the viewport
3601
  // covers the visible area.
3602
  function updateScrollTop(cm, val) {
3603
    if (Math.abs(cm.doc.scrollTop - val) < 2) { return }
3604
    if (!gecko) { updateDisplaySimple(cm, {top: val}); }
3605
    setScrollTop(cm, val, true);
3606
    if (gecko) { updateDisplaySimple(cm); }
3607
    startWorker(cm, 100);
3608
  }
3609
 
3610
  function setScrollTop(cm, val, forceScroll) {
3611
    val = Math.max(0, Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val));
3612
    if (cm.display.scroller.scrollTop == val && !forceScroll) { return }
3613
    cm.doc.scrollTop = val;
3614
    cm.display.scrollbars.setScrollTop(val);
3615
    if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = val; }
3616
  }
3617
 
3618
  // Sync scroller and scrollbar, ensure the gutter elements are
3619
  // aligned.
3620
  function setScrollLeft(cm, val, isScroller, forceScroll) {
3621
    val = Math.max(0, Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth));
3622
    if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) { return }
3623
    cm.doc.scrollLeft = val;
3624
    alignHorizontally(cm);
3625
    if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val; }
3626
    cm.display.scrollbars.setScrollLeft(val);
3627
  }
3628
 
3629
  // SCROLLBARS
3630
 
3631
  // Prepare DOM reads needed to update the scrollbars. Done in one
3632
  // shot to minimize update/measure roundtrips.
3633
  function measureForScrollbars(cm) {
3634
    var d = cm.display, gutterW = d.gutters.offsetWidth;
3635
    var docH = Math.round(cm.doc.height + paddingVert(cm.display));
3636
    return {
3637
      clientHeight: d.scroller.clientHeight,
3638
      viewHeight: d.wrapper.clientHeight,
3639
      scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,
3640
      viewWidth: d.wrapper.clientWidth,
3641
      barLeft: cm.options.fixedGutter ? gutterW : 0,
3642
      docHeight: docH,
3643
      scrollHeight: docH + scrollGap(cm) + d.barHeight,
3644
      nativeBarWidth: d.nativeBarWidth,
3645
      gutterWidth: gutterW
3646
    }
3647
  }
3648
 
3649
  var NativeScrollbars = function(place, scroll, cm) {
3650
    this.cm = cm;
3651
    var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar");
3652
    var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar");
3653
    vert.tabIndex = horiz.tabIndex = -1;
3654
    place(vert); place(horiz);
3655
 
3656
    on(vert, "scroll", function () {
3657
      if (vert.clientHeight) { scroll(vert.scrollTop, "vertical"); }
3658
    });
3659
    on(horiz, "scroll", function () {
3660
      if (horiz.clientWidth) { scroll(horiz.scrollLeft, "horizontal"); }
3661
    });
3662
 
3663
    this.checkedZeroWidth = false;
3664
    // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
3665
    if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = "18px"; }
3666
  };
3667
 
3668
  NativeScrollbars.prototype.update = function (measure) {
3669
    var needsH = measure.scrollWidth > measure.clientWidth + 1;
3670
    var needsV = measure.scrollHeight > measure.clientHeight + 1;
3671
    var sWidth = measure.nativeBarWidth;
3672
 
3673
    if (needsV) {
3674
      this.vert.style.display = "block";
3675
      this.vert.style.bottom = needsH ? sWidth + "px" : "0";
3676
      var totalHeight = measure.viewHeight - (needsH ? sWidth : 0);
3677
      // A bug in IE8 can cause this value to be negative, so guard it.
3678
      this.vert.firstChild.style.height =
3679
        Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px";
3680
    } else {
3681
      this.vert.scrollTop = 0;
3682
      this.vert.style.display = "";
3683
      this.vert.firstChild.style.height = "0";
3684
    }
3685
 
3686
    if (needsH) {
3687
      this.horiz.style.display = "block";
3688
      this.horiz.style.right = needsV ? sWidth + "px" : "0";
3689
      this.horiz.style.left = measure.barLeft + "px";
3690
      var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0);
3691
      this.horiz.firstChild.style.width =
3692
        Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + "px";
3693
    } else {
3694
      this.horiz.style.display = "";
3695
      this.horiz.firstChild.style.width = "0";
3696
    }
3697
 
3698
    if (!this.checkedZeroWidth && measure.clientHeight > 0) {
3699
      if (sWidth == 0) { this.zeroWidthHack(); }
3700
      this.checkedZeroWidth = true;
3701
    }
3702
 
3703
    return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0}
3704
  };
3705
 
3706
  NativeScrollbars.prototype.setScrollLeft = function (pos) {
3707
    if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos; }
3708
    if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz, "horiz"); }
3709
  };
3710
 
3711
  NativeScrollbars.prototype.setScrollTop = function (pos) {
3712
    if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos; }
3713
    if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert, "vert"); }
3714
  };
3715
 
3716
  NativeScrollbars.prototype.zeroWidthHack = function () {
3717
    var w = mac && !mac_geMountainLion ? "12px" : "18px";
3718
    this.horiz.style.height = this.vert.style.width = w;
3719
    this.horiz.style.visibility = this.vert.style.visibility = "hidden";
3720
    this.disableHoriz = new Delayed;
3721
    this.disableVert = new Delayed;
3722
  };
3723
 
3724
  NativeScrollbars.prototype.enableZeroWidthBar = function (bar, delay, type) {
3725
    bar.style.visibility = "";
3726
    function maybeDisable() {
3727
      // To find out whether the scrollbar is still visible, we
3728
      // check whether the element under the pixel in the bottom
3729
      // right corner of the scrollbar box is the scrollbar box
3730
      // itself (when the bar is still visible) or its filler child
3731
      // (when the bar is hidden). If it is still visible, we keep
3732
      // it enabled, if it's hidden, we disable pointer events.
3733
      var box = bar.getBoundingClientRect();
3734
      var elt = type == "vert" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2)
3735
          : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1);
3736
      if (elt != bar) { bar.style.visibility = "hidden"; }
3737
      else { delay.set(1000, maybeDisable); }
3738
    }
3739
    delay.set(1000, maybeDisable);
3740
  };
3741
 
3742
  NativeScrollbars.prototype.clear = function () {
3743
    var parent = this.horiz.parentNode;
3744
    parent.removeChild(this.horiz);
3745
    parent.removeChild(this.vert);
3746
  };
3747
 
3748
  var NullScrollbars = function () {};
3749
 
3750
  NullScrollbars.prototype.update = function () { return {bottom: 0, right: 0} };
3751
  NullScrollbars.prototype.setScrollLeft = function () {};
3752
  NullScrollbars.prototype.setScrollTop = function () {};
3753
  NullScrollbars.prototype.clear = function () {};
3754
 
3755
  function updateScrollbars(cm, measure) {
3756
    if (!measure) { measure = measureForScrollbars(cm); }
3757
    var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight;
3758
    updateScrollbarsInner(cm, measure);
3759
    for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) {
3760
      if (startWidth != cm.display.barWidth && cm.options.lineWrapping)
3761
        { updateHeightsInViewport(cm); }
3762
      updateScrollbarsInner(cm, measureForScrollbars(cm));
3763
      startWidth = cm.display.barWidth; startHeight = cm.display.barHeight;
3764
    }
3765
  }
3766
 
3767
  // Re-synchronize the fake scrollbars with the actual size of the
3768
  // content.
3769
  function updateScrollbarsInner(cm, measure) {
3770
    var d = cm.display;
3771
    var sizes = d.scrollbars.update(measure);
3772
 
3773
    d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px";
3774
    d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px";
3775
    d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent";
3776
 
3777
    if (sizes.right && sizes.bottom) {
3778
      d.scrollbarFiller.style.display = "block";
3779
      d.scrollbarFiller.style.height = sizes.bottom + "px";
3780
      d.scrollbarFiller.style.width = sizes.right + "px";
3781
    } else { d.scrollbarFiller.style.display = ""; }
3782
    if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
3783
      d.gutterFiller.style.display = "block";
3784
      d.gutterFiller.style.height = sizes.bottom + "px";
3785
      d.gutterFiller.style.width = measure.gutterWidth + "px";
3786
    } else { d.gutterFiller.style.display = ""; }
3787
  }
3788
 
3789
  var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars};
3790
 
3791
  function initScrollbars(cm) {
3792
    if (cm.display.scrollbars) {
3793
      cm.display.scrollbars.clear();
3794
      if (cm.display.scrollbars.addClass)
3795
        { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass); }
3796
    }
3797
 
3798
    cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) {
3799
      cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller);
3800
      // Prevent clicks in the scrollbars from killing focus
3801
      on(node, "mousedown", function () {
3802
        if (cm.state.focused) { setTimeout(function () { return cm.display.input.focus(); }, 0); }
3803
      });
3804
      node.setAttribute("cm-not-content", "true");
3805
    }, function (pos, axis) {
3806
      if (axis == "horizontal") { setScrollLeft(cm, pos); }
3807
      else { updateScrollTop(cm, pos); }
3808
    }, cm);
3809
    if (cm.display.scrollbars.addClass)
3810
      { addClass(cm.display.wrapper, cm.display.scrollbars.addClass); }
3811
  }
3812
 
3813
  // Operations are used to wrap a series of changes to the editor
3814
  // state in such a way that each change won't have to update the
3815
  // cursor and display (which would be awkward, slow, and
3816
  // error-prone). Instead, display updates are batched and then all
3817
  // combined and executed at once.
3818
 
3819
  var nextOpId = 0;
3820
  // Start a new operation.
3821
  function startOperation(cm) {
3822
    cm.curOp = {
3823
      cm: cm,
3824
      viewChanged: false,      // Flag that indicates that lines might need to be redrawn
3825
      startHeight: cm.doc.height, // Used to detect need to update scrollbar
3826
      forceUpdate: false,      // Used to force a redraw
3827
      updateInput: 0,       // Whether to reset the input textarea
3828
      typing: false,           // Whether this reset should be careful to leave existing text (for compositing)
3829
      changeObjs: null,        // Accumulated changes, for firing change events
3830
      cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on
3831
      cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
3832
      selectionChanged: false, // Whether the selection needs to be redrawn
3833
      updateMaxLine: false,    // Set when the widest line needs to be determined anew
3834
      scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
3835
      scrollToPos: null,       // Used to scroll to a specific position
3836
      focus: false,
3837
      id: ++nextOpId,          // Unique ID
3838
      markArrays: null         // Used by addMarkedSpan
3839
    };
3840
    pushOperation(cm.curOp);
3841
  }
3842
 
3843
  // Finish an operation, updating the display and signalling delayed events
3844
  function endOperation(cm) {
3845
    var op = cm.curOp;
3846
    if (op) { finishOperation(op, function (group) {
3847
      for (var i = 0; i < group.ops.length; i++)
3848
        { group.ops[i].cm.curOp = null; }
3849
      endOperations(group);
3850
    }); }
3851
  }
3852
 
3853
  // The DOM updates done when an operation finishes are batched so
3854
  // that the minimum number of relayouts are required.
3855
  function endOperations(group) {
3856
    var ops = group.ops;
3857
    for (var i = 0; i < ops.length; i++) // Read DOM
3858
      { endOperation_R1(ops[i]); }
3859
    for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe)
3860
      { endOperation_W1(ops[i$1]); }
3861
    for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM
3862
      { endOperation_R2(ops[i$2]); }
3863
    for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe)
3864
      { endOperation_W2(ops[i$3]); }
3865
    for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM
3866
      { endOperation_finish(ops[i$4]); }
3867
  }
3868
 
3869
  function endOperation_R1(op) {
3870
    var cm = op.cm, display = cm.display;
3871
    maybeClipScrollbars(cm);
3872
    if (op.updateMaxLine) { findMaxLine(cm); }
3873
 
3874
    op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||
3875
      op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
3876
                         op.scrollToPos.to.line >= display.viewTo) ||
3877
      display.maxLineChanged && cm.options.lineWrapping;
3878
    op.update = op.mustUpdate &&
3879
      new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate);
3880
  }
3881
 
3882
  function endOperation_W1(op) {
3883
    op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update);
3884
  }
3885
 
3886
  function endOperation_R2(op) {
3887
    var cm = op.cm, display = cm.display;
3888
    if (op.updatedDisplay) { updateHeightsInViewport(cm); }
3889
 
3890
    op.barMeasure = measureForScrollbars(cm);
3891
 
3892
    // If the max line changed since it was last measured, measure it,
3893
    // and ensure the document's width matches it.
3894
    // updateDisplay_W2 will use these properties to do the actual resizing
3895
    if (display.maxLineChanged && !cm.options.lineWrapping) {
3896
      op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3;
3897
      cm.display.sizerWidth = op.adjustWidthTo;
3898
      op.barMeasure.scrollWidth =
3899
        Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth);
3900
      op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm));
3901
    }
3902
 
3903
    if (op.updatedDisplay || op.selectionChanged)
3904
      { op.preparedSelection = display.input.prepareSelection(); }
3905
  }
3906
 
3907
  function endOperation_W2(op) {
3908
    var cm = op.cm;
3909
 
3910
    if (op.adjustWidthTo != null) {
3911
      cm.display.sizer.style.minWidth = op.adjustWidthTo + "px";
3912
      if (op.maxScrollLeft < cm.doc.scrollLeft)
3913
        { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true); }
3914
      cm.display.maxLineChanged = false;
3915
    }
3916
 
3917
    var takeFocus = op.focus && op.focus == activeElt(root(cm));
3918
    if (op.preparedSelection)
3919
      { cm.display.input.showSelection(op.preparedSelection, takeFocus); }
3920
    if (op.updatedDisplay || op.startHeight != cm.doc.height)
3921
      { updateScrollbars(cm, op.barMeasure); }
3922
    if (op.updatedDisplay)
3923
      { setDocumentHeight(cm, op.barMeasure); }
3924
 
3925
    if (op.selectionChanged) { restartBlink(cm); }
3926
 
3927
    if (cm.state.focused && op.updateInput)
3928
      { cm.display.input.reset(op.typing); }
3929
    if (takeFocus) { ensureFocus(op.cm); }
3930
  }
3931
 
3932
  function endOperation_finish(op) {
3933
    var cm = op.cm, display = cm.display, doc = cm.doc;
3934
 
3935
    if (op.updatedDisplay) { postUpdateDisplay(cm, op.update); }
3936
 
3937
    // Abort mouse wheel delta measurement, when scrolling explicitly
3938
    if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))
3939
      { display.wheelStartX = display.wheelStartY = null; }
3940
 
3941
    // Propagate the scroll position to the actual DOM scroller
3942
    if (op.scrollTop != null) { setScrollTop(cm, op.scrollTop, op.forceScroll); }
3943
 
3944
    if (op.scrollLeft != null) { setScrollLeft(cm, op.scrollLeft, true, true); }
3945
    // If we need to scroll a specific position into view, do so.
3946
    if (op.scrollToPos) {
3947
      var rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
3948
                                   clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin);
3949
      maybeScrollWindow(cm, rect);
3950
    }
3951
 
3952
    // Fire events for markers that are hidden/unidden by editing or
3953
    // undoing
3954
    var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
3955
    if (hidden) { for (var i = 0; i < hidden.length; ++i)
3956
      { if (!hidden[i].lines.length) { signal(hidden[i], "hide"); } } }
3957
    if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1)
3958
      { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], "unhide"); } } }
3959
 
3960
    if (display.wrapper.offsetHeight)
3961
      { doc.scrollTop = cm.display.scroller.scrollTop; }
3962
 
3963
    // Fire change events, and delayed event handlers
3964
    if (op.changeObjs)
3965
      { signal(cm, "changes", cm, op.changeObjs); }
3966
    if (op.update)
3967
      { op.update.finish(); }
3968
  }
3969
 
3970
  // Run the given function in an operation
3971
  function runInOp(cm, f) {
3972
    if (cm.curOp) { return f() }
3973
    startOperation(cm);
3974
    try { return f() }
3975
    finally { endOperation(cm); }
3976
  }
3977
  // Wraps a function in an operation. Returns the wrapped function.
3978
  function operation(cm, f) {
3979
    return function() {
3980
      if (cm.curOp) { return f.apply(cm, arguments) }
3981
      startOperation(cm);
3982
      try { return f.apply(cm, arguments) }
3983
      finally { endOperation(cm); }
3984
    }
3985
  }
3986
  // Used to add methods to editor and doc instances, wrapping them in
3987
  // operations.
3988
  function methodOp(f) {
3989
    return function() {
3990
      if (this.curOp) { return f.apply(this, arguments) }
3991
      startOperation(this);
3992
      try { return f.apply(this, arguments) }
3993
      finally { endOperation(this); }
3994
    }
3995
  }
3996
  function docMethodOp(f) {
3997
    return function() {
3998
      var cm = this.cm;
3999
      if (!cm || cm.curOp) { return f.apply(this, arguments) }
4000
      startOperation(cm);
4001
      try { return f.apply(this, arguments) }
4002
      finally { endOperation(cm); }
4003
    }
4004
  }
4005
 
4006
  // HIGHLIGHT WORKER
4007
 
4008
  function startWorker(cm, time) {
4009
    if (cm.doc.highlightFrontier < cm.display.viewTo)
4010
      { cm.state.highlight.set(time, bind(highlightWorker, cm)); }
4011
  }
4012
 
4013
  function highlightWorker(cm) {
4014
    var doc = cm.doc;
4015
    if (doc.highlightFrontier >= cm.display.viewTo) { return }
4016
    var end = +new Date + cm.options.workTime;
4017
    var context = getContextBefore(cm, doc.highlightFrontier);
4018
    var changedLines = [];
4019
 
4020
    doc.iter(context.line, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function (line) {
4021
      if (context.line >= cm.display.viewFrom) { // Visible
4022
        var oldStyles = line.styles;
4023
        var resetState = line.text.length > cm.options.maxHighlightLength ? copyState(doc.mode, context.state) : null;
4024
        var highlighted = highlightLine(cm, line, context, true);
4025
        if (resetState) { context.state = resetState; }
4026
        line.styles = highlighted.styles;
4027
        var oldCls = line.styleClasses, newCls = highlighted.classes;
4028
        if (newCls) { line.styleClasses = newCls; }
4029
        else if (oldCls) { line.styleClasses = null; }
4030
        var ischange = !oldStyles || oldStyles.length != line.styles.length ||
4031
          oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass);
4032
        for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldStyles[i] != line.styles[i]; }
4033
        if (ischange) { changedLines.push(context.line); }
4034
        line.stateAfter = context.save();
4035
        context.nextLine();
4036
      } else {
4037
        if (line.text.length <= cm.options.maxHighlightLength)
4038
          { processLine(cm, line.text, context); }
4039
        line.stateAfter = context.line % 5 == 0 ? context.save() : null;
4040
        context.nextLine();
4041
      }
4042
      if (+new Date > end) {
4043
        startWorker(cm, cm.options.workDelay);
4044
        return true
4045
      }
4046
    });
4047
    doc.highlightFrontier = context.line;
4048
    doc.modeFrontier = Math.max(doc.modeFrontier, context.line);
4049
    if (changedLines.length) { runInOp(cm, function () {
4050
      for (var i = 0; i < changedLines.length; i++)
4051
        { regLineChange(cm, changedLines[i], "text"); }
4052
    }); }
4053
  }
4054
 
4055
  // DISPLAY DRAWING
4056
 
4057
  var DisplayUpdate = function(cm, viewport, force) {
4058
    var display = cm.display;
4059
 
4060
    this.viewport = viewport;
4061
    // Store some values that we'll need later (but don't want to force a relayout for)
4062
    this.visible = visibleLines(display, cm.doc, viewport);
4063
    this.editorIsHidden = !display.wrapper.offsetWidth;
4064
    this.wrapperHeight = display.wrapper.clientHeight;
4065
    this.wrapperWidth = display.wrapper.clientWidth;
4066
    this.oldDisplayWidth = displayWidth(cm);
4067
    this.force = force;
4068
    this.dims = getDimensions(cm);
4069
    this.events = [];
4070
  };
4071
 
4072
  DisplayUpdate.prototype.signal = function (emitter, type) {
4073
    if (hasHandler(emitter, type))
4074
      { this.events.push(arguments); }
4075
  };
4076
  DisplayUpdate.prototype.finish = function () {
4077
    for (var i = 0; i < this.events.length; i++)
4078
      { signal.apply(null, this.events[i]); }
4079
  };
4080
 
4081
  function maybeClipScrollbars(cm) {
4082
    var display = cm.display;
4083
    if (!display.scrollbarsClipped && display.scroller.offsetWidth) {
4084
      display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth;
4085
      display.heightForcer.style.height = scrollGap(cm) + "px";
4086
      display.sizer.style.marginBottom = -display.nativeBarWidth + "px";
4087
      display.sizer.style.borderRightWidth = scrollGap(cm) + "px";
4088
      display.scrollbarsClipped = true;
4089
    }
4090
  }
4091
 
4092
  function selectionSnapshot(cm) {
4093
    if (cm.hasFocus()) { return null }
4094
    var active = activeElt(root(cm));
4095
    if (!active || !contains(cm.display.lineDiv, active)) { return null }
4096
    var result = {activeElt: active};
4097
    if (window.getSelection) {
4098
      var sel = win(cm).getSelection();
4099
      if (sel.anchorNode && sel.extend && contains(cm.display.lineDiv, sel.anchorNode)) {
4100
        result.anchorNode = sel.anchorNode;
4101
        result.anchorOffset = sel.anchorOffset;
4102
        result.focusNode = sel.focusNode;
4103
        result.focusOffset = sel.focusOffset;
4104
      }
4105
    }
4106
    return result
4107
  }
4108
 
4109
  function restoreSelection(snapshot) {
4110
    if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt(rootNode(snapshot.activeElt))) { return }
4111
    snapshot.activeElt.focus();
4112
    if (!/^(INPUT|TEXTAREA)$/.test(snapshot.activeElt.nodeName) &&
4113
        snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) {
4114
      var doc = snapshot.activeElt.ownerDocument;
4115
      var sel = doc.defaultView.getSelection(), range = doc.createRange();
4116
      range.setEnd(snapshot.anchorNode, snapshot.anchorOffset);
4117
      range.collapse(false);
4118
      sel.removeAllRanges();
4119
      sel.addRange(range);
4120
      sel.extend(snapshot.focusNode, snapshot.focusOffset);
4121
    }
4122
  }
4123
 
4124
  // Does the actual updating of the line display. Bails out
4125
  // (returning false) when there is nothing to be done and forced is
4126
  // false.
4127
  function updateDisplayIfNeeded(cm, update) {
4128
    var display = cm.display, doc = cm.doc;
4129
 
4130
    if (update.editorIsHidden) {
4131
      resetView(cm);
4132
      return false
4133
    }
4134
 
4135
    // Bail out if the visible area is already rendered and nothing changed.
4136
    if (!update.force &&
4137
        update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&
4138
        (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&
4139
        display.renderedView == display.view && countDirtyView(cm) == 0)
4140
      { return false }
4141
 
4142
    if (maybeUpdateLineNumberWidth(cm)) {
4143
      resetView(cm);
4144
      update.dims = getDimensions(cm);
4145
    }
4146
 
4147
    // Compute a suitable new viewport (from & to)
4148
    var end = doc.first + doc.size;
4149
    var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first);
4150
    var to = Math.min(end, update.visible.to + cm.options.viewportMargin);
4151
    if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max(doc.first, display.viewFrom); }
4152
    if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, display.viewTo); }
4153
    if (sawCollapsedSpans) {
4154
      from = visualLineNo(cm.doc, from);
4155
      to = visualLineEndNo(cm.doc, to);
4156
    }
4157
 
4158
    var different = from != display.viewFrom || to != display.viewTo ||
4159
      display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth;
4160
    adjustView(cm, from, to);
4161
 
4162
    display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom));
4163
    // Position the mover div to align with the current scroll position
4164
    cm.display.mover.style.top = display.viewOffset + "px";
4165
 
4166
    var toUpdate = countDirtyView(cm);
4167
    if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view &&
4168
        (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))
4169
      { return false }
4170
 
4171
    // For big changes, we hide the enclosing element during the
4172
    // update, since that speeds up the operations on most browsers.
4173
    var selSnapshot = selectionSnapshot(cm);
4174
    if (toUpdate > 4) { display.lineDiv.style.display = "none"; }
4175
    patchDisplay(cm, display.updateLineNumbers, update.dims);
4176
    if (toUpdate > 4) { display.lineDiv.style.display = ""; }
4177
    display.renderedView = display.view;
4178
    // There might have been a widget with a focused element that got
4179
    // hidden or updated, if so re-focus it.
4180
    restoreSelection(selSnapshot);
4181
 
4182
    // Prevent selection and cursors from interfering with the scroll
4183
    // width and height.
4184
    removeChildren(display.cursorDiv);
4185
    removeChildren(display.selectionDiv);
4186
    display.gutters.style.height = display.sizer.style.minHeight = 0;
4187
 
4188
    if (different) {
4189
      display.lastWrapHeight = update.wrapperHeight;
4190
      display.lastWrapWidth = update.wrapperWidth;
4191
      startWorker(cm, 400);
4192
    }
4193
 
4194
    display.updateLineNumbers = null;
4195
 
4196
    return true
4197
  }
4198
 
4199
  function postUpdateDisplay(cm, update) {
4200
    var viewport = update.viewport;
4201
 
4202
    for (var first = true;; first = false) {
4203
      if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) {
4204
        // Clip forced viewport to actual scrollable area.
4205
        if (viewport && viewport.top != null)
4206
          { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)}; }
4207
        // Updated line heights might result in the drawn area not
4208
        // actually covering the viewport. Keep looping until it does.
4209
        update.visible = visibleLines(cm.display, cm.doc, viewport);
4210
        if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)
4211
          { break }
4212
      } else if (first) {
4213
        update.visible = visibleLines(cm.display, cm.doc, viewport);
4214
      }
4215
      if (!updateDisplayIfNeeded(cm, update)) { break }
4216
      updateHeightsInViewport(cm);
4217
      var barMeasure = measureForScrollbars(cm);
4218
      updateSelection(cm);
4219
      updateScrollbars(cm, barMeasure);
4220
      setDocumentHeight(cm, barMeasure);
4221
      update.force = false;
4222
    }
4223
 
4224
    update.signal(cm, "update", cm);
4225
    if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {
4226
      update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo);
4227
      cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo;
4228
    }
4229
  }
4230
 
4231
  function updateDisplaySimple(cm, viewport) {
4232
    var update = new DisplayUpdate(cm, viewport);
4233
    if (updateDisplayIfNeeded(cm, update)) {
4234
      updateHeightsInViewport(cm);
4235
      postUpdateDisplay(cm, update);
4236
      var barMeasure = measureForScrollbars(cm);
4237
      updateSelection(cm);
4238
      updateScrollbars(cm, barMeasure);
4239
      setDocumentHeight(cm, barMeasure);
4240
      update.finish();
4241
    }
4242
  }
4243
 
4244
  // Sync the actual display DOM structure with display.view, removing
4245
  // nodes for lines that are no longer in view, and creating the ones
4246
  // that are not there yet, and updating the ones that are out of
4247
  // date.
4248
  function patchDisplay(cm, updateNumbersFrom, dims) {
4249
    var display = cm.display, lineNumbers = cm.options.lineNumbers;
4250
    var container = display.lineDiv, cur = container.firstChild;
4251
 
4252
    function rm(node) {
4253
      var next = node.nextSibling;
4254
      // Works around a throw-scroll bug in OS X Webkit
4255
      if (webkit && mac && cm.display.currentWheelTarget == node)
4256
        { node.style.display = "none"; }
4257
      else
4258
        { node.parentNode.removeChild(node); }
4259
      return next
4260
    }
4261
 
4262
    var view = display.view, lineN = display.viewFrom;
4263
    // Loop over the elements in the view, syncing cur (the DOM nodes
4264
    // in display.lineDiv) with the view as we go.
4265
    for (var i = 0; i < view.length; i++) {
4266
      var lineView = view[i];
4267
      if (lineView.hidden) ; else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet
4268
        var node = buildLineElement(cm, lineView, lineN, dims);
4269
        container.insertBefore(node, cur);
4270
      } else { // Already drawn
4271
        while (cur != lineView.node) { cur = rm(cur); }
4272
        var updateNumber = lineNumbers && updateNumbersFrom != null &&
4273
          updateNumbersFrom <= lineN && lineView.lineNumber;
4274
        if (lineView.changes) {
4275
          if (indexOf(lineView.changes, "gutter") > -1) { updateNumber = false; }
4276
          updateLineForChanges(cm, lineView, lineN, dims);
4277
        }
4278
        if (updateNumber) {
4279
          removeChildren(lineView.lineNumber);
4280
          lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN)));
4281
        }
4282
        cur = lineView.node.nextSibling;
4283
      }
4284
      lineN += lineView.size;
4285
    }
4286
    while (cur) { cur = rm(cur); }
4287
  }
4288
 
4289
  function updateGutterSpace(display) {
4290
    var width = display.gutters.offsetWidth;
4291
    display.sizer.style.marginLeft = width + "px";
4292
    // Send an event to consumers responding to changes in gutter width.
4293
    signalLater(display, "gutterChanged", display);
4294
  }
4295
 
4296
  function setDocumentHeight(cm, measure) {
4297
    cm.display.sizer.style.minHeight = measure.docHeight + "px";
4298
    cm.display.heightForcer.style.top = measure.docHeight + "px";
4299
    cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px";
4300
  }
4301
 
4302
  // Re-align line numbers and gutter marks to compensate for
4303
  // horizontal scrolling.
4304
  function alignHorizontally(cm) {
4305
    var display = cm.display, view = display.view;
4306
    if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) { return }
4307
    var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;
4308
    var gutterW = display.gutters.offsetWidth, left = comp + "px";
4309
    for (var i = 0; i < view.length; i++) { if (!view[i].hidden) {
4310
      if (cm.options.fixedGutter) {
4311
        if (view[i].gutter)
4312
          { view[i].gutter.style.left = left; }
4313
        if (view[i].gutterBackground)
4314
          { view[i].gutterBackground.style.left = left; }
4315
      }
4316
      var align = view[i].alignable;
4317
      if (align) { for (var j = 0; j < align.length; j++)
4318
        { align[j].style.left = left; } }
4319
    } }
4320
    if (cm.options.fixedGutter)
4321
      { display.gutters.style.left = (comp + gutterW) + "px"; }
4322
  }
4323
 
4324
  // Used to ensure that the line number gutter is still the right
4325
  // size for the current document size. Returns true when an update
4326
  // is needed.
4327
  function maybeUpdateLineNumberWidth(cm) {
4328
    if (!cm.options.lineNumbers) { return false }
4329
    var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;
4330
    if (last.length != display.lineNumChars) {
4331
      var test = display.measure.appendChild(elt("div", [elt("div", last)],
4332
                                                 "CodeMirror-linenumber CodeMirror-gutter-elt"));
4333
      var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
4334
      display.lineGutter.style.width = "";
4335
      display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1;
4336
      display.lineNumWidth = display.lineNumInnerWidth + padding;
4337
      display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
4338
      display.lineGutter.style.width = display.lineNumWidth + "px";
4339
      updateGutterSpace(cm.display);
4340
      return true
4341
    }
4342
    return false
4343
  }
4344
 
4345
  function getGutters(gutters, lineNumbers) {
4346
    var result = [], sawLineNumbers = false;
4347
    for (var i = 0; i < gutters.length; i++) {
4348
      var name = gutters[i], style = null;
4349
      if (typeof name != "string") { style = name.style; name = name.className; }
4350
      if (name == "CodeMirror-linenumbers") {
4351
        if (!lineNumbers) { continue }
4352
        else { sawLineNumbers = true; }
4353
      }
4354
      result.push({className: name, style: style});
4355
    }
4356
    if (lineNumbers && !sawLineNumbers) { result.push({className: "CodeMirror-linenumbers", style: null}); }
4357
    return result
4358
  }
4359
 
4360
  // Rebuild the gutter elements, ensure the margin to the left of the
4361
  // code matches their width.
4362
  function renderGutters(display) {
4363
    var gutters = display.gutters, specs = display.gutterSpecs;
4364
    removeChildren(gutters);
4365
    display.lineGutter = null;
4366
    for (var i = 0; i < specs.length; ++i) {
4367
      var ref = specs[i];
4368
      var className = ref.className;
4369
      var style = ref.style;
4370
      var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + className));
4371
      if (style) { gElt.style.cssText = style; }
4372
      if (className == "CodeMirror-linenumbers") {
4373
        display.lineGutter = gElt;
4374
        gElt.style.width = (display.lineNumWidth || 1) + "px";
4375
      }
4376
    }
4377
    gutters.style.display = specs.length ? "" : "none";
4378
    updateGutterSpace(display);
4379
  }
4380
 
4381
  function updateGutters(cm) {
4382
    renderGutters(cm.display);
4383
    regChange(cm);
4384
    alignHorizontally(cm);
4385
  }
4386
 
4387
  // The display handles the DOM integration, both for input reading
4388
  // and content drawing. It holds references to DOM nodes and
4389
  // display-related state.
4390
 
4391
  function Display(place, doc, input, options) {
4392
    var d = this;
4393
    this.input = input;
4394
 
4395
    // Covers bottom-right square when both scrollbars are present.
4396
    d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
4397
    d.scrollbarFiller.setAttribute("cm-not-content", "true");
4398
    // Covers bottom of gutter when coverGutterNextToScrollbar is on
4399
    // and h scrollbar is present.
4400
    d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler");
4401
    d.gutterFiller.setAttribute("cm-not-content", "true");
4402
    // Will contain the actual code, positioned to cover the viewport.
4403
    d.lineDiv = eltP("div", null, "CodeMirror-code");
4404
    // Elements are added to these to represent selection and cursors.
4405
    d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
4406
    d.cursorDiv = elt("div", null, "CodeMirror-cursors");
4407
    // A visibility: hidden element used to find the size of things.
4408
    d.measure = elt("div", null, "CodeMirror-measure");
4409
    // When lines outside of the viewport are measured, they are drawn in this.
4410
    d.lineMeasure = elt("div", null, "CodeMirror-measure");
4411
    // Wraps everything that needs to exist inside the vertically-padded coordinate system
4412
    d.lineSpace = eltP("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],
4413
                      null, "position: relative; outline: none");
4414
    var lines = eltP("div", [d.lineSpace], "CodeMirror-lines");
4415
    // Moved around its parent to cover visible view.
4416
    d.mover = elt("div", [lines], null, "position: relative");
4417
    // Set to the height of the document, allowing scrolling.
4418
    d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
4419
    d.sizerWidth = null;
4420
    // Behavior of elts with overflow: auto and padding is
4421
    // inconsistent across browsers. This is used to ensure the
4422
    // scrollable area is big enough.
4423
    d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;");
4424
    // Will contain the gutters, if any.
4425
    d.gutters = elt("div", null, "CodeMirror-gutters");
4426
    d.lineGutter = null;
4427
    // Actual scrollable element.
4428
    d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll");
4429
    d.scroller.setAttribute("tabIndex", "-1");
4430
    // The element in which the editor lives.
4431
    d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror");
4432
    // See #6982. FIXME remove when this has been fixed for a while in Chrome
4433
    if (chrome && chrome_version >= 105) { d.wrapper.style.clipPath = "inset(0px)"; }
4434
 
4435
    // This attribute is respected by automatic translation systems such as Google Translate,
4436
    // and may also be respected by tools used by human translators.
4437
    d.wrapper.setAttribute('translate', 'no');
4438
 
4439
    // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)
4440
    if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
4441
    if (!webkit && !(gecko && mobile)) { d.scroller.draggable = true; }
4442
 
4443
    if (place) {
4444
      if (place.appendChild) { place.appendChild(d.wrapper); }
4445
      else { place(d.wrapper); }
4446
    }
4447
 
4448
    // Current rendered range (may be bigger than the view window).
4449
    d.viewFrom = d.viewTo = doc.first;
4450
    d.reportedViewFrom = d.reportedViewTo = doc.first;
4451
    // Information about the rendered lines.
4452
    d.view = [];
4453
    d.renderedView = null;
4454
    // Holds info about a single rendered line when it was rendered
4455
    // for measurement, while not in view.
4456
    d.externalMeasured = null;
4457
    // Empty space (in pixels) above the view
4458
    d.viewOffset = 0;
4459
    d.lastWrapHeight = d.lastWrapWidth = 0;
4460
    d.updateLineNumbers = null;
4461
 
4462
    d.nativeBarWidth = d.barHeight = d.barWidth = 0;
4463
    d.scrollbarsClipped = false;
4464
 
4465
    // Used to only resize the line number gutter when necessary (when
4466
    // the amount of lines crosses a boundary that makes its width change)
4467
    d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
4468
    // Set to true when a non-horizontal-scrolling line widget is
4469
    // added. As an optimization, line widget aligning is skipped when
4470
    // this is false.
4471
    d.alignWidgets = false;
4472
 
4473
    d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
4474
 
4475
    // Tracks the maximum line length so that the horizontal scrollbar
4476
    // can be kept static when scrolling.
4477
    d.maxLine = null;
4478
    d.maxLineLength = 0;
4479
    d.maxLineChanged = false;
4480
 
4481
    // Used for measuring wheel scrolling granularity
4482
    d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
4483
 
4484
    // True when shift is held down.
4485
    d.shift = false;
4486
 
4487
    // Used to track whether anything happened since the context menu
4488
    // was opened.
4489
    d.selForContextMenu = null;
4490
 
4491
    d.activeTouch = null;
4492
 
4493
    d.gutterSpecs = getGutters(options.gutters, options.lineNumbers);
4494
    renderGutters(d);
4495
 
4496
    input.init(d);
4497
  }
4498
 
4499
  // Since the delta values reported on mouse wheel events are
4500
  // unstandardized between browsers and even browser versions, and
4501
  // generally horribly unpredictable, this code starts by measuring
4502
  // the scroll effect that the first few mouse wheel events have,
4503
  // and, from that, detects the way it can convert deltas to pixel
4504
  // offsets afterwards.
4505
  //
4506
  // The reason we want to know the amount a wheel event will scroll
4507
  // is that it gives us a chance to update the display before the
4508
  // actual scrolling happens, reducing flickering.
4509
 
4510
  var wheelSamples = 0, wheelPixelsPerUnit = null;
4511
  // Fill in a browser-detected starting value on browsers where we
4512
  // know one. These don't have to be accurate -- the result of them
4513
  // being wrong would just be a slight flicker on the first wheel
4514
  // scroll (if it is large enough).
4515
  if (ie) { wheelPixelsPerUnit = -.53; }
4516
  else if (gecko) { wheelPixelsPerUnit = 15; }
4517
  else if (chrome) { wheelPixelsPerUnit = -.7; }
4518
  else if (safari) { wheelPixelsPerUnit = -1/3; }
4519
 
4520
  function wheelEventDelta(e) {
4521
    var dx = e.wheelDeltaX, dy = e.wheelDeltaY;
4522
    if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail; }
4523
    if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail; }
4524
    else if (dy == null) { dy = e.wheelDelta; }
4525
    return {x: dx, y: dy}
4526
  }
4527
  function wheelEventPixels(e) {
4528
    var delta = wheelEventDelta(e);
4529
    delta.x *= wheelPixelsPerUnit;
4530
    delta.y *= wheelPixelsPerUnit;
4531
    return delta
4532
  }
4533
 
4534
  function onScrollWheel(cm, e) {
4535
    // On Chrome 102, viewport updates somehow stop wheel-based
4536
    // scrolling. Turning off pointer events during the scroll seems
4537
    // to avoid the issue.
4538
    if (chrome && chrome_version == 102) {
4539
      if (cm.display.chromeScrollHack == null) { cm.display.sizer.style.pointerEvents = "none"; }
4540
      else { clearTimeout(cm.display.chromeScrollHack); }
4541
      cm.display.chromeScrollHack = setTimeout(function () {
4542
        cm.display.chromeScrollHack = null;
4543
        cm.display.sizer.style.pointerEvents = "";
4544
      }, 100);
4545
    }
4546
    var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y;
4547
    var pixelsPerUnit = wheelPixelsPerUnit;
4548
    if (e.deltaMode === 0) {
4549
      dx = e.deltaX;
4550
      dy = e.deltaY;
4551
      pixelsPerUnit = 1;
4552
    }
4553
 
4554
    var display = cm.display, scroll = display.scroller;
4555
    // Quit if there's nothing to scroll here
4556
    var canScrollX = scroll.scrollWidth > scroll.clientWidth;
4557
    var canScrollY = scroll.scrollHeight > scroll.clientHeight;
4558
    if (!(dx && canScrollX || dy && canScrollY)) { return }
4559
 
4560
    // Webkit browsers on OS X abort momentum scrolls when the target
4561
    // of the scroll event is removed from the scrollable element.
4562
    // This hack (see related code in patchDisplay) makes sure the
4563
    // element is kept around.
4564
    if (dy && mac && webkit) {
4565
      outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {
4566
        for (var i = 0; i < view.length; i++) {
4567
          if (view[i].node == cur) {
4568
            cm.display.currentWheelTarget = cur;
4569
            break outer
4570
          }
4571
        }
4572
      }
4573
    }
4574
 
4575
    // On some browsers, horizontal scrolling will cause redraws to
4576
    // happen before the gutter has been realigned, causing it to
4577
    // wriggle around in a most unseemly way. When we have an
4578
    // estimated pixels/delta value, we just handle horizontal
4579
    // scrolling entirely here. It'll be slightly off from native, but
4580
    // better than glitching out.
4581
    if (dx && !gecko && !presto && pixelsPerUnit != null) {
4582
      if (dy && canScrollY)
4583
        { updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * pixelsPerUnit)); }
4584
      setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * pixelsPerUnit));
4585
      // Only prevent default scrolling if vertical scrolling is
4586
      // actually possible. Otherwise, it causes vertical scroll
4587
      // jitter on OSX trackpads when deltaX is small and deltaY
4588
      // is large (issue #3579)
4589
      if (!dy || (dy && canScrollY))
4590
        { e_preventDefault(e); }
4591
      display.wheelStartX = null; // Abort measurement, if in progress
4592
      return
4593
    }
4594
 
4595
    // 'Project' the visible viewport to cover the area that is being
4596
    // scrolled into view (if we know enough to estimate it).
4597
    if (dy && pixelsPerUnit != null) {
4598
      var pixels = dy * pixelsPerUnit;
4599
      var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
4600
      if (pixels < 0) { top = Math.max(0, top + pixels - 50); }
4601
      else { bot = Math.min(cm.doc.height, bot + pixels + 50); }
4602
      updateDisplaySimple(cm, {top: top, bottom: bot});
4603
    }
4604
 
4605
    if (wheelSamples < 20 && e.deltaMode !== 0) {
4606
      if (display.wheelStartX == null) {
4607
        display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;
4608
        display.wheelDX = dx; display.wheelDY = dy;
4609
        setTimeout(function () {
4610
          if (display.wheelStartX == null) { return }
4611
          var movedX = scroll.scrollLeft - display.wheelStartX;
4612
          var movedY = scroll.scrollTop - display.wheelStartY;
4613
          var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
4614
            (movedX && display.wheelDX && movedX / display.wheelDX);
4615
          display.wheelStartX = display.wheelStartY = null;
4616
          if (!sample) { return }
4617
          wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
4618
          ++wheelSamples;
4619
        }, 200);
4620
      } else {
4621
        display.wheelDX += dx; display.wheelDY += dy;
4622
      }
4623
    }
4624
  }
4625
 
4626
  // Selection objects are immutable. A new one is created every time
4627
  // the selection changes. A selection is one or more non-overlapping
4628
  // (and non-touching) ranges, sorted, and an integer that indicates
4629
  // which one is the primary selection (the one that's scrolled into
4630
  // view, that getCursor returns, etc).
4631
  var Selection = function(ranges, primIndex) {
4632
    this.ranges = ranges;
4633
    this.primIndex = primIndex;
4634
  };
4635
 
4636
  Selection.prototype.primary = function () { return this.ranges[this.primIndex] };
4637
 
4638
  Selection.prototype.equals = function (other) {
4639
    if (other == this) { return true }
4640
    if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false }
4641
    for (var i = 0; i < this.ranges.length; i++) {
4642
      var here = this.ranges[i], there = other.ranges[i];
4643
      if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) { return false }
4644
    }
4645
    return true
4646
  };
4647
 
4648
  Selection.prototype.deepCopy = function () {
4649
    var out = [];
4650
    for (var i = 0; i < this.ranges.length; i++)
4651
      { out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head)); }
4652
    return new Selection(out, this.primIndex)
4653
  };
4654
 
4655
  Selection.prototype.somethingSelected = function () {
4656
    for (var i = 0; i < this.ranges.length; i++)
4657
      { if (!this.ranges[i].empty()) { return true } }
4658
    return false
4659
  };
4660
 
4661
  Selection.prototype.contains = function (pos, end) {
4662
    if (!end) { end = pos; }
4663
    for (var i = 0; i < this.ranges.length; i++) {
4664
      var range = this.ranges[i];
4665
      if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)
4666
        { return i }
4667
    }
4668
    return -1
4669
  };
4670
 
4671
  var Range = function(anchor, head) {
4672
    this.anchor = anchor; this.head = head;
4673
  };
4674
 
4675
  Range.prototype.from = function () { return minPos(this.anchor, this.head) };
4676
  Range.prototype.to = function () { return maxPos(this.anchor, this.head) };
4677
  Range.prototype.empty = function () { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch };
4678
 
4679
  // Take an unsorted, potentially overlapping set of ranges, and
4680
  // build a selection out of it. 'Consumes' ranges array (modifying
4681
  // it).
4682
  function normalizeSelection(cm, ranges, primIndex) {
4683
    var mayTouch = cm && cm.options.selectionsMayTouch;
4684
    var prim = ranges[primIndex];
4685
    ranges.sort(function (a, b) { return cmp(a.from(), b.from()); });
4686
    primIndex = indexOf(ranges, prim);
4687
    for (var i = 1; i < ranges.length; i++) {
4688
      var cur = ranges[i], prev = ranges[i - 1];
4689
      var diff = cmp(prev.to(), cur.from());
4690
      if (mayTouch && !cur.empty() ? diff > 0 : diff >= 0) {
4691
        var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to());
4692
        var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head;
4693
        if (i <= primIndex) { --primIndex; }
4694
        ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to));
4695
      }
4696
    }
4697
    return new Selection(ranges, primIndex)
4698
  }
4699
 
4700
  function simpleSelection(anchor, head) {
4701
    return new Selection([new Range(anchor, head || anchor)], 0)
4702
  }
4703
 
4704
  // Compute the position of the end of a change (its 'to' property
4705
  // refers to the pre-change end).
4706
  function changeEnd(change) {
4707
    if (!change.text) { return change.to }
4708
    return Pos(change.from.line + change.text.length - 1,
4709
               lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0))
4710
  }
4711
 
4712
  // Adjust a position to refer to the post-change position of the
4713
  // same text, or the end of the change if the change covers it.
4714
  function adjustForChange(pos, change) {
4715
    if (cmp(pos, change.from) < 0) { return pos }
4716
    if (cmp(pos, change.to) <= 0) { return changeEnd(change) }
4717
 
4718
    var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;
4719
    if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch; }
4720
    return Pos(line, ch)
4721
  }
4722
 
4723
  function computeSelAfterChange(doc, change) {
4724
    var out = [];
4725
    for (var i = 0; i < doc.sel.ranges.length; i++) {
4726
      var range = doc.sel.ranges[i];
4727
      out.push(new Range(adjustForChange(range.anchor, change),
4728
                         adjustForChange(range.head, change)));
4729
    }
4730
    return normalizeSelection(doc.cm, out, doc.sel.primIndex)
4731
  }
4732
 
4733
  function offsetPos(pos, old, nw) {
4734
    if (pos.line == old.line)
4735
      { return Pos(nw.line, pos.ch - old.ch + nw.ch) }
4736
    else
4737
      { return Pos(nw.line + (pos.line - old.line), pos.ch) }
4738
  }
4739
 
4740
  // Used by replaceSelections to allow moving the selection to the
4741
  // start or around the replaced test. Hint may be "start" or "around".
4742
  function computeReplacedSel(doc, changes, hint) {
4743
    var out = [];
4744
    var oldPrev = Pos(doc.first, 0), newPrev = oldPrev;
4745
    for (var i = 0; i < changes.length; i++) {
4746
      var change = changes[i];
4747
      var from = offsetPos(change.from, oldPrev, newPrev);
4748
      var to = offsetPos(changeEnd(change), oldPrev, newPrev);
4749
      oldPrev = change.to;
4750
      newPrev = to;
4751
      if (hint == "around") {
4752
        var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0;
4753
        out[i] = new Range(inv ? to : from, inv ? from : to);
4754
      } else {
4755
        out[i] = new Range(from, from);
4756
      }
4757
    }
4758
    return new Selection(out, doc.sel.primIndex)
4759
  }
4760
 
4761
  // Used to get the editor into a consistent state again when options change.
4762
 
4763
  function loadMode(cm) {
4764
    cm.doc.mode = getMode(cm.options, cm.doc.modeOption);
4765
    resetModeState(cm);
4766
  }
4767
 
4768
  function resetModeState(cm) {
4769
    cm.doc.iter(function (line) {
4770
      if (line.stateAfter) { line.stateAfter = null; }
4771
      if (line.styles) { line.styles = null; }
4772
    });
4773
    cm.doc.modeFrontier = cm.doc.highlightFrontier = cm.doc.first;
4774
    startWorker(cm, 100);
4775
    cm.state.modeGen++;
4776
    if (cm.curOp) { regChange(cm); }
4777
  }
4778
 
4779
  // DOCUMENT DATA STRUCTURE
4780
 
4781
  // By default, updates that start and end at the beginning of a line
4782
  // are treated specially, in order to make the association of line
4783
  // widgets and marker elements with the text behave more intuitive.
4784
  function isWholeLineUpdate(doc, change) {
4785
    return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" &&
4786
      (!doc.cm || doc.cm.options.wholeLineUpdateBefore)
4787
  }
4788
 
4789
  // Perform a change on the document data structure.
4790
  function updateDoc(doc, change, markedSpans, estimateHeight) {
4791
    function spansFor(n) {return markedSpans ? markedSpans[n] : null}
4792
    function update(line, text, spans) {
4793
      updateLine(line, text, spans, estimateHeight);
4794
      signalLater(line, "change", line, change);
4795
    }
4796
    function linesFor(start, end) {
4797
      var result = [];
4798
      for (var i = start; i < end; ++i)
4799
        { result.push(new Line(text[i], spansFor(i), estimateHeight)); }
4800
      return result
4801
    }
4802
 
4803
    var from = change.from, to = change.to, text = change.text;
4804
    var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
4805
    var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;
4806
 
4807
    // Adjust the line structure
4808
    if (change.full) {
4809
      doc.insert(0, linesFor(0, text.length));
4810
      doc.remove(text.length, doc.size - text.length);
4811
    } else if (isWholeLineUpdate(doc, change)) {
4812
      // This is a whole-line replace. Treated specially to make
4813
      // sure line objects move the way they are supposed to.
4814
      var added = linesFor(0, text.length - 1);
4815
      update(lastLine, lastLine.text, lastSpans);
4816
      if (nlines) { doc.remove(from.line, nlines); }
4817
      if (added.length) { doc.insert(from.line, added); }
4818
    } else if (firstLine == lastLine) {
4819
      if (text.length == 1) {
4820
        update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
4821
      } else {
4822
        var added$1 = linesFor(1, text.length - 1);
4823
        added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight));
4824
        update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
4825
        doc.insert(from.line + 1, added$1);
4826
      }
4827
    } else if (text.length == 1) {
4828
      update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0));
4829
      doc.remove(from.line + 1, nlines);
4830
    } else {
4831
      update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
4832
      update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
4833
      var added$2 = linesFor(1, text.length - 1);
4834
      if (nlines > 1) { doc.remove(from.line + 1, nlines - 1); }
4835
      doc.insert(from.line + 1, added$2);
4836
    }
4837
 
4838
    signalLater(doc, "change", doc, change);
4839
  }
4840
 
4841
  // Call f for all linked documents.
4842
  function linkedDocs(doc, f, sharedHistOnly) {
4843
    function propagate(doc, skip, sharedHist) {
4844
      if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) {
4845
        var rel = doc.linked[i];
4846
        if (rel.doc == skip) { continue }
4847
        var shared = sharedHist && rel.sharedHist;
4848
        if (sharedHistOnly && !shared) { continue }
4849
        f(rel.doc, shared);
4850
        propagate(rel.doc, doc, shared);
4851
      } }
4852
    }
4853
    propagate(doc, null, true);
4854
  }
4855
 
4856
  // Attach a document to an editor.
4857
  function attachDoc(cm, doc) {
4858
    if (doc.cm) { throw new Error("This document is already in use.") }
4859
    cm.doc = doc;
4860
    doc.cm = cm;
4861
    estimateLineHeights(cm);
4862
    loadMode(cm);
4863
    setDirectionClass(cm);
4864
    cm.options.direction = doc.direction;
4865
    if (!cm.options.lineWrapping) { findMaxLine(cm); }
4866
    cm.options.mode = doc.modeOption;
4867
    regChange(cm);
4868
  }
4869
 
4870
  function setDirectionClass(cm) {
4871
  (cm.doc.direction == "rtl" ? addClass : rmClass)(cm.display.lineDiv, "CodeMirror-rtl");
4872
  }
4873
 
4874
  function directionChanged(cm) {
4875
    runInOp(cm, function () {
4876
      setDirectionClass(cm);
4877
      regChange(cm);
4878
    });
4879
  }
4880
 
4881
  function History(prev) {
4882
    // Arrays of change events and selections. Doing something adds an
4883
    // event to done and clears undo. Undoing moves events from done
4884
    // to undone, redoing moves them in the other direction.
4885
    this.done = []; this.undone = [];
4886
    this.undoDepth = prev ? prev.undoDepth : Infinity;
4887
    // Used to track when changes can be merged into a single undo
4888
    // event
4889
    this.lastModTime = this.lastSelTime = 0;
4890
    this.lastOp = this.lastSelOp = null;
4891
    this.lastOrigin = this.lastSelOrigin = null;
4892
    // Used by the isClean() method
4893
    this.generation = this.maxGeneration = prev ? prev.maxGeneration : 1;
4894
  }
4895
 
4896
  // Create a history change event from an updateDoc-style change
4897
  // object.
4898
  function historyChangeFromChange(doc, change) {
4899
    var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
4900
    attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
4901
    linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); }, true);
4902
    return histChange
4903
  }
4904
 
4905
  // Pop all selection events off the end of a history array. Stop at
4906
  // a change event.
4907
  function clearSelectionEvents(array) {
4908
    while (array.length) {
4909
      var last = lst(array);
4910
      if (last.ranges) { array.pop(); }
4911
      else { break }
4912
    }
4913
  }
4914
 
4915
  // Find the top change event in the history. Pop off selection
4916
  // events that are in the way.
4917
  function lastChangeEvent(hist, force) {
4918
    if (force) {
4919
      clearSelectionEvents(hist.done);
4920
      return lst(hist.done)
4921
    } else if (hist.done.length && !lst(hist.done).ranges) {
4922
      return lst(hist.done)
4923
    } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) {
4924
      hist.done.pop();
4925
      return lst(hist.done)
4926
    }
4927
  }
4928
 
4929
  // Register a change in the history. Merges changes that are within
4930
  // a single operation, or are close together with an origin that
4931
  // allows merging (starting with "+") into a single event.
4932
  function addChangeToHistory(doc, change, selAfter, opId) {
4933
    var hist = doc.history;
4934
    hist.undone.length = 0;
4935
    var time = +new Date, cur;
4936
    var last;
4937
 
4938
    if ((hist.lastOp == opId ||
4939
         hist.lastOrigin == change.origin && change.origin &&
4940
         ((change.origin.charAt(0) == "+" && hist.lastModTime > time - (doc.cm ? doc.cm.options.historyEventDelay : 500)) ||
4941
          change.origin.charAt(0) == "*")) &&
4942
        (cur = lastChangeEvent(hist, hist.lastOp == opId))) {
4943
      // Merge this change into the last event
4944
      last = lst(cur.changes);
4945
      if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) {
4946
        // Optimized case for simple insertion -- don't want to add
4947
        // new changesets for every character typed
4948
        last.to = changeEnd(change);
4949
      } else {
4950
        // Add new sub-event
4951
        cur.changes.push(historyChangeFromChange(doc, change));
4952
      }
4953
    } else {
4954
      // Can not be merged, start a new event.
4955
      var before = lst(hist.done);
4956
      if (!before || !before.ranges)
4957
        { pushSelectionToHistory(doc.sel, hist.done); }
4958
      cur = {changes: [historyChangeFromChange(doc, change)],
4959
             generation: hist.generation};
4960
      hist.done.push(cur);
4961
      while (hist.done.length > hist.undoDepth) {
4962
        hist.done.shift();
4963
        if (!hist.done[0].ranges) { hist.done.shift(); }
4964
      }
4965
    }
4966
    hist.done.push(selAfter);
4967
    hist.generation = ++hist.maxGeneration;
4968
    hist.lastModTime = hist.lastSelTime = time;
4969
    hist.lastOp = hist.lastSelOp = opId;
4970
    hist.lastOrigin = hist.lastSelOrigin = change.origin;
4971
 
4972
    if (!last) { signal(doc, "historyAdded"); }
4973
  }
4974
 
4975
  function selectionEventCanBeMerged(doc, origin, prev, sel) {
4976
    var ch = origin.charAt(0);
4977
    return ch == "*" ||
4978
      ch == "+" &&
4979
      prev.ranges.length == sel.ranges.length &&
4980
      prev.somethingSelected() == sel.somethingSelected() &&
4981
      new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500)
4982
  }
4983
 
4984
  // Called whenever the selection changes, sets the new selection as
4985
  // the pending selection in the history, and pushes the old pending
4986
  // selection into the 'done' array when it was significantly
4987
  // different (in number of selected ranges, emptiness, or time).
4988
  function addSelectionToHistory(doc, sel, opId, options) {
4989
    var hist = doc.history, origin = options && options.origin;
4990
 
4991
    // A new event is started when the previous origin does not match
4992
    // the current, or the origins don't allow matching. Origins
4993
    // starting with * are always merged, those starting with + are
4994
    // merged when similar and close together in time.
4995
    if (opId == hist.lastSelOp ||
4996
        (origin && hist.lastSelOrigin == origin &&
4997
         (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||
4998
          selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))
4999
      { hist.done[hist.done.length - 1] = sel; }
5000
    else
5001
      { pushSelectionToHistory(sel, hist.done); }
5002
 
5003
    hist.lastSelTime = +new Date;
5004
    hist.lastSelOrigin = origin;
5005
    hist.lastSelOp = opId;
5006
    if (options && options.clearRedo !== false)
5007
      { clearSelectionEvents(hist.undone); }
5008
  }
5009
 
5010
  function pushSelectionToHistory(sel, dest) {
5011
    var top = lst(dest);
5012
    if (!(top && top.ranges && top.equals(sel)))
5013
      { dest.push(sel); }
5014
  }
5015
 
5016
  // Used to store marked span information in the history.
5017
  function attachLocalSpans(doc, change, from, to) {
5018
    var existing = change["spans_" + doc.id], n = 0;
5019
    doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function (line) {
5020
      if (line.markedSpans)
5021
        { (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans; }
5022
      ++n;
5023
    });
5024
  }
5025
 
5026
  // When un/re-doing restores text containing marked spans, those
5027
  // that have been explicitly cleared should not be restored.
5028
  function removeClearedSpans(spans) {
5029
    if (!spans) { return null }
5030
    var out;
5031
    for (var i = 0; i < spans.length; ++i) {
5032
      if (spans[i].marker.explicitlyCleared) { if (!out) { out = spans.slice(0, i); } }
5033
      else if (out) { out.push(spans[i]); }
5034
    }
5035
    return !out ? spans : out.length ? out : null
5036
  }
5037
 
5038
  // Retrieve and filter the old marked spans stored in a change event.
5039
  function getOldSpans(doc, change) {
5040
    var found = change["spans_" + doc.id];
5041
    if (!found) { return null }
5042
    var nw = [];
5043
    for (var i = 0; i < change.text.length; ++i)
5044
      { nw.push(removeClearedSpans(found[i])); }
5045
    return nw
5046
  }
5047
 
5048
  // Used for un/re-doing changes from the history. Combines the
5049
  // result of computing the existing spans with the set of spans that
5050
  // existed in the history (so that deleting around a span and then
5051
  // undoing brings back the span).
5052
  function mergeOldSpans(doc, change) {
5053
    var old = getOldSpans(doc, change);
5054
    var stretched = stretchSpansOverChange(doc, change);
5055
    if (!old) { return stretched }
5056
    if (!stretched) { return old }
5057
 
5058
    for (var i = 0; i < old.length; ++i) {
5059
      var oldCur = old[i], stretchCur = stretched[i];
5060
      if (oldCur && stretchCur) {
5061
        spans: for (var j = 0; j < stretchCur.length; ++j) {
5062
          var span = stretchCur[j];
5063
          for (var k = 0; k < oldCur.length; ++k)
5064
            { if (oldCur[k].marker == span.marker) { continue spans } }
5065
          oldCur.push(span);
5066
        }
5067
      } else if (stretchCur) {
5068
        old[i] = stretchCur;
5069
      }
5070
    }
5071
    return old
5072
  }
5073
 
5074
  // Used both to provide a JSON-safe object in .getHistory, and, when
5075
  // detaching a document, to split the history in two
5076
  function copyHistoryArray(events, newGroup, instantiateSel) {
5077
    var copy = [];
5078
    for (var i = 0; i < events.length; ++i) {
5079
      var event = events[i];
5080
      if (event.ranges) {
5081
        copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event);
5082
        continue
5083
      }
5084
      var changes = event.changes, newChanges = [];
5085
      copy.push({changes: newChanges});
5086
      for (var j = 0; j < changes.length; ++j) {
5087
        var change = changes[j], m = (void 0);
5088
        newChanges.push({from: change.from, to: change.to, text: change.text});
5089
        if (newGroup) { for (var prop in change) { if (m = prop.match(/^spans_(\d+)$/)) {
5090
          if (indexOf(newGroup, Number(m[1])) > -1) {
5091
            lst(newChanges)[prop] = change[prop];
5092
            delete change[prop];
5093
          }
5094
        } } }
5095
      }
5096
    }
5097
    return copy
5098
  }
5099
 
5100
  // The 'scroll' parameter given to many of these indicated whether
5101
  // the new cursor position should be scrolled into view after
5102
  // modifying the selection.
5103
 
5104
  // If shift is held or the extend flag is set, extends a range to
5105
  // include a given position (and optionally a second position).
5106
  // Otherwise, simply returns the range between the given positions.
5107
  // Used for cursor motion and such.
5108
  function extendRange(range, head, other, extend) {
5109
    if (extend) {
5110
      var anchor = range.anchor;
5111
      if (other) {
5112
        var posBefore = cmp(head, anchor) < 0;
5113
        if (posBefore != (cmp(other, anchor) < 0)) {
5114
          anchor = head;
5115
          head = other;
5116
        } else if (posBefore != (cmp(head, other) < 0)) {
5117
          head = other;
5118
        }
5119
      }
5120
      return new Range(anchor, head)
5121
    } else {
5122
      return new Range(other || head, head)
5123
    }
5124
  }
5125
 
5126
  // Extend the primary selection range, discard the rest.
5127
  function extendSelection(doc, head, other, options, extend) {
5128
    if (extend == null) { extend = doc.cm && (doc.cm.display.shift || doc.extend); }
5129
    setSelection(doc, new Selection([extendRange(doc.sel.primary(), head, other, extend)], 0), options);
5130
  }
5131
 
5132
  // Extend all selections (pos is an array of selections with length
5133
  // equal the number of selections)
5134
  function extendSelections(doc, heads, options) {
5135
    var out = [];
5136
    var extend = doc.cm && (doc.cm.display.shift || doc.extend);
5137
    for (var i = 0; i < doc.sel.ranges.length; i++)
5138
      { out[i] = extendRange(doc.sel.ranges[i], heads[i], null, extend); }
5139
    var newSel = normalizeSelection(doc.cm, out, doc.sel.primIndex);
5140
    setSelection(doc, newSel, options);
5141
  }
5142
 
5143
  // Updates a single range in the selection.
5144
  function replaceOneSelection(doc, i, range, options) {
5145
    var ranges = doc.sel.ranges.slice(0);
5146
    ranges[i] = range;
5147
    setSelection(doc, normalizeSelection(doc.cm, ranges, doc.sel.primIndex), options);
5148
  }
5149
 
5150
  // Reset the selection to a single range.
5151
  function setSimpleSelection(doc, anchor, head, options) {
5152
    setSelection(doc, simpleSelection(anchor, head), options);
5153
  }
5154
 
5155
  // Give beforeSelectionChange handlers a change to influence a
5156
  // selection update.
5157
  function filterSelectionChange(doc, sel, options) {
5158
    var obj = {
5159
      ranges: sel.ranges,
5160
      update: function(ranges) {
5161
        this.ranges = [];
5162
        for (var i = 0; i < ranges.length; i++)
5163
          { this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),
5164
                                     clipPos(doc, ranges[i].head)); }
5165
      },
5166
      origin: options && options.origin
5167
    };
5168
    signal(doc, "beforeSelectionChange", doc, obj);
5169
    if (doc.cm) { signal(doc.cm, "beforeSelectionChange", doc.cm, obj); }
5170
    if (obj.ranges != sel.ranges) { return normalizeSelection(doc.cm, obj.ranges, obj.ranges.length - 1) }
5171
    else { return sel }
5172
  }
5173
 
5174
  function setSelectionReplaceHistory(doc, sel, options) {
5175
    var done = doc.history.done, last = lst(done);
5176
    if (last && last.ranges) {
5177
      done[done.length - 1] = sel;
5178
      setSelectionNoUndo(doc, sel, options);
5179
    } else {
5180
      setSelection(doc, sel, options);
5181
    }
5182
  }
5183
 
5184
  // Set a new selection.
5185
  function setSelection(doc, sel, options) {
5186
    setSelectionNoUndo(doc, sel, options);
5187
    addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options);
5188
  }
5189
 
5190
  function setSelectionNoUndo(doc, sel, options) {
5191
    if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange"))
5192
      { sel = filterSelectionChange(doc, sel, options); }
5193
 
5194
    var bias = options && options.bias ||
5195
      (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1);
5196
    setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true));
5197
 
5198
    if (!(options && options.scroll === false) && doc.cm && doc.cm.getOption("readOnly") != "nocursor")
5199
      { ensureCursorVisible(doc.cm); }
5200
  }
5201
 
5202
  function setSelectionInner(doc, sel) {
5203
    if (sel.equals(doc.sel)) { return }
5204
 
5205
    doc.sel = sel;
5206
 
5207
    if (doc.cm) {
5208
      doc.cm.curOp.updateInput = 1;
5209
      doc.cm.curOp.selectionChanged = true;
5210
      signalCursorActivity(doc.cm);
5211
    }
5212
    signalLater(doc, "cursorActivity", doc);
5213
  }
5214
 
5215
  // Verify that the selection does not partially select any atomic
5216
  // marked ranges.
5217
  function reCheckSelection(doc) {
5218
    setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false));
5219
  }
5220
 
5221
  // Return a selection that does not partially select any atomic
5222
  // ranges.
5223
  function skipAtomicInSelection(doc, sel, bias, mayClear) {
5224
    var out;
5225
    for (var i = 0; i < sel.ranges.length; i++) {
5226
      var range = sel.ranges[i];
5227
      var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i];
5228
      var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear);
5229
      var newHead = range.head == range.anchor ? newAnchor : skipAtomic(doc, range.head, old && old.head, bias, mayClear);
5230
      if (out || newAnchor != range.anchor || newHead != range.head) {
5231
        if (!out) { out = sel.ranges.slice(0, i); }
5232
        out[i] = new Range(newAnchor, newHead);
5233
      }
5234
    }
5235
    return out ? normalizeSelection(doc.cm, out, sel.primIndex) : sel
5236
  }
5237
 
5238
  function skipAtomicInner(doc, pos, oldPos, dir, mayClear) {
5239
    var line = getLine(doc, pos.line);
5240
    if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
5241
      var sp = line.markedSpans[i], m = sp.marker;
5242
 
5243
      // Determine if we should prevent the cursor being placed to the left/right of an atomic marker
5244
      // Historically this was determined using the inclusiveLeft/Right option, but the new way to control it
5245
      // is with selectLeft/Right
5246
      var preventCursorLeft = ("selectLeft" in m) ? !m.selectLeft : m.inclusiveLeft;
5247
      var preventCursorRight = ("selectRight" in m) ? !m.selectRight : m.inclusiveRight;
5248
 
5249
      if ((sp.from == null || (preventCursorLeft ? sp.from <= pos.ch : sp.from < pos.ch)) &&
5250
          (sp.to == null || (preventCursorRight ? sp.to >= pos.ch : sp.to > pos.ch))) {
5251
        if (mayClear) {
5252
          signal(m, "beforeCursorEnter");
5253
          if (m.explicitlyCleared) {
5254
            if (!line.markedSpans) { break }
5255
            else {--i; continue}
5256
          }
5257
        }
5258
        if (!m.atomic) { continue }
5259
 
5260
        if (oldPos) {
5261
          var near = m.find(dir < 0 ? 1 : -1), diff = (void 0);
5262
          if (dir < 0 ? preventCursorRight : preventCursorLeft)
5263
            { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null); }
5264
          if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0))
5265
            { return skipAtomicInner(doc, near, pos, dir, mayClear) }
5266
        }
5267
 
5268
        var far = m.find(dir < 0 ? -1 : 1);
5269
        if (dir < 0 ? preventCursorLeft : preventCursorRight)
5270
          { far = movePos(doc, far, dir, far.line == pos.line ? line : null); }
5271
        return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null
5272
      }
5273
    } }
5274
    return pos
5275
  }
5276
 
5277
  // Ensure a given position is not inside an atomic range.
5278
  function skipAtomic(doc, pos, oldPos, bias, mayClear) {
5279
    var dir = bias || 1;
5280
    var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) ||
5281
        (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) ||
5282
        skipAtomicInner(doc, pos, oldPos, -dir, mayClear) ||
5283
        (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true));
5284
    if (!found) {
5285
      doc.cantEdit = true;
5286
      return Pos(doc.first, 0)
5287
    }
5288
    return found
5289
  }
5290
 
5291
  function movePos(doc, pos, dir, line) {
5292
    if (dir < 0 && pos.ch == 0) {
5293
      if (pos.line > doc.first) { return clipPos(doc, Pos(pos.line - 1)) }
5294
      else { return null }
5295
    } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) {
5296
      if (pos.line < doc.first + doc.size - 1) { return Pos(pos.line + 1, 0) }
5297
      else { return null }
5298
    } else {
5299
      return new Pos(pos.line, pos.ch + dir)
5300
    }
5301
  }
5302
 
5303
  function selectAll(cm) {
5304
    cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll);
5305
  }
5306
 
5307
  // UPDATING
5308
 
5309
  // Allow "beforeChange" event handlers to influence a change
5310
  function filterChange(doc, change, update) {
5311
    var obj = {
5312
      canceled: false,
5313
      from: change.from,
5314
      to: change.to,
5315
      text: change.text,
5316
      origin: change.origin,
5317
      cancel: function () { return obj.canceled = true; }
5318
    };
5319
    if (update) { obj.update = function (from, to, text, origin) {
5320
      if (from) { obj.from = clipPos(doc, from); }
5321
      if (to) { obj.to = clipPos(doc, to); }
5322
      if (text) { obj.text = text; }
5323
      if (origin !== undefined) { obj.origin = origin; }
5324
    }; }
5325
    signal(doc, "beforeChange", doc, obj);
5326
    if (doc.cm) { signal(doc.cm, "beforeChange", doc.cm, obj); }
5327
 
5328
    if (obj.canceled) {
5329
      if (doc.cm) { doc.cm.curOp.updateInput = 2; }
5330
      return null
5331
    }
5332
    return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin}
5333
  }
5334
 
5335
  // Apply a change to a document, and add it to the document's
5336
  // history, and propagating it to all linked documents.
5337
  function makeChange(doc, change, ignoreReadOnly) {
5338
    if (doc.cm) {
5339
      if (!doc.cm.curOp) { return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) }
5340
      if (doc.cm.state.suppressEdits) { return }
5341
    }
5342
 
5343
    if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
5344
      change = filterChange(doc, change, true);
5345
      if (!change) { return }
5346
    }
5347
 
5348
    // Possibly split or suppress the update based on the presence
5349
    // of read-only spans in its range.
5350
    var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);
5351
    if (split) {
5352
      for (var i = split.length - 1; i >= 0; --i)
5353
        { makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text, origin: change.origin}); }
5354
    } else {
5355
      makeChangeInner(doc, change);
5356
    }
5357
  }
5358
 
5359
  function makeChangeInner(doc, change) {
5360
    if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) { return }
5361
    var selAfter = computeSelAfterChange(doc, change);
5362
    addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);
5363
 
5364
    makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));
5365
    var rebased = [];
5366
 
5367
    linkedDocs(doc, function (doc, sharedHist) {
5368
      if (!sharedHist && indexOf(rebased, doc.history) == -1) {
5369
        rebaseHist(doc.history, change);
5370
        rebased.push(doc.history);
5371
      }
5372
      makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change));
5373
    });
5374
  }
5375
 
5376
  // Revert a change stored in a document's history.
5377
  function makeChangeFromHistory(doc, type, allowSelectionOnly) {
5378
    var suppress = doc.cm && doc.cm.state.suppressEdits;
5379
    if (suppress && !allowSelectionOnly) { return }
5380
 
5381
    var hist = doc.history, event, selAfter = doc.sel;
5382
    var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done;
5383
 
5384
    // Verify that there is a useable event (so that ctrl-z won't
5385
    // needlessly clear selection events)
5386
    var i = 0;
5387
    for (; i < source.length; i++) {
5388
      event = source[i];
5389
      if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges)
5390
        { break }
5391
    }
5392
    if (i == source.length) { return }
5393
    hist.lastOrigin = hist.lastSelOrigin = null;
5394
 
5395
    for (;;) {
5396
      event = source.pop();
5397
      if (event.ranges) {
5398
        pushSelectionToHistory(event, dest);
5399
        if (allowSelectionOnly && !event.equals(doc.sel)) {
5400
          setSelection(doc, event, {clearRedo: false});
5401
          return
5402
        }
5403
        selAfter = event;
5404
      } else if (suppress) {
5405
        source.push(event);
5406
        return
5407
      } else { break }
5408
    }
5409
 
5410
    // Build up a reverse change object to add to the opposite history
5411
    // stack (redo when undoing, and vice versa).
5412
    var antiChanges = [];
5413
    pushSelectionToHistory(selAfter, dest);
5414
    dest.push({changes: antiChanges, generation: hist.generation});
5415
    hist.generation = event.generation || ++hist.maxGeneration;
5416
 
5417
    var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange");
5418
 
5419
    var loop = function ( i ) {
5420
      var change = event.changes[i];
5421
      change.origin = type;
5422
      if (filter && !filterChange(doc, change, false)) {
5423
        source.length = 0;
5424
        return {}
5425
      }
5426
 
5427
      antiChanges.push(historyChangeFromChange(doc, change));
5428
 
5429
      var after = i ? computeSelAfterChange(doc, change) : lst(source);
5430
      makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
5431
      if (!i && doc.cm) { doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}); }
5432
      var rebased = [];
5433
 
5434
      // Propagate to the linked documents
5435
      linkedDocs(doc, function (doc, sharedHist) {
5436
        if (!sharedHist && indexOf(rebased, doc.history) == -1) {
5437
          rebaseHist(doc.history, change);
5438
          rebased.push(doc.history);
5439
        }
5440
        makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change));
5441
      });
5442
    };
5443
 
5444
    for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) {
5445
      var returned = loop( i$1 );
5446
 
5447
      if ( returned ) return returned.v;
5448
    }
5449
  }
5450
 
5451
  // Sub-views need their line numbers shifted when text is added
5452
  // above or below them in the parent document.
5453
  function shiftDoc(doc, distance) {
5454
    if (distance == 0) { return }
5455
    doc.first += distance;
5456
    doc.sel = new Selection(map(doc.sel.ranges, function (range) { return new Range(
5457
      Pos(range.anchor.line + distance, range.anchor.ch),
5458
      Pos(range.head.line + distance, range.head.ch)
5459
    ); }), doc.sel.primIndex);
5460
    if (doc.cm) {
5461
      regChange(doc.cm, doc.first, doc.first - distance, distance);
5462
      for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++)
5463
        { regLineChange(doc.cm, l, "gutter"); }
5464
    }
5465
  }
5466
 
5467
  // More lower-level change function, handling only a single document
5468
  // (not linked ones).
5469
  function makeChangeSingleDoc(doc, change, selAfter, spans) {
5470
    if (doc.cm && !doc.cm.curOp)
5471
      { return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) }
5472
 
5473
    if (change.to.line < doc.first) {
5474
      shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line));
5475
      return
5476
    }
5477
    if (change.from.line > doc.lastLine()) { return }
5478
 
5479
    // Clip the change to the size of this doc
5480
    if (change.from.line < doc.first) {
5481
      var shift = change.text.length - 1 - (doc.first - change.from.line);
5482
      shiftDoc(doc, shift);
5483
      change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
5484
                text: [lst(change.text)], origin: change.origin};
5485
    }
5486
    var last = doc.lastLine();
5487
    if (change.to.line > last) {
5488
      change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
5489
                text: [change.text[0]], origin: change.origin};
5490
    }
5491
 
5492
    change.removed = getBetween(doc, change.from, change.to);
5493
 
5494
    if (!selAfter) { selAfter = computeSelAfterChange(doc, change); }
5495
    if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans); }
5496
    else { updateDoc(doc, change, spans); }
5497
    setSelectionNoUndo(doc, selAfter, sel_dontScroll);
5498
 
5499
    if (doc.cantEdit && skipAtomic(doc, Pos(doc.firstLine(), 0)))
5500
      { doc.cantEdit = false; }
5501
  }
5502
 
5503
  // Handle the interaction of a change to a document with the editor
5504
  // that this document is part of.
5505
  function makeChangeSingleDocInEditor(cm, change, spans) {
5506
    var doc = cm.doc, display = cm.display, from = change.from, to = change.to;
5507
 
5508
    var recomputeMaxLength = false, checkWidthStart = from.line;
5509
    if (!cm.options.lineWrapping) {
5510
      checkWidthStart = lineNo(visualLine(getLine(doc, from.line)));
5511
      doc.iter(checkWidthStart, to.line + 1, function (line) {
5512
        if (line == display.maxLine) {
5513
          recomputeMaxLength = true;
5514
          return true
5515
        }
5516
      });
5517
    }
5518
 
5519
    if (doc.sel.contains(change.from, change.to) > -1)
5520
      { signalCursorActivity(cm); }
5521
 
5522
    updateDoc(doc, change, spans, estimateHeight(cm));
5523
 
5524
    if (!cm.options.lineWrapping) {
5525
      doc.iter(checkWidthStart, from.line + change.text.length, function (line) {
5526
        var len = lineLength(line);
5527
        if (len > display.maxLineLength) {
5528
          display.maxLine = line;
5529
          display.maxLineLength = len;
5530
          display.maxLineChanged = true;
5531
          recomputeMaxLength = false;
5532
        }
5533
      });
5534
      if (recomputeMaxLength) { cm.curOp.updateMaxLine = true; }
5535
    }
5536
 
5537
    retreatFrontier(doc, from.line);
5538
    startWorker(cm, 400);
5539
 
5540
    var lendiff = change.text.length - (to.line - from.line) - 1;
5541
    // Remember that these lines changed, for updating the display
5542
    if (change.full)
5543
      { regChange(cm); }
5544
    else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change))
5545
      { regLineChange(cm, from.line, "text"); }
5546
    else
5547
      { regChange(cm, from.line, to.line + 1, lendiff); }
5548
 
5549
    var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change");
5550
    if (changeHandler || changesHandler) {
5551
      var obj = {
5552
        from: from, to: to,
5553
        text: change.text,
5554
        removed: change.removed,
5555
        origin: change.origin
5556
      };
5557
      if (changeHandler) { signalLater(cm, "change", cm, obj); }
5558
      if (changesHandler) { (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj); }
5559
    }
5560
    cm.display.selForContextMenu = null;
5561
  }
5562
 
5563
  function replaceRange(doc, code, from, to, origin) {
5564
    var assign;
5565
 
5566
    if (!to) { to = from; }
5567
    if (cmp(to, from) < 0) { (assign = [to, from], from = assign[0], to = assign[1]); }
5568
    if (typeof code == "string") { code = doc.splitLines(code); }
5569
    makeChange(doc, {from: from, to: to, text: code, origin: origin});
5570
  }
5571
 
5572
  // Rebasing/resetting history to deal with externally-sourced changes
5573
 
5574
  function rebaseHistSelSingle(pos, from, to, diff) {
5575
    if (to < pos.line) {
5576
      pos.line += diff;
5577
    } else if (from < pos.line) {
5578
      pos.line = from;
5579
      pos.ch = 0;
5580
    }
5581
  }
5582
 
5583
  // Tries to rebase an array of history events given a change in the
5584
  // document. If the change touches the same lines as the event, the
5585
  // event, and everything 'behind' it, is discarded. If the change is
5586
  // before the event, the event's positions are updated. Uses a
5587
  // copy-on-write scheme for the positions, to avoid having to
5588
  // reallocate them all on every rebase, but also avoid problems with
5589
  // shared position objects being unsafely updated.
5590
  function rebaseHistArray(array, from, to, diff) {
5591
    for (var i = 0; i < array.length; ++i) {
5592
      var sub = array[i], ok = true;
5593
      if (sub.ranges) {
5594
        if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; }
5595
        for (var j = 0; j < sub.ranges.length; j++) {
5596
          rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff);
5597
          rebaseHistSelSingle(sub.ranges[j].head, from, to, diff);
5598
        }
5599
        continue
5600
      }
5601
      for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) {
5602
        var cur = sub.changes[j$1];
5603
        if (to < cur.from.line) {
5604
          cur.from = Pos(cur.from.line + diff, cur.from.ch);
5605
          cur.to = Pos(cur.to.line + diff, cur.to.ch);
5606
        } else if (from <= cur.to.line) {
5607
          ok = false;
5608
          break
5609
        }
5610
      }
5611
      if (!ok) {
5612
        array.splice(0, i + 1);
5613
        i = 0;
5614
      }
5615
    }
5616
  }
5617
 
5618
  function rebaseHist(hist, change) {
5619
    var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1;
5620
    rebaseHistArray(hist.done, from, to, diff);
5621
    rebaseHistArray(hist.undone, from, to, diff);
5622
  }
5623
 
5624
  // Utility for applying a change to a line by handle or number,
5625
  // returning the number and optionally registering the line as
5626
  // changed.
5627
  function changeLine(doc, handle, changeType, op) {
5628
    var no = handle, line = handle;
5629
    if (typeof handle == "number") { line = getLine(doc, clipLine(doc, handle)); }
5630
    else { no = lineNo(handle); }
5631
    if (no == null) { return null }
5632
    if (op(line, no) && doc.cm) { regLineChange(doc.cm, no, changeType); }
5633
    return line
5634
  }
5635
 
5636
  // The document is represented as a BTree consisting of leaves, with
5637
  // chunk of lines in them, and branches, with up to ten leaves or
5638
  // other branch nodes below them. The top node is always a branch
5639
  // node, and is the document object itself (meaning it has
5640
  // additional methods and properties).
5641
  //
5642
  // All nodes have parent links. The tree is used both to go from
5643
  // line numbers to line objects, and to go from objects to numbers.
5644
  // It also indexes by height, and is used to convert between height
5645
  // and line object, and to find the total height of the document.
5646
  //
5647
  // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html
5648
 
5649
  function LeafChunk(lines) {
5650
    this.lines = lines;
5651
    this.parent = null;
5652
    var height = 0;
5653
    for (var i = 0; i < lines.length; ++i) {
5654
      lines[i].parent = this;
5655
      height += lines[i].height;
5656
    }
5657
    this.height = height;
5658
  }
5659
 
5660
  LeafChunk.prototype = {
5661
    chunkSize: function() { return this.lines.length },
5662
 
5663
    // Remove the n lines at offset 'at'.
5664
    removeInner: function(at, n) {
5665
      for (var i = at, e = at + n; i < e; ++i) {
5666
        var line = this.lines[i];
5667
        this.height -= line.height;
5668
        cleanUpLine(line);
5669
        signalLater(line, "delete");
5670
      }
5671
      this.lines.splice(at, n);
5672
    },
5673
 
5674
    // Helper used to collapse a small branch into a single leaf.
5675
    collapse: function(lines) {
5676
      lines.push.apply(lines, this.lines);
5677
    },
5678
 
5679
    // Insert the given array of lines at offset 'at', count them as
5680
    // having the given height.
5681
    insertInner: function(at, lines, height) {
5682
      this.height += height;
5683
      this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
5684
      for (var i = 0; i < lines.length; ++i) { lines[i].parent = this; }
5685
    },
5686
 
5687
    // Used to iterate over a part of the tree.
5688
    iterN: function(at, n, op) {
5689
      for (var e = at + n; at < e; ++at)
5690
        { if (op(this.lines[at])) { return true } }
5691
    }
5692
  };
5693
 
5694
  function BranchChunk(children) {
5695
    this.children = children;
5696
    var size = 0, height = 0;
5697
    for (var i = 0; i < children.length; ++i) {
5698
      var ch = children[i];
5699
      size += ch.chunkSize(); height += ch.height;
5700
      ch.parent = this;
5701
    }
5702
    this.size = size;
5703
    this.height = height;
5704
    this.parent = null;
5705
  }
5706
 
5707
  BranchChunk.prototype = {
5708
    chunkSize: function() { return this.size },
5709
 
5710
    removeInner: function(at, n) {
5711
      this.size -= n;
5712
      for (var i = 0; i < this.children.length; ++i) {
5713
        var child = this.children[i], sz = child.chunkSize();
5714
        if (at < sz) {
5715
          var rm = Math.min(n, sz - at), oldHeight = child.height;
5716
          child.removeInner(at, rm);
5717
          this.height -= oldHeight - child.height;
5718
          if (sz == rm) { this.children.splice(i--, 1); child.parent = null; }
5719
          if ((n -= rm) == 0) { break }
5720
          at = 0;
5721
        } else { at -= sz; }
5722
      }
5723
      // If the result is smaller than 25 lines, ensure that it is a
5724
      // single leaf node.
5725
      if (this.size - n < 25 &&
5726
          (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) {
5727
        var lines = [];
5728
        this.collapse(lines);
5729
        this.children = [new LeafChunk(lines)];
5730
        this.children[0].parent = this;
5731
      }
5732
    },
5733
 
5734
    collapse: function(lines) {
5735
      for (var i = 0; i < this.children.length; ++i) { this.children[i].collapse(lines); }
5736
    },
5737
 
5738
    insertInner: function(at, lines, height) {
5739
      this.size += lines.length;
5740
      this.height += height;
5741
      for (var i = 0; i < this.children.length; ++i) {
5742
        var child = this.children[i], sz = child.chunkSize();
5743
        if (at <= sz) {
5744
          child.insertInner(at, lines, height);
5745
          if (child.lines && child.lines.length > 50) {
5746
            // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced.
5747
            // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest.
5748
            var remaining = child.lines.length % 25 + 25;
5749
            for (var pos = remaining; pos < child.lines.length;) {
5750
              var leaf = new LeafChunk(child.lines.slice(pos, pos += 25));
5751
              child.height -= leaf.height;
5752
              this.children.splice(++i, 0, leaf);
5753
              leaf.parent = this;
5754
            }
5755
            child.lines = child.lines.slice(0, remaining);
5756
            this.maybeSpill();
5757
          }
5758
          break
5759
        }
5760
        at -= sz;
5761
      }
5762
    },
5763
 
5764
    // When a node has grown, check whether it should be split.
5765
    maybeSpill: function() {
5766
      if (this.children.length <= 10) { return }
5767
      var me = this;
5768
      do {
5769
        var spilled = me.children.splice(me.children.length - 5, 5);
5770
        var sibling = new BranchChunk(spilled);
5771
        if (!me.parent) { // Become the parent node
5772
          var copy = new BranchChunk(me.children);
5773
          copy.parent = me;
5774
          me.children = [copy, sibling];
5775
          me = copy;
5776
       } else {
5777
          me.size -= sibling.size;
5778
          me.height -= sibling.height;
5779
          var myIndex = indexOf(me.parent.children, me);
5780
          me.parent.children.splice(myIndex + 1, 0, sibling);
5781
        }
5782
        sibling.parent = me.parent;
5783
      } while (me.children.length > 10)
5784
      me.parent.maybeSpill();
5785
    },
5786
 
5787
    iterN: function(at, n, op) {
5788
      for (var i = 0; i < this.children.length; ++i) {
5789
        var child = this.children[i], sz = child.chunkSize();
5790
        if (at < sz) {
5791
          var used = Math.min(n, sz - at);
5792
          if (child.iterN(at, used, op)) { return true }
5793
          if ((n -= used) == 0) { break }
5794
          at = 0;
5795
        } else { at -= sz; }
5796
      }
5797
    }
5798
  };
5799
 
5800
  // Line widgets are block elements displayed above or below a line.
5801
 
5802
  var LineWidget = function(doc, node, options) {
5803
    if (options) { for (var opt in options) { if (options.hasOwnProperty(opt))
5804
      { this[opt] = options[opt]; } } }
5805
    this.doc = doc;
5806
    this.node = node;
5807
  };
5808
 
5809
  LineWidget.prototype.clear = function () {
5810
    var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line);
5811
    if (no == null || !ws) { return }
5812
    for (var i = 0; i < ws.length; ++i) { if (ws[i] == this) { ws.splice(i--, 1); } }
5813
    if (!ws.length) { line.widgets = null; }
5814
    var height = widgetHeight(this);
5815
    updateLineHeight(line, Math.max(0, line.height - height));
5816
    if (cm) {
5817
      runInOp(cm, function () {
5818
        adjustScrollWhenAboveVisible(cm, line, -height);
5819
        regLineChange(cm, no, "widget");
5820
      });
5821
      signalLater(cm, "lineWidgetCleared", cm, this, no);
5822
    }
5823
  };
5824
 
5825
  LineWidget.prototype.changed = function () {
5826
      var this$1 = this;
5827
 
5828
    var oldH = this.height, cm = this.doc.cm, line = this.line;
5829
    this.height = null;
5830
    var diff = widgetHeight(this) - oldH;
5831
    if (!diff) { return }
5832
    if (!lineIsHidden(this.doc, line)) { updateLineHeight(line, line.height + diff); }
5833
    if (cm) {
5834
      runInOp(cm, function () {
5835
        cm.curOp.forceUpdate = true;
5836
        adjustScrollWhenAboveVisible(cm, line, diff);
5837
        signalLater(cm, "lineWidgetChanged", cm, this$1, lineNo(line));
5838
      });
5839
    }
5840
  };
5841
  eventMixin(LineWidget);
5842
 
5843
  function adjustScrollWhenAboveVisible(cm, line, diff) {
5844
    if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))
5845
      { addToScrollTop(cm, diff); }
5846
  }
5847
 
5848
  function addLineWidget(doc, handle, node, options) {
5849
    var widget = new LineWidget(doc, node, options);
5850
    var cm = doc.cm;
5851
    if (cm && widget.noHScroll) { cm.display.alignWidgets = true; }
5852
    changeLine(doc, handle, "widget", function (line) {
5853
      var widgets = line.widgets || (line.widgets = []);
5854
      if (widget.insertAt == null) { widgets.push(widget); }
5855
      else { widgets.splice(Math.min(widgets.length, Math.max(0, widget.insertAt)), 0, widget); }
5856
      widget.line = line;
5857
      if (cm && !lineIsHidden(doc, line)) {
5858
        var aboveVisible = heightAtLine(line) < doc.scrollTop;
5859
        updateLineHeight(line, line.height + widgetHeight(widget));
5860
        if (aboveVisible) { addToScrollTop(cm, widget.height); }
5861
        cm.curOp.forceUpdate = true;
5862
      }
5863
      return true
5864
    });
5865
    if (cm) { signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle)); }
5866
    return widget
5867
  }
5868
 
5869
  // TEXTMARKERS
5870
 
5871
  // Created with markText and setBookmark methods. A TextMarker is a
5872
  // handle that can be used to clear or find a marked position in the
5873
  // document. Line objects hold arrays (markedSpans) containing
5874
  // {from, to, marker} object pointing to such marker objects, and
5875
  // indicating that such a marker is present on that line. Multiple
5876
  // lines may point to the same marker when it spans across lines.
5877
  // The spans will have null for their from/to properties when the
5878
  // marker continues beyond the start/end of the line. Markers have
5879
  // links back to the lines they currently touch.
5880
 
5881
  // Collapsed markers have unique ids, in order to be able to order
5882
  // them, which is needed for uniquely determining an outer marker
5883
  // when they overlap (they may nest, but not partially overlap).
5884
  var nextMarkerId = 0;
5885
 
5886
  var TextMarker = function(doc, type) {
5887
    this.lines = [];
5888
    this.type = type;
5889
    this.doc = doc;
5890
    this.id = ++nextMarkerId;
5891
  };
5892
 
5893
  // Clear the marker.
5894
  TextMarker.prototype.clear = function () {
5895
    if (this.explicitlyCleared) { return }
5896
    var cm = this.doc.cm, withOp = cm && !cm.curOp;
5897
    if (withOp) { startOperation(cm); }
5898
    if (hasHandler(this, "clear")) {
5899
      var found = this.find();
5900
      if (found) { signalLater(this, "clear", found.from, found.to); }
5901
    }
5902
    var min = null, max = null;
5903
    for (var i = 0; i < this.lines.length; ++i) {
5904
      var line = this.lines[i];
5905
      var span = getMarkedSpanFor(line.markedSpans, this);
5906
      if (cm && !this.collapsed) { regLineChange(cm, lineNo(line), "text"); }
5907
      else if (cm) {
5908
        if (span.to != null) { max = lineNo(line); }
5909
        if (span.from != null) { min = lineNo(line); }
5910
      }
5911
      line.markedSpans = removeMarkedSpan(line.markedSpans, span);
5912
      if (span.from == null && this.collapsed && !lineIsHidden(this.doc, line) && cm)
5913
        { updateLineHeight(line, textHeight(cm.display)); }
5914
    }
5915
    if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) {
5916
      var visual = visualLine(this.lines[i$1]), len = lineLength(visual);
5917
      if (len > cm.display.maxLineLength) {
5918
        cm.display.maxLine = visual;
5919
        cm.display.maxLineLength = len;
5920
        cm.display.maxLineChanged = true;
5921
      }
5922
    } }
5923
 
5924
    if (min != null && cm && this.collapsed) { regChange(cm, min, max + 1); }
5925
    this.lines.length = 0;
5926
    this.explicitlyCleared = true;
5927
    if (this.atomic && this.doc.cantEdit) {
5928
      this.doc.cantEdit = false;
5929
      if (cm) { reCheckSelection(cm.doc); }
5930
    }
5931
    if (cm) { signalLater(cm, "markerCleared", cm, this, min, max); }
5932
    if (withOp) { endOperation(cm); }
5933
    if (this.parent) { this.parent.clear(); }
5934
  };
5935
 
5936
  // Find the position of the marker in the document. Returns a {from,
5937
  // to} object by default. Side can be passed to get a specific side
5938
  // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the
5939
  // Pos objects returned contain a line object, rather than a line
5940
  // number (used to prevent looking up the same line twice).
5941
  TextMarker.prototype.find = function (side, lineObj) {
5942
    if (side == null && this.type == "bookmark") { side = 1; }
5943
    var from, to;
5944
    for (var i = 0; i < this.lines.length; ++i) {
5945
      var line = this.lines[i];
5946
      var span = getMarkedSpanFor(line.markedSpans, this);
5947
      if (span.from != null) {
5948
        from = Pos(lineObj ? line : lineNo(line), span.from);
5949
        if (side == -1) { return from }
5950
      }
5951
      if (span.to != null) {
5952
        to = Pos(lineObj ? line : lineNo(line), span.to);
5953
        if (side == 1) { return to }
5954
      }
5955
    }
5956
    return from && {from: from, to: to}
5957
  };
5958
 
5959
  // Signals that the marker's widget changed, and surrounding layout
5960
  // should be recomputed.
5961
  TextMarker.prototype.changed = function () {
5962
      var this$1 = this;
5963
 
5964
    var pos = this.find(-1, true), widget = this, cm = this.doc.cm;
5965
    if (!pos || !cm) { return }
5966
    runInOp(cm, function () {
5967
      var line = pos.line, lineN = lineNo(pos.line);
5968
      var view = findViewForLine(cm, lineN);
5969
      if (view) {
5970
        clearLineMeasurementCacheFor(view);
5971
        cm.curOp.selectionChanged = cm.curOp.forceUpdate = true;
5972
      }
5973
      cm.curOp.updateMaxLine = true;
5974
      if (!lineIsHidden(widget.doc, line) && widget.height != null) {
5975
        var oldHeight = widget.height;
5976
        widget.height = null;
5977
        var dHeight = widgetHeight(widget) - oldHeight;
5978
        if (dHeight)
5979
          { updateLineHeight(line, line.height + dHeight); }
5980
      }
5981
      signalLater(cm, "markerChanged", cm, this$1);
5982
    });
5983
  };
5984
 
5985
  TextMarker.prototype.attachLine = function (line) {
5986
    if (!this.lines.length && this.doc.cm) {
5987
      var op = this.doc.cm.curOp;
5988
      if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
5989
        { (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this); }
5990
    }
5991
    this.lines.push(line);
5992
  };
5993
 
5994
  TextMarker.prototype.detachLine = function (line) {
5995
    this.lines.splice(indexOf(this.lines, line), 1);
5996
    if (!this.lines.length && this.doc.cm) {
5997
      var op = this.doc.cm.curOp
5998
      ;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this);
5999
    }
6000
  };
6001
  eventMixin(TextMarker);
6002
 
6003
  // Create a marker, wire it up to the right lines, and
6004
  function markText(doc, from, to, options, type) {
6005
    // Shared markers (across linked documents) are handled separately
6006
    // (markTextShared will call out to this again, once per
6007
    // document).
6008
    if (options && options.shared) { return markTextShared(doc, from, to, options, type) }
6009
    // Ensure we are in an operation.
6010
    if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, markText)(doc, from, to, options, type) }
6011
 
6012
    var marker = new TextMarker(doc, type), diff = cmp(from, to);
6013
    if (options) { copyObj(options, marker, false); }
6014
    // Don't connect empty markers unless clearWhenEmpty is false
6015
    if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false)
6016
      { return marker }
6017
    if (marker.replacedWith) {
6018
      // Showing up as a widget implies collapsed (widget replaces text)
6019
      marker.collapsed = true;
6020
      marker.widgetNode = eltP("span", [marker.replacedWith], "CodeMirror-widget");
6021
      if (!options.handleMouseEvents) { marker.widgetNode.setAttribute("cm-ignore-events", "true"); }
6022
      if (options.insertLeft) { marker.widgetNode.insertLeft = true; }
6023
    }
6024
    if (marker.collapsed) {
6025
      if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||
6026
          from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker))
6027
        { throw new Error("Inserting collapsed marker partially overlapping an existing one") }
6028
      seeCollapsedSpans();
6029
    }
6030
 
6031
    if (marker.addToHistory)
6032
      { addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN); }
6033
 
6034
    var curLine = from.line, cm = doc.cm, updateMaxLine;
6035
    doc.iter(curLine, to.line + 1, function (line) {
6036
      if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine)
6037
        { updateMaxLine = true; }
6038
      if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0); }
6039
      addMarkedSpan(line, new MarkedSpan(marker,
6040
                                         curLine == from.line ? from.ch : null,
6041
                                         curLine == to.line ? to.ch : null), doc.cm && doc.cm.curOp);
6042
      ++curLine;
6043
    });
6044
    // lineIsHidden depends on the presence of the spans, so needs a second pass
6045
    if (marker.collapsed) { doc.iter(from.line, to.line + 1, function (line) {
6046
      if (lineIsHidden(doc, line)) { updateLineHeight(line, 0); }
6047
    }); }
6048
 
6049
    if (marker.clearOnEnter) { on(marker, "beforeCursorEnter", function () { return marker.clear(); }); }
6050
 
6051
    if (marker.readOnly) {
6052
      seeReadOnlySpans();
6053
      if (doc.history.done.length || doc.history.undone.length)
6054
        { doc.clearHistory(); }
6055
    }
6056
    if (marker.collapsed) {
6057
      marker.id = ++nextMarkerId;
6058
      marker.atomic = true;
6059
    }
6060
    if (cm) {
6061
      // Sync editor state
6062
      if (updateMaxLine) { cm.curOp.updateMaxLine = true; }
6063
      if (marker.collapsed)
6064
        { regChange(cm, from.line, to.line + 1); }
6065
      else if (marker.className || marker.startStyle || marker.endStyle || marker.css ||
6066
               marker.attributes || marker.title)
6067
        { for (var i = from.line; i <= to.line; i++) { regLineChange(cm, i, "text"); } }
6068
      if (marker.atomic) { reCheckSelection(cm.doc); }
6069
      signalLater(cm, "markerAdded", cm, marker);
6070
    }
6071
    return marker
6072
  }
6073
 
6074
  // SHARED TEXTMARKERS
6075
 
6076
  // A shared marker spans multiple linked documents. It is
6077
  // implemented as a meta-marker-object controlling multiple normal
6078
  // markers.
6079
  var SharedTextMarker = function(markers, primary) {
6080
    this.markers = markers;
6081
    this.primary = primary;
6082
    for (var i = 0; i < markers.length; ++i)
6083
      { markers[i].parent = this; }
6084
  };
6085
 
6086
  SharedTextMarker.prototype.clear = function () {
6087
    if (this.explicitlyCleared) { return }
6088
    this.explicitlyCleared = true;
6089
    for (var i = 0; i < this.markers.length; ++i)
6090
      { this.markers[i].clear(); }
6091
    signalLater(this, "clear");
6092
  };
6093
 
6094
  SharedTextMarker.prototype.find = function (side, lineObj) {
6095
    return this.primary.find(side, lineObj)
6096
  };
6097
  eventMixin(SharedTextMarker);
6098
 
6099
  function markTextShared(doc, from, to, options, type) {
6100
    options = copyObj(options);
6101
    options.shared = false;
6102
    var markers = [markText(doc, from, to, options, type)], primary = markers[0];
6103
    var widget = options.widgetNode;
6104
    linkedDocs(doc, function (doc) {
6105
      if (widget) { options.widgetNode = widget.cloneNode(true); }
6106
      markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));
6107
      for (var i = 0; i < doc.linked.length; ++i)
6108
        { if (doc.linked[i].isParent) { return } }
6109
      primary = lst(markers);
6110
    });
6111
    return new SharedTextMarker(markers, primary)
6112
  }
6113
 
6114
  function findSharedMarkers(doc) {
6115
    return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function (m) { return m.parent; })
6116
  }
6117
 
6118
  function copySharedMarkers(doc, markers) {
6119
    for (var i = 0; i < markers.length; i++) {
6120
      var marker = markers[i], pos = marker.find();
6121
      var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to);
6122
      if (cmp(mFrom, mTo)) {
6123
        var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type);
6124
        marker.markers.push(subMark);
6125
        subMark.parent = marker;
6126
      }
6127
    }
6128
  }
6129
 
6130
  function detachSharedMarkers(markers) {
6131
    var loop = function ( i ) {
6132
      var marker = markers[i], linked = [marker.primary.doc];
6133
      linkedDocs(marker.primary.doc, function (d) { return linked.push(d); });
6134
      for (var j = 0; j < marker.markers.length; j++) {
6135
        var subMarker = marker.markers[j];
6136
        if (indexOf(linked, subMarker.doc) == -1) {
6137
          subMarker.parent = null;
6138
          marker.markers.splice(j--, 1);
6139
        }
6140
      }
6141
    };
6142
 
6143
    for (var i = 0; i < markers.length; i++) loop( i );
6144
  }
6145
 
6146
  var nextDocId = 0;
6147
  var Doc = function(text, mode, firstLine, lineSep, direction) {
6148
    if (!(this instanceof Doc)) { return new Doc(text, mode, firstLine, lineSep, direction) }
6149
    if (firstLine == null) { firstLine = 0; }
6150
 
6151
    BranchChunk.call(this, [new LeafChunk([new Line("", null)])]);
6152
    this.first = firstLine;
6153
    this.scrollTop = this.scrollLeft = 0;
6154
    this.cantEdit = false;
6155
    this.cleanGeneration = 1;
6156
    this.modeFrontier = this.highlightFrontier = firstLine;
6157
    var start = Pos(firstLine, 0);
6158
    this.sel = simpleSelection(start);
6159
    this.history = new History(null);
6160
    this.id = ++nextDocId;
6161
    this.modeOption = mode;
6162
    this.lineSep = lineSep;
6163
    this.direction = (direction == "rtl") ? "rtl" : "ltr";
6164
    this.extend = false;
6165
 
6166
    if (typeof text == "string") { text = this.splitLines(text); }
6167
    updateDoc(this, {from: start, to: start, text: text});
6168
    setSelection(this, simpleSelection(start), sel_dontScroll);
6169
  };
6170
 
6171
  Doc.prototype = createObj(BranchChunk.prototype, {
6172
    constructor: Doc,
6173
    // Iterate over the document. Supports two forms -- with only one
6174
    // argument, it calls that for each line in the document. With
6175
    // three, it iterates over the range given by the first two (with
6176
    // the second being non-inclusive).
6177
    iter: function(from, to, op) {
6178
      if (op) { this.iterN(from - this.first, to - from, op); }
6179
      else { this.iterN(this.first, this.first + this.size, from); }
6180
    },
6181
 
6182
    // Non-public interface for adding and removing lines.
6183
    insert: function(at, lines) {
6184
      var height = 0;
6185
      for (var i = 0; i < lines.length; ++i) { height += lines[i].height; }
6186
      this.insertInner(at - this.first, lines, height);
6187
    },
6188
    remove: function(at, n) { this.removeInner(at - this.first, n); },
6189
 
6190
    // From here, the methods are part of the public interface. Most
6191
    // are also available from CodeMirror (editor) instances.
6192
 
6193
    getValue: function(lineSep) {
6194
      var lines = getLines(this, this.first, this.first + this.size);
6195
      if (lineSep === false) { return lines }
6196
      return lines.join(lineSep || this.lineSeparator())
6197
    },
6198
    setValue: docMethodOp(function(code) {
6199
      var top = Pos(this.first, 0), last = this.first + this.size - 1;
6200
      makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
6201
                        text: this.splitLines(code), origin: "setValue", full: true}, true);
6202
      if (this.cm) { scrollToCoords(this.cm, 0, 0); }
6203
      setSelection(this, simpleSelection(top), sel_dontScroll);
6204
    }),
6205
    replaceRange: function(code, from, to, origin) {
6206
      from = clipPos(this, from);
6207
      to = to ? clipPos(this, to) : from;
6208
      replaceRange(this, code, from, to, origin);
6209
    },
6210
    getRange: function(from, to, lineSep) {
6211
      var lines = getBetween(this, clipPos(this, from), clipPos(this, to));
6212
      if (lineSep === false) { return lines }
6213
      if (lineSep === '') { return lines.join('') }
6214
      return lines.join(lineSep || this.lineSeparator())
6215
    },
6216
 
6217
    getLine: function(line) {var l = this.getLineHandle(line); return l && l.text},
6218
 
6219
    getLineHandle: function(line) {if (isLine(this, line)) { return getLine(this, line) }},
6220
    getLineNumber: function(line) {return lineNo(line)},
6221
 
6222
    getLineHandleVisualStart: function(line) {
6223
      if (typeof line == "number") { line = getLine(this, line); }
6224
      return visualLine(line)
6225
    },
6226
 
6227
    lineCount: function() {return this.size},
6228
    firstLine: function() {return this.first},
6229
    lastLine: function() {return this.first + this.size - 1},
6230
 
6231
    clipPos: function(pos) {return clipPos(this, pos)},
6232
 
6233
    getCursor: function(start) {
6234
      var range = this.sel.primary(), pos;
6235
      if (start == null || start == "head") { pos = range.head; }
6236
      else if (start == "anchor") { pos = range.anchor; }
6237
      else if (start == "end" || start == "to" || start === false) { pos = range.to(); }
6238
      else { pos = range.from(); }
6239
      return pos
6240
    },
6241
    listSelections: function() { return this.sel.ranges },
6242
    somethingSelected: function() {return this.sel.somethingSelected()},
6243
 
6244
    setCursor: docMethodOp(function(line, ch, options) {
6245
      setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options);
6246
    }),
6247
    setSelection: docMethodOp(function(anchor, head, options) {
6248
      setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options);
6249
    }),
6250
    extendSelection: docMethodOp(function(head, other, options) {
6251
      extendSelection(this, clipPos(this, head), other && clipPos(this, other), options);
6252
    }),
6253
    extendSelections: docMethodOp(function(heads, options) {
6254
      extendSelections(this, clipPosArray(this, heads), options);
6255
    }),
6256
    extendSelectionsBy: docMethodOp(function(f, options) {
6257
      var heads = map(this.sel.ranges, f);
6258
      extendSelections(this, clipPosArray(this, heads), options);
6259
    }),
6260
    setSelections: docMethodOp(function(ranges, primary, options) {
6261
      if (!ranges.length) { return }
6262
      var out = [];
6263
      for (var i = 0; i < ranges.length; i++)
6264
        { out[i] = new Range(clipPos(this, ranges[i].anchor),
6265
                           clipPos(this, ranges[i].head || ranges[i].anchor)); }
6266
      if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex); }
6267
      setSelection(this, normalizeSelection(this.cm, out, primary), options);
6268
    }),
6269
    addSelection: docMethodOp(function(anchor, head, options) {
6270
      var ranges = this.sel.ranges.slice(0);
6271
      ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)));
6272
      setSelection(this, normalizeSelection(this.cm, ranges, ranges.length - 1), options);
6273
    }),
6274
 
6275
    getSelection: function(lineSep) {
6276
      var ranges = this.sel.ranges, lines;
6277
      for (var i = 0; i < ranges.length; i++) {
6278
        var sel = getBetween(this, ranges[i].from(), ranges[i].to());
6279
        lines = lines ? lines.concat(sel) : sel;
6280
      }
6281
      if (lineSep === false) { return lines }
6282
      else { return lines.join(lineSep || this.lineSeparator()) }
6283
    },
6284
    getSelections: function(lineSep) {
6285
      var parts = [], ranges = this.sel.ranges;
6286
      for (var i = 0; i < ranges.length; i++) {
6287
        var sel = getBetween(this, ranges[i].from(), ranges[i].to());
6288
        if (lineSep !== false) { sel = sel.join(lineSep || this.lineSeparator()); }
6289
        parts[i] = sel;
6290
      }
6291
      return parts
6292
    },
6293
    replaceSelection: function(code, collapse, origin) {
6294
      var dup = [];
6295
      for (var i = 0; i < this.sel.ranges.length; i++)
6296
        { dup[i] = code; }
6297
      this.replaceSelections(dup, collapse, origin || "+input");
6298
    },
6299
    replaceSelections: docMethodOp(function(code, collapse, origin) {
6300
      var changes = [], sel = this.sel;
6301
      for (var i = 0; i < sel.ranges.length; i++) {
6302
        var range = sel.ranges[i];
6303
        changes[i] = {from: range.from(), to: range.to(), text: this.splitLines(code[i]), origin: origin};
6304
      }
6305
      var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse);
6306
      for (var i$1 = changes.length - 1; i$1 >= 0; i$1--)
6307
        { makeChange(this, changes[i$1]); }
6308
      if (newSel) { setSelectionReplaceHistory(this, newSel); }
6309
      else if (this.cm) { ensureCursorVisible(this.cm); }
6310
    }),
6311
    undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}),
6312
    redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}),
6313
    undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}),
6314
    redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true);}),
6315
 
6316
    setExtending: function(val) {this.extend = val;},
6317
    getExtending: function() {return this.extend},
6318
 
6319
    historySize: function() {
6320
      var hist = this.history, done = 0, undone = 0;
6321
      for (var i = 0; i < hist.done.length; i++) { if (!hist.done[i].ranges) { ++done; } }
6322
      for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].ranges) { ++undone; } }
6323
      return {undo: done, redo: undone}
6324
    },
6325
    clearHistory: function() {
6326
      var this$1 = this;
6327
 
6328
      this.history = new History(this.history);
6329
      linkedDocs(this, function (doc) { return doc.history = this$1.history; }, true);
6330
    },
6331
 
6332
    markClean: function() {
6333
      this.cleanGeneration = this.changeGeneration(true);
6334
    },
6335
    changeGeneration: function(forceSplit) {
6336
      if (forceSplit)
6337
        { this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null; }
6338
      return this.history.generation
6339
    },
6340
    isClean: function (gen) {
6341
      return this.history.generation == (gen || this.cleanGeneration)
6342
    },
6343
 
6344
    getHistory: function() {
6345
      return {done: copyHistoryArray(this.history.done),
6346
              undone: copyHistoryArray(this.history.undone)}
6347
    },
6348
    setHistory: function(histData) {
6349
      var hist = this.history = new History(this.history);
6350
      hist.done = copyHistoryArray(histData.done.slice(0), null, true);
6351
      hist.undone = copyHistoryArray(histData.undone.slice(0), null, true);
6352
    },
6353
 
6354
    setGutterMarker: docMethodOp(function(line, gutterID, value) {
6355
      return changeLine(this, line, "gutter", function (line) {
6356
        var markers = line.gutterMarkers || (line.gutterMarkers = {});
6357
        markers[gutterID] = value;
6358
        if (!value && isEmpty(markers)) { line.gutterMarkers = null; }
6359
        return true
6360
      })
6361
    }),
6362
 
6363
    clearGutter: docMethodOp(function(gutterID) {
6364
      var this$1 = this;
6365
 
6366
      this.iter(function (line) {
6367
        if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
6368
          changeLine(this$1, line, "gutter", function () {
6369
            line.gutterMarkers[gutterID] = null;
6370
            if (isEmpty(line.gutterMarkers)) { line.gutterMarkers = null; }
6371
            return true
6372
          });
6373
        }
6374
      });
6375
    }),
6376
 
6377
    lineInfo: function(line) {
6378
      var n;
6379
      if (typeof line == "number") {
6380
        if (!isLine(this, line)) { return null }
6381
        n = line;
6382
        line = getLine(this, line);
6383
        if (!line) { return null }
6384
      } else {
6385
        n = lineNo(line);
6386
        if (n == null) { return null }
6387
      }
6388
      return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
6389
              textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
6390
              widgets: line.widgets}
6391
    },
6392
 
6393
    addLineClass: docMethodOp(function(handle, where, cls) {
6394
      return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
6395
        var prop = where == "text" ? "textClass"
6396
                 : where == "background" ? "bgClass"
6397
                 : where == "gutter" ? "gutterClass" : "wrapClass";
6398
        if (!line[prop]) { line[prop] = cls; }
6399
        else if (classTest(cls).test(line[prop])) { return false }
6400
        else { line[prop] += " " + cls; }
6401
        return true
6402
      })
6403
    }),
6404
    removeLineClass: docMethodOp(function(handle, where, cls) {
6405
      return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
6406
        var prop = where == "text" ? "textClass"
6407
                 : where == "background" ? "bgClass"
6408
                 : where == "gutter" ? "gutterClass" : "wrapClass";
6409
        var cur = line[prop];
6410
        if (!cur) { return false }
6411
        else if (cls == null) { line[prop] = null; }
6412
        else {
6413
          var found = cur.match(classTest(cls));
6414
          if (!found) { return false }
6415
          var end = found.index + found[0].length;
6416
          line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null;
6417
        }
6418
        return true
6419
      })
6420
    }),
6421
 
6422
    addLineWidget: docMethodOp(function(handle, node, options) {
6423
      return addLineWidget(this, handle, node, options)
6424
    }),
6425
    removeLineWidget: function(widget) { widget.clear(); },
6426
 
6427
    markText: function(from, to, options) {
6428
      return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range")
6429
    },
6430
    setBookmark: function(pos, options) {
6431
      var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
6432
                      insertLeft: options && options.insertLeft,
6433
                      clearWhenEmpty: false, shared: options && options.shared,
6434
                      handleMouseEvents: options && options.handleMouseEvents};
6435
      pos = clipPos(this, pos);
6436
      return markText(this, pos, pos, realOpts, "bookmark")
6437
    },
6438
    findMarksAt: function(pos) {
6439
      pos = clipPos(this, pos);
6440
      var markers = [], spans = getLine(this, pos.line).markedSpans;
6441
      if (spans) { for (var i = 0; i < spans.length; ++i) {
6442
        var span = spans[i];
6443
        if ((span.from == null || span.from <= pos.ch) &&
6444
            (span.to == null || span.to >= pos.ch))
6445
          { markers.push(span.marker.parent || span.marker); }
6446
      } }
6447
      return markers
6448
    },
6449
    findMarks: function(from, to, filter) {
6450
      from = clipPos(this, from); to = clipPos(this, to);
6451
      var found = [], lineNo = from.line;
6452
      this.iter(from.line, to.line + 1, function (line) {
6453
        var spans = line.markedSpans;
6454
        if (spans) { for (var i = 0; i < spans.length; i++) {
6455
          var span = spans[i];
6456
          if (!(span.to != null && lineNo == from.line && from.ch >= span.to ||
6457
                span.from == null && lineNo != from.line ||
6458
                span.from != null && lineNo == to.line && span.from >= to.ch) &&
6459
              (!filter || filter(span.marker)))
6460
            { found.push(span.marker.parent || span.marker); }
6461
        } }
6462
        ++lineNo;
6463
      });
6464
      return found
6465
    },
6466
    getAllMarks: function() {
6467
      var markers = [];
6468
      this.iter(function (line) {
6469
        var sps = line.markedSpans;
6470
        if (sps) { for (var i = 0; i < sps.length; ++i)
6471
          { if (sps[i].from != null) { markers.push(sps[i].marker); } } }
6472
      });
6473
      return markers
6474
    },
6475
 
6476
    posFromIndex: function(off) {
6477
      var ch, lineNo = this.first, sepSize = this.lineSeparator().length;
6478
      this.iter(function (line) {
6479
        var sz = line.text.length + sepSize;
6480
        if (sz > off) { ch = off; return true }
6481
        off -= sz;
6482
        ++lineNo;
6483
      });
6484
      return clipPos(this, Pos(lineNo, ch))
6485
    },
6486
    indexFromPos: function (coords) {
6487
      coords = clipPos(this, coords);
6488
      var index = coords.ch;
6489
      if (coords.line < this.first || coords.ch < 0) { return 0 }
6490
      var sepSize = this.lineSeparator().length;
6491
      this.iter(this.first, coords.line, function (line) { // iter aborts when callback returns a truthy value
6492
        index += line.text.length + sepSize;
6493
      });
6494
      return index
6495
    },
6496
 
6497
    copy: function(copyHistory) {
6498
      var doc = new Doc(getLines(this, this.first, this.first + this.size),
6499
                        this.modeOption, this.first, this.lineSep, this.direction);
6500
      doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;
6501
      doc.sel = this.sel;
6502
      doc.extend = false;
6503
      if (copyHistory) {
6504
        doc.history.undoDepth = this.history.undoDepth;
6505
        doc.setHistory(this.getHistory());
6506
      }
6507
      return doc
6508
    },
6509
 
6510
    linkedDoc: function(options) {
6511
      if (!options) { options = {}; }
6512
      var from = this.first, to = this.first + this.size;
6513
      if (options.from != null && options.from > from) { from = options.from; }
6514
      if (options.to != null && options.to < to) { to = options.to; }
6515
      var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep, this.direction);
6516
      if (options.sharedHist) { copy.history = this.history
6517
      ; }(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});
6518
      copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];
6519
      copySharedMarkers(copy, findSharedMarkers(this));
6520
      return copy
6521
    },
6522
    unlinkDoc: function(other) {
6523
      if (other instanceof CodeMirror) { other = other.doc; }
6524
      if (this.linked) { for (var i = 0; i < this.linked.length; ++i) {
6525
        var link = this.linked[i];
6526
        if (link.doc != other) { continue }
6527
        this.linked.splice(i, 1);
6528
        other.unlinkDoc(this);
6529
        detachSharedMarkers(findSharedMarkers(this));
6530
        break
6531
      } }
6532
      // If the histories were shared, split them again
6533
      if (other.history == this.history) {
6534
        var splitIds = [other.id];
6535
        linkedDocs(other, function (doc) { return splitIds.push(doc.id); }, true);
6536
        other.history = new History(null);
6537
        other.history.done = copyHistoryArray(this.history.done, splitIds);
6538
        other.history.undone = copyHistoryArray(this.history.undone, splitIds);
6539
      }
6540
    },
6541
    iterLinkedDocs: function(f) {linkedDocs(this, f);},
6542
 
6543
    getMode: function() {return this.mode},
6544
    getEditor: function() {return this.cm},
6545
 
6546
    splitLines: function(str) {
6547
      if (this.lineSep) { return str.split(this.lineSep) }
6548
      return splitLinesAuto(str)
6549
    },
6550
    lineSeparator: function() { return this.lineSep || "\n" },
6551
 
6552
    setDirection: docMethodOp(function (dir) {
6553
      if (dir != "rtl") { dir = "ltr"; }
6554
      if (dir == this.direction) { return }
6555
      this.direction = dir;
6556
      this.iter(function (line) { return line.order = null; });
6557
      if (this.cm) { directionChanged(this.cm); }
6558
    })
6559
  });
6560
 
6561
  // Public alias.
6562
  Doc.prototype.eachLine = Doc.prototype.iter;
6563
 
6564
  // Kludge to work around strange IE behavior where it'll sometimes
6565
  // re-fire a series of drag-related events right after the drop (#1551)
6566
  var lastDrop = 0;
6567
 
6568
  function onDrop(e) {
6569
    var cm = this;
6570
    clearDragCursor(cm);
6571
    if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))
6572
      { return }
6573
    e_preventDefault(e);
6574
    if (ie) { lastDrop = +new Date; }
6575
    var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
6576
    if (!pos || cm.isReadOnly()) { return }
6577
    // Might be a file drop, in which case we simply extract the text
6578
    // and insert it.
6579
    if (files && files.length && window.FileReader && window.File) {
6580
      var n = files.length, text = Array(n), read = 0;
6581
      var markAsReadAndPasteIfAllFilesAreRead = function () {
6582
        if (++read == n) {
6583
          operation(cm, function () {
6584
            pos = clipPos(cm.doc, pos);
6585
            var change = {from: pos, to: pos,
6586
                          text: cm.doc.splitLines(
6587
                              text.filter(function (t) { return t != null; }).join(cm.doc.lineSeparator())),
6588
                          origin: "paste"};
6589
            makeChange(cm.doc, change);
6590
            setSelectionReplaceHistory(cm.doc, simpleSelection(clipPos(cm.doc, pos), clipPos(cm.doc, changeEnd(change))));
6591
          })();
6592
        }
6593
      };
6594
      var readTextFromFile = function (file, i) {
6595
        if (cm.options.allowDropFileTypes &&
6596
            indexOf(cm.options.allowDropFileTypes, file.type) == -1) {
6597
          markAsReadAndPasteIfAllFilesAreRead();
6598
          return
6599
        }
6600
        var reader = new FileReader;
6601
        reader.onerror = function () { return markAsReadAndPasteIfAllFilesAreRead(); };
6602
        reader.onload = function () {
6603
          var content = reader.result;
6604
          if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) {
6605
            markAsReadAndPasteIfAllFilesAreRead();
6606
            return
6607
          }
6608
          text[i] = content;
6609
          markAsReadAndPasteIfAllFilesAreRead();
6610
        };
6611
        reader.readAsText(file);
6612
      };
6613
      for (var i = 0; i < files.length; i++) { readTextFromFile(files[i], i); }
6614
    } else { // Normal drop
6615
      // Don't do a replace if the drop happened inside of the selected text.
6616
      if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {
6617
        cm.state.draggingText(e);
6618
        // Ensure the editor is re-focused
6619
        setTimeout(function () { return cm.display.input.focus(); }, 20);
6620
        return
6621
      }
6622
      try {
6623
        var text$1 = e.dataTransfer.getData("Text");
6624
        if (text$1) {
6625
          var selected;
6626
          if (cm.state.draggingText && !cm.state.draggingText.copy)
6627
            { selected = cm.listSelections(); }
6628
          setSelectionNoUndo(cm.doc, simpleSelection(pos, pos));
6629
          if (selected) { for (var i$1 = 0; i$1 < selected.length; ++i$1)
6630
            { replaceRange(cm.doc, "", selected[i$1].anchor, selected[i$1].head, "drag"); } }
6631
          cm.replaceSelection(text$1, "around", "paste");
6632
          cm.display.input.focus();
6633
        }
6634
      }
6635
      catch(e$1){}
6636
    }
6637
  }
6638
 
6639
  function onDragStart(cm, e) {
6640
    if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return }
6641
    if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return }
6642
 
6643
    e.dataTransfer.setData("Text", cm.getSelection());
6644
    e.dataTransfer.effectAllowed = "copyMove";
6645
 
6646
    // Use dummy image instead of default browsers image.
6647
    // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
6648
    if (e.dataTransfer.setDragImage && !safari) {
6649
      var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
6650
      img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
6651
      if (presto) {
6652
        img.width = img.height = 1;
6653
        cm.display.wrapper.appendChild(img);
6654
        // Force a relayout, or Opera won't use our image for some obscure reason
6655
        img._top = img.offsetTop;
6656
      }
6657
      e.dataTransfer.setDragImage(img, 0, 0);
6658
      if (presto) { img.parentNode.removeChild(img); }
6659
    }
6660
  }
6661
 
6662
  function onDragOver(cm, e) {
6663
    var pos = posFromMouse(cm, e);
6664
    if (!pos) { return }
6665
    var frag = document.createDocumentFragment();
6666
    drawSelectionCursor(cm, pos, frag);
6667
    if (!cm.display.dragCursor) {
6668
      cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors");
6669
      cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv);
6670
    }
6671
    removeChildrenAndAdd(cm.display.dragCursor, frag);
6672
  }
6673
 
6674
  function clearDragCursor(cm) {
6675
    if (cm.display.dragCursor) {
6676
      cm.display.lineSpace.removeChild(cm.display.dragCursor);
6677
      cm.display.dragCursor = null;
6678
    }
6679
  }
6680
 
6681
  // These must be handled carefully, because naively registering a
6682
  // handler for each editor will cause the editors to never be
6683
  // garbage collected.
6684
 
6685
  function forEachCodeMirror(f) {
6686
    if (!document.getElementsByClassName) { return }
6687
    var byClass = document.getElementsByClassName("CodeMirror"), editors = [];
6688
    for (var i = 0; i < byClass.length; i++) {
6689
      var cm = byClass[i].CodeMirror;
6690
      if (cm) { editors.push(cm); }
6691
    }
6692
    if (editors.length) { editors[0].operation(function () {
6693
      for (var i = 0; i < editors.length; i++) { f(editors[i]); }
6694
    }); }
6695
  }
6696
 
6697
  var globalsRegistered = false;
6698
  function ensureGlobalHandlers() {
6699
    if (globalsRegistered) { return }
6700
    registerGlobalHandlers();
6701
    globalsRegistered = true;
6702
  }
6703
  function registerGlobalHandlers() {
6704
    // When the window resizes, we need to refresh active editors.
6705
    var resizeTimer;
6706
    on(window, "resize", function () {
6707
      if (resizeTimer == null) { resizeTimer = setTimeout(function () {
6708
        resizeTimer = null;
6709
        forEachCodeMirror(onResize);
6710
      }, 100); }
6711
    });
6712
    // When the window loses focus, we want to show the editor as blurred
6713
    on(window, "blur", function () { return forEachCodeMirror(onBlur); });
6714
  }
6715
  // Called when the window resizes
6716
  function onResize(cm) {
6717
    var d = cm.display;
6718
    // Might be a text scaling operation, clear size caches.
6719
    d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
6720
    d.scrollbarsClipped = false;
6721
    cm.setSize();
6722
  }
6723
 
6724
  var keyNames = {
6725
    3: "Pause", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
6726
    19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
6727
    36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
6728
    46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod",
6729
    106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 145: "ScrollLock",
6730
    173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
6731
    221: "]", 222: "'", 224: "Mod", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete",
6732
    63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert"
6733
  };
6734
 
6735
  // Number keys
6736
  for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i); }
6737
  // Alphabetic keys
6738
  for (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1); }
6739
  // Function keys
6740
  for (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235] = "F" + i$2; }
6741
 
6742
  var keyMap = {};
6743
 
6744
  keyMap.basic = {
6745
    "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
6746
    "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
6747
    "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore",
6748
    "Tab": "defaultTab", "Shift-Tab": "indentAuto",
6749
    "Enter": "newlineAndIndent", "Insert": "toggleOverwrite",
6750
    "Esc": "singleSelection"
6751
  };
6752
  // Note that the save and find-related commands aren't defined by
6753
  // default. User code or addons can define them. Unknown commands
6754
  // are simply ignored.
6755
  keyMap.pcDefault = {
6756
    "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
6757
    "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown",
6758
    "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
6759
    "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
6760
    "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
6761
    "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
6762
    "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection",
6763
    "fallthrough": "basic"
6764
  };
6765
  // Very basic readline/emacs-style bindings, which are standard on Mac.
6766
  keyMap.emacsy = {
6767
    "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
6768
    "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd", "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp",
6769
    "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine",
6770
    "Ctrl-T": "transposeChars", "Ctrl-O": "openLine"
6771
  };
6772
  keyMap.macDefault = {
6773
    "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
6774
    "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
6775
    "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore",
6776
    "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
6777
    "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
6778
    "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight",
6779
    "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd",
6780
    "fallthrough": ["basic", "emacsy"]
6781
  };
6782
  keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
6783
 
6784
  // KEYMAP DISPATCH
6785
 
6786
  function normalizeKeyName(name) {
6787
    var parts = name.split(/-(?!$)/);
6788
    name = parts[parts.length - 1];
6789
    var alt, ctrl, shift, cmd;
6790
    for (var i = 0; i < parts.length - 1; i++) {
6791
      var mod = parts[i];
6792
      if (/^(cmd|meta|m)$/i.test(mod)) { cmd = true; }
6793
      else if (/^a(lt)?$/i.test(mod)) { alt = true; }
6794
      else if (/^(c|ctrl|control)$/i.test(mod)) { ctrl = true; }
6795
      else if (/^s(hift)?$/i.test(mod)) { shift = true; }
6796
      else { throw new Error("Unrecognized modifier name: " + mod) }
6797
    }
6798
    if (alt) { name = "Alt-" + name; }
6799
    if (ctrl) { name = "Ctrl-" + name; }
6800
    if (cmd) { name = "Cmd-" + name; }
6801
    if (shift) { name = "Shift-" + name; }
6802
    return name
6803
  }
6804
 
6805
  // This is a kludge to keep keymaps mostly working as raw objects
6806
  // (backwards compatibility) while at the same time support features
6807
  // like normalization and multi-stroke key bindings. It compiles a
6808
  // new normalized keymap, and then updates the old object to reflect
6809
  // this.
6810
  function normalizeKeyMap(keymap) {
6811
    var copy = {};
6812
    for (var keyname in keymap) { if (keymap.hasOwnProperty(keyname)) {
6813
      var value = keymap[keyname];
6814
      if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) { continue }
6815
      if (value == "...") { delete keymap[keyname]; continue }
6816
 
6817
      var keys = map(keyname.split(" "), normalizeKeyName);
6818
      for (var i = 0; i < keys.length; i++) {
6819
        var val = (void 0), name = (void 0);
6820
        if (i == keys.length - 1) {
6821
          name = keys.join(" ");
6822
          val = value;
6823
        } else {
6824
          name = keys.slice(0, i + 1).join(" ");
6825
          val = "...";
6826
        }
6827
        var prev = copy[name];
6828
        if (!prev) { copy[name] = val; }
6829
        else if (prev != val) { throw new Error("Inconsistent bindings for " + name) }
6830
      }
6831
      delete keymap[keyname];
6832
    } }
6833
    for (var prop in copy) { keymap[prop] = copy[prop]; }
6834
    return keymap
6835
  }
6836
 
6837
  function lookupKey(key, map, handle, context) {
6838
    map = getKeyMap(map);
6839
    var found = map.call ? map.call(key, context) : map[key];
6840
    if (found === false) { return "nothing" }
6841
    if (found === "...") { return "multi" }
6842
    if (found != null && handle(found)) { return "handled" }
6843
 
6844
    if (map.fallthrough) {
6845
      if (Object.prototype.toString.call(map.fallthrough) != "[object Array]")
6846
        { return lookupKey(key, map.fallthrough, handle, context) }
6847
      for (var i = 0; i < map.fallthrough.length; i++) {
6848
        var result = lookupKey(key, map.fallthrough[i], handle, context);
6849
        if (result) { return result }
6850
      }
6851
    }
6852
  }
6853
 
6854
  // Modifier key presses don't count as 'real' key presses for the
6855
  // purpose of keymap fallthrough.
6856
  function isModifierKey(value) {
6857
    var name = typeof value == "string" ? value : keyNames[value.keyCode];
6858
    return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod"
6859
  }
6860
 
6861
  function addModifierNames(name, event, noShift) {
6862
    var base = name;
6863
    if (event.altKey && base != "Alt") { name = "Alt-" + name; }
6864
    if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") { name = "Ctrl-" + name; }
6865
    if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Mod") { name = "Cmd-" + name; }
6866
    if (!noShift && event.shiftKey && base != "Shift") { name = "Shift-" + name; }
6867
    return name
6868
  }
6869
 
6870
  // Look up the name of a key as indicated by an event object.
6871
  function keyName(event, noShift) {
6872
    if (presto && event.keyCode == 34 && event["char"]) { return false }
6873
    var name = keyNames[event.keyCode];
6874
    if (name == null || event.altGraphKey) { return false }
6875
    // Ctrl-ScrollLock has keyCode 3, same as Ctrl-Pause,
6876
    // so we'll use event.code when available (Chrome 48+, FF 38+, Safari 10.1+)
6877
    if (event.keyCode == 3 && event.code) { name = event.code; }
6878
    return addModifierNames(name, event, noShift)
6879
  }
6880
 
6881
  function getKeyMap(val) {
6882
    return typeof val == "string" ? keyMap[val] : val
6883
  }
6884
 
6885
  // Helper for deleting text near the selection(s), used to implement
6886
  // backspace, delete, and similar functionality.
6887
  function deleteNearSelection(cm, compute) {
6888
    var ranges = cm.doc.sel.ranges, kill = [];
6889
    // Build up a set of ranges to kill first, merging overlapping
6890
    // ranges.
6891
    for (var i = 0; i < ranges.length; i++) {
6892
      var toKill = compute(ranges[i]);
6893
      while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {
6894
        var replaced = kill.pop();
6895
        if (cmp(replaced.from, toKill.from) < 0) {
6896
          toKill.from = replaced.from;
6897
          break
6898
        }
6899
      }
6900
      kill.push(toKill);
6901
    }
6902
    // Next, remove those actual ranges.
6903
    runInOp(cm, function () {
6904
      for (var i = kill.length - 1; i >= 0; i--)
6905
        { replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete"); }
6906
      ensureCursorVisible(cm);
6907
    });
6908
  }
6909
 
6910
  function moveCharLogically(line, ch, dir) {
6911
    var target = skipExtendingChars(line.text, ch + dir, dir);
6912
    return target < 0 || target > line.text.length ? null : target
6913
  }
6914
 
6915
  function moveLogically(line, start, dir) {
6916
    var ch = moveCharLogically(line, start.ch, dir);
6917
    return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before")
6918
  }
6919
 
6920
  function endOfLine(visually, cm, lineObj, lineNo, dir) {
6921
    if (visually) {
6922
      if (cm.doc.direction == "rtl") { dir = -dir; }
6923
      var order = getOrder(lineObj, cm.doc.direction);
6924
      if (order) {
6925
        var part = dir < 0 ? lst(order) : order[0];
6926
        var moveInStorageOrder = (dir < 0) == (part.level == 1);
6927
        var sticky = moveInStorageOrder ? "after" : "before";
6928
        var ch;
6929
        // With a wrapped rtl chunk (possibly spanning multiple bidi parts),
6930
        // it could be that the last bidi part is not on the last visual line,
6931
        // since visual lines contain content order-consecutive chunks.
6932
        // Thus, in rtl, we are looking for the first (content-order) character
6933
        // in the rtl chunk that is on the last line (that is, the same line
6934
        // as the last (content-order) character).
6935
        if (part.level > 0 || cm.doc.direction == "rtl") {
6936
          var prep = prepareMeasureForLine(cm, lineObj);
6937
          ch = dir < 0 ? lineObj.text.length - 1 : 0;
6938
          var targetTop = measureCharPrepared(cm, prep, ch).top;
6939
          ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch);
6940
          if (sticky == "before") { ch = moveCharLogically(lineObj, ch, 1); }
6941
        } else { ch = dir < 0 ? part.to : part.from; }
6942
        return new Pos(lineNo, ch, sticky)
6943
      }
6944
    }
6945
    return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after")
6946
  }
6947
 
6948
  function moveVisually(cm, line, start, dir) {
6949
    var bidi = getOrder(line, cm.doc.direction);
6950
    if (!bidi) { return moveLogically(line, start, dir) }
6951
    if (start.ch >= line.text.length) {
6952
      start.ch = line.text.length;
6953
      start.sticky = "before";
6954
    } else if (start.ch <= 0) {
6955
      start.ch = 0;
6956
      start.sticky = "after";
6957
    }
6958
    var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos];
6959
    if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) {
6960
      // Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines,
6961
      // nothing interesting happens.
6962
      return moveLogically(line, start, dir)
6963
    }
6964
 
6965
    var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); };
6966
    var prep;
6967
    var getWrappedLineExtent = function (ch) {
6968
      if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} }
6969
      prep = prep || prepareMeasureForLine(cm, line);
6970
      return wrappedLineExtentChar(cm, line, prep, ch)
6971
    };
6972
    var wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch);
6973
 
6974
    if (cm.doc.direction == "rtl" || part.level == 1) {
6975
      var moveInStorageOrder = (part.level == 1) == (dir < 0);
6976
      var ch = mv(start, moveInStorageOrder ? 1 : -1);
6977
      if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) {
6978
        // Case 2: We move within an rtl part or in an rtl editor on the same visual line
6979
        var sticky = moveInStorageOrder ? "before" : "after";
6980
        return new Pos(start.line, ch, sticky)
6981
      }
6982
    }
6983
 
6984
    // Case 3: Could not move within this bidi part in this visual line, so leave
6985
    // the current bidi part
6986
 
6987
    var searchInVisualLine = function (partPos, dir, wrappedLineExtent) {
6988
      var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder
6989
        ? new Pos(start.line, mv(ch, 1), "before")
6990
        : new Pos(start.line, ch, "after"); };
6991
 
6992
      for (; partPos >= 0 && partPos < bidi.length; partPos += dir) {
6993
        var part = bidi[partPos];
6994
        var moveInStorageOrder = (dir > 0) == (part.level != 1);
6995
        var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1);
6996
        if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) }
6997
        ch = moveInStorageOrder ? part.from : mv(part.to, -1);
6998
        if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) }
6999
      }
7000
    };
7001
 
7002
    // Case 3a: Look for other bidi parts on the same visual line
7003
    var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent);
7004
    if (res) { return res }
7005
 
7006
    // Case 3b: Look for other bidi parts on the next visual line
7007
    var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1);
7008
    if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) {
7009
      res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh));
7010
      if (res) { return res }
7011
    }
7012
 
7013
    // Case 4: Nowhere to move
7014
    return null
7015
  }
7016
 
7017
  // Commands are parameter-less actions that can be performed on an
7018
  // editor, mostly used for keybindings.
7019
  var commands = {
7020
    selectAll: selectAll,
7021
    singleSelection: function (cm) { return cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll); },
7022
    killLine: function (cm) { return deleteNearSelection(cm, function (range) {
7023
      if (range.empty()) {
7024
        var len = getLine(cm.doc, range.head.line).text.length;
7025
        if (range.head.ch == len && range.head.line < cm.lastLine())
7026
          { return {from: range.head, to: Pos(range.head.line + 1, 0)} }
7027
        else
7028
          { return {from: range.head, to: Pos(range.head.line, len)} }
7029
      } else {
7030
        return {from: range.from(), to: range.to()}
7031
      }
7032
    }); },
7033
    deleteLine: function (cm) { return deleteNearSelection(cm, function (range) { return ({
7034
      from: Pos(range.from().line, 0),
7035
      to: clipPos(cm.doc, Pos(range.to().line + 1, 0))
7036
    }); }); },
7037
    delLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { return ({
7038
      from: Pos(range.from().line, 0), to: range.from()
7039
    }); }); },
7040
    delWrappedLineLeft: function (cm) { return deleteNearSelection(cm, function (range) {
7041
      var top = cm.charCoords(range.head, "div").top + 5;
7042
      var leftPos = cm.coordsChar({left: 0, top: top}, "div");
7043
      return {from: leftPos, to: range.from()}
7044
    }); },
7045
    delWrappedLineRight: function (cm) { return deleteNearSelection(cm, function (range) {
7046
      var top = cm.charCoords(range.head, "div").top + 5;
7047
      var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div");
7048
      return {from: range.from(), to: rightPos }
7049
    }); },
7050
    undo: function (cm) { return cm.undo(); },
7051
    redo: function (cm) { return cm.redo(); },
7052
    undoSelection: function (cm) { return cm.undoSelection(); },
7053
    redoSelection: function (cm) { return cm.redoSelection(); },
7054
    goDocStart: function (cm) { return cm.extendSelection(Pos(cm.firstLine(), 0)); },
7055
    goDocEnd: function (cm) { return cm.extendSelection(Pos(cm.lastLine())); },
7056
    goLineStart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStart(cm, range.head.line); },
7057
      {origin: "+move", bias: 1}
7058
    ); },
7059
    goLineStartSmart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStartSmart(cm, range.head); },
7060
      {origin: "+move", bias: 1}
7061
    ); },
7062
    goLineEnd: function (cm) { return cm.extendSelectionsBy(function (range) { return lineEnd(cm, range.head.line); },
7063
      {origin: "+move", bias: -1}
7064
    ); },
7065
    goLineRight: function (cm) { return cm.extendSelectionsBy(function (range) {
7066
      var top = cm.cursorCoords(range.head, "div").top + 5;
7067
      return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div")
7068
    }, sel_move); },
7069
    goLineLeft: function (cm) { return cm.extendSelectionsBy(function (range) {
7070
      var top = cm.cursorCoords(range.head, "div").top + 5;
7071
      return cm.coordsChar({left: 0, top: top}, "div")
7072
    }, sel_move); },
7073
    goLineLeftSmart: function (cm) { return cm.extendSelectionsBy(function (range) {
7074
      var top = cm.cursorCoords(range.head, "div").top + 5;
7075
      var pos = cm.coordsChar({left: 0, top: top}, "div");
7076
      if (pos.ch < cm.getLine(pos.line).search(/\S/)) { return lineStartSmart(cm, range.head) }
7077
      return pos
7078
    }, sel_move); },
7079
    goLineUp: function (cm) { return cm.moveV(-1, "line"); },
7080
    goLineDown: function (cm) { return cm.moveV(1, "line"); },
7081
    goPageUp: function (cm) { return cm.moveV(-1, "page"); },
7082
    goPageDown: function (cm) { return cm.moveV(1, "page"); },
7083
    goCharLeft: function (cm) { return cm.moveH(-1, "char"); },
7084
    goCharRight: function (cm) { return cm.moveH(1, "char"); },
7085
    goColumnLeft: function (cm) { return cm.moveH(-1, "column"); },
7086
    goColumnRight: function (cm) { return cm.moveH(1, "column"); },
7087
    goWordLeft: function (cm) { return cm.moveH(-1, "word"); },
7088
    goGroupRight: function (cm) { return cm.moveH(1, "group"); },
7089
    goGroupLeft: function (cm) { return cm.moveH(-1, "group"); },
7090
    goWordRight: function (cm) { return cm.moveH(1, "word"); },
7091
    delCharBefore: function (cm) { return cm.deleteH(-1, "codepoint"); },
7092
    delCharAfter: function (cm) { return cm.deleteH(1, "char"); },
7093
    delWordBefore: function (cm) { return cm.deleteH(-1, "word"); },
7094
    delWordAfter: function (cm) { return cm.deleteH(1, "word"); },
7095
    delGroupBefore: function (cm) { return cm.deleteH(-1, "group"); },
7096
    delGroupAfter: function (cm) { return cm.deleteH(1, "group"); },
7097
    indentAuto: function (cm) { return cm.indentSelection("smart"); },
7098
    indentMore: function (cm) { return cm.indentSelection("add"); },
7099
    indentLess: function (cm) { return cm.indentSelection("subtract"); },
7100
    insertTab: function (cm) { return cm.replaceSelection("\t"); },
7101
    insertSoftTab: function (cm) {
7102
      var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize;
7103
      for (var i = 0; i < ranges.length; i++) {
7104
        var pos = ranges[i].from();
7105
        var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize);
7106
        spaces.push(spaceStr(tabSize - col % tabSize));
7107
      }
7108
      cm.replaceSelections(spaces);
7109
    },
7110
    defaultTab: function (cm) {
7111
      if (cm.somethingSelected()) { cm.indentSelection("add"); }
7112
      else { cm.execCommand("insertTab"); }
7113
    },
7114
    // Swap the two chars left and right of each selection's head.
7115
    // Move cursor behind the two swapped characters afterwards.
7116
    //
7117
    // Doesn't consider line feeds a character.
7118
    // Doesn't scan more than one line above to find a character.
7119
    // Doesn't do anything on an empty line.
7120
    // Doesn't do anything with non-empty selections.
7121
    transposeChars: function (cm) { return runInOp(cm, function () {
7122
      var ranges = cm.listSelections(), newSel = [];
7123
      for (var i = 0; i < ranges.length; i++) {
7124
        if (!ranges[i].empty()) { continue }
7125
        var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text;
7126
        if (line) {
7127
          if (cur.ch == line.length) { cur = new Pos(cur.line, cur.ch - 1); }
7128
          if (cur.ch > 0) {
7129
            cur = new Pos(cur.line, cur.ch + 1);
7130
            cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),
7131
                            Pos(cur.line, cur.ch - 2), cur, "+transpose");
7132
          } else if (cur.line > cm.doc.first) {
7133
            var prev = getLine(cm.doc, cur.line - 1).text;
7134
            if (prev) {
7135
              cur = new Pos(cur.line, 1);
7136
              cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() +
7137
                              prev.charAt(prev.length - 1),
7138
                              Pos(cur.line - 1, prev.length - 1), cur, "+transpose");
7139
            }
7140
          }
7141
        }
7142
        newSel.push(new Range(cur, cur));
7143
      }
7144
      cm.setSelections(newSel);
7145
    }); },
7146
    newlineAndIndent: function (cm) { return runInOp(cm, function () {
7147
      var sels = cm.listSelections();
7148
      for (var i = sels.length - 1; i >= 0; i--)
7149
        { cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input"); }
7150
      sels = cm.listSelections();
7151
      for (var i$1 = 0; i$1 < sels.length; i$1++)
7152
        { cm.indentLine(sels[i$1].from().line, null, true); }
7153
      ensureCursorVisible(cm);
7154
    }); },
7155
    openLine: function (cm) { return cm.replaceSelection("\n", "start"); },
7156
    toggleOverwrite: function (cm) { return cm.toggleOverwrite(); }
7157
  };
7158
 
7159
 
7160
  function lineStart(cm, lineN) {
7161
    var line = getLine(cm.doc, lineN);
7162
    var visual = visualLine(line);
7163
    if (visual != line) { lineN = lineNo(visual); }
7164
    return endOfLine(true, cm, visual, lineN, 1)
7165
  }
7166
  function lineEnd(cm, lineN) {
7167
    var line = getLine(cm.doc, lineN);
7168
    var visual = visualLineEnd(line);
7169
    if (visual != line) { lineN = lineNo(visual); }
7170
    return endOfLine(true, cm, line, lineN, -1)
7171
  }
7172
  function lineStartSmart(cm, pos) {
7173
    var start = lineStart(cm, pos.line);
7174
    var line = getLine(cm.doc, start.line);
7175
    var order = getOrder(line, cm.doc.direction);
7176
    if (!order || order[0].level == 0) {
7177
      var firstNonWS = Math.max(start.ch, line.text.search(/\S/));
7178
      var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch;
7179
      return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky)
7180
    }
7181
    return start
7182
  }
7183
 
7184
  // Run a handler that was bound to a key.
7185
  function doHandleBinding(cm, bound, dropShift) {
7186
    if (typeof bound == "string") {
7187
      bound = commands[bound];
7188
      if (!bound) { return false }
7189
    }
7190
    // Ensure previous input has been read, so that the handler sees a
7191
    // consistent view of the document
7192
    cm.display.input.ensurePolled();
7193
    var prevShift = cm.display.shift, done = false;
7194
    try {
7195
      if (cm.isReadOnly()) { cm.state.suppressEdits = true; }
7196
      if (dropShift) { cm.display.shift = false; }
7197
      done = bound(cm) != Pass;
7198
    } finally {
7199
      cm.display.shift = prevShift;
7200
      cm.state.suppressEdits = false;
7201
    }
7202
    return done
7203
  }
7204
 
7205
  function lookupKeyForEditor(cm, name, handle) {
7206
    for (var i = 0; i < cm.state.keyMaps.length; i++) {
7207
      var result = lookupKey(name, cm.state.keyMaps[i], handle, cm);
7208
      if (result) { return result }
7209
    }
7210
    return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm))
7211
      || lookupKey(name, cm.options.keyMap, handle, cm)
7212
  }
7213
 
7214
  // Note that, despite the name, this function is also used to check
7215
  // for bound mouse clicks.
7216
 
7217
  var stopSeq = new Delayed;
7218
 
7219
  function dispatchKey(cm, name, e, handle) {
7220
    var seq = cm.state.keySeq;
7221
    if (seq) {
7222
      if (isModifierKey(name)) { return "handled" }
7223
      if (/\'$/.test(name))
7224
        { cm.state.keySeq = null; }
7225
      else
7226
        { stopSeq.set(50, function () {
7227
          if (cm.state.keySeq == seq) {
7228
            cm.state.keySeq = null;
7229
            cm.display.input.reset();
7230
          }
7231
        }); }
7232
      if (dispatchKeyInner(cm, seq + " " + name, e, handle)) { return true }
7233
    }
7234
    return dispatchKeyInner(cm, name, e, handle)
7235
  }
7236
 
7237
  function dispatchKeyInner(cm, name, e, handle) {
7238
    var result = lookupKeyForEditor(cm, name, handle);
7239
 
7240
    if (result == "multi")
7241
      { cm.state.keySeq = name; }
7242
    if (result == "handled")
7243
      { signalLater(cm, "keyHandled", cm, name, e); }
7244
 
7245
    if (result == "handled" || result == "multi") {
7246
      e_preventDefault(e);
7247
      restartBlink(cm);
7248
    }
7249
 
7250
    return !!result
7251
  }
7252
 
7253
  // Handle a key from the keydown event.
7254
  function handleKeyBinding(cm, e) {
7255
    var name = keyName(e, true);
7256
    if (!name) { return false }
7257
 
7258
    if (e.shiftKey && !cm.state.keySeq) {
7259
      // First try to resolve full name (including 'Shift-'). Failing
7260
      // that, see if there is a cursor-motion command (starting with
7261
      // 'go') bound to the keyname without 'Shift-'.
7262
      return dispatchKey(cm, "Shift-" + name, e, function (b) { return doHandleBinding(cm, b, true); })
7263
          || dispatchKey(cm, name, e, function (b) {
7264
               if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
7265
                 { return doHandleBinding(cm, b) }
7266
             })
7267
    } else {
7268
      return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b); })
7269
    }
7270
  }
7271
 
7272
  // Handle a key from the keypress event
7273
  function handleCharBinding(cm, e, ch) {
7274
    return dispatchKey(cm, "'" + ch + "'", e, function (b) { return doHandleBinding(cm, b, true); })
7275
  }
7276
 
7277
  var lastStoppedKey = null;
7278
  function onKeyDown(e) {
7279
    var cm = this;
7280
    if (e.target && e.target != cm.display.input.getField()) { return }
7281
    cm.curOp.focus = activeElt(root(cm));
7282
    if (signalDOMEvent(cm, e)) { return }
7283
    // IE does strange things with escape.
7284
    if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false; }
7285
    var code = e.keyCode;
7286
    cm.display.shift = code == 16 || e.shiftKey;
7287
    var handled = handleKeyBinding(cm, e);
7288
    if (presto) {
7289
      lastStoppedKey = handled ? code : null;
7290
      // Opera has no cut event... we try to at least catch the key combo
7291
      if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
7292
        { cm.replaceSelection("", null, "cut"); }
7293
    }
7294
    if (gecko && !mac && !handled && code == 46 && e.shiftKey && !e.ctrlKey && document.execCommand)
7295
      { document.execCommand("cut"); }
7296
 
7297
    // Turn mouse into crosshair when Alt is held on Mac.
7298
    if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className))
7299
      { showCrossHair(cm); }
7300
  }
7301
 
7302
  function showCrossHair(cm) {
7303
    var lineDiv = cm.display.lineDiv;
7304
    addClass(lineDiv, "CodeMirror-crosshair");
7305
 
7306
    function up(e) {
7307
      if (e.keyCode == 18 || !e.altKey) {
7308
        rmClass(lineDiv, "CodeMirror-crosshair");
7309
        off(document, "keyup", up);
7310
        off(document, "mouseover", up);
7311
      }
7312
    }
7313
    on(document, "keyup", up);
7314
    on(document, "mouseover", up);
7315
  }
7316
 
7317
  function onKeyUp(e) {
7318
    if (e.keyCode == 16) { this.doc.sel.shift = false; }
7319
    signalDOMEvent(this, e);
7320
  }
7321
 
7322
  function onKeyPress(e) {
7323
    var cm = this;
7324
    if (e.target && e.target != cm.display.input.getField()) { return }
7325
    if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return }
7326
    var keyCode = e.keyCode, charCode = e.charCode;
7327
    if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return}
7328
    if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) { return }
7329
    var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
7330
    // Some browsers fire keypress events for backspace
7331
    if (ch == "\x08") { return }
7332
    if (handleCharBinding(cm, e, ch)) { return }
7333
    cm.display.input.onKeyPress(e);
7334
  }
7335
 
7336
  var DOUBLECLICK_DELAY = 400;
7337
 
7338
  var PastClick = function(time, pos, button) {
7339
    this.time = time;
7340
    this.pos = pos;
7341
    this.button = button;
7342
  };
7343
 
7344
  PastClick.prototype.compare = function (time, pos, button) {
7345
    return this.time + DOUBLECLICK_DELAY > time &&
7346
      cmp(pos, this.pos) == 0 && button == this.button
7347
  };
7348
 
7349
  var lastClick, lastDoubleClick;
7350
  function clickRepeat(pos, button) {
7351
    var now = +new Date;
7352
    if (lastDoubleClick && lastDoubleClick.compare(now, pos, button)) {
7353
      lastClick = lastDoubleClick = null;
7354
      return "triple"
7355
    } else if (lastClick && lastClick.compare(now, pos, button)) {
7356
      lastDoubleClick = new PastClick(now, pos, button);
7357
      lastClick = null;
7358
      return "double"
7359
    } else {
7360
      lastClick = new PastClick(now, pos, button);
7361
      lastDoubleClick = null;
7362
      return "single"
7363
    }
7364
  }
7365
 
7366
  // A mouse down can be a single click, double click, triple click,
7367
  // start of selection drag, start of text drag, new cursor
7368
  // (ctrl-click), rectangle drag (alt-drag), or xwin
7369
  // middle-click-paste. Or it might be a click on something we should
7370
  // not interfere with, such as a scrollbar or widget.
7371
  function onMouseDown(e) {
7372
    var cm = this, display = cm.display;
7373
    if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) { return }
7374
    display.input.ensurePolled();
7375
    display.shift = e.shiftKey;
7376
 
7377
    if (eventInWidget(display, e)) {
7378
      if (!webkit) {
7379
        // Briefly turn off draggability, to allow widgets to do
7380
        // normal dragging things.
7381
        display.scroller.draggable = false;
7382
        setTimeout(function () { return display.scroller.draggable = true; }, 100);
7383
      }
7384
      return
7385
    }
7386
    if (clickInGutter(cm, e)) { return }
7387
    var pos = posFromMouse(cm, e), button = e_button(e), repeat = pos ? clickRepeat(pos, button) : "single";
7388
    win(cm).focus();
7389
 
7390
    // #3261: make sure, that we're not starting a second selection
7391
    if (button == 1 && cm.state.selectingText)
7392
      { cm.state.selectingText(e); }
7393
 
7394
    if (pos && handleMappedButton(cm, button, pos, repeat, e)) { return }
7395
 
7396
    if (button == 1) {
7397
      if (pos) { leftButtonDown(cm, pos, repeat, e); }
7398
      else if (e_target(e) == display.scroller) { e_preventDefault(e); }
7399
    } else if (button == 2) {
7400
      if (pos) { extendSelection(cm.doc, pos); }
7401
      setTimeout(function () { return display.input.focus(); }, 20);
7402
    } else if (button == 3) {
7403
      if (captureRightClick) { cm.display.input.onContextMenu(e); }
7404
      else { delayBlurEvent(cm); }
7405
    }
7406
  }
7407
 
7408
  function handleMappedButton(cm, button, pos, repeat, event) {
7409
    var name = "Click";
7410
    if (repeat == "double") { name = "Double" + name; }
7411
    else if (repeat == "triple") { name = "Triple" + name; }
7412
    name = (button == 1 ? "Left" : button == 2 ? "Middle" : "Right") + name;
7413
 
7414
    return dispatchKey(cm,  addModifierNames(name, event), event, function (bound) {
7415
      if (typeof bound == "string") { bound = commands[bound]; }
7416
      if (!bound) { return false }
7417
      var done = false;
7418
      try {
7419
        if (cm.isReadOnly()) { cm.state.suppressEdits = true; }
7420
        done = bound(cm, pos) != Pass;
7421
      } finally {
7422
        cm.state.suppressEdits = false;
7423
      }
7424
      return done
7425
    })
7426
  }
7427
 
7428
  function configureMouse(cm, repeat, event) {
7429
    var option = cm.getOption("configureMouse");
7430
    var value = option ? option(cm, repeat, event) : {};
7431
    if (value.unit == null) {
7432
      var rect = chromeOS ? event.shiftKey && event.metaKey : event.altKey;
7433
      value.unit = rect ? "rectangle" : repeat == "single" ? "char" : repeat == "double" ? "word" : "line";
7434
    }
7435
    if (value.extend == null || cm.doc.extend) { value.extend = cm.doc.extend || event.shiftKey; }
7436
    if (value.addNew == null) { value.addNew = mac ? event.metaKey : event.ctrlKey; }
7437
    if (value.moveOnDrag == null) { value.moveOnDrag = !(mac ? event.altKey : event.ctrlKey); }
7438
    return value
7439
  }
7440
 
7441
  function leftButtonDown(cm, pos, repeat, event) {
7442
    if (ie) { setTimeout(bind(ensureFocus, cm), 0); }
7443
    else { cm.curOp.focus = activeElt(root(cm)); }
7444
 
7445
    var behavior = configureMouse(cm, repeat, event);
7446
 
7447
    var sel = cm.doc.sel, contained;
7448
    if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() &&
7449
        repeat == "single" && (contained = sel.contains(pos)) > -1 &&
7450
        (cmp((contained = sel.ranges[contained]).from(), pos) < 0 || pos.xRel > 0) &&
7451
        (cmp(contained.to(), pos) > 0 || pos.xRel < 0))
7452
      { leftButtonStartDrag(cm, event, pos, behavior); }
7453
    else
7454
      { leftButtonSelect(cm, event, pos, behavior); }
7455
  }
7456
 
7457
  // Start a text drag. When it ends, see if any dragging actually
7458
  // happen, and treat as a click if it didn't.
7459
  function leftButtonStartDrag(cm, event, pos, behavior) {
7460
    var display = cm.display, moved = false;
7461
    var dragEnd = operation(cm, function (e) {
7462
      if (webkit) { display.scroller.draggable = false; }
7463
      cm.state.draggingText = false;
7464
      if (cm.state.delayingBlurEvent) {
7465
        if (cm.hasFocus()) { cm.state.delayingBlurEvent = false; }
7466
        else { delayBlurEvent(cm); }
7467
      }
7468
      off(display.wrapper.ownerDocument, "mouseup", dragEnd);
7469
      off(display.wrapper.ownerDocument, "mousemove", mouseMove);
7470
      off(display.scroller, "dragstart", dragStart);
7471
      off(display.scroller, "drop", dragEnd);
7472
      if (!moved) {
7473
        e_preventDefault(e);
7474
        if (!behavior.addNew)
7475
          { extendSelection(cm.doc, pos, null, null, behavior.extend); }
7476
        // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)
7477
        if ((webkit && !safari) || ie && ie_version == 9)
7478
          { setTimeout(function () {display.wrapper.ownerDocument.body.focus({preventScroll: true}); display.input.focus();}, 20); }
7479
        else
7480
          { display.input.focus(); }
7481
      }
7482
    });
7483
    var mouseMove = function(e2) {
7484
      moved = moved || Math.abs(event.clientX - e2.clientX) + Math.abs(event.clientY - e2.clientY) >= 10;
7485
    };
7486
    var dragStart = function () { return moved = true; };
7487
    // Let the drag handler handle this.
7488
    if (webkit) { display.scroller.draggable = true; }
7489
    cm.state.draggingText = dragEnd;
7490
    dragEnd.copy = !behavior.moveOnDrag;
7491
    on(display.wrapper.ownerDocument, "mouseup", dragEnd);
7492
    on(display.wrapper.ownerDocument, "mousemove", mouseMove);
7493
    on(display.scroller, "dragstart", dragStart);
7494
    on(display.scroller, "drop", dragEnd);
7495
 
7496
    cm.state.delayingBlurEvent = true;
7497
    setTimeout(function () { return display.input.focus(); }, 20);
7498
    // IE's approach to draggable
7499
    if (display.scroller.dragDrop) { display.scroller.dragDrop(); }
7500
  }
7501
 
7502
  function rangeForUnit(cm, pos, unit) {
7503
    if (unit == "char") { return new Range(pos, pos) }
7504
    if (unit == "word") { return cm.findWordAt(pos) }
7505
    if (unit == "line") { return new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) }
7506
    var result = unit(cm, pos);
7507
    return new Range(result.from, result.to)
7508
  }
7509
 
7510
  // Normal selection, as opposed to text dragging.
7511
  function leftButtonSelect(cm, event, start, behavior) {
7512
    if (ie) { delayBlurEvent(cm); }
7513
    var display = cm.display, doc = cm.doc;
7514
    e_preventDefault(event);
7515
 
7516
    var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges;
7517
    if (behavior.addNew && !behavior.extend) {
7518
      ourIndex = doc.sel.contains(start);
7519
      if (ourIndex > -1)
7520
        { ourRange = ranges[ourIndex]; }
7521
      else
7522
        { ourRange = new Range(start, start); }
7523
    } else {
7524
      ourRange = doc.sel.primary();
7525
      ourIndex = doc.sel.primIndex;
7526
    }
7527
 
7528
    if (behavior.unit == "rectangle") {
7529
      if (!behavior.addNew) { ourRange = new Range(start, start); }
7530
      start = posFromMouse(cm, event, true, true);
7531
      ourIndex = -1;
7532
    } else {
7533
      var range = rangeForUnit(cm, start, behavior.unit);
7534
      if (behavior.extend)
7535
        { ourRange = extendRange(ourRange, range.anchor, range.head, behavior.extend); }
7536
      else
7537
        { ourRange = range; }
7538
    }
7539
 
7540
    if (!behavior.addNew) {
7541
      ourIndex = 0;
7542
      setSelection(doc, new Selection([ourRange], 0), sel_mouse);
7543
      startSel = doc.sel;
7544
    } else if (ourIndex == -1) {
7545
      ourIndex = ranges.length;
7546
      setSelection(doc, normalizeSelection(cm, ranges.concat([ourRange]), ourIndex),
7547
                   {scroll: false, origin: "*mouse"});
7548
    } else if (ranges.length > 1 && ranges[ourIndex].empty() && behavior.unit == "char" && !behavior.extend) {
7549
      setSelection(doc, normalizeSelection(cm, ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0),
7550
                   {scroll: false, origin: "*mouse"});
7551
      startSel = doc.sel;
7552
    } else {
7553
      replaceOneSelection(doc, ourIndex, ourRange, sel_mouse);
7554
    }
7555
 
7556
    var lastPos = start;
7557
    function extendTo(pos) {
7558
      if (cmp(lastPos, pos) == 0) { return }
7559
      lastPos = pos;
7560
 
7561
      if (behavior.unit == "rectangle") {
7562
        var ranges = [], tabSize = cm.options.tabSize;
7563
        var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize);
7564
        var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize);
7565
        var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol);
7566
        for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line));
7567
             line <= end; line++) {
7568
          var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize);
7569
          if (left == right)
7570
            { ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))); }
7571
          else if (text.length > leftPos)
7572
            { ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))); }
7573
        }
7574
        if (!ranges.length) { ranges.push(new Range(start, start)); }
7575
        setSelection(doc, normalizeSelection(cm, startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex),
7576
                     {origin: "*mouse", scroll: false});
7577
        cm.scrollIntoView(pos);
7578
      } else {
7579
        var oldRange = ourRange;
7580
        var range = rangeForUnit(cm, pos, behavior.unit);
7581
        var anchor = oldRange.anchor, head;
7582
        if (cmp(range.anchor, anchor) > 0) {
7583
          head = range.head;
7584
          anchor = minPos(oldRange.from(), range.anchor);
7585
        } else {
7586
          head = range.anchor;
7587
          anchor = maxPos(oldRange.to(), range.head);
7588
        }
7589
        var ranges$1 = startSel.ranges.slice(0);
7590
        ranges$1[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc, anchor), head));
7591
        setSelection(doc, normalizeSelection(cm, ranges$1, ourIndex), sel_mouse);
7592
      }
7593
    }
7594
 
7595
    var editorSize = display.wrapper.getBoundingClientRect();
7596
    // Used to ensure timeout re-tries don't fire when another extend
7597
    // happened in the meantime (clearTimeout isn't reliable -- at
7598
    // least on Chrome, the timeouts still happen even when cleared,
7599
    // if the clear happens after their scheduled firing time).
7600
    var counter = 0;
7601
 
7602
    function extend(e) {
7603
      var curCount = ++counter;
7604
      var cur = posFromMouse(cm, e, true, behavior.unit == "rectangle");
7605
      if (!cur) { return }
7606
      if (cmp(cur, lastPos) != 0) {
7607
        cm.curOp.focus = activeElt(root(cm));
7608
        extendTo(cur);
7609
        var visible = visibleLines(display, doc);
7610
        if (cur.line >= visible.to || cur.line < visible.from)
7611
          { setTimeout(operation(cm, function () {if (counter == curCount) { extend(e); }}), 150); }
7612
      } else {
7613
        var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;
7614
        if (outside) { setTimeout(operation(cm, function () {
7615
          if (counter != curCount) { return }
7616
          display.scroller.scrollTop += outside;
7617
          extend(e);
7618
        }), 50); }
7619
      }
7620
    }
7621
 
7622
    function done(e) {
7623
      cm.state.selectingText = false;
7624
      counter = Infinity;
7625
      // If e is null or undefined we interpret this as someone trying
7626
      // to explicitly cancel the selection rather than the user
7627
      // letting go of the mouse button.
7628
      if (e) {
7629
        e_preventDefault(e);
7630
        display.input.focus();
7631
      }
7632
      off(display.wrapper.ownerDocument, "mousemove", move);
7633
      off(display.wrapper.ownerDocument, "mouseup", up);
7634
      doc.history.lastSelOrigin = null;
7635
    }
7636
 
7637
    var move = operation(cm, function (e) {
7638
      if (e.buttons === 0 || !e_button(e)) { done(e); }
7639
      else { extend(e); }
7640
    });
7641
    var up = operation(cm, done);
7642
    cm.state.selectingText = up;
7643
    on(display.wrapper.ownerDocument, "mousemove", move);
7644
    on(display.wrapper.ownerDocument, "mouseup", up);
7645
  }
7646
 
7647
  // Used when mouse-selecting to adjust the anchor to the proper side
7648
  // of a bidi jump depending on the visual position of the head.
7649
  function bidiSimplify(cm, range) {
7650
    var anchor = range.anchor;
7651
    var head = range.head;
7652
    var anchorLine = getLine(cm.doc, anchor.line);
7653
    if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) { return range }
7654
    var order = getOrder(anchorLine);
7655
    if (!order) { return range }
7656
    var index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index];
7657
    if (part.from != anchor.ch && part.to != anchor.ch) { return range }
7658
    var boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1);
7659
    if (boundary == 0 || boundary == order.length) { return range }
7660
 
7661
    // Compute the relative visual position of the head compared to the
7662
    // anchor (<0 is to the left, >0 to the right)
7663
    var leftSide;
7664
    if (head.line != anchor.line) {
7665
      leftSide = (head.line - anchor.line) * (cm.doc.direction == "ltr" ? 1 : -1) > 0;
7666
    } else {
7667
      var headIndex = getBidiPartAt(order, head.ch, head.sticky);
7668
      var dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1);
7669
      if (headIndex == boundary - 1 || headIndex == boundary)
7670
        { leftSide = dir < 0; }
7671
      else
7672
        { leftSide = dir > 0; }
7673
    }
7674
 
7675
    var usePart = order[boundary + (leftSide ? -1 : 0)];
7676
    var from = leftSide == (usePart.level == 1);
7677
    var ch = from ? usePart.from : usePart.to, sticky = from ? "after" : "before";
7678
    return anchor.ch == ch && anchor.sticky == sticky ? range : new Range(new Pos(anchor.line, ch, sticky), head)
7679
  }
7680
 
7681
 
7682
  // Determines whether an event happened in the gutter, and fires the
7683
  // handlers for the corresponding event.
7684
  function gutterEvent(cm, e, type, prevent) {
7685
    var mX, mY;
7686
    if (e.touches) {
7687
      mX = e.touches[0].clientX;
7688
      mY = e.touches[0].clientY;
7689
    } else {
7690
      try { mX = e.clientX; mY = e.clientY; }
7691
      catch(e$1) { return false }
7692
    }
7693
    if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false }
7694
    if (prevent) { e_preventDefault(e); }
7695
 
7696
    var display = cm.display;
7697
    var lineBox = display.lineDiv.getBoundingClientRect();
7698
 
7699
    if (mY > lineBox.bottom || !hasHandler(cm, type)) { return e_defaultPrevented(e) }
7700
    mY -= lineBox.top - display.viewOffset;
7701
 
7702
    for (var i = 0; i < cm.display.gutterSpecs.length; ++i) {
7703
      var g = display.gutters.childNodes[i];
7704
      if (g && g.getBoundingClientRect().right >= mX) {
7705
        var line = lineAtHeight(cm.doc, mY);
7706
        var gutter = cm.display.gutterSpecs[i];
7707
        signal(cm, type, cm, line, gutter.className, e);
7708
        return e_defaultPrevented(e)
7709
      }
7710
    }
7711
  }
7712
 
7713
  function clickInGutter(cm, e) {
7714
    return gutterEvent(cm, e, "gutterClick", true)
7715
  }
7716
 
7717
  // CONTEXT MENU HANDLING
7718
 
7719
  // To make the context menu work, we need to briefly unhide the
7720
  // textarea (making it as unobtrusive as possible) to let the
7721
  // right-click take effect on it.
7722
  function onContextMenu(cm, e) {
7723
    if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) { return }
7724
    if (signalDOMEvent(cm, e, "contextmenu")) { return }
7725
    if (!captureRightClick) { cm.display.input.onContextMenu(e); }
7726
  }
7727
 
7728
  function contextMenuInGutter(cm, e) {
7729
    if (!hasHandler(cm, "gutterContextMenu")) { return false }
7730
    return gutterEvent(cm, e, "gutterContextMenu", false)
7731
  }
7732
 
7733
  function themeChanged(cm) {
7734
    cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
7735
      cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
7736
    clearCaches(cm);
7737
  }
7738
 
7739
  var Init = {toString: function(){return "CodeMirror.Init"}};
7740
 
7741
  var defaults = {};
7742
  var optionHandlers = {};
7743
 
7744
  function defineOptions(CodeMirror) {
7745
    var optionHandlers = CodeMirror.optionHandlers;
7746
 
7747
    function option(name, deflt, handle, notOnInit) {
7748
      CodeMirror.defaults[name] = deflt;
7749
      if (handle) { optionHandlers[name] =
7750
        notOnInit ? function (cm, val, old) {if (old != Init) { handle(cm, val, old); }} : handle; }
7751
    }
7752
 
7753
    CodeMirror.defineOption = option;
7754
 
7755
    // Passed to option handlers when there is no old value.
7756
    CodeMirror.Init = Init;
7757
 
7758
    // These two are, on init, called from the constructor because they
7759
    // have to be initialized before the editor can start at all.
7760
    option("value", "", function (cm, val) { return cm.setValue(val); }, true);
7761
    option("mode", null, function (cm, val) {
7762
      cm.doc.modeOption = val;
7763
      loadMode(cm);
7764
    }, true);
7765
 
7766
    option("indentUnit", 2, loadMode, true);
7767
    option("indentWithTabs", false);
7768
    option("smartIndent", true);
7769
    option("tabSize", 4, function (cm) {
7770
      resetModeState(cm);
7771
      clearCaches(cm);
7772
      regChange(cm);
7773
    }, true);
7774
 
7775
    option("lineSeparator", null, function (cm, val) {
7776
      cm.doc.lineSep = val;
7777
      if (!val) { return }
7778
      var newBreaks = [], lineNo = cm.doc.first;
7779
      cm.doc.iter(function (line) {
7780
        for (var pos = 0;;) {
7781
          var found = line.text.indexOf(val, pos);
7782
          if (found == -1) { break }
7783
          pos = found + val.length;
7784
          newBreaks.push(Pos(lineNo, found));
7785
        }
7786
        lineNo++;
7787
      });
7788
      for (var i = newBreaks.length - 1; i >= 0; i--)
7789
        { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)); }
7790
    });
7791
    option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b\u200e\u200f\u2028\u2029\u202d\u202e\u2066\u2067\u2069\ufeff\ufff9-\ufffc]/g, function (cm, val, old) {
7792
      cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g");
7793
      if (old != Init) { cm.refresh(); }
7794
    });
7795
    option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function (cm) { return cm.refresh(); }, true);
7796
    option("electricChars", true);
7797
    option("inputStyle", mobile ? "contenteditable" : "textarea", function () {
7798
      throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME
7799
    }, true);
7800
    option("spellcheck", false, function (cm, val) { return cm.getInputField().spellcheck = val; }, true);
7801
    option("autocorrect", false, function (cm, val) { return cm.getInputField().autocorrect = val; }, true);
7802
    option("autocapitalize", false, function (cm, val) { return cm.getInputField().autocapitalize = val; }, true);
7803
    option("rtlMoveVisually", !windows);
7804
    option("wholeLineUpdateBefore", true);
7805
 
7806
    option("theme", "default", function (cm) {
7807
      themeChanged(cm);
7808
      updateGutters(cm);
7809
    }, true);
7810
    option("keyMap", "default", function (cm, val, old) {
7811
      var next = getKeyMap(val);
7812
      var prev = old != Init && getKeyMap(old);
7813
      if (prev && prev.detach) { prev.detach(cm, next); }
7814
      if (next.attach) { next.attach(cm, prev || null); }
7815
    });
7816
    option("extraKeys", null);
7817
    option("configureMouse", null);
7818
 
7819
    option("lineWrapping", false, wrappingChanged, true);
7820
    option("gutters", [], function (cm, val) {
7821
      cm.display.gutterSpecs = getGutters(val, cm.options.lineNumbers);
7822
      updateGutters(cm);
7823
    }, true);
7824
    option("fixedGutter", true, function (cm, val) {
7825
      cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
7826
      cm.refresh();
7827
    }, true);
7828
    option("coverGutterNextToScrollbar", false, function (cm) { return updateScrollbars(cm); }, true);
7829
    option("scrollbarStyle", "native", function (cm) {
7830
      initScrollbars(cm);
7831
      updateScrollbars(cm);
7832
      cm.display.scrollbars.setScrollTop(cm.doc.scrollTop);
7833
      cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft);
7834
    }, true);
7835
    option("lineNumbers", false, function (cm, val) {
7836
      cm.display.gutterSpecs = getGutters(cm.options.gutters, val);
7837
      updateGutters(cm);
7838
    }, true);
7839
    option("firstLineNumber", 1, updateGutters, true);
7840
    option("lineNumberFormatter", function (integer) { return integer; }, updateGutters, true);
7841
    option("showCursorWhenSelecting", false, updateSelection, true);
7842
 
7843
    option("resetSelectionOnContextMenu", true);
7844
    option("lineWiseCopyCut", true);
7845
    option("pasteLinesPerSelection", true);
7846
    option("selectionsMayTouch", false);
7847
 
7848
    option("readOnly", false, function (cm, val) {
7849
      if (val == "nocursor") {
7850
        onBlur(cm);
7851
        cm.display.input.blur();
7852
      }
7853
      cm.display.input.readOnlyChanged(val);
7854
    });
7855
 
7856
    option("screenReaderLabel", null, function (cm, val) {
7857
      val = (val === '') ? null : val;
7858
      cm.display.input.screenReaderLabelChanged(val);
7859
    });
7860
 
7861
    option("disableInput", false, function (cm, val) {if (!val) { cm.display.input.reset(); }}, true);
7862
    option("dragDrop", true, dragDropChanged);
7863
    option("allowDropFileTypes", null);
7864
 
7865
    option("cursorBlinkRate", 530);
7866
    option("cursorScrollMargin", 0);
7867
    option("cursorHeight", 1, updateSelection, true);
7868
    option("singleCursorHeightPerLine", true, updateSelection, true);
7869
    option("workTime", 100);
7870
    option("workDelay", 100);
7871
    option("flattenSpans", true, resetModeState, true);
7872
    option("addModeClass", false, resetModeState, true);
7873
    option("pollInterval", 100);
7874
    option("undoDepth", 200, function (cm, val) { return cm.doc.history.undoDepth = val; });
7875
    option("historyEventDelay", 1250);
7876
    option("viewportMargin", 10, function (cm) { return cm.refresh(); }, true);
7877
    option("maxHighlightLength", 10000, resetModeState, true);
7878
    option("moveInputWithCursor", true, function (cm, val) {
7879
      if (!val) { cm.display.input.resetPosition(); }
7880
    });
7881
 
7882
    option("tabindex", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || ""; });
7883
    option("autofocus", null);
7884
    option("direction", "ltr", function (cm, val) { return cm.doc.setDirection(val); }, true);
7885
    option("phrases", null);
7886
  }
7887
 
7888
  function dragDropChanged(cm, value, old) {
7889
    var wasOn = old && old != Init;
7890
    if (!value != !wasOn) {
7891
      var funcs = cm.display.dragFunctions;
7892
      var toggle = value ? on : off;
7893
      toggle(cm.display.scroller, "dragstart", funcs.start);
7894
      toggle(cm.display.scroller, "dragenter", funcs.enter);
7895
      toggle(cm.display.scroller, "dragover", funcs.over);
7896
      toggle(cm.display.scroller, "dragleave", funcs.leave);
7897
      toggle(cm.display.scroller, "drop", funcs.drop);
7898
    }
7899
  }
7900
 
7901
  function wrappingChanged(cm) {
7902
    if (cm.options.lineWrapping) {
7903
      addClass(cm.display.wrapper, "CodeMirror-wrap");
7904
      cm.display.sizer.style.minWidth = "";
7905
      cm.display.sizerWidth = null;
7906
    } else {
7907
      rmClass(cm.display.wrapper, "CodeMirror-wrap");
7908
      findMaxLine(cm);
7909
    }
7910
    estimateLineHeights(cm);
7911
    regChange(cm);
7912
    clearCaches(cm);
7913
    setTimeout(function () { return updateScrollbars(cm); }, 100);
7914
  }
7915
 
7916
  // A CodeMirror instance represents an editor. This is the object
7917
  // that user code is usually dealing with.
7918
 
7919
  function CodeMirror(place, options) {
7920
    var this$1 = this;
7921
 
7922
    if (!(this instanceof CodeMirror)) { return new CodeMirror(place, options) }
7923
 
7924
    this.options = options = options ? copyObj(options) : {};
7925
    // Determine effective options based on given values and defaults.
7926
    copyObj(defaults, options, false);
7927
 
7928
    var doc = options.value;
7929
    if (typeof doc == "string") { doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction); }
7930
    else if (options.mode) { doc.modeOption = options.mode; }
7931
    this.doc = doc;
7932
 
7933
    var input = new CodeMirror.inputStyles[options.inputStyle](this);
7934
    var display = this.display = new Display(place, doc, input, options);
7935
    display.wrapper.CodeMirror = this;
7936
    themeChanged(this);
7937
    if (options.lineWrapping)
7938
      { this.display.wrapper.className += " CodeMirror-wrap"; }
7939
    initScrollbars(this);
7940
 
7941
    this.state = {
7942
      keyMaps: [],  // stores maps added by addKeyMap
7943
      overlays: [], // highlighting overlays, as added by addOverlay
7944
      modeGen: 0,   // bumped when mode/overlay changes, used to invalidate highlighting info
7945
      overwrite: false,
7946
      delayingBlurEvent: false,
7947
      focused: false,
7948
      suppressEdits: false, // used to disable editing during key handlers when in readOnly mode
7949
      pasteIncoming: -1, cutIncoming: -1, // help recognize paste/cut edits in input.poll
7950
      selectingText: false,
7951
      draggingText: false,
7952
      highlight: new Delayed(), // stores highlight worker timeout
7953
      keySeq: null,  // Unfinished key sequence
7954
      specialChars: null
7955
    };
7956
 
7957
    if (options.autofocus && !mobile) { display.input.focus(); }
7958
 
7959
    // Override magic textarea content restore that IE sometimes does
7960
    // on our hidden textarea on reload
7961
    if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20); }
7962
 
7963
    registerEventHandlers(this);
7964
    ensureGlobalHandlers();
7965
 
7966
    startOperation(this);
7967
    this.curOp.forceUpdate = true;
7968
    attachDoc(this, doc);
7969
 
7970
    if ((options.autofocus && !mobile) || this.hasFocus())
7971
      { setTimeout(function () {
7972
        if (this$1.hasFocus() && !this$1.state.focused) { onFocus(this$1); }
7973
      }, 20); }
7974
    else
7975
      { onBlur(this); }
7976
 
7977
    for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt))
7978
      { optionHandlers[opt](this, options[opt], Init); } }
7979
    maybeUpdateLineNumberWidth(this);
7980
    if (options.finishInit) { options.finishInit(this); }
7981
    for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this); }
7982
    endOperation(this);
7983
    // Suppress optimizelegibility in Webkit, since it breaks text
7984
    // measuring on line wrapping boundaries.
7985
    if (webkit && options.lineWrapping &&
7986
        getComputedStyle(display.lineDiv).textRendering == "optimizelegibility")
7987
      { display.lineDiv.style.textRendering = "auto"; }
7988
  }
7989
 
7990
  // The default configuration options.
7991
  CodeMirror.defaults = defaults;
7992
  // Functions to run when options are changed.
7993
  CodeMirror.optionHandlers = optionHandlers;
7994
 
7995
  // Attach the necessary event handlers when initializing the editor
7996
  function registerEventHandlers(cm) {
7997
    var d = cm.display;
7998
    on(d.scroller, "mousedown", operation(cm, onMouseDown));
7999
    // Older IE's will not fire a second mousedown for a double click
8000
    if (ie && ie_version < 11)
8001
      { on(d.scroller, "dblclick", operation(cm, function (e) {
8002
        if (signalDOMEvent(cm, e)) { return }
8003
        var pos = posFromMouse(cm, e);
8004
        if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) { return }
8005
        e_preventDefault(e);
8006
        var word = cm.findWordAt(pos);
8007
        extendSelection(cm.doc, word.anchor, word.head);
8008
      })); }
8009
    else
8010
      { on(d.scroller, "dblclick", function (e) { return signalDOMEvent(cm, e) || e_preventDefault(e); }); }
8011
    // Some browsers fire contextmenu *after* opening the menu, at
8012
    // which point we can't mess with it anymore. Context menu is
8013
    // handled in onMouseDown for these browsers.
8014
    on(d.scroller, "contextmenu", function (e) { return onContextMenu(cm, e); });
8015
    on(d.input.getField(), "contextmenu", function (e) {
8016
      if (!d.scroller.contains(e.target)) { onContextMenu(cm, e); }
8017
    });
8018
 
8019
    // Used to suppress mouse event handling when a touch happens
8020
    var touchFinished, prevTouch = {end: 0};
8021
    function finishTouch() {
8022
      if (d.activeTouch) {
8023
        touchFinished = setTimeout(function () { return d.activeTouch = null; }, 1000);
8024
        prevTouch = d.activeTouch;
8025
        prevTouch.end = +new Date;
8026
      }
8027
    }
8028
    function isMouseLikeTouchEvent(e) {
8029
      if (e.touches.length != 1) { return false }
8030
      var touch = e.touches[0];
8031
      return touch.radiusX <= 1 && touch.radiusY <= 1
8032
    }
8033
    function farAway(touch, other) {
8034
      if (other.left == null) { return true }
8035
      var dx = other.left - touch.left, dy = other.top - touch.top;
8036
      return dx * dx + dy * dy > 20 * 20
8037
    }
8038
    on(d.scroller, "touchstart", function (e) {
8039
      if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e) && !clickInGutter(cm, e)) {
8040
        d.input.ensurePolled();
8041
        clearTimeout(touchFinished);
8042
        var now = +new Date;
8043
        d.activeTouch = {start: now, moved: false,
8044
                         prev: now - prevTouch.end <= 300 ? prevTouch : null};
8045
        if (e.touches.length == 1) {
8046
          d.activeTouch.left = e.touches[0].pageX;
8047
          d.activeTouch.top = e.touches[0].pageY;
8048
        }
8049
      }
8050
    });
8051
    on(d.scroller, "touchmove", function () {
8052
      if (d.activeTouch) { d.activeTouch.moved = true; }
8053
    });
8054
    on(d.scroller, "touchend", function (e) {
8055
      var touch = d.activeTouch;
8056
      if (touch && !eventInWidget(d, e) && touch.left != null &&
8057
          !touch.moved && new Date - touch.start < 300) {
8058
        var pos = cm.coordsChar(d.activeTouch, "page"), range;
8059
        if (!touch.prev || farAway(touch, touch.prev)) // Single tap
8060
          { range = new Range(pos, pos); }
8061
        else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap
8062
          { range = cm.findWordAt(pos); }
8063
        else // Triple tap
8064
          { range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))); }
8065
        cm.setSelection(range.anchor, range.head);
8066
        cm.focus();
8067
        e_preventDefault(e);
8068
      }
8069
      finishTouch();
8070
    });
8071
    on(d.scroller, "touchcancel", finishTouch);
8072
 
8073
    // Sync scrolling between fake scrollbars and real scrollable
8074
    // area, ensure viewport is updated when scrolling.
8075
    on(d.scroller, "scroll", function () {
8076
      if (d.scroller.clientHeight) {
8077
        updateScrollTop(cm, d.scroller.scrollTop);
8078
        setScrollLeft(cm, d.scroller.scrollLeft, true);
8079
        signal(cm, "scroll", cm);
8080
      }
8081
    });
8082
 
8083
    // Listen to wheel events in order to try and update the viewport on time.
8084
    on(d.scroller, "mousewheel", function (e) { return onScrollWheel(cm, e); });
8085
    on(d.scroller, "DOMMouseScroll", function (e) { return onScrollWheel(cm, e); });
8086
 
8087
    // Prevent wrapper from ever scrolling
8088
    on(d.wrapper, "scroll", function () { return d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
8089
 
8090
    d.dragFunctions = {
8091
      enter: function (e) {if (!signalDOMEvent(cm, e)) { e_stop(e); }},
8092
      over: function (e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e); }},
8093
      start: function (e) { return onDragStart(cm, e); },
8094
      drop: operation(cm, onDrop),
8095
      leave: function (e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm); }}
8096
    };
8097
 
8098
    var inp = d.input.getField();
8099
    on(inp, "keyup", function (e) { return onKeyUp.call(cm, e); });
8100
    on(inp, "keydown", operation(cm, onKeyDown));
8101
    on(inp, "keypress", operation(cm, onKeyPress));
8102
    on(inp, "focus", function (e) { return onFocus(cm, e); });
8103
    on(inp, "blur", function (e) { return onBlur(cm, e); });
8104
  }
8105
 
8106
  var initHooks = [];
8107
  CodeMirror.defineInitHook = function (f) { return initHooks.push(f); };
8108
 
8109
  // Indent the given line. The how parameter can be "smart",
8110
  // "add"/null, "subtract", or "prev". When aggressive is false
8111
  // (typically set to true for forced single-line indents), empty
8112
  // lines are not indented, and places where the mode returns Pass
8113
  // are left alone.
8114
  function indentLine(cm, n, how, aggressive) {
8115
    var doc = cm.doc, state;
8116
    if (how == null) { how = "add"; }
8117
    if (how == "smart") {
8118
      // Fall back to "prev" when the mode doesn't have an indentation
8119
      // method.
8120
      if (!doc.mode.indent) { how = "prev"; }
8121
      else { state = getContextBefore(cm, n).state; }
8122
    }
8123
 
8124
    var tabSize = cm.options.tabSize;
8125
    var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
8126
    if (line.stateAfter) { line.stateAfter = null; }
8127
    var curSpaceString = line.text.match(/^\s*/)[0], indentation;
8128
    if (!aggressive && !/\S/.test(line.text)) {
8129
      indentation = 0;
8130
      how = "not";
8131
    } else if (how == "smart") {
8132
      indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
8133
      if (indentation == Pass || indentation > 150) {
8134
        if (!aggressive) { return }
8135
        how = "prev";
8136
      }
8137
    }
8138
    if (how == "prev") {
8139
      if (n > doc.first) { indentation = countColumn(getLine(doc, n-1).text, null, tabSize); }
8140
      else { indentation = 0; }
8141
    } else if (how == "add") {
8142
      indentation = curSpace + cm.options.indentUnit;
8143
    } else if (how == "subtract") {
8144
      indentation = curSpace - cm.options.indentUnit;
8145
    } else if (typeof how == "number") {
8146
      indentation = curSpace + how;
8147
    }
8148
    indentation = Math.max(0, indentation);
8149
 
8150
    var indentString = "", pos = 0;
8151
    if (cm.options.indentWithTabs)
8152
      { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";} }
8153
    if (pos < indentation) { indentString += spaceStr(indentation - pos); }
8154
 
8155
    if (indentString != curSpaceString) {
8156
      replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
8157
      line.stateAfter = null;
8158
      return true
8159
    } else {
8160
      // Ensure that, if the cursor was in the whitespace at the start
8161
      // of the line, it is moved to the end of that space.
8162
      for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) {
8163
        var range = doc.sel.ranges[i$1];
8164
        if (range.head.line == n && range.head.ch < curSpaceString.length) {
8165
          var pos$1 = Pos(n, curSpaceString.length);
8166
          replaceOneSelection(doc, i$1, new Range(pos$1, pos$1));
8167
          break
8168
        }
8169
      }
8170
    }
8171
  }
8172
 
8173
  // This will be set to a {lineWise: bool, text: [string]} object, so
8174
  // that, when pasting, we know what kind of selections the copied
8175
  // text was made out of.
8176
  var lastCopied = null;
8177
 
8178
  function setLastCopied(newLastCopied) {
8179
    lastCopied = newLastCopied;
8180
  }
8181
 
8182
  function applyTextInput(cm, inserted, deleted, sel, origin) {
8183
    var doc = cm.doc;
8184
    cm.display.shift = false;
8185
    if (!sel) { sel = doc.sel; }
8186
 
8187
    var recent = +new Date - 200;
8188
    var paste = origin == "paste" || cm.state.pasteIncoming > recent;
8189
    var textLines = splitLinesAuto(inserted), multiPaste = null;
8190
    // When pasting N lines into N selections, insert one line per selection
8191
    if (paste && sel.ranges.length > 1) {
8192
      if (lastCopied && lastCopied.text.join("\n") == inserted) {
8193
        if (sel.ranges.length % lastCopied.text.length == 0) {
8194
          multiPaste = [];
8195
          for (var i = 0; i < lastCopied.text.length; i++)
8196
            { multiPaste.push(doc.splitLines(lastCopied.text[i])); }
8197
        }
8198
      } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) {
8199
        multiPaste = map(textLines, function (l) { return [l]; });
8200
      }
8201
    }
8202
 
8203
    var updateInput = cm.curOp.updateInput;
8204
    // Normal behavior is to insert the new text into every selection
8205
    for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) {
8206
      var range = sel.ranges[i$1];
8207
      var from = range.from(), to = range.to();
8208
      if (range.empty()) {
8209
        if (deleted && deleted > 0) // Handle deletion
8210
          { from = Pos(from.line, from.ch - deleted); }
8211
        else if (cm.state.overwrite && !paste) // Handle overwrite
8212
          { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); }
8213
        else if (paste && lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == textLines.join("\n"))
8214
          { from = to = Pos(from.line, 0); }
8215
      }
8216
      var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines,
8217
                         origin: origin || (paste ? "paste" : cm.state.cutIncoming > recent ? "cut" : "+input")};
8218
      makeChange(cm.doc, changeEvent);
8219
      signalLater(cm, "inputRead", cm, changeEvent);
8220
    }
8221
    if (inserted && !paste)
8222
      { triggerElectric(cm, inserted); }
8223
 
8224
    ensureCursorVisible(cm);
8225
    if (cm.curOp.updateInput < 2) { cm.curOp.updateInput = updateInput; }
8226
    cm.curOp.typing = true;
8227
    cm.state.pasteIncoming = cm.state.cutIncoming = -1;
8228
  }
8229
 
8230
  function handlePaste(e, cm) {
8231
    var pasted = e.clipboardData && e.clipboardData.getData("Text");
8232
    if (pasted) {
8233
      e.preventDefault();
8234
      if (!cm.isReadOnly() && !cm.options.disableInput && cm.hasFocus())
8235
        { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, "paste"); }); }
8236
      return true
8237
    }
8238
  }
8239
 
8240
  function triggerElectric(cm, inserted) {
8241
    // When an 'electric' character is inserted, immediately trigger a reindent
8242
    if (!cm.options.electricChars || !cm.options.smartIndent) { return }
8243
    var sel = cm.doc.sel;
8244
 
8245
    for (var i = sel.ranges.length - 1; i >= 0; i--) {
8246
      var range = sel.ranges[i];
8247
      if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head.line)) { continue }
8248
      var mode = cm.getModeAt(range.head);
8249
      var indented = false;
8250
      if (mode.electricChars) {
8251
        for (var j = 0; j < mode.electricChars.length; j++)
8252
          { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
8253
            indented = indentLine(cm, range.head.line, "smart");
8254
            break
8255
          } }
8256
      } else if (mode.electricInput) {
8257
        if (mode.electricInput.test(getLine(cm.doc, range.head.line).text.slice(0, range.head.ch)))
8258
          { indented = indentLine(cm, range.head.line, "smart"); }
8259
      }
8260
      if (indented) { signalLater(cm, "electricInput", cm, range.head.line); }
8261
    }
8262
  }
8263
 
8264
  function copyableRanges(cm) {
8265
    var text = [], ranges = [];
8266
    for (var i = 0; i < cm.doc.sel.ranges.length; i++) {
8267
      var line = cm.doc.sel.ranges[i].head.line;
8268
      var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)};
8269
      ranges.push(lineRange);
8270
      text.push(cm.getRange(lineRange.anchor, lineRange.head));
8271
    }
8272
    return {text: text, ranges: ranges}
8273
  }
8274
 
8275
  function disableBrowserMagic(field, spellcheck, autocorrect, autocapitalize) {
8276
    field.setAttribute("autocorrect", autocorrect ? "on" : "off");
8277
    field.setAttribute("autocapitalize", autocapitalize ? "on" : "off");
8278
    field.setAttribute("spellcheck", !!spellcheck);
8279
  }
8280
 
8281
  function hiddenTextarea() {
8282
    var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; min-height: 1em; outline: none");
8283
    var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
8284
    // The textarea is kept positioned near the cursor to prevent the
8285
    // fact that it'll be scrolled into view on input from scrolling
8286
    // our fake cursor out of view. On webkit, when wrap=off, paste is
8287
    // very slow. So make the area wide instead.
8288
    if (webkit) { te.style.width = "1000px"; }
8289
    else { te.setAttribute("wrap", "off"); }
8290
    // If border: 0; -- iOS fails to open keyboard (issue #1287)
8291
    if (ios) { te.style.border = "1px solid black"; }
8292
    return div
8293
  }
8294
 
8295
  // The publicly visible API. Note that methodOp(f) means
8296
  // 'wrap f in an operation, performed on its `this` parameter'.
8297
 
8298
  // This is not the complete set of editor methods. Most of the
8299
  // methods defined on the Doc type are also injected into
8300
  // CodeMirror.prototype, for backwards compatibility and
8301
  // convenience.
8302
 
8303
  function addEditorMethods(CodeMirror) {
8304
    var optionHandlers = CodeMirror.optionHandlers;
8305
 
8306
    var helpers = CodeMirror.helpers = {};
8307
 
8308
    CodeMirror.prototype = {
8309
      constructor: CodeMirror,
8310
      focus: function(){win(this).focus(); this.display.input.focus();},
8311
 
8312
      setOption: function(option, value) {
8313
        var options = this.options, old = options[option];
8314
        if (options[option] == value && option != "mode") { return }
8315
        options[option] = value;
8316
        if (optionHandlers.hasOwnProperty(option))
8317
          { operation(this, optionHandlers[option])(this, value, old); }
8318
        signal(this, "optionChange", this, option);
8319
      },
8320
 
8321
      getOption: function(option) {return this.options[option]},
8322
      getDoc: function() {return this.doc},
8323
 
8324
      addKeyMap: function(map, bottom) {
8325
        this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map));
8326
      },
8327
      removeKeyMap: function(map) {
8328
        var maps = this.state.keyMaps;
8329
        for (var i = 0; i < maps.length; ++i)
8330
          { if (maps[i] == map || maps[i].name == map) {
8331
            maps.splice(i, 1);
8332
            return true
8333
          } }
8334
      },
8335
 
8336
      addOverlay: methodOp(function(spec, options) {
8337
        var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
8338
        if (mode.startState) { throw new Error("Overlays may not be stateful.") }
8339
        insertSorted(this.state.overlays,
8340
                     {mode: mode, modeSpec: spec, opaque: options && options.opaque,
8341
                      priority: (options && options.priority) || 0},
8342
                     function (overlay) { return overlay.priority; });
8343
        this.state.modeGen++;
8344
        regChange(this);
8345
      }),
8346
      removeOverlay: methodOp(function(spec) {
8347
        var overlays = this.state.overlays;
8348
        for (var i = 0; i < overlays.length; ++i) {
8349
          var cur = overlays[i].modeSpec;
8350
          if (cur == spec || typeof spec == "string" && cur.name == spec) {
8351
            overlays.splice(i, 1);
8352
            this.state.modeGen++;
8353
            regChange(this);
8354
            return
8355
          }
8356
        }
8357
      }),
8358
 
8359
      indentLine: methodOp(function(n, dir, aggressive) {
8360
        if (typeof dir != "string" && typeof dir != "number") {
8361
          if (dir == null) { dir = this.options.smartIndent ? "smart" : "prev"; }
8362
          else { dir = dir ? "add" : "subtract"; }
8363
        }
8364
        if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive); }
8365
      }),
8366
      indentSelection: methodOp(function(how) {
8367
        var ranges = this.doc.sel.ranges, end = -1;
8368
        for (var i = 0; i < ranges.length; i++) {
8369
          var range = ranges[i];
8370
          if (!range.empty()) {
8371
            var from = range.from(), to = range.to();
8372
            var start = Math.max(end, from.line);
8373
            end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1;
8374
            for (var j = start; j < end; ++j)
8375
              { indentLine(this, j, how); }
8376
            var newRanges = this.doc.sel.ranges;
8377
            if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0)
8378
              { replaceOneSelection(this.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll); }
8379
          } else if (range.head.line > end) {
8380
            indentLine(this, range.head.line, how, true);
8381
            end = range.head.line;
8382
            if (i == this.doc.sel.primIndex) { ensureCursorVisible(this); }
8383
          }
8384
        }
8385
      }),
8386
 
8387
      // Fetch the parser token for a given character. Useful for hacks
8388
      // that want to inspect the mode state (say, for completion).
8389
      getTokenAt: function(pos, precise) {
8390
        return takeToken(this, pos, precise)
8391
      },
8392
 
8393
      getLineTokens: function(line, precise) {
8394
        return takeToken(this, Pos(line), precise, true)
8395
      },
8396
 
8397
      getTokenTypeAt: function(pos) {
8398
        pos = clipPos(this.doc, pos);
8399
        var styles = getLineStyles(this, getLine(this.doc, pos.line));
8400
        var before = 0, after = (styles.length - 1) / 2, ch = pos.ch;
8401
        var type;
8402
        if (ch == 0) { type = styles[2]; }
8403
        else { for (;;) {
8404
          var mid = (before + after) >> 1;
8405
          if ((mid ? styles[mid * 2 - 1] : 0) >= ch) { after = mid; }
8406
          else if (styles[mid * 2 + 1] < ch) { before = mid + 1; }
8407
          else { type = styles[mid * 2 + 2]; break }
8408
        } }
8409
        var cut = type ? type.indexOf("overlay ") : -1;
8410
        return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1)
8411
      },
8412
 
8413
      getModeAt: function(pos) {
8414
        var mode = this.doc.mode;
8415
        if (!mode.innerMode) { return mode }
8416
        return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode
8417
      },
8418
 
8419
      getHelper: function(pos, type) {
8420
        return this.getHelpers(pos, type)[0]
8421
      },
8422
 
8423
      getHelpers: function(pos, type) {
8424
        var found = [];
8425
        if (!helpers.hasOwnProperty(type)) { return found }
8426
        var help = helpers[type], mode = this.getModeAt(pos);
8427
        if (typeof mode[type] == "string") {
8428
          if (help[mode[type]]) { found.push(help[mode[type]]); }
8429
        } else if (mode[type]) {
8430
          for (var i = 0; i < mode[type].length; i++) {
8431
            var val = help[mode[type][i]];
8432
            if (val) { found.push(val); }
8433
          }
8434
        } else if (mode.helperType && help[mode.helperType]) {
8435
          found.push(help[mode.helperType]);
8436
        } else if (help[mode.name]) {
8437
          found.push(help[mode.name]);
8438
        }
8439
        for (var i$1 = 0; i$1 < help._global.length; i$1++) {
8440
          var cur = help._global[i$1];
8441
          if (cur.pred(mode, this) && indexOf(found, cur.val) == -1)
8442
            { found.push(cur.val); }
8443
        }
8444
        return found
8445
      },
8446
 
8447
      getStateAfter: function(line, precise) {
8448
        var doc = this.doc;
8449
        line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);
8450
        return getContextBefore(this, line + 1, precise).state
8451
      },
8452
 
8453
      cursorCoords: function(start, mode) {
8454
        var pos, range = this.doc.sel.primary();
8455
        if (start == null) { pos = range.head; }
8456
        else if (typeof start == "object") { pos = clipPos(this.doc, start); }
8457
        else { pos = start ? range.from() : range.to(); }
8458
        return cursorCoords(this, pos, mode || "page")
8459
      },
8460
 
8461
      charCoords: function(pos, mode) {
8462
        return charCoords(this, clipPos(this.doc, pos), mode || "page")
8463
      },
8464
 
8465
      coordsChar: function(coords, mode) {
8466
        coords = fromCoordSystem(this, coords, mode || "page");
8467
        return coordsChar(this, coords.left, coords.top)
8468
      },
8469
 
8470
      lineAtHeight: function(height, mode) {
8471
        height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top;
8472
        return lineAtHeight(this.doc, height + this.display.viewOffset)
8473
      },
8474
      heightAtLine: function(line, mode, includeWidgets) {
8475
        var end = false, lineObj;
8476
        if (typeof line == "number") {
8477
          var last = this.doc.first + this.doc.size - 1;
8478
          if (line < this.doc.first) { line = this.doc.first; }
8479
          else if (line > last) { line = last; end = true; }
8480
          lineObj = getLine(this.doc, line);
8481
        } else {
8482
          lineObj = line;
8483
        }
8484
        return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page", includeWidgets || end).top +
8485
          (end ? this.doc.height - heightAtLine(lineObj) : 0)
8486
      },
8487
 
8488
      defaultTextHeight: function() { return textHeight(this.display) },
8489
      defaultCharWidth: function() { return charWidth(this.display) },
8490
 
8491
      getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}},
8492
 
8493
      addWidget: function(pos, node, scroll, vert, horiz) {
8494
        var display = this.display;
8495
        pos = cursorCoords(this, clipPos(this.doc, pos));
8496
        var top = pos.bottom, left = pos.left;
8497
        node.style.position = "absolute";
8498
        node.setAttribute("cm-ignore-events", "true");
8499
        this.display.input.setUneditable(node);
8500
        display.sizer.appendChild(node);
8501
        if (vert == "over") {
8502
          top = pos.top;
8503
        } else if (vert == "above" || vert == "near") {
8504
          var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
8505
          hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
8506
          // Default to positioning above (if specified and possible); otherwise default to positioning below
8507
          if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
8508
            { top = pos.top - node.offsetHeight; }
8509
          else if (pos.bottom + node.offsetHeight <= vspace)
8510
            { top = pos.bottom; }
8511
          if (left + node.offsetWidth > hspace)
8512
            { left = hspace - node.offsetWidth; }
8513
        }
8514
        node.style.top = top + "px";
8515
        node.style.left = node.style.right = "";
8516
        if (horiz == "right") {
8517
          left = display.sizer.clientWidth - node.offsetWidth;
8518
          node.style.right = "0px";
8519
        } else {
8520
          if (horiz == "left") { left = 0; }
8521
          else if (horiz == "middle") { left = (display.sizer.clientWidth - node.offsetWidth) / 2; }
8522
          node.style.left = left + "px";
8523
        }
8524
        if (scroll)
8525
          { scrollIntoView(this, {left: left, top: top, right: left + node.offsetWidth, bottom: top + node.offsetHeight}); }
8526
      },
8527
 
8528
      triggerOnKeyDown: methodOp(onKeyDown),
8529
      triggerOnKeyPress: methodOp(onKeyPress),
8530
      triggerOnKeyUp: onKeyUp,
8531
      triggerOnMouseDown: methodOp(onMouseDown),
8532
 
8533
      execCommand: function(cmd) {
8534
        if (commands.hasOwnProperty(cmd))
8535
          { return commands[cmd].call(null, this) }
8536
      },
8537
 
8538
      triggerElectric: methodOp(function(text) { triggerElectric(this, text); }),
8539
 
8540
      findPosH: function(from, amount, unit, visually) {
8541
        var dir = 1;
8542
        if (amount < 0) { dir = -1; amount = -amount; }
8543
        var cur = clipPos(this.doc, from);
8544
        for (var i = 0; i < amount; ++i) {
8545
          cur = findPosH(this.doc, cur, dir, unit, visually);
8546
          if (cur.hitSide) { break }
8547
        }
8548
        return cur
8549
      },
8550
 
8551
      moveH: methodOp(function(dir, unit) {
8552
        var this$1 = this;
8553
 
8554
        this.extendSelectionsBy(function (range) {
8555
          if (this$1.display.shift || this$1.doc.extend || range.empty())
8556
            { return findPosH(this$1.doc, range.head, dir, unit, this$1.options.rtlMoveVisually) }
8557
          else
8558
            { return dir < 0 ? range.from() : range.to() }
8559
        }, sel_move);
8560
      }),
8561
 
8562
      deleteH: methodOp(function(dir, unit) {
8563
        var sel = this.doc.sel, doc = this.doc;
8564
        if (sel.somethingSelected())
8565
          { doc.replaceSelection("", null, "+delete"); }
8566
        else
8567
          { deleteNearSelection(this, function (range) {
8568
            var other = findPosH(doc, range.head, dir, unit, false);
8569
            return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other}
8570
          }); }
8571
      }),
8572
 
8573
      findPosV: function(from, amount, unit, goalColumn) {
8574
        var dir = 1, x = goalColumn;
8575
        if (amount < 0) { dir = -1; amount = -amount; }
8576
        var cur = clipPos(this.doc, from);
8577
        for (var i = 0; i < amount; ++i) {
8578
          var coords = cursorCoords(this, cur, "div");
8579
          if (x == null) { x = coords.left; }
8580
          else { coords.left = x; }
8581
          cur = findPosV(this, coords, dir, unit);
8582
          if (cur.hitSide) { break }
8583
        }
8584
        return cur
8585
      },
8586
 
8587
      moveV: methodOp(function(dir, unit) {
8588
        var this$1 = this;
8589
 
8590
        var doc = this.doc, goals = [];
8591
        var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected();
8592
        doc.extendSelectionsBy(function (range) {
8593
          if (collapse)
8594
            { return dir < 0 ? range.from() : range.to() }
8595
          var headPos = cursorCoords(this$1, range.head, "div");
8596
          if (range.goalColumn != null) { headPos.left = range.goalColumn; }
8597
          goals.push(headPos.left);
8598
          var pos = findPosV(this$1, headPos, dir, unit);
8599
          if (unit == "page" && range == doc.sel.primary())
8600
            { addToScrollTop(this$1, charCoords(this$1, pos, "div").top - headPos.top); }
8601
          return pos
8602
        }, sel_move);
8603
        if (goals.length) { for (var i = 0; i < doc.sel.ranges.length; i++)
8604
          { doc.sel.ranges[i].goalColumn = goals[i]; } }
8605
      }),
8606
 
8607
      // Find the word at the given position (as returned by coordsChar).
8608
      findWordAt: function(pos) {
8609
        var doc = this.doc, line = getLine(doc, pos.line).text;
8610
        var start = pos.ch, end = pos.ch;
8611
        if (line) {
8612
          var helper = this.getHelper(pos, "wordChars");
8613
          if ((pos.sticky == "before" || end == line.length) && start) { --start; } else { ++end; }
8614
          var startChar = line.charAt(start);
8615
          var check = isWordChar(startChar, helper)
8616
            ? function (ch) { return isWordChar(ch, helper); }
8617
            : /\s/.test(startChar) ? function (ch) { return /\s/.test(ch); }
8618
            : function (ch) { return (!/\s/.test(ch) && !isWordChar(ch)); };
8619
          while (start > 0 && check(line.charAt(start - 1))) { --start; }
8620
          while (end < line.length && check(line.charAt(end))) { ++end; }
8621
        }
8622
        return new Range(Pos(pos.line, start), Pos(pos.line, end))
8623
      },
8624
 
8625
      toggleOverwrite: function(value) {
8626
        if (value != null && value == this.state.overwrite) { return }
8627
        if (this.state.overwrite = !this.state.overwrite)
8628
          { addClass(this.display.cursorDiv, "CodeMirror-overwrite"); }
8629
        else
8630
          { rmClass(this.display.cursorDiv, "CodeMirror-overwrite"); }
8631
 
8632
        signal(this, "overwriteToggle", this, this.state.overwrite);
8633
      },
8634
      hasFocus: function() { return this.display.input.getField() == activeElt(root(this)) },
8635
      isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) },
8636
 
8637
      scrollTo: methodOp(function (x, y) { scrollToCoords(this, x, y); }),
8638
      getScrollInfo: function() {
8639
        var scroller = this.display.scroller;
8640
        return {left: scroller.scrollLeft, top: scroller.scrollTop,
8641
                height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight,
8642
                width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth,
8643
                clientHeight: displayHeight(this), clientWidth: displayWidth(this)}
8644
      },
8645
 
8646
      scrollIntoView: methodOp(function(range, margin) {
8647
        if (range == null) {
8648
          range = {from: this.doc.sel.primary().head, to: null};
8649
          if (margin == null) { margin = this.options.cursorScrollMargin; }
8650
        } else if (typeof range == "number") {
8651
          range = {from: Pos(range, 0), to: null};
8652
        } else if (range.from == null) {
8653
          range = {from: range, to: null};
8654
        }
8655
        if (!range.to) { range.to = range.from; }
8656
        range.margin = margin || 0;
8657
 
8658
        if (range.from.line != null) {
8659
          scrollToRange(this, range);
8660
        } else {
8661
          scrollToCoordsRange(this, range.from, range.to, range.margin);
8662
        }
8663
      }),
8664
 
8665
      setSize: methodOp(function(width, height) {
8666
        var this$1 = this;
8667
 
8668
        var interpret = function (val) { return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; };
8669
        if (width != null) { this.display.wrapper.style.width = interpret(width); }
8670
        if (height != null) { this.display.wrapper.style.height = interpret(height); }
8671
        if (this.options.lineWrapping) { clearLineMeasurementCache(this); }
8672
        var lineNo = this.display.viewFrom;
8673
        this.doc.iter(lineNo, this.display.viewTo, function (line) {
8674
          if (line.widgets) { for (var i = 0; i < line.widgets.length; i++)
8675
            { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo, "widget"); break } } }
8676
          ++lineNo;
8677
        });
8678
        this.curOp.forceUpdate = true;
8679
        signal(this, "refresh", this);
8680
      }),
8681
 
8682
      operation: function(f){return runInOp(this, f)},
8683
      startOperation: function(){return startOperation(this)},
8684
      endOperation: function(){return endOperation(this)},
8685
 
8686
      refresh: methodOp(function() {
8687
        var oldHeight = this.display.cachedTextHeight;
8688
        regChange(this);
8689
        this.curOp.forceUpdate = true;
8690
        clearCaches(this);
8691
        scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop);
8692
        updateGutterSpace(this.display);
8693
        if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5 || this.options.lineWrapping)
8694
          { estimateLineHeights(this); }
8695
        signal(this, "refresh", this);
8696
      }),
8697
 
8698
      swapDoc: methodOp(function(doc) {
8699
        var old = this.doc;
8700
        old.cm = null;
8701
        // Cancel the current text selection if any (#5821)
8702
        if (this.state.selectingText) { this.state.selectingText(); }
8703
        attachDoc(this, doc);
8704
        clearCaches(this);
8705
        this.display.input.reset();
8706
        scrollToCoords(this, doc.scrollLeft, doc.scrollTop);
8707
        this.curOp.forceScroll = true;
8708
        signalLater(this, "swapDoc", this, old);
8709
        return old
8710
      }),
8711
 
8712
      phrase: function(phraseText) {
8713
        var phrases = this.options.phrases;
8714
        return phrases && Object.prototype.hasOwnProperty.call(phrases, phraseText) ? phrases[phraseText] : phraseText
8715
      },
8716
 
8717
      getInputField: function(){return this.display.input.getField()},
8718
      getWrapperElement: function(){return this.display.wrapper},
8719
      getScrollerElement: function(){return this.display.scroller},
8720
      getGutterElement: function(){return this.display.gutters}
8721
    };
8722
    eventMixin(CodeMirror);
8723
 
8724
    CodeMirror.registerHelper = function(type, name, value) {
8725
      if (!helpers.hasOwnProperty(type)) { helpers[type] = CodeMirror[type] = {_global: []}; }
8726
      helpers[type][name] = value;
8727
    };
8728
    CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {
8729
      CodeMirror.registerHelper(type, name, value);
8730
      helpers[type]._global.push({pred: predicate, val: value});
8731
    };
8732
  }
8733
 
8734
  // Used for horizontal relative motion. Dir is -1 or 1 (left or
8735
  // right), unit can be "codepoint", "char", "column" (like char, but
8736
  // doesn't cross line boundaries), "word" (across next word), or
8737
  // "group" (to the start of next group of word or
8738
  // non-word-non-whitespace chars). The visually param controls
8739
  // whether, in right-to-left text, direction 1 means to move towards
8740
  // the next index in the string, or towards the character to the right
8741
  // of the current position. The resulting position will have a
8742
  // hitSide=true property if it reached the end of the document.
8743
  function findPosH(doc, pos, dir, unit, visually) {
8744
    var oldPos = pos;
8745
    var origDir = dir;
8746
    var lineObj = getLine(doc, pos.line);
8747
    var lineDir = visually && doc.direction == "rtl" ? -dir : dir;
8748
    function findNextLine() {
8749
      var l = pos.line + lineDir;
8750
      if (l < doc.first || l >= doc.first + doc.size) { return false }
8751
      pos = new Pos(l, pos.ch, pos.sticky);
8752
      return lineObj = getLine(doc, l)
8753
    }
8754
    function moveOnce(boundToLine) {
8755
      var next;
8756
      if (unit == "codepoint") {
8757
        var ch = lineObj.text.charCodeAt(pos.ch + (dir > 0 ? 0 : -1));
8758
        if (isNaN(ch)) {
8759
          next = null;
8760
        } else {
8761
          var astral = dir > 0 ? ch >= 0xD800 && ch < 0xDC00 : ch >= 0xDC00 && ch < 0xDFFF;
8762
          next = new Pos(pos.line, Math.max(0, Math.min(lineObj.text.length, pos.ch + dir * (astral ? 2 : 1))), -dir);
8763
        }
8764
      } else if (visually) {
8765
        next = moveVisually(doc.cm, lineObj, pos, dir);
8766
      } else {
8767
        next = moveLogically(lineObj, pos, dir);
8768
      }
8769
      if (next == null) {
8770
        if (!boundToLine && findNextLine())
8771
          { pos = endOfLine(visually, doc.cm, lineObj, pos.line, lineDir); }
8772
        else
8773
          { return false }
8774
      } else {
8775
        pos = next;
8776
      }
8777
      return true
8778
    }
8779
 
8780
    if (unit == "char" || unit == "codepoint") {
8781
      moveOnce();
8782
    } else if (unit == "column") {
8783
      moveOnce(true);
8784
    } else if (unit == "word" || unit == "group") {
8785
      var sawType = null, group = unit == "group";
8786
      var helper = doc.cm && doc.cm.getHelper(pos, "wordChars");
8787
      for (var first = true;; first = false) {
8788
        if (dir < 0 && !moveOnce(!first)) { break }
8789
        var cur = lineObj.text.charAt(pos.ch) || "\n";
8790
        var type = isWordChar(cur, helper) ? "w"
8791
          : group && cur == "\n" ? "n"
8792
          : !group || /\s/.test(cur) ? null
8793
          : "p";
8794
        if (group && !first && !type) { type = "s"; }
8795
        if (sawType && sawType != type) {
8796
          if (dir < 0) {dir = 1; moveOnce(); pos.sticky = "after";}
8797
          break
8798
        }
8799
 
8800
        if (type) { sawType = type; }
8801
        if (dir > 0 && !moveOnce(!first)) { break }
8802
      }
8803
    }
8804
    var result = skipAtomic(doc, pos, oldPos, origDir, true);
8805
    if (equalCursorPos(oldPos, result)) { result.hitSide = true; }
8806
    return result
8807
  }
8808
 
8809
  // For relative vertical movement. Dir may be -1 or 1. Unit can be
8810
  // "page" or "line". The resulting position will have a hitSide=true
8811
  // property if it reached the end of the document.
8812
  function findPosV(cm, pos, dir, unit) {
8813
    var doc = cm.doc, x = pos.left, y;
8814
    if (unit == "page") {
8815
      var pageSize = Math.min(cm.display.wrapper.clientHeight, win(cm).innerHeight || doc(cm).documentElement.clientHeight);
8816
      var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3);
8817
      y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount;
8818
 
8819
    } else if (unit == "line") {
8820
      y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
8821
    }
8822
    var target;
8823
    for (;;) {
8824
      target = coordsChar(cm, x, y);
8825
      if (!target.outside) { break }
8826
      if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break }
8827
      y += dir * 5;
8828
    }
8829
    return target
8830
  }
8831
 
8832
  // CONTENTEDITABLE INPUT STYLE
8833
 
8834
  var ContentEditableInput = function(cm) {
8835
    this.cm = cm;
8836
    this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null;
8837
    this.polling = new Delayed();
8838
    this.composing = null;
8839
    this.gracePeriod = false;
8840
    this.readDOMTimeout = null;
8841
  };
8842
 
8843
  ContentEditableInput.prototype.init = function (display) {
8844
      var this$1 = this;
8845
 
8846
    var input = this, cm = input.cm;
8847
    var div = input.div = display.lineDiv;
8848
    div.contentEditable = true;
8849
    disableBrowserMagic(div, cm.options.spellcheck, cm.options.autocorrect, cm.options.autocapitalize);
8850
 
8851
    function belongsToInput(e) {
8852
      for (var t = e.target; t; t = t.parentNode) {
8853
        if (t == div) { return true }
8854
        if (/\bCodeMirror-(?:line)?widget\b/.test(t.className)) { break }
8855
      }
8856
      return false
8857
    }
8858
 
8859
    on(div, "paste", function (e) {
8860
      if (!belongsToInput(e) || signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
8861
      // IE doesn't fire input events, so we schedule a read for the pasted content in this way
8862
      if (ie_version <= 11) { setTimeout(operation(cm, function () { return this$1.updateFromDOM(); }), 20); }
8863
    });
8864
 
8865
    on(div, "compositionstart", function (e) {
8866
      this$1.composing = {data: e.data, done: false};
8867
    });
8868
    on(div, "compositionupdate", function (e) {
8869
      if (!this$1.composing) { this$1.composing = {data: e.data, done: false}; }
8870
    });
8871
    on(div, "compositionend", function (e) {
8872
      if (this$1.composing) {
8873
        if (e.data != this$1.composing.data) { this$1.readFromDOMSoon(); }
8874
        this$1.composing.done = true;
8875
      }
8876
    });
8877
 
8878
    on(div, "touchstart", function () { return input.forceCompositionEnd(); });
8879
 
8880
    on(div, "input", function () {
8881
      if (!this$1.composing) { this$1.readFromDOMSoon(); }
8882
    });
8883
 
8884
    function onCopyCut(e) {
8885
      if (!belongsToInput(e) || signalDOMEvent(cm, e)) { return }
8886
      if (cm.somethingSelected()) {
8887
        setLastCopied({lineWise: false, text: cm.getSelections()});
8888
        if (e.type == "cut") { cm.replaceSelection("", null, "cut"); }
8889
      } else if (!cm.options.lineWiseCopyCut) {
8890
        return
8891
      } else {
8892
        var ranges = copyableRanges(cm);
8893
        setLastCopied({lineWise: true, text: ranges.text});
8894
        if (e.type == "cut") {
8895
          cm.operation(function () {
8896
            cm.setSelections(ranges.ranges, 0, sel_dontScroll);
8897
            cm.replaceSelection("", null, "cut");
8898
          });
8899
        }
8900
      }
8901
      if (e.clipboardData) {
8902
        e.clipboardData.clearData();
8903
        var content = lastCopied.text.join("\n");
8904
        // iOS exposes the clipboard API, but seems to discard content inserted into it
8905
        e.clipboardData.setData("Text", content);
8906
        if (e.clipboardData.getData("Text") == content) {
8907
          e.preventDefault();
8908
          return
8909
        }
8910
      }
8911
      // Old-fashioned briefly-focus-a-textarea hack
8912
      var kludge = hiddenTextarea(), te = kludge.firstChild;
8913
      disableBrowserMagic(te);
8914
      cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild);
8915
      te.value = lastCopied.text.join("\n");
8916
      var hadFocus = activeElt(rootNode(div));
8917
      selectInput(te);
8918
      setTimeout(function () {
8919
        cm.display.lineSpace.removeChild(kludge);
8920
        hadFocus.focus();
8921
        if (hadFocus == div) { input.showPrimarySelection(); }
8922
      }, 50);
8923
    }
8924
    on(div, "copy", onCopyCut);
8925
    on(div, "cut", onCopyCut);
8926
  };
8927
 
8928
  ContentEditableInput.prototype.screenReaderLabelChanged = function (label) {
8929
    // Label for screenreaders, accessibility
8930
    if(label) {
8931
      this.div.setAttribute('aria-label', label);
8932
    } else {
8933
      this.div.removeAttribute('aria-label');
8934
    }
8935
  };
8936
 
8937
  ContentEditableInput.prototype.prepareSelection = function () {
8938
    var result = prepareSelection(this.cm, false);
8939
    result.focus = activeElt(rootNode(this.div)) == this.div;
8940
    return result
8941
  };
8942
 
8943
  ContentEditableInput.prototype.showSelection = function (info, takeFocus) {
8944
    if (!info || !this.cm.display.view.length) { return }
8945
    if (info.focus || takeFocus) { this.showPrimarySelection(); }
8946
    this.showMultipleSelections(info);
8947
  };
8948
 
8949
  ContentEditableInput.prototype.getSelection = function () {
8950
    return this.cm.display.wrapper.ownerDocument.getSelection()
8951
  };
8952
 
8953
  ContentEditableInput.prototype.showPrimarySelection = function () {
8954
    var sel = this.getSelection(), cm = this.cm, prim = cm.doc.sel.primary();
8955
    var from = prim.from(), to = prim.to();
8956
 
8957
    if (cm.display.viewTo == cm.display.viewFrom || from.line >= cm.display.viewTo || to.line < cm.display.viewFrom) {
8958
      sel.removeAllRanges();
8959
      return
8960
    }
8961
 
8962
    var curAnchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
8963
    var curFocus = domToPos(cm, sel.focusNode, sel.focusOffset);
8964
    if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad &&
8965
        cmp(minPos(curAnchor, curFocus), from) == 0 &&
8966
        cmp(maxPos(curAnchor, curFocus), to) == 0)
8967
      { return }
8968
 
8969
    var view = cm.display.view;
8970
    var start = (from.line >= cm.display.viewFrom && posToDOM(cm, from)) ||
8971
        {node: view[0].measure.map[2], offset: 0};
8972
    var end = to.line < cm.display.viewTo && posToDOM(cm, to);
8973
    if (!end) {
8974
      var measure = view[view.length - 1].measure;
8975
      var map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map;
8976
      end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map.length - 3]};
8977
    }
8978
 
8979
    if (!start || !end) {
8980
      sel.removeAllRanges();
8981
      return
8982
    }
8983
 
8984
    var old = sel.rangeCount && sel.getRangeAt(0), rng;
8985
    try { rng = range(start.node, start.offset, end.offset, end.node); }
8986
    catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible
8987
    if (rng) {
8988
      if (!gecko && cm.state.focused) {
8989
        sel.collapse(start.node, start.offset);
8990
        if (!rng.collapsed) {
8991
          sel.removeAllRanges();
8992
          sel.addRange(rng);
8993
        }
8994
      } else {
8995
        sel.removeAllRanges();
8996
        sel.addRange(rng);
8997
      }
8998
      if (old && sel.anchorNode == null) { sel.addRange(old); }
8999
      else if (gecko) { this.startGracePeriod(); }
9000
    }
9001
    this.rememberSelection();
9002
  };
9003
 
9004
  ContentEditableInput.prototype.startGracePeriod = function () {
9005
      var this$1 = this;
9006
 
9007
    clearTimeout(this.gracePeriod);
9008
    this.gracePeriod = setTimeout(function () {
9009
      this$1.gracePeriod = false;
9010
      if (this$1.selectionChanged())
9011
        { this$1.cm.operation(function () { return this$1.cm.curOp.selectionChanged = true; }); }
9012
    }, 20);
9013
  };
9014
 
9015
  ContentEditableInput.prototype.showMultipleSelections = function (info) {
9016
    removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors);
9017
    removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection);
9018
  };
9019
 
9020
  ContentEditableInput.prototype.rememberSelection = function () {
9021
    var sel = this.getSelection();
9022
    this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset;
9023
    this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset;
9024
  };
9025
 
9026
  ContentEditableInput.prototype.selectionInEditor = function () {
9027
    var sel = this.getSelection();
9028
    if (!sel.rangeCount) { return false }
9029
    var node = sel.getRangeAt(0).commonAncestorContainer;
9030
    return contains(this.div, node)
9031
  };
9032
 
9033
  ContentEditableInput.prototype.focus = function () {
9034
    if (this.cm.options.readOnly != "nocursor") {
9035
      if (!this.selectionInEditor() || activeElt(rootNode(this.div)) != this.div)
9036
        { this.showSelection(this.prepareSelection(), true); }
9037
      this.div.focus();
9038
    }
9039
  };
9040
  ContentEditableInput.prototype.blur = function () { this.div.blur(); };
9041
  ContentEditableInput.prototype.getField = function () { return this.div };
9042
 
9043
  ContentEditableInput.prototype.supportsTouch = function () { return true };
9044
 
9045
  ContentEditableInput.prototype.receivedFocus = function () {
9046
      var this$1 = this;
9047
 
9048
    var input = this;
9049
    if (this.selectionInEditor())
9050
      { setTimeout(function () { return this$1.pollSelection(); }, 20); }
9051
    else
9052
      { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }); }
9053
 
9054
    function poll() {
9055
      if (input.cm.state.focused) {
9056
        input.pollSelection();
9057
        input.polling.set(input.cm.options.pollInterval, poll);
9058
      }
9059
    }
9060
    this.polling.set(this.cm.options.pollInterval, poll);
9061
  };
9062
 
9063
  ContentEditableInput.prototype.selectionChanged = function () {
9064
    var sel = this.getSelection();
9065
    return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||
9066
      sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset
9067
  };
9068
 
9069
  ContentEditableInput.prototype.pollSelection = function () {
9070
    if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) { return }
9071
    var sel = this.getSelection(), cm = this.cm;
9072
    // On Android Chrome (version 56, at least), backspacing into an
9073
    // uneditable block element will put the cursor in that element,
9074
    // and then, because it's not editable, hide the virtual keyboard.
9075
    // Because Android doesn't allow us to actually detect backspace
9076
    // presses in a sane way, this code checks for when that happens
9077
    // and simulates a backspace press in this case.
9078
    if (android && chrome && this.cm.display.gutterSpecs.length && isInGutter(sel.anchorNode)) {
9079
      this.cm.triggerOnKeyDown({type: "keydown", keyCode: 8, preventDefault: Math.abs});
9080
      this.blur();
9081
      this.focus();
9082
      return
9083
    }
9084
    if (this.composing) { return }
9085
    this.rememberSelection();
9086
    var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
9087
    var head = domToPos(cm, sel.focusNode, sel.focusOffset);
9088
    if (anchor && head) { runInOp(cm, function () {
9089
      setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll);
9090
      if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true; }
9091
    }); }
9092
  };
9093
 
9094
  ContentEditableInput.prototype.pollContent = function () {
9095
    if (this.readDOMTimeout != null) {
9096
      clearTimeout(this.readDOMTimeout);
9097
      this.readDOMTimeout = null;
9098
    }
9099
 
9100
    var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary();
9101
    var from = sel.from(), to = sel.to();
9102
    if (from.ch == 0 && from.line > cm.firstLine())
9103
      { from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length); }
9104
    if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine())
9105
      { to = Pos(to.line + 1, 0); }
9106
    if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return false }
9107
 
9108
    var fromIndex, fromLine, fromNode;
9109
    if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) {
9110
      fromLine = lineNo(display.view[0].line);
9111
      fromNode = display.view[0].node;
9112
    } else {
9113
      fromLine = lineNo(display.view[fromIndex].line);
9114
      fromNode = display.view[fromIndex - 1].node.nextSibling;
9115
    }
9116
    var toIndex = findViewIndex(cm, to.line);
9117
    var toLine, toNode;
9118
    if (toIndex == display.view.length - 1) {
9119
      toLine = display.viewTo - 1;
9120
      toNode = display.lineDiv.lastChild;
9121
    } else {
9122
      toLine = lineNo(display.view[toIndex + 1].line) - 1;
9123
      toNode = display.view[toIndex + 1].node.previousSibling;
9124
    }
9125
 
9126
    if (!fromNode) { return false }
9127
    var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine));
9128
    var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length));
9129
    while (newText.length > 1 && oldText.length > 1) {
9130
      if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; }
9131
      else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; }
9132
      else { break }
9133
    }
9134
 
9135
    var cutFront = 0, cutEnd = 0;
9136
    var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length);
9137
    while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront))
9138
      { ++cutFront; }
9139
    var newBot = lst(newText), oldBot = lst(oldText);
9140
    var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0),
9141
                             oldBot.length - (oldText.length == 1 ? cutFront : 0));
9142
    while (cutEnd < maxCutEnd &&
9143
           newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1))
9144
      { ++cutEnd; }
9145
    // Try to move start of change to start of selection if ambiguous
9146
    if (newText.length == 1 && oldText.length == 1 && fromLine == from.line) {
9147
      while (cutFront && cutFront > from.ch &&
9148
             newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) {
9149
        cutFront--;
9150
        cutEnd++;
9151
      }
9152
    }
9153
 
9154
    newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, "");
9155
    newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, "");
9156
 
9157
    var chFrom = Pos(fromLine, cutFront);
9158
    var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0);
9159
    if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {
9160
      replaceRange(cm.doc, newText, chFrom, chTo, "+input");
9161
      return true
9162
    }
9163
  };
9164
 
9165
  ContentEditableInput.prototype.ensurePolled = function () {
9166
    this.forceCompositionEnd();
9167
  };
9168
  ContentEditableInput.prototype.reset = function () {
9169
    this.forceCompositionEnd();
9170
  };
9171
  ContentEditableInput.prototype.forceCompositionEnd = function () {
9172
    if (!this.composing) { return }
9173
    clearTimeout(this.readDOMTimeout);
9174
    this.composing = null;
9175
    this.updateFromDOM();
9176
    this.div.blur();
9177
    this.div.focus();
9178
  };
9179
  ContentEditableInput.prototype.readFromDOMSoon = function () {
9180
      var this$1 = this;
9181
 
9182
    if (this.readDOMTimeout != null) { return }
9183
    this.readDOMTimeout = setTimeout(function () {
9184
      this$1.readDOMTimeout = null;
9185
      if (this$1.composing) {
9186
        if (this$1.composing.done) { this$1.composing = null; }
9187
        else { return }
9188
      }
9189
      this$1.updateFromDOM();
9190
    }, 80);
9191
  };
9192
 
9193
  ContentEditableInput.prototype.updateFromDOM = function () {
9194
      var this$1 = this;
9195
 
9196
    if (this.cm.isReadOnly() || !this.pollContent())
9197
      { runInOp(this.cm, function () { return regChange(this$1.cm); }); }
9198
  };
9199
 
9200
  ContentEditableInput.prototype.setUneditable = function (node) {
9201
    node.contentEditable = "false";
9202
  };
9203
 
9204
  ContentEditableInput.prototype.onKeyPress = function (e) {
9205
    if (e.charCode == 0 || this.composing) { return }
9206
    e.preventDefault();
9207
    if (!this.cm.isReadOnly())
9208
      { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0); }
9209
  };
9210
 
9211
  ContentEditableInput.prototype.readOnlyChanged = function (val) {
9212
    this.div.contentEditable = String(val != "nocursor");
9213
  };
9214
 
9215
  ContentEditableInput.prototype.onContextMenu = function () {};
9216
  ContentEditableInput.prototype.resetPosition = function () {};
9217
 
9218
  ContentEditableInput.prototype.needsContentAttribute = true;
9219
 
9220
  function posToDOM(cm, pos) {
9221
    var view = findViewForLine(cm, pos.line);
9222
    if (!view || view.hidden) { return null }
9223
    var line = getLine(cm.doc, pos.line);
9224
    var info = mapFromLineView(view, line, pos.line);
9225
 
9226
    var order = getOrder(line, cm.doc.direction), side = "left";
9227
    if (order) {
9228
      var partPos = getBidiPartAt(order, pos.ch);
9229
      side = partPos % 2 ? "right" : "left";
9230
    }
9231
    var result = nodeAndOffsetInLineMap(info.map, pos.ch, side);
9232
    result.offset = result.collapse == "right" ? result.end : result.start;
9233
    return result
9234
  }
9235
 
9236
  function isInGutter(node) {
9237
    for (var scan = node; scan; scan = scan.parentNode)
9238
      { if (/CodeMirror-gutter-wrapper/.test(scan.className)) { return true } }
9239
    return false
9240
  }
9241
 
9242
  function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos }
9243
 
9244
  function domTextBetween(cm, from, to, fromLine, toLine) {
9245
    var text = "", closing = false, lineSep = cm.doc.lineSeparator(), extraLinebreak = false;
9246
    function recognizeMarker(id) { return function (marker) { return marker.id == id; } }
9247
    function close() {
9248
      if (closing) {
9249
        text += lineSep;
9250
        if (extraLinebreak) { text += lineSep; }
9251
        closing = extraLinebreak = false;
9252
      }
9253
    }
9254
    function addText(str) {
9255
      if (str) {
9256
        close();
9257
        text += str;
9258
      }
9259
    }
9260
    function walk(node) {
9261
      if (node.nodeType == 1) {
9262
        var cmText = node.getAttribute("cm-text");
9263
        if (cmText) {
9264
          addText(cmText);
9265
          return
9266
        }
9267
        var markerID = node.getAttribute("cm-marker"), range;
9268
        if (markerID) {
9269
          var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID));
9270
          if (found.length && (range = found[0].find(0)))
9271
            { addText(getBetween(cm.doc, range.from, range.to).join(lineSep)); }
9272
          return
9273
        }
9274
        if (node.getAttribute("contenteditable") == "false") { return }
9275
        var isBlock = /^(pre|div|p|li|table|br)$/i.test(node.nodeName);
9276
        if (!/^br$/i.test(node.nodeName) && node.textContent.length == 0) { return }
9277
 
9278
        if (isBlock) { close(); }
9279
        for (var i = 0; i < node.childNodes.length; i++)
9280
          { walk(node.childNodes[i]); }
9281
 
9282
        if (/^(pre|p)$/i.test(node.nodeName)) { extraLinebreak = true; }
9283
        if (isBlock) { closing = true; }
9284
      } else if (node.nodeType == 3) {
9285
        addText(node.nodeValue.replace(/\u200b/g, "").replace(/\u00a0/g, " "));
9286
      }
9287
    }
9288
    for (;;) {
9289
      walk(from);
9290
      if (from == to) { break }
9291
      from = from.nextSibling;
9292
      extraLinebreak = false;
9293
    }
9294
    return text
9295
  }
9296
 
9297
  function domToPos(cm, node, offset) {
9298
    var lineNode;
9299
    if (node == cm.display.lineDiv) {
9300
      lineNode = cm.display.lineDiv.childNodes[offset];
9301
      if (!lineNode) { return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) }
9302
      node = null; offset = 0;
9303
    } else {
9304
      for (lineNode = node;; lineNode = lineNode.parentNode) {
9305
        if (!lineNode || lineNode == cm.display.lineDiv) { return null }
9306
        if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) { break }
9307
      }
9308
    }
9309
    for (var i = 0; i < cm.display.view.length; i++) {
9310
      var lineView = cm.display.view[i];
9311
      if (lineView.node == lineNode)
9312
        { return locateNodeInLineView(lineView, node, offset) }
9313
    }
9314
  }
9315
 
9316
  function locateNodeInLineView(lineView, node, offset) {
9317
    var wrapper = lineView.text.firstChild, bad = false;
9318
    if (!node || !contains(wrapper, node)) { return badPos(Pos(lineNo(lineView.line), 0), true) }
9319
    if (node == wrapper) {
9320
      bad = true;
9321
      node = wrapper.childNodes[offset];
9322
      offset = 0;
9323
      if (!node) {
9324
        var line = lineView.rest ? lst(lineView.rest) : lineView.line;
9325
        return badPos(Pos(lineNo(line), line.text.length), bad)
9326
      }
9327
    }
9328
 
9329
    var textNode = node.nodeType == 3 ? node : null, topNode = node;
9330
    if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {
9331
      textNode = node.firstChild;
9332
      if (offset) { offset = textNode.nodeValue.length; }
9333
    }
9334
    while (topNode.parentNode != wrapper) { topNode = topNode.parentNode; }
9335
    var measure = lineView.measure, maps = measure.maps;
9336
 
9337
    function find(textNode, topNode, offset) {
9338
      for (var i = -1; i < (maps ? maps.length : 0); i++) {
9339
        var map = i < 0 ? measure.map : maps[i];
9340
        for (var j = 0; j < map.length; j += 3) {
9341
          var curNode = map[j + 2];
9342
          if (curNode == textNode || curNode == topNode) {
9343
            var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]);
9344
            var ch = map[j] + offset;
9345
            if (offset < 0 || curNode != textNode) { ch = map[j + (offset ? 1 : 0)]; }
9346
            return Pos(line, ch)
9347
          }
9348
        }
9349
      }
9350
    }
9351
    var found = find(textNode, topNode, offset);
9352
    if (found) { return badPos(found, bad) }
9353
 
9354
    // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems
9355
    for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) {
9356
      found = find(after, after.firstChild, 0);
9357
      if (found)
9358
        { return badPos(Pos(found.line, found.ch - dist), bad) }
9359
      else
9360
        { dist += after.textContent.length; }
9361
    }
9362
    for (var before = topNode.previousSibling, dist$1 = offset; before; before = before.previousSibling) {
9363
      found = find(before, before.firstChild, -1);
9364
      if (found)
9365
        { return badPos(Pos(found.line, found.ch + dist$1), bad) }
9366
      else
9367
        { dist$1 += before.textContent.length; }
9368
    }
9369
  }
9370
 
9371
  // TEXTAREA INPUT STYLE
9372
 
9373
  var TextareaInput = function(cm) {
9374
    this.cm = cm;
9375
    // See input.poll and input.reset
9376
    this.prevInput = "";
9377
 
9378
    // Flag that indicates whether we expect input to appear real soon
9379
    // now (after some event like 'keypress' or 'input') and are
9380
    // polling intensively.
9381
    this.pollingFast = false;
9382
    // Self-resetting timeout for the poller
9383
    this.polling = new Delayed();
9384
    // Used to work around IE issue with selection being forgotten when focus moves away from textarea
9385
    this.hasSelection = false;
9386
    this.composing = null;
9387
    this.resetting = false;
9388
  };
9389
 
9390
  TextareaInput.prototype.init = function (display) {
9391
      var this$1 = this;
9392
 
9393
    var input = this, cm = this.cm;
9394
    this.createField(display);
9395
    var te = this.textarea;
9396
 
9397
    display.wrapper.insertBefore(this.wrapper, display.wrapper.firstChild);
9398
 
9399
    // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)
9400
    if (ios) { te.style.width = "0px"; }
9401
 
9402
    on(te, "input", function () {
9403
      if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = null; }
9404
      input.poll();
9405
    });
9406
 
9407
    on(te, "paste", function (e) {
9408
      if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
9409
 
9410
      cm.state.pasteIncoming = +new Date;
9411
      input.fastPoll();
9412
    });
9413
 
9414
    function prepareCopyCut(e) {
9415
      if (signalDOMEvent(cm, e)) { return }
9416
      if (cm.somethingSelected()) {
9417
        setLastCopied({lineWise: false, text: cm.getSelections()});
9418
      } else if (!cm.options.lineWiseCopyCut) {
9419
        return
9420
      } else {
9421
        var ranges = copyableRanges(cm);
9422
        setLastCopied({lineWise: true, text: ranges.text});
9423
        if (e.type == "cut") {
9424
          cm.setSelections(ranges.ranges, null, sel_dontScroll);
9425
        } else {
9426
          input.prevInput = "";
9427
          te.value = ranges.text.join("\n");
9428
          selectInput(te);
9429
        }
9430
      }
9431
      if (e.type == "cut") { cm.state.cutIncoming = +new Date; }
9432
    }
9433
    on(te, "cut", prepareCopyCut);
9434
    on(te, "copy", prepareCopyCut);
9435
 
9436
    on(display.scroller, "paste", function (e) {
9437
      if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return }
9438
      if (!te.dispatchEvent) {
9439
        cm.state.pasteIncoming = +new Date;
9440
        input.focus();
9441
        return
9442
      }
9443
 
9444
      // Pass the `paste` event to the textarea so it's handled by its event listener.
9445
      var event = new Event("paste");
9446
      event.clipboardData = e.clipboardData;
9447
      te.dispatchEvent(event);
9448
    });
9449
 
9450
    // Prevent normal selection in the editor (we handle our own)
9451
    on(display.lineSpace, "selectstart", function (e) {
9452
      if (!eventInWidget(display, e)) { e_preventDefault(e); }
9453
    });
9454
 
9455
    on(te, "compositionstart", function () {
9456
      var start = cm.getCursor("from");
9457
      if (input.composing) { input.composing.range.clear(); }
9458
      input.composing = {
9459
        start: start,
9460
        range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"})
9461
      };
9462
    });
9463
    on(te, "compositionend", function () {
9464
      if (input.composing) {
9465
        input.poll();
9466
        input.composing.range.clear();
9467
        input.composing = null;
9468
      }
9469
    });
9470
  };
9471
 
9472
  TextareaInput.prototype.createField = function (_display) {
9473
    // Wraps and hides input textarea
9474
    this.wrapper = hiddenTextarea();
9475
    // The semihidden textarea that is focused when the editor is
9476
    // focused, and receives input.
9477
    this.textarea = this.wrapper.firstChild;
9478
    var opts = this.cm.options;
9479
    disableBrowserMagic(this.textarea, opts.spellcheck, opts.autocorrect, opts.autocapitalize);
9480
  };
9481
 
9482
  TextareaInput.prototype.screenReaderLabelChanged = function (label) {
9483
    // Label for screenreaders, accessibility
9484
    if(label) {
9485
      this.textarea.setAttribute('aria-label', label);
9486
    } else {
9487
      this.textarea.removeAttribute('aria-label');
9488
    }
9489
  };
9490
 
9491
  TextareaInput.prototype.prepareSelection = function () {
9492
    // Redraw the selection and/or cursor
9493
    var cm = this.cm, display = cm.display, doc = cm.doc;
9494
    var result = prepareSelection(cm);
9495
 
9496
    // Move the hidden textarea near the cursor to prevent scrolling artifacts
9497
    if (cm.options.moveInputWithCursor) {
9498
      var headPos = cursorCoords(cm, doc.sel.primary().head, "div");
9499
      var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();
9500
      result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
9501
                                          headPos.top + lineOff.top - wrapOff.top));
9502
      result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
9503
                                           headPos.left + lineOff.left - wrapOff.left));
9504
    }
9505
 
9506
    return result
9507
  };
9508
 
9509
  TextareaInput.prototype.showSelection = function (drawn) {
9510
    var cm = this.cm, display = cm.display;
9511
    removeChildrenAndAdd(display.cursorDiv, drawn.cursors);
9512
    removeChildrenAndAdd(display.selectionDiv, drawn.selection);
9513
    if (drawn.teTop != null) {
9514
      this.wrapper.style.top = drawn.teTop + "px";
9515
      this.wrapper.style.left = drawn.teLeft + "px";
9516
    }
9517
  };
9518
 
9519
  // Reset the input to correspond to the selection (or to be empty,
9520
  // when not typing and nothing is selected)
9521
  TextareaInput.prototype.reset = function (typing) {
9522
    if (this.contextMenuPending || this.composing && typing) { return }
9523
    var cm = this.cm;
9524
    this.resetting = true;
9525
    if (cm.somethingSelected()) {
9526
      this.prevInput = "";
9527
      var content = cm.getSelection();
9528
      this.textarea.value = content;
9529
      if (cm.state.focused) { selectInput(this.textarea); }
9530
      if (ie && ie_version >= 9) { this.hasSelection = content; }
9531
    } else if (!typing) {
9532
      this.prevInput = this.textarea.value = "";
9533
      if (ie && ie_version >= 9) { this.hasSelection = null; }
9534
    }
9535
    this.resetting = false;
9536
  };
9537
 
9538
  TextareaInput.prototype.getField = function () { return this.textarea };
9539
 
9540
  TextareaInput.prototype.supportsTouch = function () { return false };
9541
 
9542
  TextareaInput.prototype.focus = function () {
9543
    if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt(rootNode(this.textarea)) != this.textarea)) {
9544
      try { this.textarea.focus(); }
9545
      catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM
9546
    }
9547
  };
9548
 
9549
  TextareaInput.prototype.blur = function () { this.textarea.blur(); };
9550
 
9551
  TextareaInput.prototype.resetPosition = function () {
9552
    this.wrapper.style.top = this.wrapper.style.left = 0;
9553
  };
9554
 
9555
  TextareaInput.prototype.receivedFocus = function () { this.slowPoll(); };
9556
 
9557
  // Poll for input changes, using the normal rate of polling. This
9558
  // runs as long as the editor is focused.
9559
  TextareaInput.prototype.slowPoll = function () {
9560
      var this$1 = this;
9561
 
9562
    if (this.pollingFast) { return }
9563
    this.polling.set(this.cm.options.pollInterval, function () {
9564
      this$1.poll();
9565
      if (this$1.cm.state.focused) { this$1.slowPoll(); }
9566
    });
9567
  };
9568
 
9569
  // When an event has just come in that is likely to add or change
9570
  // something in the input textarea, we poll faster, to ensure that
9571
  // the change appears on the screen quickly.
9572
  TextareaInput.prototype.fastPoll = function () {
9573
    var missed = false, input = this;
9574
    input.pollingFast = true;
9575
    function p() {
9576
      var changed = input.poll();
9577
      if (!changed && !missed) {missed = true; input.polling.set(60, p);}
9578
      else {input.pollingFast = false; input.slowPoll();}
9579
    }
9580
    input.polling.set(20, p);
9581
  };
9582
 
9583
  // Read input from the textarea, and update the document to match.
9584
  // When something is selected, it is present in the textarea, and
9585
  // selected (unless it is huge, in which case a placeholder is
9586
  // used). When nothing is selected, the cursor sits after previously
9587
  // seen text (can be empty), which is stored in prevInput (we must
9588
  // not reset the textarea when typing, because that breaks IME).
9589
  TextareaInput.prototype.poll = function () {
9590
      var this$1 = this;
9591
 
9592
    var cm = this.cm, input = this.textarea, prevInput = this.prevInput;
9593
    // Since this is called a *lot*, try to bail out as cheaply as
9594
    // possible when it is clear that nothing happened. hasSelection
9595
    // will be the case when there is a lot of text in the textarea,
9596
    // in which case reading its value would be expensive.
9597
    if (this.contextMenuPending || this.resetting || !cm.state.focused ||
9598
        (hasSelection(input) && !prevInput && !this.composing) ||
9599
        cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq)
9600
      { return false }
9601
 
9602
    var text = input.value;
9603
    // If nothing changed, bail.
9604
    if (text == prevInput && !cm.somethingSelected()) { return false }
9605
    // Work around nonsensical selection resetting in IE9/10, and
9606
    // inexplicable appearance of private area unicode characters on
9607
    // some key combos in Mac (#2689).
9608
    if (ie && ie_version >= 9 && this.hasSelection === text ||
9609
        mac && /[\uf700-\uf7ff]/.test(text)) {
9610
      cm.display.input.reset();
9611
      return false
9612
    }
9613
 
9614
    if (cm.doc.sel == cm.display.selForContextMenu) {
9615
      var first = text.charCodeAt(0);
9616
      if (first == 0x200b && !prevInput) { prevInput = "\u200b"; }
9617
      if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") }
9618
    }
9619
    // Find the part of the input that is actually new
9620
    var same = 0, l = Math.min(prevInput.length, text.length);
9621
    while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same; }
9622
 
9623
    runInOp(cm, function () {
9624
      applyTextInput(cm, text.slice(same), prevInput.length - same,
9625
                     null, this$1.composing ? "*compose" : null);
9626
 
9627
      // Don't leave long text in the textarea, since it makes further polling slow
9628
      if (text.length > 1000 || text.indexOf("\n") > -1) { input.value = this$1.prevInput = ""; }
9629
      else { this$1.prevInput = text; }
9630
 
9631
      if (this$1.composing) {
9632
        this$1.composing.range.clear();
9633
        this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor("to"),
9634
                                           {className: "CodeMirror-composing"});
9635
      }
9636
    });
9637
    return true
9638
  };
9639
 
9640
  TextareaInput.prototype.ensurePolled = function () {
9641
    if (this.pollingFast && this.poll()) { this.pollingFast = false; }
9642
  };
9643
 
9644
  TextareaInput.prototype.onKeyPress = function () {
9645
    if (ie && ie_version >= 9) { this.hasSelection = null; }
9646
    this.fastPoll();
9647
  };
9648
 
9649
  TextareaInput.prototype.onContextMenu = function (e) {
9650
    var input = this, cm = input.cm, display = cm.display, te = input.textarea;
9651
    if (input.contextMenuPending) { input.contextMenuPending(); }
9652
    var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
9653
    if (!pos || presto) { return } // Opera is difficult.
9654
 
9655
    // Reset the current text selection only if the click is done outside of the selection
9656
    // and 'resetSelectionOnContextMenu' option is true.
9657
    var reset = cm.options.resetSelectionOnContextMenu;
9658
    if (reset && cm.doc.sel.contains(pos) == -1)
9659
      { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll); }
9660
 
9661
    var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText;
9662
    var wrapperBox = input.wrapper.offsetParent.getBoundingClientRect();
9663
    input.wrapper.style.cssText = "position: static";
9664
    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);";
9665
    var oldScrollY;
9666
    if (webkit) { oldScrollY = te.ownerDocument.defaultView.scrollY; } // Work around Chrome issue (#2712)
9667
    display.input.focus();
9668
    if (webkit) { te.ownerDocument.defaultView.scrollTo(null, oldScrollY); }
9669
    display.input.reset();
9670
    // Adds "Select all" to context menu in FF
9671
    if (!cm.somethingSelected()) { te.value = input.prevInput = " "; }
9672
    input.contextMenuPending = rehide;
9673
    display.selForContextMenu = cm.doc.sel;
9674
    clearTimeout(display.detectingSelectAll);
9675
 
9676
    // Select-all will be greyed out if there's nothing to select, so
9677
    // this adds a zero-width space so that we can later check whether
9678
    // it got selected.
9679
    function prepareSelectAllHack() {
9680
      if (te.selectionStart != null) {
9681
        var selected = cm.somethingSelected();
9682
        var extval = "\u200b" + (selected ? te.value : "");
9683
        te.value = "\u21da"; // Used to catch context-menu undo
9684
        te.value = extval;
9685
        input.prevInput = selected ? "" : "\u200b";
9686
        te.selectionStart = 1; te.selectionEnd = extval.length;
9687
        // Re-set this, in case some other handler touched the
9688
        // selection in the meantime.
9689
        display.selForContextMenu = cm.doc.sel;
9690
      }
9691
    }
9692
    function rehide() {
9693
      if (input.contextMenuPending != rehide) { return }
9694
      input.contextMenuPending = false;
9695
      input.wrapper.style.cssText = oldWrapperCSS;
9696
      te.style.cssText = oldCSS;
9697
      if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos); }
9698
 
9699
      // Try to detect the user choosing select-all
9700
      if (te.selectionStart != null) {
9701
        if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack(); }
9702
        var i = 0, poll = function () {
9703
          if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 &&
9704
              te.selectionEnd > 0 && input.prevInput == "\u200b") {
9705
            operation(cm, selectAll)(cm);
9706
          } else if (i++ < 10) {
9707
            display.detectingSelectAll = setTimeout(poll, 500);
9708
          } else {
9709
            display.selForContextMenu = null;
9710
            display.input.reset();
9711
          }
9712
        };
9713
        display.detectingSelectAll = setTimeout(poll, 200);
9714
      }
9715
    }
9716
 
9717
    if (ie && ie_version >= 9) { prepareSelectAllHack(); }
9718
    if (captureRightClick) {
9719
      e_stop(e);
9720
      var mouseup = function () {
9721
        off(window, "mouseup", mouseup);
9722
        setTimeout(rehide, 20);
9723
      };
9724
      on(window, "mouseup", mouseup);
9725
    } else {
9726
      setTimeout(rehide, 50);
9727
    }
9728
  };
9729
 
9730
  TextareaInput.prototype.readOnlyChanged = function (val) {
9731
    if (!val) { this.reset(); }
9732
    this.textarea.disabled = val == "nocursor";
9733
    this.textarea.readOnly = !!val;
9734
  };
9735
 
9736
  TextareaInput.prototype.setUneditable = function () {};
9737
 
9738
  TextareaInput.prototype.needsContentAttribute = false;
9739
 
9740
  function fromTextArea(textarea, options) {
9741
    options = options ? copyObj(options) : {};
9742
    options.value = textarea.value;
9743
    if (!options.tabindex && textarea.tabIndex)
9744
      { options.tabindex = textarea.tabIndex; }
9745
    if (!options.placeholder && textarea.placeholder)
9746
      { options.placeholder = textarea.placeholder; }
9747
    // Set autofocus to true if this textarea is focused, or if it has
9748
    // autofocus and no other element is focused.
9749
    if (options.autofocus == null) {
9750
      var hasFocus = activeElt(rootNode(textarea));
9751
      options.autofocus = hasFocus == textarea ||
9752
        textarea.getAttribute("autofocus") != null && hasFocus == document.body;
9753
    }
9754
 
9755
    function save() {textarea.value = cm.getValue();}
9756
 
9757
    var realSubmit;
9758
    if (textarea.form) {
9759
      on(textarea.form, "submit", save);
9760
      // Deplorable hack to make the submit method do the right thing.
9761
      if (!options.leaveSubmitMethodAlone) {
9762
        var form = textarea.form;
9763
        realSubmit = form.submit;
9764
        try {
9765
          var wrappedSubmit = form.submit = function () {
9766
            save();
9767
            form.submit = realSubmit;
9768
            form.submit();
9769
            form.submit = wrappedSubmit;
9770
          };
9771
        } catch(e) {}
9772
      }
9773
    }
9774
 
9775
    options.finishInit = function (cm) {
9776
      cm.save = save;
9777
      cm.getTextArea = function () { return textarea; };
9778
      cm.toTextArea = function () {
9779
        cm.toTextArea = isNaN; // Prevent this from being ran twice
9780
        save();
9781
        textarea.parentNode.removeChild(cm.getWrapperElement());
9782
        textarea.style.display = "";
9783
        if (textarea.form) {
9784
          off(textarea.form, "submit", save);
9785
          if (!options.leaveSubmitMethodAlone && typeof textarea.form.submit == "function")
9786
            { textarea.form.submit = realSubmit; }
9787
        }
9788
      };
9789
    };
9790
 
9791
    textarea.style.display = "none";
9792
    var cm = CodeMirror(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); },
9793
      options);
9794
    return cm
9795
  }
9796
 
9797
  function addLegacyProps(CodeMirror) {
9798
    CodeMirror.off = off;
9799
    CodeMirror.on = on;
9800
    CodeMirror.wheelEventPixels = wheelEventPixels;
9801
    CodeMirror.Doc = Doc;
9802
    CodeMirror.splitLines = splitLinesAuto;
9803
    CodeMirror.countColumn = countColumn;
9804
    CodeMirror.findColumn = findColumn;
9805
    CodeMirror.isWordChar = isWordCharBasic;
9806
    CodeMirror.Pass = Pass;
9807
    CodeMirror.signal = signal;
9808
    CodeMirror.Line = Line;
9809
    CodeMirror.changeEnd = changeEnd;
9810
    CodeMirror.scrollbarModel = scrollbarModel;
9811
    CodeMirror.Pos = Pos;
9812
    CodeMirror.cmpPos = cmp;
9813
    CodeMirror.modes = modes;
9814
    CodeMirror.mimeModes = mimeModes;
9815
    CodeMirror.resolveMode = resolveMode;
9816
    CodeMirror.getMode = getMode;
9817
    CodeMirror.modeExtensions = modeExtensions;
9818
    CodeMirror.extendMode = extendMode;
9819
    CodeMirror.copyState = copyState;
9820
    CodeMirror.startState = startState;
9821
    CodeMirror.innerMode = innerMode;
9822
    CodeMirror.commands = commands;
9823
    CodeMirror.keyMap = keyMap;
9824
    CodeMirror.keyName = keyName;
9825
    CodeMirror.isModifierKey = isModifierKey;
9826
    CodeMirror.lookupKey = lookupKey;
9827
    CodeMirror.normalizeKeyMap = normalizeKeyMap;
9828
    CodeMirror.StringStream = StringStream;
9829
    CodeMirror.SharedTextMarker = SharedTextMarker;
9830
    CodeMirror.TextMarker = TextMarker;
9831
    CodeMirror.LineWidget = LineWidget;
9832
    CodeMirror.e_preventDefault = e_preventDefault;
9833
    CodeMirror.e_stopPropagation = e_stopPropagation;
9834
    CodeMirror.e_stop = e_stop;
9835
    CodeMirror.addClass = addClass;
9836
    CodeMirror.contains = contains;
9837
    CodeMirror.rmClass = rmClass;
9838
    CodeMirror.keyNames = keyNames;
9839
  }
9840
 
9841
  // EDITOR CONSTRUCTOR
9842
 
9843
  defineOptions(CodeMirror);
9844
 
9845
  addEditorMethods(CodeMirror);
9846
 
9847
  // Set up methods on CodeMirror's prototype to redirect to the editor's document.
9848
  var dontDelegate = "iter insert remove copy getEditor constructor".split(" ");
9849
  for (var prop in Doc.prototype) { if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
9850
    { CodeMirror.prototype[prop] = (function(method) {
9851
      return function() {return method.apply(this.doc, arguments)}
9852
    })(Doc.prototype[prop]); } }
9853
 
9854
  eventMixin(Doc);
9855
  CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput};
9856
 
9857
  // Extra arguments are stored as the mode's dependencies, which is
9858
  // used by (legacy) mechanisms like loadmode.js to automatically
9859
  // load a mode. (Preferred mechanism is the require/define calls.)
9860
  CodeMirror.defineMode = function(name/*, mode, …*/) {
9861
    if (!CodeMirror.defaults.mode && name != "null") { CodeMirror.defaults.mode = name; }
9862
    defineMode.apply(this, arguments);
9863
  };
9864
 
9865
  CodeMirror.defineMIME = defineMIME;
9866
 
9867
  // Minimal default mode.
9868
  CodeMirror.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); });
9869
  CodeMirror.defineMIME("text/plain", "null");
9870
 
9871
  // EXTENSIONS
9872
 
9873
  CodeMirror.defineExtension = function (name, func) {
9874
    CodeMirror.prototype[name] = func;
9875
  };
9876
  CodeMirror.defineDocExtension = function (name, func) {
9877
    Doc.prototype[name] = func;
9878
  };
9879
 
9880
  CodeMirror.fromTextArea = fromTextArea;
9881
 
9882
  addLegacyProps(CodeMirror);
9883
 
9884
  CodeMirror.version = "5.65.16";
9885
 
9886
  return CodeMirror;
9887
 
9888
})));
9889
var CodeMirror = this.CodeMirror;
9890
// CodeMirror, copyright (c) by Marijn Haverbeke and others
9891
// Distributed under an MIT license: https://codemirror.net/5/LICENSE
9892
 
9893
(function(mod) {
9894
  if (typeof exports == "object" && typeof module == "object") // CommonJS
9895
    mod(require("../../lib/codemirror"), require("../xml/xml"), require("../javascript/javascript"), require("../css/css"));
9896
  else if (typeof define == "function" && define.amd) // AMD
9897
    define(["../../lib/codemirror", "../xml/xml", "../javascript/javascript", "../css/css"], mod);
9898
  else // Plain browser env
9899
    mod(CodeMirror);
9900
})(function(CodeMirror) {
9901
  "use strict";
9902
 
9903
  var defaultTags = {
9904
    script: [
9905
      ["lang", /(javascript|babel)/i, "javascript"],
9906
      ["type", /^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^module$|^$/i, "javascript"],
9907
      ["type", /./, "text/plain"],
9908
      [null, null, "javascript"]
9909
    ],
9910
    style:  [
9911
      ["lang", /^css$/i, "css"],
9912
      ["type", /^(text\/)?(x-)?(stylesheet|css)$/i, "css"],
9913
      ["type", /./, "text/plain"],
9914
      [null, null, "css"]
9915
    ]
9916
  };
9917
 
9918
  function maybeBackup(stream, pat, style) {
9919
    var cur = stream.current(), close = cur.search(pat);
9920
    if (close > -1) {
9921
      stream.backUp(cur.length - close);
9922
    } else if (cur.match(/<\/?$/)) {
9923
      stream.backUp(cur.length);
9924
      if (!stream.match(pat, false)) stream.match(cur);
9925
    }
9926
    return style;
9927
  }
9928
 
9929
  var attrRegexpCache = {};
9930
  function getAttrRegexp(attr) {
9931
    var regexp = attrRegexpCache[attr];
9932
    if (regexp) return regexp;
9933
    return attrRegexpCache[attr] = new RegExp("\\s+" + attr + "\\s*=\\s*('|\")?([^'\"]+)('|\")?\\s*");
9934
  }
9935
 
9936
  function getAttrValue(text, attr) {
9937
    var match = text.match(getAttrRegexp(attr))
9938
    return match ? /^\s*(.*?)\s*$/.exec(match[2])[1] : ""
9939
  }
9940
 
9941
  function getTagRegexp(tagName, anchored) {
9942
    return new RegExp((anchored ? "^" : "") + "<\/\\s*" + tagName + "\\s*>", "i");
9943
  }
9944
 
9945
  function addTags(from, to) {
9946
    for (var tag in from) {
9947
      var dest = to[tag] || (to[tag] = []);
9948
      var source = from[tag];
9949
      for (var i = source.length - 1; i >= 0; i--)
9950
        dest.unshift(source[i])
9951
    }
9952
  }
9953
 
9954
  function findMatchingMode(tagInfo, tagText) {
9955
    for (var i = 0; i < tagInfo.length; i++) {
9956
      var spec = tagInfo[i];
9957
      if (!spec[0] || spec[1].test(getAttrValue(tagText, spec[0]))) return spec[2];
9958
    }
9959
  }
9960
 
9961
  CodeMirror.defineMode("htmlmixed", function (config, parserConfig) {
9962
    var htmlMode = CodeMirror.getMode(config, {
9963
      name: "xml",
9964
      htmlMode: true,
9965
      multilineTagIndentFactor: parserConfig.multilineTagIndentFactor,
9966
      multilineTagIndentPastTag: parserConfig.multilineTagIndentPastTag,
9967
      allowMissingTagName: parserConfig.allowMissingTagName,
9968
    });
9969
 
9970
    var tags = {};
9971
    var configTags = parserConfig && parserConfig.tags, configScript = parserConfig && parserConfig.scriptTypes;
9972
    addTags(defaultTags, tags);
9973
    if (configTags) addTags(configTags, tags);
9974
    if (configScript) for (var i = configScript.length - 1; i >= 0; i--)
9975
      tags.script.unshift(["type", configScript[i].matches, configScript[i].mode])
9976
 
9977
    function html(stream, state) {
9978
      var style = htmlMode.token(stream, state.htmlState), tag = /\btag\b/.test(style), tagName
9979
      if (tag && !/[<>\s\/]/.test(stream.current()) &&
9980
          (tagName = state.htmlState.tagName && state.htmlState.tagName.toLowerCase()) &&
9981
          tags.hasOwnProperty(tagName)) {
9982
        state.inTag = tagName + " "
9983
      } else if (state.inTag && tag && />$/.test(stream.current())) {
9984
        var inTag = /^([\S]+) (.*)/.exec(state.inTag)
9985
        state.inTag = null
9986
        var modeSpec = stream.current() == ">" && findMatchingMode(tags[inTag[1]], inTag[2])
9987
        var mode = CodeMirror.getMode(config, modeSpec)
9988
        var endTagA = getTagRegexp(inTag[1], true), endTag = getTagRegexp(inTag[1], false);
9989
        state.token = function (stream, state) {
9990
          if (stream.match(endTagA, false)) {
9991
            state.token = html;
9992
            state.localState = state.localMode = null;
9993
            return null;
9994
          }
9995
          return maybeBackup(stream, endTag, state.localMode.token(stream, state.localState));
9996
        };
9997
        state.localMode = mode;
9998
        state.localState = CodeMirror.startState(mode, htmlMode.indent(state.htmlState, "", ""));
9999
      } else if (state.inTag) {
10000
        state.inTag += stream.current()
10001
        if (stream.eol()) state.inTag += " "
10002
      }
10003
      return style;
10004
    };
10005
 
10006
    return {
10007
      startState: function () {
10008
        var state = CodeMirror.startState(htmlMode);
10009
        return {token: html, inTag: null, localMode: null, localState: null, htmlState: state};
10010
      },
10011
 
10012
      copyState: function (state) {
10013
        var local;
10014
        if (state.localState) {
10015
          local = CodeMirror.copyState(state.localMode, state.localState);
10016
        }
10017
        return {token: state.token, inTag: state.inTag,
10018
                localMode: state.localMode, localState: local,
10019
                htmlState: CodeMirror.copyState(htmlMode, state.htmlState)};
10020
      },
10021
 
10022
      token: function (stream, state) {
10023
        return state.token(stream, state);
10024
      },
10025
 
10026
      indent: function (state, textAfter, line) {
10027
        if (!state.localMode || /^\s*<\//.test(textAfter))
10028
          return htmlMode.indent(state.htmlState, textAfter, line);
10029
        else if (state.localMode.indent)
10030
          return state.localMode.indent(state.localState, textAfter, line);
10031
        else
10032
          return CodeMirror.Pass;
10033
      },
10034
 
10035
      innerMode: function (state) {
10036
        return {state: state.localState || state.htmlState, mode: state.localMode || htmlMode};
10037
      }
10038
    };
10039
  }, "xml", "javascript", "css");
10040
 
10041
  CodeMirror.defineMIME("text/html", "htmlmixed");
10042
});
10043
// CodeMirror, copyright (c) by Marijn Haverbeke and others
10044
// Distributed under an MIT license: https://codemirror.net/5/LICENSE
10045
 
10046
(function(mod) {
10047
  if (typeof exports == "object" && typeof module == "object") // CommonJS
10048
    mod(require("../../lib/codemirror"));
10049
  else if (typeof define == "function" && define.amd) // AMD
10050
    define(["../../lib/codemirror"], mod);
10051
  else // Plain browser env
10052
    mod(CodeMirror);
10053
})(function(CodeMirror) {
10054
"use strict";
10055
 
10056
var htmlConfig = {
10057
  autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true,
10058
                    'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true,
10059
                    'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true,
10060
                    'track': true, 'wbr': true, 'menuitem': true},
10061
  implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true,
10062
                     'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true,
10063
                     'th': true, 'tr': true},
10064
  contextGrabbers: {
10065
    'dd': {'dd': true, 'dt': true},
10066
    'dt': {'dd': true, 'dt': true},
10067
    'li': {'li': true},
10068
    'option': {'option': true, 'optgroup': true},
10069
    'optgroup': {'optgroup': true},
10070
    'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true,
10071
          'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true,
10072
          'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true,
10073
          'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true,
10074
          'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true},
10075
    'rp': {'rp': true, 'rt': true},
10076
    'rt': {'rp': true, 'rt': true},
10077
    'tbody': {'tbody': true, 'tfoot': true},
10078
    'td': {'td': true, 'th': true},
10079
    'tfoot': {'tbody': true},
10080
    'th': {'td': true, 'th': true},
10081
    'thead': {'tbody': true, 'tfoot': true},
10082
    'tr': {'tr': true}
10083
  },
10084
  doNotIndent: {"pre": true},
10085
  allowUnquoted: true,
10086
  allowMissing: true,
10087
  caseFold: true
10088
}
10089
 
10090
var xmlConfig = {
10091
  autoSelfClosers: {},
10092
  implicitlyClosed: {},
10093
  contextGrabbers: {},
10094
  doNotIndent: {},
10095
  allowUnquoted: false,
10096
  allowMissing: false,
10097
  allowMissingTagName: false,
10098
  caseFold: false
10099
}
10100
 
10101
CodeMirror.defineMode("xml", function(editorConf, config_) {
10102
  var indentUnit = editorConf.indentUnit
10103
  var config = {}
10104
  var defaults = config_.htmlMode ? htmlConfig : xmlConfig
10105
  for (var prop in defaults) config[prop] = defaults[prop]
10106
  for (var prop in config_) config[prop] = config_[prop]
10107
 
10108
  // Return variables for tokenizers
10109
  var type, setStyle;
10110
 
10111
  function inText(stream, state) {
10112
    function chain(parser) {
10113
      state.tokenize = parser;
10114
      return parser(stream, state);
10115
    }
10116
 
10117
    var ch = stream.next();
10118
    if (ch == "<") {
10119
      if (stream.eat("!")) {
10120
        if (stream.eat("[")) {
10121
          if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));
10122
          else return null;
10123
        } else if (stream.match("--")) {
10124
          return chain(inBlock("comment", "-->"));
10125
        } else if (stream.match("DOCTYPE", true, true)) {
10126
          stream.eatWhile(/[\w\._\-]/);
10127
          return chain(doctype(1));
10128
        } else {
10129
          return null;
10130
        }
10131
      } else if (stream.eat("?")) {
10132
        stream.eatWhile(/[\w\._\-]/);
10133
        state.tokenize = inBlock("meta", "?>");
10134
        return "meta";
10135
      } else {
10136
        type = stream.eat("/") ? "closeTag" : "openTag";
10137
        state.tokenize = inTag;
10138
        return "tag bracket";
10139
      }
10140
    } else if (ch == "&") {
10141
      var ok;
10142
      if (stream.eat("#")) {
10143
        if (stream.eat("x")) {
10144
          ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";");
10145
        } else {
10146
          ok = stream.eatWhile(/[\d]/) && stream.eat(";");
10147
        }
10148
      } else {
10149
        ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";");
10150
      }
10151
      return ok ? "atom" : "error";
10152
    } else {
10153
      stream.eatWhile(/[^&<]/);
10154
      return null;
10155
    }
10156
  }
10157
  inText.isInText = true;
10158
 
10159
  function inTag(stream, state) {
10160
    var ch = stream.next();
10161
    if (ch == ">" || (ch == "/" && stream.eat(">"))) {
10162
      state.tokenize = inText;
10163
      type = ch == ">" ? "endTag" : "selfcloseTag";
10164
      return "tag bracket";
10165
    } else if (ch == "=") {
10166
      type = "equals";
10167
      return null;
10168
    } else if (ch == "<") {
10169
      state.tokenize = inText;
10170
      state.state = baseState;
10171
      state.tagName = state.tagStart = null;
10172
      var next = state.tokenize(stream, state);
10173
      return next ? next + " tag error" : "tag error";
10174
    } else if (/[\'\"]/.test(ch)) {
10175
      state.tokenize = inAttribute(ch);
10176
      state.stringStartCol = stream.column();
10177
      return state.tokenize(stream, state);
10178
    } else {
10179
      stream.match(/^[^\s\u00a0=<>\"\']*[^\s\u00a0=<>\"\'\/]/);
10180
      return "word";
10181
    }
10182
  }
10183
 
10184
  function inAttribute(quote) {
10185
    var closure = function(stream, state) {
10186
      while (!stream.eol()) {
10187
        if (stream.next() == quote) {
10188
          state.tokenize = inTag;
10189
          break;
10190
        }
10191
      }
10192
      return "string";
10193
    };
10194
    closure.isInAttribute = true;
10195
    return closure;
10196
  }
10197
 
10198
  function inBlock(style, terminator) {
10199
    return function(stream, state) {
10200
      while (!stream.eol()) {
10201
        if (stream.match(terminator)) {
10202
          state.tokenize = inText;
10203
          break;
10204
        }
10205
        stream.next();
10206
      }
10207
      return style;
10208
    }
10209
  }
10210
 
10211
  function doctype(depth) {
10212
    return function(stream, state) {
10213
      var ch;
10214
      while ((ch = stream.next()) != null) {
10215
        if (ch == "<") {
10216
          state.tokenize = doctype(depth + 1);
10217
          return state.tokenize(stream, state);
10218
        } else if (ch == ">") {
10219
          if (depth == 1) {
10220
            state.tokenize = inText;
10221
            break;
10222
          } else {
10223
            state.tokenize = doctype(depth - 1);
10224
            return state.tokenize(stream, state);
10225
          }
10226
        }
10227
      }
10228
      return "meta";
10229
    };
10230
  }
10231
 
10232
  function lower(tagName) {
10233
    return tagName && tagName.toLowerCase();
10234
  }
10235
 
10236
  function Context(state, tagName, startOfLine) {
10237
    this.prev = state.context;
10238
    this.tagName = tagName || "";
10239
    this.indent = state.indented;
10240
    this.startOfLine = startOfLine;
10241
    if (config.doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.noIndent))
10242
      this.noIndent = true;
10243
  }
10244
  function popContext(state) {
10245
    if (state.context) state.context = state.context.prev;
10246
  }
10247
  function maybePopContext(state, nextTagName) {
10248
    var parentTagName;
10249
    while (true) {
10250
      if (!state.context) {
10251
        return;
10252
      }
10253
      parentTagName = state.context.tagName;
10254
      if (!config.contextGrabbers.hasOwnProperty(lower(parentTagName)) ||
10255
          !config.contextGrabbers[lower(parentTagName)].hasOwnProperty(lower(nextTagName))) {
10256
        return;
10257
      }
10258
      popContext(state);
10259
    }
10260
  }
10261
 
10262
  function baseState(type, stream, state) {
10263
    if (type == "openTag") {
10264
      state.tagStart = stream.column();
10265
      return tagNameState;
10266
    } else if (type == "closeTag") {
10267
      return closeTagNameState;
10268
    } else {
10269
      return baseState;
10270
    }
10271
  }
10272
  function tagNameState(type, stream, state) {
10273
    if (type == "word") {
10274
      state.tagName = stream.current();
10275
      setStyle = "tag";
10276
      return attrState;
10277
    } else if (config.allowMissingTagName && type == "endTag") {
10278
      setStyle = "tag bracket";
10279
      return attrState(type, stream, state);
10280
    } else {
10281
      setStyle = "error";
10282
      return tagNameState;
10283
    }
10284
  }
10285
  function closeTagNameState(type, stream, state) {
10286
    if (type == "word") {
10287
      var tagName = stream.current();
10288
      if (state.context && state.context.tagName != tagName &&
10289
          config.implicitlyClosed.hasOwnProperty(lower(state.context.tagName)))
10290
        popContext(state);
10291
      if ((state.context && state.context.tagName == tagName) || config.matchClosing === false) {
10292
        setStyle = "tag";
10293
        return closeState;
10294
      } else {
10295
        setStyle = "tag error";
10296
        return closeStateErr;
10297
      }
10298
    } else if (config.allowMissingTagName && type == "endTag") {
10299
      setStyle = "tag bracket";
10300
      return closeState(type, stream, state);
10301
    } else {
10302
      setStyle = "error";
10303
      return closeStateErr;
10304
    }
10305
  }
10306
 
10307
  function closeState(type, _stream, state) {
10308
    if (type != "endTag") {
10309
      setStyle = "error";
10310
      return closeState;
10311
    }
10312
    popContext(state);
10313
    return baseState;
10314
  }
10315
  function closeStateErr(type, stream, state) {
10316
    setStyle = "error";
10317
    return closeState(type, stream, state);
10318
  }
10319
 
10320
  function attrState(type, _stream, state) {
10321
    if (type == "word") {
10322
      setStyle = "attribute";
10323
      return attrEqState;
10324
    } else if (type == "endTag" || type == "selfcloseTag") {
10325
      var tagName = state.tagName, tagStart = state.tagStart;
10326
      state.tagName = state.tagStart = null;
10327
      if (type == "selfcloseTag" ||
10328
          config.autoSelfClosers.hasOwnProperty(lower(tagName))) {
10329
        maybePopContext(state, tagName);
10330
      } else {
10331
        maybePopContext(state, tagName);
10332
        state.context = new Context(state, tagName, tagStart == state.indented);
10333
      }
10334
      return baseState;
10335
    }
10336
    setStyle = "error";
10337
    return attrState;
10338
  }
10339
  function attrEqState(type, stream, state) {
10340
    if (type == "equals") return attrValueState;
10341
    if (!config.allowMissing) setStyle = "error";
10342
    return attrState(type, stream, state);
10343
  }
10344
  function attrValueState(type, stream, state) {
10345
    if (type == "string") return attrContinuedState;
10346
    if (type == "word" && config.allowUnquoted) {setStyle = "string"; return attrState;}
10347
    setStyle = "error";
10348
    return attrState(type, stream, state);
10349
  }
10350
  function attrContinuedState(type, stream, state) {
10351
    if (type == "string") return attrContinuedState;
10352
    return attrState(type, stream, state);
10353
  }
10354
 
10355
  return {
10356
    startState: function(baseIndent) {
10357
      var state = {tokenize: inText,
10358
                   state: baseState,
10359
                   indented: baseIndent || 0,
10360
                   tagName: null, tagStart: null,
10361
                   context: null}
10362
      if (baseIndent != null) state.baseIndent = baseIndent
10363
      return state
10364
    },
10365
 
10366
    token: function(stream, state) {
10367
      if (!state.tagName && stream.sol())
10368
        state.indented = stream.indentation();
10369
 
10370
      if (stream.eatSpace()) return null;
10371
      type = null;
10372
      var style = state.tokenize(stream, state);
10373
      if ((style || type) && style != "comment") {
10374
        setStyle = null;
10375
        state.state = state.state(type || style, stream, state);
10376
        if (setStyle)
10377
          style = setStyle == "error" ? style + " error" : setStyle;
10378
      }
10379
      return style;
10380
    },
10381
 
10382
    indent: function(state, textAfter, fullLine) {
10383
      var context = state.context;
10384
      // Indent multi-line strings (e.g. css).
10385
      if (state.tokenize.isInAttribute) {
10386
        if (state.tagStart == state.indented)
10387
          return state.stringStartCol + 1;
10388
        else
10389
          return state.indented + indentUnit;
10390
      }
10391
      if (context && context.noIndent) return CodeMirror.Pass;
10392
      if (state.tokenize != inTag && state.tokenize != inText)
10393
        return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
10394
      // Indent the starts of attribute names.
10395
      if (state.tagName) {
10396
        if (config.multilineTagIndentPastTag !== false)
10397
          return state.tagStart + state.tagName.length + 2;
10398
        else
10399
          return state.tagStart + indentUnit * (config.multilineTagIndentFactor || 1);
10400
      }
10401
      if (config.alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
10402
      var tagAfter = textAfter && /^<(\/)?([\w_:\.-]*)/.exec(textAfter);
10403
      if (tagAfter && tagAfter[1]) { // Closing tag spotted
10404
        while (context) {
10405
          if (context.tagName == tagAfter[2]) {
10406
            context = context.prev;
10407
            break;
10408
          } else if (config.implicitlyClosed.hasOwnProperty(lower(context.tagName))) {
10409
            context = context.prev;
10410
          } else {
10411
            break;
10412
          }
10413
        }
10414
      } else if (tagAfter) { // Opening tag spotted
10415
        while (context) {
10416
          var grabbers = config.contextGrabbers[lower(context.tagName)];
10417
          if (grabbers && grabbers.hasOwnProperty(lower(tagAfter[2])))
10418
            context = context.prev;
10419
          else
10420
            break;
10421
        }
10422
      }
10423
      while (context && context.prev && !context.startOfLine)
10424
        context = context.prev;
10425
      if (context) return context.indent + indentUnit;
10426
      else return state.baseIndent || 0;
10427
    },
10428
 
10429
    electricInput: /<\/[\s\w:]+>$/,
10430
    blockCommentStart: "<!--",
10431
    blockCommentEnd: "-->",
10432
 
10433
    configuration: config.htmlMode ? "html" : "xml",
10434
    helperType: config.htmlMode ? "html" : "xml",
10435
 
10436
    skipAttribute: function(state) {
10437
      if (state.state == attrValueState)
10438
        state.state = attrState
10439
    },
10440
 
10441
    xmlCurrentTag: function(state) {
10442
      return state.tagName ? {name: state.tagName, close: state.type == "closeTag"} : null
10443
    },
10444
 
10445
    xmlCurrentContext: function(state) {
10446
      var context = []
10447
      for (var cx = state.context; cx; cx = cx.prev)
10448
        context.push(cx.tagName)
10449
      return context.reverse()
10450
    }
10451
  };
10452
});
10453
 
10454
CodeMirror.defineMIME("text/xml", "xml");
10455
CodeMirror.defineMIME("application/xml", "xml");
10456
if (!CodeMirror.mimeModes.hasOwnProperty("text/html"))
10457
  CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
10458
 
10459
});
10460
// CodeMirror, copyright (c) by Marijn Haverbeke and others
10461
// Distributed under an MIT license: https://codemirror.net/5/LICENSE
10462
 
10463
(function(mod) {
10464
  if (typeof exports == "object" && typeof module == "object") // CommonJS
10465
    mod(require("../../lib/codemirror"));
10466
  else if (typeof define == "function" && define.amd) // AMD
10467
    define(["../../lib/codemirror"], mod);
10468
  else // Plain browser env
10469
    mod(CodeMirror);
10470
})(function(CodeMirror) {
10471
"use strict";
10472
 
10473
CodeMirror.defineMode("javascript", function(config, parserConfig) {
10474
  var indentUnit = config.indentUnit;
10475
  var statementIndent = parserConfig.statementIndent;
10476
  var jsonldMode = parserConfig.jsonld;
10477
  var jsonMode = parserConfig.json || jsonldMode;
10478
  var trackScope = parserConfig.trackScope !== false
10479
  var isTS = parserConfig.typescript;
10480
  var wordRE = parserConfig.wordCharacters || /[\w$\xa1-\uffff]/;
10481
 
10482
  // Tokenizer
10483
 
10484
  var keywords = function(){
10485
    function kw(type) {return {type: type, style: "keyword"};}
10486
    var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"), D = kw("keyword d");
10487
    var operator = kw("operator"), atom = {type: "atom", style: "atom"};
10488
 
10489
    return {
10490
      "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
10491
      "return": D, "break": D, "continue": D, "new": kw("new"), "delete": C, "void": C, "throw": C,
10492
      "debugger": kw("debugger"), "var": kw("var"), "const": kw("var"), "let": kw("var"),
10493
      "function": kw("function"), "catch": kw("catch"),
10494
      "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
10495
      "in": operator, "typeof": operator, "instanceof": operator,
10496
      "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom,
10497
      "this": kw("this"), "class": kw("class"), "super": kw("atom"),
10498
      "yield": C, "export": kw("export"), "import": kw("import"), "extends": C,
10499
      "await": C
10500
    };
10501
  }();
10502
 
10503
  var isOperatorChar = /[+\-*&%=<>!?|~^@]/;
10504
  var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/;
10505
 
10506
  function readRegexp(stream) {
10507
    var escaped = false, next, inSet = false;
10508
    while ((next = stream.next()) != null) {
10509
      if (!escaped) {
10510
        if (next == "/" && !inSet) return;
10511
        if (next == "[") inSet = true;
10512
        else if (inSet && next == "]") inSet = false;
10513
      }
10514
      escaped = !escaped && next == "\\";
10515
    }
10516
  }
10517
 
10518
  // Used as scratch variables to communicate multiple values without
10519
  // consing up tons of objects.
10520
  var type, content;
10521
  function ret(tp, style, cont) {
10522
    type = tp; content = cont;
10523
    return style;
10524
  }
10525
  function tokenBase(stream, state) {
10526
    var ch = stream.next();
10527
    if (ch == '"' || ch == "'") {
10528
      state.tokenize = tokenString(ch);
10529
      return state.tokenize(stream, state);
10530
    } else if (ch == "." && stream.match(/^\d[\d_]*(?:[eE][+\-]?[\d_]+)?/)) {
10531
      return ret("number", "number");
10532
    } else if (ch == "." && stream.match("..")) {
10533
      return ret("spread", "meta");
10534
    } else if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
10535
      return ret(ch);
10536
    } else if (ch == "=" && stream.eat(">")) {
10537
      return ret("=>", "operator");
10538
    } else if (ch == "0" && stream.match(/^(?:x[\dA-Fa-f_]+|o[0-7_]+|b[01_]+)n?/)) {
10539
      return ret("number", "number");
10540
    } else if (/\d/.test(ch)) {
10541
      stream.match(/^[\d_]*(?:n|(?:\.[\d_]*)?(?:[eE][+\-]?[\d_]+)?)?/);
10542
      return ret("number", "number");
10543
    } else if (ch == "/") {
10544
      if (stream.eat("*")) {
10545
        state.tokenize = tokenComment;
10546
        return tokenComment(stream, state);
10547
      } else if (stream.eat("/")) {
10548
        stream.skipToEnd();
10549
        return ret("comment", "comment");
10550
      } else if (expressionAllowed(stream, state, 1)) {
10551
        readRegexp(stream);
10552
        stream.match(/^\b(([gimyus])(?![gimyus]*\2))+\b/);
10553
        return ret("regexp", "string-2");
10554
      } else {
10555
        stream.eat("=");
10556
        return ret("operator", "operator", stream.current());
10557
      }
10558
    } else if (ch == "`") {
10559
      state.tokenize = tokenQuasi;
10560
      return tokenQuasi(stream, state);
10561
    } else if (ch == "#" && stream.peek() == "!") {
10562
      stream.skipToEnd();
10563
      return ret("meta", "meta");
10564
    } else if (ch == "#" && stream.eatWhile(wordRE)) {
10565
      return ret("variable", "property")
10566
    } else if (ch == "<" && stream.match("!--") ||
10567
               (ch == "-" && stream.match("->") && !/\S/.test(stream.string.slice(0, stream.start)))) {
10568
      stream.skipToEnd()
10569
      return ret("comment", "comment")
10570
    } else if (isOperatorChar.test(ch)) {
10571
      if (ch != ">" || !state.lexical || state.lexical.type != ">") {
10572
        if (stream.eat("=")) {
10573
          if (ch == "!" || ch == "=") stream.eat("=")
10574
        } else if (/[<>*+\-|&?]/.test(ch)) {
10575
          stream.eat(ch)
10576
          if (ch == ">") stream.eat(ch)
10577
        }
10578
      }
10579
      if (ch == "?" && stream.eat(".")) return ret(".")
10580
      return ret("operator", "operator", stream.current());
10581
    } else if (wordRE.test(ch)) {
10582
      stream.eatWhile(wordRE);
10583
      var word = stream.current()
10584
      if (state.lastType != ".") {
10585
        if (keywords.propertyIsEnumerable(word)) {
10586
          var kw = keywords[word]
10587
          return ret(kw.type, kw.style, word)
10588
        }
10589
        if (word == "async" && stream.match(/^(\s|\/\*([^*]|\*(?!\/))*?\*\/)*[\[\(\w]/, false))
10590
          return ret("async", "keyword", word)
10591
      }
10592
      return ret("variable", "variable", word)
10593
    }
10594
  }
10595
 
10596
  function tokenString(quote) {
10597
    return function(stream, state) {
10598
      var escaped = false, next;
10599
      if (jsonldMode && stream.peek() == "@" && stream.match(isJsonldKeyword)){
10600
        state.tokenize = tokenBase;
10601
        return ret("jsonld-keyword", "meta");
10602
      }
10603
      while ((next = stream.next()) != null) {
10604
        if (next == quote && !escaped) break;
10605
        escaped = !escaped && next == "\\";
10606
      }
10607
      if (!escaped) state.tokenize = tokenBase;
10608
      return ret("string", "string");
10609
    };
10610
  }
10611
 
10612
  function tokenComment(stream, state) {
10613
    var maybeEnd = false, ch;
10614
    while (ch = stream.next()) {
10615
      if (ch == "/" && maybeEnd) {
10616
        state.tokenize = tokenBase;
10617
        break;
10618
      }
10619
      maybeEnd = (ch == "*");
10620
    }
10621
    return ret("comment", "comment");
10622
  }
10623
 
10624
  function tokenQuasi(stream, state) {
10625
    var escaped = false, next;
10626
    while ((next = stream.next()) != null) {
10627
      if (!escaped && (next == "`" || next == "$" && stream.eat("{"))) {
10628
        state.tokenize = tokenBase;
10629
        break;
10630
      }
10631
      escaped = !escaped && next == "\\";
10632
    }
10633
    return ret("quasi", "string-2", stream.current());
10634
  }
10635
 
10636
  var brackets = "([{}])";
10637
  // This is a crude lookahead trick to try and notice that we're
10638
  // parsing the argument patterns for a fat-arrow function before we
10639
  // actually hit the arrow token. It only works if the arrow is on
10640
  // the same line as the arguments and there's no strange noise
10641
  // (comments) in between. Fallback is to only notice when we hit the
10642
  // arrow, and not declare the arguments as locals for the arrow
10643
  // body.
10644
  function findFatArrow(stream, state) {
10645
    if (state.fatArrowAt) state.fatArrowAt = null;
10646
    var arrow = stream.string.indexOf("=>", stream.start);
10647
    if (arrow < 0) return;
10648
 
10649
    if (isTS) { // Try to skip TypeScript return type declarations after the arguments
10650
      var m = /:\s*(?:\w+(?:<[^>]*>|\[\])?|\{[^}]*\})\s*$/.exec(stream.string.slice(stream.start, arrow))
10651
      if (m) arrow = m.index
10652
    }
10653
 
10654
    var depth = 0, sawSomething = false;
10655
    for (var pos = arrow - 1; pos >= 0; --pos) {
10656
      var ch = stream.string.charAt(pos);
10657
      var bracket = brackets.indexOf(ch);
10658
      if (bracket >= 0 && bracket < 3) {
10659
        if (!depth) { ++pos; break; }
10660
        if (--depth == 0) { if (ch == "(") sawSomething = true; break; }
10661
      } else if (bracket >= 3 && bracket < 6) {
10662
        ++depth;
10663
      } else if (wordRE.test(ch)) {
10664
        sawSomething = true;
10665
      } else if (/["'\/`]/.test(ch)) {
10666
        for (;; --pos) {
10667
          if (pos == 0) return
10668
          var next = stream.string.charAt(pos - 1)
10669
          if (next == ch && stream.string.charAt(pos - 2) != "\\") { pos--; break }
10670
        }
10671
      } else if (sawSomething && !depth) {
10672
        ++pos;
10673
        break;
10674
      }
10675
    }
10676
    if (sawSomething && !depth) state.fatArrowAt = pos;
10677
  }
10678
 
10679
  // Parser
10680
 
10681
  var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true,
10682
                     "regexp": true, "this": true, "import": true, "jsonld-keyword": true};
10683
 
10684
  function JSLexical(indented, column, type, align, prev, info) {
10685
    this.indented = indented;
10686
    this.column = column;
10687
    this.type = type;
10688
    this.prev = prev;
10689
    this.info = info;
10690
    if (align != null) this.align = align;
10691
  }
10692
 
10693
  function inScope(state, varname) {
10694
    if (!trackScope) return false
10695
    for (var v = state.localVars; v; v = v.next)
10696
      if (v.name == varname) return true;
10697
    for (var cx = state.context; cx; cx = cx.prev) {
10698
      for (var v = cx.vars; v; v = v.next)
10699
        if (v.name == varname) return true;
10700
    }
10701
  }
10702
 
10703
  function parseJS(state, style, type, content, stream) {
10704
    var cc = state.cc;
10705
    // Communicate our context to the combinators.
10706
    // (Less wasteful than consing up a hundred closures on every call.)
10707
    cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; cx.style = style;
10708
 
10709
    if (!state.lexical.hasOwnProperty("align"))
10710
      state.lexical.align = true;
10711
 
10712
    while(true) {
10713
      var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;
10714
      if (combinator(type, content)) {
10715
        while(cc.length && cc[cc.length - 1].lex)
10716
          cc.pop()();
10717
        if (cx.marked) return cx.marked;
10718
        if (type == "variable" && inScope(state, content)) return "variable-2";
10719
        return style;
10720
      }
10721
    }
10722
  }
10723
 
10724
  // Combinator utils
10725
 
10726
  var cx = {state: null, column: null, marked: null, cc: null};
10727
  function pass() {
10728
    for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
10729
  }
10730
  function cont() {
10731
    pass.apply(null, arguments);
10732
    return true;
10733
  }
10734
  function inList(name, list) {
10735
    for (var v = list; v; v = v.next) if (v.name == name) return true
10736
    return false;
10737
  }
10738
  function register(varname) {
10739
    var state = cx.state;
10740
    cx.marked = "def";
10741
    if (!trackScope) return
10742
    if (state.context) {
10743
      if (state.lexical.info == "var" && state.context && state.context.block) {
10744
        // FIXME function decls are also not block scoped
10745
        var newContext = registerVarScoped(varname, state.context)
10746
        if (newContext != null) {
10747
          state.context = newContext
10748
          return
10749
        }
10750
      } else if (!inList(varname, state.localVars)) {
10751
        state.localVars = new Var(varname, state.localVars)
10752
        return
10753
      }
10754
    }
10755
    // Fall through means this is global
10756
    if (parserConfig.globalVars && !inList(varname, state.globalVars))
10757
      state.globalVars = new Var(varname, state.globalVars)
10758
  }
10759
  function registerVarScoped(varname, context) {
10760
    if (!context) {
10761
      return null
10762
    } else if (context.block) {
10763
      var inner = registerVarScoped(varname, context.prev)
10764
      if (!inner) return null
10765
      if (inner == context.prev) return context
10766
      return new Context(inner, context.vars, true)
10767
    } else if (inList(varname, context.vars)) {
10768
      return context
10769
    } else {
10770
      return new Context(context.prev, new Var(varname, context.vars), false)
10771
    }
10772
  }
10773
 
10774
  function isModifier(name) {
10775
    return name == "public" || name == "private" || name == "protected" || name == "abstract" || name == "readonly"
10776
  }
10777
 
10778
  // Combinators
10779
 
10780
  function Context(prev, vars, block) { this.prev = prev; this.vars = vars; this.block = block }
10781
  function Var(name, next) { this.name = name; this.next = next }
10782
 
10783
  var defaultVars = new Var("this", new Var("arguments", null))
10784
  function pushcontext() {
10785
    cx.state.context = new Context(cx.state.context, cx.state.localVars, false)
10786
    cx.state.localVars = defaultVars
10787
  }
10788
  function pushblockcontext() {
10789
    cx.state.context = new Context(cx.state.context, cx.state.localVars, true)
10790
    cx.state.localVars = null
10791
  }
10792
  pushcontext.lex = pushblockcontext.lex = true
10793
  function popcontext() {
10794
    cx.state.localVars = cx.state.context.vars
10795
    cx.state.context = cx.state.context.prev
10796
  }
10797
  popcontext.lex = true
10798
  function pushlex(type, info) {
10799
    var result = function() {
10800
      var state = cx.state, indent = state.indented;
10801
      if (state.lexical.type == "stat") indent = state.lexical.indented;
10802
      else for (var outer = state.lexical; outer && outer.type == ")" && outer.align; outer = outer.prev)
10803
        indent = outer.indented;
10804
      state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info);
10805
    };
10806
    result.lex = true;
10807
    return result;
10808
  }
10809
  function poplex() {
10810
    var state = cx.state;
10811
    if (state.lexical.prev) {
10812
      if (state.lexical.type == ")")
10813
        state.indented = state.lexical.indented;
10814
      state.lexical = state.lexical.prev;
10815
    }
10816
  }
10817
  poplex.lex = true;
10818
 
10819
  function expect(wanted) {
10820
    function exp(type) {
10821
      if (type == wanted) return cont();
10822
      else if (wanted == ";" || type == "}" || type == ")" || type == "]") return pass();
10823
      else return cont(exp);
10824
    };
10825
    return exp;
10826
  }
10827
 
10828
  function statement(type, value) {
10829
    if (type == "var") return cont(pushlex("vardef", value), vardef, expect(";"), poplex);
10830
    if (type == "keyword a") return cont(pushlex("form"), parenExpr, statement, poplex);
10831
    if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
10832
    if (type == "keyword d") return cx.stream.match(/^\s*$/, false) ? cont() : cont(pushlex("stat"), maybeexpression, expect(";"), poplex);
10833
    if (type == "debugger") return cont(expect(";"));
10834
    if (type == "{") return cont(pushlex("}"), pushblockcontext, block, poplex, popcontext);
10835
    if (type == ";") return cont();
10836
    if (type == "if") {
10837
      if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex)
10838
        cx.state.cc.pop()();
10839
      return cont(pushlex("form"), parenExpr, statement, poplex, maybeelse);
10840
    }
10841
    if (type == "function") return cont(functiondef);
10842
    if (type == "for") return cont(pushlex("form"), pushblockcontext, forspec, statement, popcontext, poplex);
10843
    if (type == "class" || (isTS && value == "interface")) {
10844
      cx.marked = "keyword"
10845
      return cont(pushlex("form", type == "class" ? type : value), className, poplex)
10846
    }
10847
    if (type == "variable") {
10848
      if (isTS && value == "declare") {
10849
        cx.marked = "keyword"
10850
        return cont(statement)
10851
      } else if (isTS && (value == "module" || value == "enum" || value == "type") && cx.stream.match(/^\s*\w/, false)) {
10852
        cx.marked = "keyword"
10853
        if (value == "enum") return cont(enumdef);
10854
        else if (value == "type") return cont(typename, expect("operator"), typeexpr, expect(";"));
10855
        else return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex)
10856
      } else if (isTS && value == "namespace") {
10857
        cx.marked = "keyword"
10858
        return cont(pushlex("form"), expression, statement, poplex)
10859
      } else if (isTS && value == "abstract") {
10860
        cx.marked = "keyword"
10861
        return cont(statement)
10862
      } else {
10863
        return cont(pushlex("stat"), maybelabel);
10864
      }
10865
    }
10866
    if (type == "switch") return cont(pushlex("form"), parenExpr, expect("{"), pushlex("}", "switch"), pushblockcontext,
10867
                                      block, poplex, poplex, popcontext);
10868
    if (type == "case") return cont(expression, expect(":"));
10869
    if (type == "default") return cont(expect(":"));
10870
    if (type == "catch") return cont(pushlex("form"), pushcontext, maybeCatchBinding, statement, poplex, popcontext);
10871
    if (type == "export") return cont(pushlex("stat"), afterExport, poplex);
10872
    if (type == "import") return cont(pushlex("stat"), afterImport, poplex);
10873
    if (type == "async") return cont(statement)
10874
    if (value == "@") return cont(expression, statement)
10875
    return pass(pushlex("stat"), expression, expect(";"), poplex);
10876
  }
10877
  function maybeCatchBinding(type) {
10878
    if (type == "(") return cont(funarg, expect(")"))
10879
  }
10880
  function expression(type, value) {
10881
    return expressionInner(type, value, false);
10882
  }
10883
  function expressionNoComma(type, value) {
10884
    return expressionInner(type, value, true);
10885
  }
10886
  function parenExpr(type) {
10887
    if (type != "(") return pass()
10888
    return cont(pushlex(")"), maybeexpression, expect(")"), poplex)
10889
  }
10890
  function expressionInner(type, value, noComma) {
10891
    if (cx.state.fatArrowAt == cx.stream.start) {
10892
      var body = noComma ? arrowBodyNoComma : arrowBody;
10893
      if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, expect("=>"), body, popcontext);
10894
      else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext);
10895
    }
10896
 
10897
    var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
10898
    if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
10899
    if (type == "function") return cont(functiondef, maybeop);
10900
    if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword"; return cont(pushlex("form"), classExpression, poplex); }
10901
    if (type == "keyword c" || type == "async") return cont(noComma ? expressionNoComma : expression);
10902
    if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop);
10903
    if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression);
10904
    if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop);
10905
    if (type == "{") return contCommasep(objprop, "}", null, maybeop);
10906
    if (type == "quasi") return pass(quasi, maybeop);
10907
    if (type == "new") return cont(maybeTarget(noComma));
10908
    return cont();
10909
  }
10910
  function maybeexpression(type) {
10911
    if (type.match(/[;\}\)\],]/)) return pass();
10912
    return pass(expression);
10913
  }
10914
 
10915
  function maybeoperatorComma(type, value) {
10916
    if (type == ",") return cont(maybeexpression);
10917
    return maybeoperatorNoComma(type, value, false);
10918
  }
10919
  function maybeoperatorNoComma(type, value, noComma) {
10920
    var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma;
10921
    var expr = noComma == false ? expression : expressionNoComma;
10922
    if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext);
10923
    if (type == "operator") {
10924
      if (/\+\+|--/.test(value) || isTS && value == "!") return cont(me);
10925
      if (isTS && value == "<" && cx.stream.match(/^([^<>]|<[^<>]*>)*>\s*\(/, false))
10926
        return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, me);
10927
      if (value == "?") return cont(expression, expect(":"), expr);
10928
      return cont(expr);
10929
    }
10930
    if (type == "quasi") { return pass(quasi, me); }
10931
    if (type == ";") return;
10932
    if (type == "(") return contCommasep(expressionNoComma, ")", "call", me);
10933
    if (type == ".") return cont(property, me);
10934
    if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me);
10935
    if (isTS && value == "as") { cx.marked = "keyword"; return cont(typeexpr, me) }
10936
    if (type == "regexp") {
10937
      cx.state.lastType = cx.marked = "operator"
10938
      cx.stream.backUp(cx.stream.pos - cx.stream.start - 1)
10939
      return cont(expr)
10940
    }
10941
  }
10942
  function quasi(type, value) {
10943
    if (type != "quasi") return pass();
10944
    if (value.slice(value.length - 2) != "${") return cont(quasi);
10945
    return cont(maybeexpression, continueQuasi);
10946
  }
10947
  function continueQuasi(type) {
10948
    if (type == "}") {
10949
      cx.marked = "string-2";
10950
      cx.state.tokenize = tokenQuasi;
10951
      return cont(quasi);
10952
    }
10953
  }
10954
  function arrowBody(type) {
10955
    findFatArrow(cx.stream, cx.state);
10956
    return pass(type == "{" ? statement : expression);
10957
  }
10958
  function arrowBodyNoComma(type) {
10959
    findFatArrow(cx.stream, cx.state);
10960
    return pass(type == "{" ? statement : expressionNoComma);
10961
  }
10962
  function maybeTarget(noComma) {
10963
    return function(type) {
10964
      if (type == ".") return cont(noComma ? targetNoComma : target);
10965
      else if (type == "variable" && isTS) return cont(maybeTypeArgs, noComma ? maybeoperatorNoComma : maybeoperatorComma)
10966
      else return pass(noComma ? expressionNoComma : expression);
10967
    };
10968
  }
10969
  function target(_, value) {
10970
    if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorComma); }
10971
  }
10972
  function targetNoComma(_, value) {
10973
    if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorNoComma); }
10974
  }
10975
  function maybelabel(type) {
10976
    if (type == ":") return cont(poplex, statement);
10977
    return pass(maybeoperatorComma, expect(";"), poplex);
10978
  }
10979
  function property(type) {
10980
    if (type == "variable") {cx.marked = "property"; return cont();}
10981
  }
10982
  function objprop(type, value) {
10983
    if (type == "async") {
10984
      cx.marked = "property";
10985
      return cont(objprop);
10986
    } else if (type == "variable" || cx.style == "keyword") {
10987
      cx.marked = "property";
10988
      if (value == "get" || value == "set") return cont(getterSetter);
10989
      var m // Work around fat-arrow-detection complication for detecting typescript typed arrow params
10990
      if (isTS && cx.state.fatArrowAt == cx.stream.start && (m = cx.stream.match(/^\s*:\s*/, false)))
10991
        cx.state.fatArrowAt = cx.stream.pos + m[0].length
10992
      return cont(afterprop);
10993
    } else if (type == "number" || type == "string") {
10994
      cx.marked = jsonldMode ? "property" : (cx.style + " property");
10995
      return cont(afterprop);
10996
    } else if (type == "jsonld-keyword") {
10997
      return cont(afterprop);
10998
    } else if (isTS && isModifier(value)) {
10999
      cx.marked = "keyword"
11000
      return cont(objprop)
11001
    } else if (type == "[") {
11002
      return cont(expression, maybetype, expect("]"), afterprop);
11003
    } else if (type == "spread") {
11004
      return cont(expressionNoComma, afterprop);
11005
    } else if (value == "*") {
11006
      cx.marked = "keyword";
11007
      return cont(objprop);
11008
    } else if (type == ":") {
11009
      return pass(afterprop)
11010
    }
11011
  }
11012
  function getterSetter(type) {
11013
    if (type != "variable") return pass(afterprop);
11014
    cx.marked = "property";
11015
    return cont(functiondef);
11016
  }
11017
  function afterprop(type) {
11018
    if (type == ":") return cont(expressionNoComma);
11019
    if (type == "(") return pass(functiondef);
11020
  }
11021
  function commasep(what, end, sep) {
11022
    function proceed(type, value) {
11023
      if (sep ? sep.indexOf(type) > -1 : type == ",") {
11024
        var lex = cx.state.lexical;
11025
        if (lex.info == "call") lex.pos = (lex.pos || 0) + 1;
11026
        return cont(function(type, value) {
11027
          if (type == end || value == end) return pass()
11028
          return pass(what)
11029
        }, proceed);
11030
      }
11031
      if (type == end || value == end) return cont();
11032
      if (sep && sep.indexOf(";") > -1) return pass(what)
11033
      return cont(expect(end));
11034
    }
11035
    return function(type, value) {
11036
      if (type == end || value == end) return cont();
11037
      return pass(what, proceed);
11038
    };
11039
  }
11040
  function contCommasep(what, end, info) {
11041
    for (var i = 3; i < arguments.length; i++)
11042
      cx.cc.push(arguments[i]);
11043
    return cont(pushlex(end, info), commasep(what, end), poplex);
11044
  }
11045
  function block(type) {
11046
    if (type == "}") return cont();
11047
    return pass(statement, block);
11048
  }
11049
  function maybetype(type, value) {
11050
    if (isTS) {
11051
      if (type == ":") return cont(typeexpr);
11052
      if (value == "?") return cont(maybetype);
11053
    }
11054
  }
11055
  function maybetypeOrIn(type, value) {
11056
    if (isTS && (type == ":" || value == "in")) return cont(typeexpr)
11057
  }
11058
  function mayberettype(type) {
11059
    if (isTS && type == ":") {
11060
      if (cx.stream.match(/^\s*\w+\s+is\b/, false)) return cont(expression, isKW, typeexpr)
11061
      else return cont(typeexpr)
11062
    }
11063
  }
11064
  function isKW(_, value) {
11065
    if (value == "is") {
11066
      cx.marked = "keyword"
11067
      return cont()
11068
    }
11069
  }
11070
  function typeexpr(type, value) {
11071
    if (value == "keyof" || value == "typeof" || value == "infer" || value == "readonly") {
11072
      cx.marked = "keyword"
11073
      return cont(value == "typeof" ? expressionNoComma : typeexpr)
11074
    }
11075
    if (type == "variable" || value == "void") {
11076
      cx.marked = "type"
11077
      return cont(afterType)
11078
    }
11079
    if (value == "|" || value == "&") return cont(typeexpr)
11080
    if (type == "string" || type == "number" || type == "atom") return cont(afterType);
11081
    if (type == "[") return cont(pushlex("]"), commasep(typeexpr, "]", ","), poplex, afterType)
11082
    if (type == "{") return cont(pushlex("}"), typeprops, poplex, afterType)
11083
    if (type == "(") return cont(commasep(typearg, ")"), maybeReturnType, afterType)
11084
    if (type == "<") return cont(commasep(typeexpr, ">"), typeexpr)
11085
    if (type == "quasi") { return pass(quasiType, afterType); }
11086
  }
11087
  function maybeReturnType(type) {
11088
    if (type == "=>") return cont(typeexpr)
11089
  }
11090
  function typeprops(type) {
11091
    if (type.match(/[\}\)\]]/)) return cont()
11092
    if (type == "," || type == ";") return cont(typeprops)
11093
    return pass(typeprop, typeprops)
11094
  }
11095
  function typeprop(type, value) {
11096
    if (type == "variable" || cx.style == "keyword") {
11097
      cx.marked = "property"
11098
      return cont(typeprop)
11099
    } else if (value == "?" || type == "number" || type == "string") {
11100
      return cont(typeprop)
11101
    } else if (type == ":") {
11102
      return cont(typeexpr)
11103
    } else if (type == "[") {
11104
      return cont(expect("variable"), maybetypeOrIn, expect("]"), typeprop)
11105
    } else if (type == "(") {
11106
      return pass(functiondecl, typeprop)
11107
    } else if (!type.match(/[;\}\)\],]/)) {
11108
      return cont()
11109
    }
11110
  }
11111
  function quasiType(type, value) {
11112
    if (type != "quasi") return pass();
11113
    if (value.slice(value.length - 2) != "${") return cont(quasiType);
11114
    return cont(typeexpr, continueQuasiType);
11115
  }
11116
  function continueQuasiType(type) {
11117
    if (type == "}") {
11118
      cx.marked = "string-2";
11119
      cx.state.tokenize = tokenQuasi;
11120
      return cont(quasiType);
11121
    }
11122
  }
11123
  function typearg(type, value) {
11124
    if (type == "variable" && cx.stream.match(/^\s*[?:]/, false) || value == "?") return cont(typearg)
11125
    if (type == ":") return cont(typeexpr)
11126
    if (type == "spread") return cont(typearg)
11127
    return pass(typeexpr)
11128
  }
11129
  function afterType(type, value) {
11130
    if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
11131
    if (value == "|" || type == "." || value == "&") return cont(typeexpr)
11132
    if (type == "[") return cont(typeexpr, expect("]"), afterType)
11133
    if (value == "extends" || value == "implements") { cx.marked = "keyword"; return cont(typeexpr) }
11134
    if (value == "?") return cont(typeexpr, expect(":"), typeexpr)
11135
  }
11136
  function maybeTypeArgs(_, value) {
11137
    if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
11138
  }
11139
  function typeparam() {
11140
    return pass(typeexpr, maybeTypeDefault)
11141
  }
11142
  function maybeTypeDefault(_, value) {
11143
    if (value == "=") return cont(typeexpr)
11144
  }
11145
  function vardef(_, value) {
11146
    if (value == "enum") {cx.marked = "keyword"; return cont(enumdef)}
11147
    return pass(pattern, maybetype, maybeAssign, vardefCont);
11148
  }
11149
  function pattern(type, value) {
11150
    if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(pattern) }
11151
    if (type == "variable") { register(value); return cont(); }
11152
    if (type == "spread") return cont(pattern);
11153
    if (type == "[") return contCommasep(eltpattern, "]");
11154
    if (type == "{") return contCommasep(proppattern, "}");
11155
  }
11156
  function proppattern(type, value) {
11157
    if (type == "variable" && !cx.stream.match(/^\s*:/, false)) {
11158
      register(value);
11159
      return cont(maybeAssign);
11160
    }
11161
    if (type == "variable") cx.marked = "property";
11162
    if (type == "spread") return cont(pattern);
11163
    if (type == "}") return pass();
11164
    if (type == "[") return cont(expression, expect(']'), expect(':'), proppattern);
11165
    return cont(expect(":"), pattern, maybeAssign);
11166
  }
11167
  function eltpattern() {
11168
    return pass(pattern, maybeAssign)
11169
  }
11170
  function maybeAssign(_type, value) {
11171
    if (value == "=") return cont(expressionNoComma);
11172
  }
11173
  function vardefCont(type) {
11174
    if (type == ",") return cont(vardef);
11175
  }
11176
  function maybeelse(type, value) {
11177
    if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex);
11178
  }
11179
  function forspec(type, value) {
11180
    if (value == "await") return cont(forspec);
11181
    if (type == "(") return cont(pushlex(")"), forspec1, poplex);
11182
  }
11183
  function forspec1(type) {
11184
    if (type == "var") return cont(vardef, forspec2);
11185
    if (type == "variable") return cont(forspec2);
11186
    return pass(forspec2)
11187
  }
11188
  function forspec2(type, value) {
11189
    if (type == ")") return cont()
11190
    if (type == ";") return cont(forspec2)
11191
    if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression, forspec2) }
11192
    return pass(expression, forspec2)
11193
  }
11194
  function functiondef(type, value) {
11195
    if (value == "*") {cx.marked = "keyword"; return cont(functiondef);}
11196
    if (type == "variable") {register(value); return cont(functiondef);}
11197
    if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, statement, popcontext);
11198
    if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondef)
11199
  }
11200
  function functiondecl(type, value) {
11201
    if (value == "*") {cx.marked = "keyword"; return cont(functiondecl);}
11202
    if (type == "variable") {register(value); return cont(functiondecl);}
11203
    if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, popcontext);
11204
    if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondecl)
11205
  }
11206
  function typename(type, value) {
11207
    if (type == "keyword" || type == "variable") {
11208
      cx.marked = "type"
11209
      return cont(typename)
11210
    } else if (value == "<") {
11211
      return cont(pushlex(">"), commasep(typeparam, ">"), poplex)
11212
    }
11213
  }
11214
  function funarg(type, value) {
11215
    if (value == "@") cont(expression, funarg)
11216
    if (type == "spread") return cont(funarg);
11217
    if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(funarg); }
11218
    if (isTS && type == "this") return cont(maybetype, maybeAssign)
11219
    return pass(pattern, maybetype, maybeAssign);
11220
  }
11221
  function classExpression(type, value) {
11222
    // Class expressions may have an optional name.
11223
    if (type == "variable") return className(type, value);
11224
    return classNameAfter(type, value);
11225
  }
11226
  function className(type, value) {
11227
    if (type == "variable") {register(value); return cont(classNameAfter);}
11228
  }
11229
  function classNameAfter(type, value) {
11230
    if (value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, classNameAfter)
11231
    if (value == "extends" || value == "implements" || (isTS && type == ",")) {
11232
      if (value == "implements") cx.marked = "keyword";
11233
      return cont(isTS ? typeexpr : expression, classNameAfter);
11234
    }
11235
    if (type == "{") return cont(pushlex("}"), classBody, poplex);
11236
  }
11237
  function classBody(type, value) {
11238
    if (type == "async" ||
11239
        (type == "variable" &&
11240
         (value == "static" || value == "get" || value == "set" || (isTS && isModifier(value))) &&
11241
         cx.stream.match(/^\s+#?[\w$\xa1-\uffff]/, false))) {
11242
      cx.marked = "keyword";
11243
      return cont(classBody);
11244
    }
11245
    if (type == "variable" || cx.style == "keyword") {
11246
      cx.marked = "property";
11247
      return cont(classfield, classBody);
11248
    }
11249
    if (type == "number" || type == "string") return cont(classfield, classBody);
11250
    if (type == "[")
11251
      return cont(expression, maybetype, expect("]"), classfield, classBody)
11252
    if (value == "*") {
11253
      cx.marked = "keyword";
11254
      return cont(classBody);
11255
    }
11256
    if (isTS && type == "(") return pass(functiondecl, classBody)
11257
    if (type == ";" || type == ",") return cont(classBody);
11258
    if (type == "}") return cont();
11259
    if (value == "@") return cont(expression, classBody)
11260
  }
11261
  function classfield(type, value) {
11262
    if (value == "!") return cont(classfield)
11263
    if (value == "?") return cont(classfield)
11264
    if (type == ":") return cont(typeexpr, maybeAssign)
11265
    if (value == "=") return cont(expressionNoComma)
11266
    var context = cx.state.lexical.prev, isInterface = context && context.info == "interface"
11267
    return pass(isInterface ? functiondecl : functiondef)
11268
  }
11269
  function afterExport(type, value) {
11270
    if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); }
11271
    if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); }
11272
    if (type == "{") return cont(commasep(exportField, "}"), maybeFrom, expect(";"));
11273
    return pass(statement);
11274
  }
11275
  function exportField(type, value) {
11276
    if (value == "as") { cx.marked = "keyword"; return cont(expect("variable")); }
11277
    if (type == "variable") return pass(expressionNoComma, exportField);
11278
  }
11279
  function afterImport(type) {
11280
    if (type == "string") return cont();
11281
    if (type == "(") return pass(expression);
11282
    if (type == ".") return pass(maybeoperatorComma);
11283
    return pass(importSpec, maybeMoreImports, maybeFrom);
11284
  }
11285
  function importSpec(type, value) {
11286
    if (type == "{") return contCommasep(importSpec, "}");
11287
    if (type == "variable") register(value);
11288
    if (value == "*") cx.marked = "keyword";
11289
    return cont(maybeAs);
11290
  }
11291
  function maybeMoreImports(type) {
11292
    if (type == ",") return cont(importSpec, maybeMoreImports)
11293
  }
11294
  function maybeAs(_type, value) {
11295
    if (value == "as") { cx.marked = "keyword"; return cont(importSpec); }
11296
  }
11297
  function maybeFrom(_type, value) {
11298
    if (value == "from") { cx.marked = "keyword"; return cont(expression); }
11299
  }
11300
  function arrayLiteral(type) {
11301
    if (type == "]") return cont();
11302
    return pass(commasep(expressionNoComma, "]"));
11303
  }
11304
  function enumdef() {
11305
    return pass(pushlex("form"), pattern, expect("{"), pushlex("}"), commasep(enummember, "}"), poplex, poplex)
11306
  }
11307
  function enummember() {
11308
    return pass(pattern, maybeAssign);
11309
  }
11310
 
11311
  function isContinuedStatement(state, textAfter) {
11312
    return state.lastType == "operator" || state.lastType == "," ||
11313
      isOperatorChar.test(textAfter.charAt(0)) ||
11314
      /[,.]/.test(textAfter.charAt(0));
11315
  }
11316
 
11317
  function expressionAllowed(stream, state, backUp) {
11318
    return state.tokenize == tokenBase &&
11319
      /^(?:operator|sof|keyword [bcd]|case|new|export|default|spread|[\[{}\(,;:]|=>)$/.test(state.lastType) ||
11320
      (state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0))))
11321
  }
11322
 
11323
  // Interface
11324
 
11325
  return {
11326
    startState: function(basecolumn) {
11327
      var state = {
11328
        tokenize: tokenBase,
11329
        lastType: "sof",
11330
        cc: [],
11331
        lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
11332
        localVars: parserConfig.localVars,
11333
        context: parserConfig.localVars && new Context(null, null, false),
11334
        indented: basecolumn || 0
11335
      };
11336
      if (parserConfig.globalVars && typeof parserConfig.globalVars == "object")
11337
        state.globalVars = parserConfig.globalVars;
11338
      return state;
11339
    },
11340
 
11341
    token: function(stream, state) {
11342
      if (stream.sol()) {
11343
        if (!state.lexical.hasOwnProperty("align"))
11344
          state.lexical.align = false;
11345
        state.indented = stream.indentation();
11346
        findFatArrow(stream, state);
11347
      }
11348
      if (state.tokenize != tokenComment && stream.eatSpace()) return null;
11349
      var style = state.tokenize(stream, state);
11350
      if (type == "comment") return style;
11351
      state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type;
11352
      return parseJS(state, style, type, content, stream);
11353
    },
11354
 
11355
    indent: function(state, textAfter) {
11356
      if (state.tokenize == tokenComment || state.tokenize == tokenQuasi) return CodeMirror.Pass;
11357
      if (state.tokenize != tokenBase) return 0;
11358
      var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical, top
11359
      // Kludge to prevent 'maybelse' from blocking lexical scope pops
11360
      if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) {
11361
        var c = state.cc[i];
11362
        if (c == poplex) lexical = lexical.prev;
11363
        else if (c != maybeelse && c != popcontext) break;
11364
      }
11365
      while ((lexical.type == "stat" || lexical.type == "form") &&
11366
             (firstChar == "}" || ((top = state.cc[state.cc.length - 1]) &&
11367
                                   (top == maybeoperatorComma || top == maybeoperatorNoComma) &&
11368
                                   !/^[,\.=+\-*:?[\(]/.test(textAfter))))
11369
        lexical = lexical.prev;
11370
      if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat")
11371
        lexical = lexical.prev;
11372
      var type = lexical.type, closing = firstChar == type;
11373
 
11374
      if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info.length + 1 : 0);
11375
      else if (type == "form" && firstChar == "{") return lexical.indented;
11376
      else if (type == "form") return lexical.indented + indentUnit;
11377
      else if (type == "stat")
11378
        return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0);
11379
      else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false)
11380
        return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
11381
      else if (lexical.align) return lexical.column + (closing ? 0 : 1);
11382
      else return lexical.indented + (closing ? 0 : indentUnit);
11383
    },
11384
 
11385
    electricInput: /^\s*(?:case .*?:|default:|\{|\})$/,
11386
    blockCommentStart: jsonMode ? null : "/*",
11387
    blockCommentEnd: jsonMode ? null : "*/",
11388
    blockCommentContinue: jsonMode ? null : " * ",
11389
    lineComment: jsonMode ? null : "//",
11390
    fold: "brace",
11391
    closeBrackets: "()[]{}''\"\"``",
11392
 
11393
    helperType: jsonMode ? "json" : "javascript",
11394
    jsonldMode: jsonldMode,
11395
    jsonMode: jsonMode,
11396
 
11397
    expressionAllowed: expressionAllowed,
11398
 
11399
    skipExpression: function(state) {
11400
      parseJS(state, "atom", "atom", "true", new CodeMirror.StringStream("", 2, null))
11401
    }
11402
  };
11403
});
11404
 
11405
CodeMirror.registerHelper("wordChars", "javascript", /[\w$]/);
11406
 
11407
CodeMirror.defineMIME("text/javascript", "javascript");
11408
CodeMirror.defineMIME("text/ecmascript", "javascript");
11409
CodeMirror.defineMIME("application/javascript", "javascript");
11410
CodeMirror.defineMIME("application/x-javascript", "javascript");
11411
CodeMirror.defineMIME("application/ecmascript", "javascript");
11412
CodeMirror.defineMIME("application/json", { name: "javascript", json: true });
11413
CodeMirror.defineMIME("application/x-json", { name: "javascript", json: true });
11414
CodeMirror.defineMIME("application/manifest+json", { name: "javascript", json: true })
11415
CodeMirror.defineMIME("application/ld+json", { name: "javascript", jsonld: true });
11416
CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true });
11417
CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true });
11418
 
11419
});
11420
// CodeMirror, copyright (c) by Marijn Haverbeke and others
11421
// Distributed under an MIT license: https://codemirror.net/5/LICENSE
11422
 
11423
(function(mod) {
11424
  if (typeof exports == "object" && typeof module == "object") // CommonJS
11425
    mod(require("../../lib/codemirror"));
11426
  else if (typeof define == "function" && define.amd) // AMD
11427
    define(["../../lib/codemirror"], mod);
11428
  else // Plain browser env
11429
    mod(CodeMirror);
11430
})(function(CodeMirror) {
11431
"use strict";
11432
 
11433
CodeMirror.defineMode("css", function(config, parserConfig) {
11434
  var inline = parserConfig.inline
11435
  if (!parserConfig.propertyKeywords) parserConfig = CodeMirror.resolveMode("text/css");
11436
 
11437
  var indentUnit = config.indentUnit,
11438
      tokenHooks = parserConfig.tokenHooks,
11439
      documentTypes = parserConfig.documentTypes || {},
11440
      mediaTypes = parserConfig.mediaTypes || {},
11441
      mediaFeatures = parserConfig.mediaFeatures || {},
11442
      mediaValueKeywords = parserConfig.mediaValueKeywords || {},
11443
      propertyKeywords = parserConfig.propertyKeywords || {},
11444
      nonStandardPropertyKeywords = parserConfig.nonStandardPropertyKeywords || {},
11445
      fontProperties = parserConfig.fontProperties || {},
11446
      counterDescriptors = parserConfig.counterDescriptors || {},
11447
      colorKeywords = parserConfig.colorKeywords || {},
11448
      valueKeywords = parserConfig.valueKeywords || {},
11449
      allowNested = parserConfig.allowNested,
11450
      lineComment = parserConfig.lineComment,
11451
      supportsAtComponent = parserConfig.supportsAtComponent === true,
11452
      highlightNonStandardPropertyKeywords = config.highlightNonStandardPropertyKeywords !== false;
11453
 
11454
  var type, override;
11455
  function ret(style, tp) { type = tp; return style; }
11456
 
11457
  // Tokenizers
11458
 
11459
  function tokenBase(stream, state) {
11460
    var ch = stream.next();
11461
    if (tokenHooks[ch]) {
11462
      var result = tokenHooks[ch](stream, state);
11463
      if (result !== false) return result;
11464
    }
11465
    if (ch == "@") {
11466
      stream.eatWhile(/[\w\\\-]/);
11467
      return ret("def", stream.current());
11468
    } else if (ch == "=" || (ch == "~" || ch == "|") && stream.eat("=")) {
11469
      return ret(null, "compare");
11470
    } else if (ch == "\"" || ch == "'") {
11471
      state.tokenize = tokenString(ch);
11472
      return state.tokenize(stream, state);
11473
    } else if (ch == "#") {
11474
      stream.eatWhile(/[\w\\\-]/);
11475
      return ret("atom", "hash");
11476
    } else if (ch == "!") {
11477
      stream.match(/^\s*\w*/);
11478
      return ret("keyword", "important");
11479
    } else if (/\d/.test(ch) || ch == "." && stream.eat(/\d/)) {
11480
      stream.eatWhile(/[\w.%]/);
11481
      return ret("number", "unit");
11482
    } else if (ch === "-") {
11483
      if (/[\d.]/.test(stream.peek())) {
11484
        stream.eatWhile(/[\w.%]/);
11485
        return ret("number", "unit");
11486
      } else if (stream.match(/^-[\w\\\-]*/)) {
11487
        stream.eatWhile(/[\w\\\-]/);
11488
        if (stream.match(/^\s*:/, false))
11489
          return ret("variable-2", "variable-definition");
11490
        return ret("variable-2", "variable");
11491
      } else if (stream.match(/^\w+-/)) {
11492
        return ret("meta", "meta");
11493
      }
11494
    } else if (/[,+>*\/]/.test(ch)) {
11495
      return ret(null, "select-op");
11496
    } else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {
11497
      return ret("qualifier", "qualifier");
11498
    } else if (/[:;{}\[\]\(\)]/.test(ch)) {
11499
      return ret(null, ch);
11500
    } else if (stream.match(/^[\w-.]+(?=\()/)) {
11501
      if (/^(url(-prefix)?|domain|regexp)$/i.test(stream.current())) {
11502
        state.tokenize = tokenParenthesized;
11503
      }
11504
      return ret("variable callee", "variable");
11505
    } else if (/[\w\\\-]/.test(ch)) {
11506
      stream.eatWhile(/[\w\\\-]/);
11507
      return ret("property", "word");
11508
    } else {
11509
      return ret(null, null);
11510
    }
11511
  }
11512
 
11513
  function tokenString(quote) {
11514
    return function(stream, state) {
11515
      var escaped = false, ch;
11516
      while ((ch = stream.next()) != null) {
11517
        if (ch == quote && !escaped) {
11518
          if (quote == ")") stream.backUp(1);
11519
          break;
11520
        }
11521
        escaped = !escaped && ch == "\\";
11522
      }
11523
      if (ch == quote || !escaped && quote != ")") state.tokenize = null;
11524
      return ret("string", "string");
11525
    };
11526
  }
11527
 
11528
  function tokenParenthesized(stream, state) {
11529
    stream.next(); // Must be '('
11530
    if (!stream.match(/^\s*[\"\')]/, false))
11531
      state.tokenize = tokenString(")");
11532
    else
11533
      state.tokenize = null;
11534
    return ret(null, "(");
11535
  }
11536
 
11537
  // Context management
11538
 
11539
  function Context(type, indent, prev) {
11540
    this.type = type;
11541
    this.indent = indent;
11542
    this.prev = prev;
11543
  }
11544
 
11545
  function pushContext(state, stream, type, indent) {
11546
    state.context = new Context(type, stream.indentation() + (indent === false ? 0 : indentUnit), state.context);
11547
    return type;
11548
  }
11549
 
11550
  function popContext(state) {
11551
    if (state.context.prev)
11552
      state.context = state.context.prev;
11553
    return state.context.type;
11554
  }
11555
 
11556
  function pass(type, stream, state) {
11557
    return states[state.context.type](type, stream, state);
11558
  }
11559
  function popAndPass(type, stream, state, n) {
11560
    for (var i = n || 1; i > 0; i--)
11561
      state.context = state.context.prev;
11562
    return pass(type, stream, state);
11563
  }
11564
 
11565
  // Parser
11566
 
11567
  function wordAsValue(stream) {
11568
    var word = stream.current().toLowerCase();
11569
    if (valueKeywords.hasOwnProperty(word))
11570
      override = "atom";
11571
    else if (colorKeywords.hasOwnProperty(word))
11572
      override = "keyword";
11573
    else
11574
      override = "variable";
11575
  }
11576
 
11577
  var states = {};
11578
 
11579
  states.top = function(type, stream, state) {
11580
    if (type == "{") {
11581
      return pushContext(state, stream, "block");
11582
    } else if (type == "}" && state.context.prev) {
11583
      return popContext(state);
11584
    } else if (supportsAtComponent && /@component/i.test(type)) {
11585
      return pushContext(state, stream, "atComponentBlock");
11586
    } else if (/^@(-moz-)?document$/i.test(type)) {
11587
      return pushContext(state, stream, "documentTypes");
11588
    } else if (/^@(media|supports|(-moz-)?document|import)$/i.test(type)) {
11589
      return pushContext(state, stream, "atBlock");
11590
    } else if (/^@(font-face|counter-style)/i.test(type)) {
11591
      state.stateArg = type;
11592
      return "restricted_atBlock_before";
11593
    } else if (/^@(-(moz|ms|o|webkit)-)?keyframes$/i.test(type)) {
11594
      return "keyframes";
11595
    } else if (type && type.charAt(0) == "@") {
11596
      return pushContext(state, stream, "at");
11597
    } else if (type == "hash") {
11598
      override = "builtin";
11599
    } else if (type == "word") {
11600
      override = "tag";
11601
    } else if (type == "variable-definition") {
11602
      return "maybeprop";
11603
    } else if (type == "interpolation") {
11604
      return pushContext(state, stream, "interpolation");
11605
    } else if (type == ":") {
11606
      return "pseudo";
11607
    } else if (allowNested && type == "(") {
11608
      return pushContext(state, stream, "parens");
11609
    }
11610
    return state.context.type;
11611
  };
11612
 
11613
  states.block = function(type, stream, state) {
11614
    if (type == "word") {
11615
      var word = stream.current().toLowerCase();
11616
      if (propertyKeywords.hasOwnProperty(word)) {
11617
        override = "property";
11618
        return "maybeprop";
11619
      } else if (nonStandardPropertyKeywords.hasOwnProperty(word)) {
11620
        override = highlightNonStandardPropertyKeywords ? "string-2" : "property";
11621
        return "maybeprop";
11622
      } else if (allowNested) {
11623
        override = stream.match(/^\s*:(?:\s|$)/, false) ? "property" : "tag";
11624
        return "block";
11625
      } else {
11626
        override += " error";
11627
        return "maybeprop";
11628
      }
11629
    } else if (type == "meta") {
11630
      return "block";
11631
    } else if (!allowNested && (type == "hash" || type == "qualifier")) {
11632
      override = "error";
11633
      return "block";
11634
    } else {
11635
      return states.top(type, stream, state);
11636
    }
11637
  };
11638
 
11639
  states.maybeprop = function(type, stream, state) {
11640
    if (type == ":") return pushContext(state, stream, "prop");
11641
    return pass(type, stream, state);
11642
  };
11643
 
11644
  states.prop = function(type, stream, state) {
11645
    if (type == ";") return popContext(state);
11646
    if (type == "{" && allowNested) return pushContext(state, stream, "propBlock");
11647
    if (type == "}" || type == "{") return popAndPass(type, stream, state);
11648
    if (type == "(") return pushContext(state, stream, "parens");
11649
 
11650
    if (type == "hash" && !/^#([0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(stream.current())) {
11651
      override += " error";
11652
    } else if (type == "word") {
11653
      wordAsValue(stream);
11654
    } else if (type == "interpolation") {
11655
      return pushContext(state, stream, "interpolation");
11656
    }
11657
    return "prop";
11658
  };
11659
 
11660
  states.propBlock = function(type, _stream, state) {
11661
    if (type == "}") return popContext(state);
11662
    if (type == "word") { override = "property"; return "maybeprop"; }
11663
    return state.context.type;
11664
  };
11665
 
11666
  states.parens = function(type, stream, state) {
11667
    if (type == "{" || type == "}") return popAndPass(type, stream, state);
11668
    if (type == ")") return popContext(state);
11669
    if (type == "(") return pushContext(state, stream, "parens");
11670
    if (type == "interpolation") return pushContext(state, stream, "interpolation");
11671
    if (type == "word") wordAsValue(stream);
11672
    return "parens";
11673
  };
11674
 
11675
  states.pseudo = function(type, stream, state) {
11676
    if (type == "meta") return "pseudo";
11677
 
11678
    if (type == "word") {
11679
      override = "variable-3";
11680
      return state.context.type;
11681
    }
11682
    return pass(type, stream, state);
11683
  };
11684
 
11685
  states.documentTypes = function(type, stream, state) {
11686
    if (type == "word" && documentTypes.hasOwnProperty(stream.current())) {
11687
      override = "tag";
11688
      return state.context.type;
11689
    } else {
11690
      return states.atBlock(type, stream, state);
11691
    }
11692
  };
11693
 
11694
  states.atBlock = function(type, stream, state) {
11695
    if (type == "(") return pushContext(state, stream, "atBlock_parens");
11696
    if (type == "}" || type == ";") return popAndPass(type, stream, state);
11697
    if (type == "{") return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top");
11698
 
11699
    if (type == "interpolation") return pushContext(state, stream, "interpolation");
11700
 
11701
    if (type == "word") {
11702
      var word = stream.current().toLowerCase();
11703
      if (word == "only" || word == "not" || word == "and" || word == "or")
11704
        override = "keyword";
11705
      else if (mediaTypes.hasOwnProperty(word))
11706
        override = "attribute";
11707
      else if (mediaFeatures.hasOwnProperty(word))
11708
        override = "property";
11709
      else if (mediaValueKeywords.hasOwnProperty(word))
11710
        override = "keyword";
11711
      else if (propertyKeywords.hasOwnProperty(word))
11712
        override = "property";
11713
      else if (nonStandardPropertyKeywords.hasOwnProperty(word))
11714
        override = highlightNonStandardPropertyKeywords ? "string-2" : "property";
11715
      else if (valueKeywords.hasOwnProperty(word))
11716
        override = "atom";
11717
      else if (colorKeywords.hasOwnProperty(word))
11718
        override = "keyword";
11719
      else
11720
        override = "error";
11721
    }
11722
    return state.context.type;
11723
  };
11724
 
11725
  states.atComponentBlock = function(type, stream, state) {
11726
    if (type == "}")
11727
      return popAndPass(type, stream, state);
11728
    if (type == "{")
11729
      return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top", false);
11730
    if (type == "word")
11731
      override = "error";
11732
    return state.context.type;
11733
  };
11734
 
11735
  states.atBlock_parens = function(type, stream, state) {
11736
    if (type == ")") return popContext(state);
11737
    if (type == "{" || type == "}") return popAndPass(type, stream, state, 2);
11738
    return states.atBlock(type, stream, state);
11739
  };
11740
 
11741
  states.restricted_atBlock_before = function(type, stream, state) {
11742
    if (type == "{")
11743
      return pushContext(state, stream, "restricted_atBlock");
11744
    if (type == "word" && state.stateArg == "@counter-style") {
11745
      override = "variable";
11746
      return "restricted_atBlock_before";
11747
    }
11748
    return pass(type, stream, state);
11749
  };
11750
 
11751
  states.restricted_atBlock = function(type, stream, state) {
11752
    if (type == "}") {
11753
      state.stateArg = null;
11754
      return popContext(state);
11755
    }
11756
    if (type == "word") {
11757
      if ((state.stateArg == "@font-face" && !fontProperties.hasOwnProperty(stream.current().toLowerCase())) ||
11758
          (state.stateArg == "@counter-style" && !counterDescriptors.hasOwnProperty(stream.current().toLowerCase())))
11759
        override = "error";
11760
      else
11761
        override = "property";
11762
      return "maybeprop";
11763
    }
11764
    return "restricted_atBlock";
11765
  };
11766
 
11767
  states.keyframes = function(type, stream, state) {
11768
    if (type == "word") { override = "variable"; return "keyframes"; }
11769
    if (type == "{") return pushContext(state, stream, "top");
11770
    return pass(type, stream, state);
11771
  };
11772
 
11773
  states.at = function(type, stream, state) {
11774
    if (type == ";") return popContext(state);
11775
    if (type == "{" || type == "}") return popAndPass(type, stream, state);
11776
    if (type == "word") override = "tag";
11777
    else if (type == "hash") override = "builtin";
11778
    return "at";
11779
  };
11780
 
11781
  states.interpolation = function(type, stream, state) {
11782
    if (type == "}") return popContext(state);
11783
    if (type == "{" || type == ";") return popAndPass(type, stream, state);
11784
    if (type == "word") override = "variable";
11785
    else if (type != "variable" && type != "(" && type != ")") override = "error";
11786
    return "interpolation";
11787
  };
11788
 
11789
  return {
11790
    startState: function(base) {
11791
      return {tokenize: null,
11792
              state: inline ? "block" : "top",
11793
              stateArg: null,
11794
              context: new Context(inline ? "block" : "top", base || 0, null)};
11795
    },
11796
 
11797
    token: function(stream, state) {
11798
      if (!state.tokenize && stream.eatSpace()) return null;
11799
      var style = (state.tokenize || tokenBase)(stream, state);
11800
      if (style && typeof style == "object") {
11801
        type = style[1];
11802
        style = style[0];
11803
      }
11804
      override = style;
11805
      if (type != "comment")
11806
        state.state = states[state.state](type, stream, state);
11807
      return override;
11808
    },
11809
 
11810
    indent: function(state, textAfter) {
11811
      var cx = state.context, ch = textAfter && textAfter.charAt(0);
11812
      var indent = cx.indent;
11813
      if (cx.type == "prop" && (ch == "}" || ch == ")")) cx = cx.prev;
11814
      if (cx.prev) {
11815
        if (ch == "}" && (cx.type == "block" || cx.type == "top" ||
11816
                          cx.type == "interpolation" || cx.type == "restricted_atBlock")) {
11817
          // Resume indentation from parent context.
11818
          cx = cx.prev;
11819
          indent = cx.indent;
11820
        } else if (ch == ")" && (cx.type == "parens" || cx.type == "atBlock_parens") ||
11821
            ch == "{" && (cx.type == "at" || cx.type == "atBlock")) {
11822
          // Dedent relative to current context.
11823
          indent = Math.max(0, cx.indent - indentUnit);
11824
        }
11825
      }
11826
      return indent;
11827
    },
11828
 
11829
    electricChars: "}",
11830
    blockCommentStart: "/*",
11831
    blockCommentEnd: "*/",
11832
    blockCommentContinue: " * ",
11833
    lineComment: lineComment,
11834
    fold: "brace"
11835
  };
11836
});
11837
 
11838
  function keySet(array) {
11839
    var keys = {};
11840
    for (var i = 0; i < array.length; ++i) {
11841
      keys[array[i].toLowerCase()] = true;
11842
    }
11843
    return keys;
11844
  }
11845
 
11846
  var documentTypes_ = [
11847
    "domain", "regexp", "url", "url-prefix"
11848
  ], documentTypes = keySet(documentTypes_);
11849
 
11850
  var mediaTypes_ = [
11851
    "all", "aural", "braille", "handheld", "print", "projection", "screen",
11852
    "tty", "tv", "embossed"
11853
  ], mediaTypes = keySet(mediaTypes_);
11854
 
11855
  var mediaFeatures_ = [
11856
    "width", "min-width", "max-width", "height", "min-height", "max-height",
11857
    "device-width", "min-device-width", "max-device-width", "device-height",
11858
    "min-device-height", "max-device-height", "aspect-ratio",
11859
    "min-aspect-ratio", "max-aspect-ratio", "device-aspect-ratio",
11860
    "min-device-aspect-ratio", "max-device-aspect-ratio", "color", "min-color",
11861
    "max-color", "color-index", "min-color-index", "max-color-index",
11862
    "monochrome", "min-monochrome", "max-monochrome", "resolution",
11863
    "min-resolution", "max-resolution", "scan", "grid", "orientation",
11864
    "device-pixel-ratio", "min-device-pixel-ratio", "max-device-pixel-ratio",
11865
    "pointer", "any-pointer", "hover", "any-hover", "prefers-color-scheme",
11866
    "dynamic-range", "video-dynamic-range"
11867
  ], mediaFeatures = keySet(mediaFeatures_);
11868
 
11869
  var mediaValueKeywords_ = [
11870
    "landscape", "portrait", "none", "coarse", "fine", "on-demand", "hover",
11871
    "interlace", "progressive",
11872
    "dark", "light",
11873
    "standard", "high"
11874
  ], mediaValueKeywords = keySet(mediaValueKeywords_);
11875
 
11876
  var propertyKeywords_ = [
11877
    "align-content", "align-items", "align-self", "alignment-adjust",
11878
    "alignment-baseline", "all", "anchor-point", "animation", "animation-delay",
11879
    "animation-direction", "animation-duration", "animation-fill-mode",
11880
    "animation-iteration-count", "animation-name", "animation-play-state",
11881
    "animation-timing-function", "appearance", "azimuth", "backdrop-filter",
11882
    "backface-visibility", "background", "background-attachment",
11883
    "background-blend-mode", "background-clip", "background-color",
11884
    "background-image", "background-origin", "background-position",
11885
    "background-position-x", "background-position-y", "background-repeat",
11886
    "background-size", "baseline-shift", "binding", "bleed", "block-size",
11887
    "bookmark-label", "bookmark-level", "bookmark-state", "bookmark-target",
11888
    "border", "border-bottom", "border-bottom-color", "border-bottom-left-radius",
11889
    "border-bottom-right-radius", "border-bottom-style", "border-bottom-width",
11890
    "border-collapse", "border-color", "border-image", "border-image-outset",
11891
    "border-image-repeat", "border-image-slice", "border-image-source",
11892
    "border-image-width", "border-left", "border-left-color", "border-left-style",
11893
    "border-left-width", "border-radius", "border-right", "border-right-color",
11894
    "border-right-style", "border-right-width", "border-spacing", "border-style",
11895
    "border-top", "border-top-color", "border-top-left-radius",
11896
    "border-top-right-radius", "border-top-style", "border-top-width",
11897
    "border-width", "bottom", "box-decoration-break", "box-shadow", "box-sizing",
11898
    "break-after", "break-before", "break-inside", "caption-side", "caret-color",
11899
    "clear", "clip", "color", "color-profile", "column-count", "column-fill",
11900
    "column-gap", "column-rule", "column-rule-color", "column-rule-style",
11901
    "column-rule-width", "column-span", "column-width", "columns", "contain",
11902
    "content", "counter-increment", "counter-reset", "crop", "cue", "cue-after",
11903
    "cue-before", "cursor", "direction", "display", "dominant-baseline",
11904
    "drop-initial-after-adjust", "drop-initial-after-align",
11905
    "drop-initial-before-adjust", "drop-initial-before-align", "drop-initial-size",
11906
    "drop-initial-value", "elevation", "empty-cells", "fit", "fit-content", "fit-position",
11907
    "flex", "flex-basis", "flex-direction", "flex-flow", "flex-grow",
11908
    "flex-shrink", "flex-wrap", "float", "float-offset", "flow-from", "flow-into",
11909
    "font", "font-family", "font-feature-settings", "font-kerning",
11910
    "font-language-override", "font-optical-sizing", "font-size",
11911
    "font-size-adjust", "font-stretch", "font-style", "font-synthesis",
11912
    "font-variant", "font-variant-alternates", "font-variant-caps",
11913
    "font-variant-east-asian", "font-variant-ligatures", "font-variant-numeric",
11914
    "font-variant-position", "font-variation-settings", "font-weight", "gap",
11915
    "grid", "grid-area", "grid-auto-columns", "grid-auto-flow", "grid-auto-rows",
11916
    "grid-column", "grid-column-end", "grid-column-gap", "grid-column-start",
11917
    "grid-gap", "grid-row", "grid-row-end", "grid-row-gap", "grid-row-start",
11918
    "grid-template", "grid-template-areas", "grid-template-columns",
11919
    "grid-template-rows", "hanging-punctuation", "height", "hyphens", "icon",
11920
    "image-orientation", "image-rendering", "image-resolution", "inline-box-align",
11921
    "inset", "inset-block", "inset-block-end", "inset-block-start", "inset-inline",
11922
    "inset-inline-end", "inset-inline-start", "isolation", "justify-content",
11923
    "justify-items", "justify-self", "left", "letter-spacing", "line-break",
11924
    "line-height", "line-height-step", "line-stacking", "line-stacking-ruby",
11925
    "line-stacking-shift", "line-stacking-strategy", "list-style",
11926
    "list-style-image", "list-style-position", "list-style-type", "margin",
11927
    "margin-bottom", "margin-left", "margin-right", "margin-top", "marks",
11928
    "marquee-direction", "marquee-loop", "marquee-play-count", "marquee-speed",
11929
    "marquee-style", "mask-clip", "mask-composite", "mask-image", "mask-mode",
11930
    "mask-origin", "mask-position", "mask-repeat", "mask-size","mask-type",
11931
    "max-block-size", "max-height", "max-inline-size",
11932
    "max-width", "min-block-size", "min-height", "min-inline-size", "min-width",
11933
    "mix-blend-mode", "move-to", "nav-down", "nav-index", "nav-left", "nav-right",
11934
    "nav-up", "object-fit", "object-position", "offset", "offset-anchor",
11935
    "offset-distance", "offset-path", "offset-position", "offset-rotate",
11936
    "opacity", "order", "orphans", "outline", "outline-color", "outline-offset",
11937
    "outline-style", "outline-width", "overflow", "overflow-style",
11938
    "overflow-wrap", "overflow-x", "overflow-y", "padding", "padding-bottom",
11939
    "padding-left", "padding-right", "padding-top", "page", "page-break-after",
11940
    "page-break-before", "page-break-inside", "page-policy", "pause",
11941
    "pause-after", "pause-before", "perspective", "perspective-origin", "pitch",
11942
    "pitch-range", "place-content", "place-items", "place-self", "play-during",
11943
    "position", "presentation-level", "punctuation-trim", "quotes",
11944
    "region-break-after", "region-break-before", "region-break-inside",
11945
    "region-fragment", "rendering-intent", "resize", "rest", "rest-after",
11946
    "rest-before", "richness", "right", "rotate", "rotation", "rotation-point",
11947
    "row-gap", "ruby-align", "ruby-overhang", "ruby-position", "ruby-span",
11948
    "scale", "scroll-behavior", "scroll-margin", "scroll-margin-block",
11949
    "scroll-margin-block-end", "scroll-margin-block-start", "scroll-margin-bottom",
11950
    "scroll-margin-inline", "scroll-margin-inline-end",
11951
    "scroll-margin-inline-start", "scroll-margin-left", "scroll-margin-right",
11952
    "scroll-margin-top", "scroll-padding", "scroll-padding-block",
11953
    "scroll-padding-block-end", "scroll-padding-block-start",
11954
    "scroll-padding-bottom", "scroll-padding-inline", "scroll-padding-inline-end",
11955
    "scroll-padding-inline-start", "scroll-padding-left", "scroll-padding-right",
11956
    "scroll-padding-top", "scroll-snap-align", "scroll-snap-type",
11957
    "shape-image-threshold", "shape-inside", "shape-margin", "shape-outside",
11958
    "size", "speak", "speak-as", "speak-header", "speak-numeral",
11959
    "speak-punctuation", "speech-rate", "stress", "string-set", "tab-size",
11960
    "table-layout", "target", "target-name", "target-new", "target-position",
11961
    "text-align", "text-align-last", "text-combine-upright", "text-decoration",
11962
    "text-decoration-color", "text-decoration-line", "text-decoration-skip",
11963
    "text-decoration-skip-ink", "text-decoration-style", "text-emphasis",
11964
    "text-emphasis-color", "text-emphasis-position", "text-emphasis-style",
11965
    "text-height", "text-indent", "text-justify", "text-orientation",
11966
    "text-outline", "text-overflow", "text-rendering", "text-shadow",
11967
    "text-size-adjust", "text-space-collapse", "text-transform",
11968
    "text-underline-position", "text-wrap", "top", "touch-action", "transform", "transform-origin",
11969
    "transform-style", "transition", "transition-delay", "transition-duration",
11970
    "transition-property", "transition-timing-function", "translate",
11971
    "unicode-bidi", "user-select", "vertical-align", "visibility", "voice-balance",
11972
    "voice-duration", "voice-family", "voice-pitch", "voice-range", "voice-rate",
11973
    "voice-stress", "voice-volume", "volume", "white-space", "widows", "width",
11974
    "will-change", "word-break", "word-spacing", "word-wrap", "writing-mode", "z-index",
11975
    // SVG-specific
11976
    "clip-path", "clip-rule", "mask", "enable-background", "filter", "flood-color",
11977
    "flood-opacity", "lighting-color", "stop-color", "stop-opacity", "pointer-events",
11978
    "color-interpolation", "color-interpolation-filters",
11979
    "color-rendering", "fill", "fill-opacity", "fill-rule", "image-rendering",
11980
    "marker", "marker-end", "marker-mid", "marker-start", "paint-order", "shape-rendering", "stroke",
11981
    "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin",
11982
    "stroke-miterlimit", "stroke-opacity", "stroke-width", "text-rendering",
11983
    "baseline-shift", "dominant-baseline", "glyph-orientation-horizontal",
11984
    "glyph-orientation-vertical", "text-anchor", "writing-mode",
11985
  ], propertyKeywords = keySet(propertyKeywords_);
11986
 
11987
  var nonStandardPropertyKeywords_ = [
11988
    "accent-color", "aspect-ratio", "border-block", "border-block-color", "border-block-end",
11989
    "border-block-end-color", "border-block-end-style", "border-block-end-width",
11990
    "border-block-start", "border-block-start-color", "border-block-start-style",
11991
    "border-block-start-width", "border-block-style", "border-block-width",
11992
    "border-inline", "border-inline-color", "border-inline-end",
11993
    "border-inline-end-color", "border-inline-end-style",
11994
    "border-inline-end-width", "border-inline-start", "border-inline-start-color",
11995
    "border-inline-start-style", "border-inline-start-width",
11996
    "border-inline-style", "border-inline-width", "content-visibility", "margin-block",
11997
    "margin-block-end", "margin-block-start", "margin-inline", "margin-inline-end",
11998
    "margin-inline-start", "overflow-anchor", "overscroll-behavior", "padding-block", "padding-block-end",
11999
    "padding-block-start", "padding-inline", "padding-inline-end",
12000
    "padding-inline-start", "scroll-snap-stop", "scrollbar-3d-light-color",
12001
    "scrollbar-arrow-color", "scrollbar-base-color", "scrollbar-dark-shadow-color",
12002
    "scrollbar-face-color", "scrollbar-highlight-color", "scrollbar-shadow-color",
12003
    "scrollbar-track-color", "searchfield-cancel-button", "searchfield-decoration",
12004
    "searchfield-results-button", "searchfield-results-decoration", "shape-inside", "zoom"
12005
  ], nonStandardPropertyKeywords = keySet(nonStandardPropertyKeywords_);
12006
 
12007
  var fontProperties_ = [
12008
    "font-display", "font-family", "src", "unicode-range", "font-variant",
12009
     "font-feature-settings", "font-stretch", "font-weight", "font-style"
12010
  ], fontProperties = keySet(fontProperties_);
12011
 
12012
  var counterDescriptors_ = [
12013
    "additive-symbols", "fallback", "negative", "pad", "prefix", "range",
12014
    "speak-as", "suffix", "symbols", "system"
12015
  ], counterDescriptors = keySet(counterDescriptors_);
12016
 
12017
  var colorKeywords_ = [
12018
    "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige",
12019
    "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown",
12020
    "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue",
12021
    "cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod",
12022
    "darkgray", "darkgreen", "darkgrey", "darkkhaki", "darkmagenta", "darkolivegreen",
12023
    "darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen",
12024
    "darkslateblue", "darkslategray", "darkslategrey", "darkturquoise", "darkviolet",
12025
    "deeppink", "deepskyblue", "dimgray", "dimgrey", "dodgerblue", "firebrick",
12026
    "floralwhite", "forestgreen", "fuchsia", "gainsboro", "ghostwhite",
12027
    "gold", "goldenrod", "gray", "grey", "green", "greenyellow", "honeydew",
12028
    "hotpink", "indianred", "indigo", "ivory", "khaki", "lavender",
12029
    "lavenderblush", "lawngreen", "lemonchiffon", "lightblue", "lightcoral",
12030
    "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightgrey", "lightpink",
12031
    "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray", "lightslategrey",
12032
    "lightsteelblue", "lightyellow", "lime", "limegreen", "linen", "magenta",
12033
    "maroon", "mediumaquamarine", "mediumblue", "mediumorchid", "mediumpurple",
12034
    "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise",
12035
    "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin",
12036
    "navajowhite", "navy", "oldlace", "olive", "olivedrab", "orange", "orangered",
12037
    "orchid", "palegoldenrod", "palegreen", "paleturquoise", "palevioletred",
12038
    "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue",
12039
    "purple", "rebeccapurple", "red", "rosybrown", "royalblue", "saddlebrown",
12040
    "salmon", "sandybrown", "seagreen", "seashell", "sienna", "silver", "skyblue",
12041
    "slateblue", "slategray", "slategrey", "snow", "springgreen", "steelblue", "tan",
12042
    "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white",
12043
    "whitesmoke", "yellow", "yellowgreen"
12044
  ], colorKeywords = keySet(colorKeywords_);
12045
 
12046
  var valueKeywords_ = [
12047
    "above", "absolute", "activeborder", "additive", "activecaption", "afar",
12048
    "after-white-space", "ahead", "alias", "all", "all-scroll", "alphabetic", "alternate",
12049
    "always", "amharic", "amharic-abegede", "antialiased", "appworkspace",
12050
    "arabic-indic", "armenian", "asterisks", "attr", "auto", "auto-flow", "avoid", "avoid-column", "avoid-page",
12051
    "avoid-region", "axis-pan", "background", "backwards", "baseline", "below", "bidi-override", "binary",
12052
    "bengali", "blink", "block", "block-axis", "blur", "bold", "bolder", "border", "border-box",
12053
    "both", "bottom", "break", "break-all", "break-word", "brightness", "bullets", "button",
12054
    "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "calc", "cambodian",
12055
    "capitalize", "caps-lock-indicator", "caption", "captiontext", "caret",
12056
    "cell", "center", "checkbox", "circle", "cjk-decimal", "cjk-earthly-branch",
12057
    "cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote",
12058
    "col-resize", "collapse", "color", "color-burn", "color-dodge", "column", "column-reverse",
12059
    "compact", "condensed", "conic-gradient", "contain", "content", "contents",
12060
    "content-box", "context-menu", "continuous", "contrast", "copy", "counter", "counters", "cover", "crop",
12061
    "cross", "crosshair", "cubic-bezier", "currentcolor", "cursive", "cyclic", "darken", "dashed", "decimal",
12062
    "decimal-leading-zero", "default", "default-button", "dense", "destination-atop",
12063
    "destination-in", "destination-out", "destination-over", "devanagari", "difference",
12064
    "disc", "discard", "disclosure-closed", "disclosure-open", "document",
12065
    "dot-dash", "dot-dot-dash",
12066
    "dotted", "double", "down", "drop-shadow", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out",
12067
    "element", "ellipse", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede",
12068
    "ethiopic-abegede-am-et", "ethiopic-abegede-gez", "ethiopic-abegede-ti-er",
12069
    "ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er",
12070
    "ethiopic-halehame-aa-et", "ethiopic-halehame-am-et",
12071
    "ethiopic-halehame-gez", "ethiopic-halehame-om-et",
12072
    "ethiopic-halehame-sid-et", "ethiopic-halehame-so-et",
12073
    "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et", "ethiopic-halehame-tig",
12074
    "ethiopic-numeric", "ew-resize", "exclusion", "expanded", "extends", "extra-condensed",
12075
    "extra-expanded", "fantasy", "fast", "fill", "fill-box", "fixed", "flat", "flex", "flex-end", "flex-start", "footnotes",
12076
    "forwards", "from", "geometricPrecision", "georgian", "grayscale", "graytext", "grid", "groove",
12077
    "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hard-light", "hebrew",
12078
    "help", "hidden", "hide", "higher", "highlight", "highlighttext",
12079
    "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "hue", "hue-rotate", "icon", "ignore",
12080
    "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite",
12081
    "infobackground", "infotext", "inherit", "initial", "inline", "inline-axis",
12082
    "inline-block", "inline-flex", "inline-grid", "inline-table", "inset", "inside", "intrinsic", "invert",
12083
    "italic", "japanese-formal", "japanese-informal", "justify", "kannada",
12084
    "katakana", "katakana-iroha", "keep-all", "khmer",
12085
    "korean-hangul-formal", "korean-hanja-formal", "korean-hanja-informal",
12086
    "landscape", "lao", "large", "larger", "left", "level", "lighter", "lighten",
12087
    "line-through", "linear", "linear-gradient", "lines", "list-item", "listbox", "listitem",
12088
    "local", "logical", "loud", "lower", "lower-alpha", "lower-armenian",
12089
    "lower-greek", "lower-hexadecimal", "lower-latin", "lower-norwegian",
12090
    "lower-roman", "lowercase", "ltr", "luminosity", "malayalam", "manipulation", "match", "matrix", "matrix3d",
12091
    "media-play-button", "media-slider", "media-sliderthumb",
12092
    "media-volume-slider", "media-volume-sliderthumb", "medium",
12093
    "menu", "menulist", "menulist-button",
12094
    "menutext", "message-box", "middle", "min-intrinsic",
12095
    "mix", "mongolian", "monospace", "move", "multiple", "multiple_mask_images", "multiply", "myanmar", "n-resize",
12096
    "narrower", "ne-resize", "nesw-resize", "no-close-quote", "no-drop",
12097
    "no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap",
12098
    "ns-resize", "numbers", "numeric", "nw-resize", "nwse-resize", "oblique", "octal", "opacity", "open-quote",
12099
    "optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset",
12100
    "outside", "outside-shape", "overlay", "overline", "padding", "padding-box",
12101
    "painted", "page", "paused", "persian", "perspective", "pinch-zoom", "plus-darker", "plus-lighter",
12102
    "pointer", "polygon", "portrait", "pre", "pre-line", "pre-wrap", "preserve-3d",
12103
    "progress", "push-button", "radial-gradient", "radio", "read-only",
12104
    "read-write", "read-write-plaintext-only", "rectangle", "region",
12105
    "relative", "repeat", "repeating-linear-gradient", "repeating-radial-gradient",
12106
    "repeating-conic-gradient", "repeat-x", "repeat-y", "reset", "reverse",
12107
    "rgb", "rgba", "ridge", "right", "rotate", "rotate3d", "rotateX", "rotateY",
12108
    "rotateZ", "round", "row", "row-resize", "row-reverse", "rtl", "run-in", "running",
12109
    "s-resize", "sans-serif", "saturate", "saturation", "scale", "scale3d", "scaleX", "scaleY", "scaleZ", "screen",
12110
    "scroll", "scrollbar", "scroll-position", "se-resize", "searchfield",
12111
    "searchfield-cancel-button", "searchfield-decoration",
12112
    "searchfield-results-button", "searchfield-results-decoration", "self-start", "self-end",
12113
    "semi-condensed", "semi-expanded", "separate", "sepia", "serif", "show", "sidama",
12114
    "simp-chinese-formal", "simp-chinese-informal", "single",
12115
    "skew", "skewX", "skewY", "skip-white-space", "slide", "slider-horizontal",
12116
    "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow",
12117
    "small", "small-caps", "small-caption", "smaller", "soft-light", "solid", "somali",
12118
    "source-atop", "source-in", "source-out", "source-over", "space", "space-around", "space-between", "space-evenly", "spell-out", "square",
12119
    "square-button", "start", "static", "status-bar", "stretch", "stroke", "stroke-box", "sub",
12120
    "subpixel-antialiased", "svg_masks", "super", "sw-resize", "symbolic", "symbols", "system-ui", "table",
12121
    "table-caption", "table-cell", "table-column", "table-column-group",
12122
    "table-footer-group", "table-header-group", "table-row", "table-row-group",
12123
    "tamil",
12124
    "telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai",
12125
    "thick", "thin", "threeddarkshadow", "threedface", "threedhighlight",
12126
    "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er",
12127
    "tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top",
12128
    "trad-chinese-formal", "trad-chinese-informal", "transform",
12129
    "translate", "translate3d", "translateX", "translateY", "translateZ",
12130
    "transparent", "ultra-condensed", "ultra-expanded", "underline", "unidirectional-pan", "unset", "up",
12131
    "upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal",
12132
    "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url",
12133
    "var", "vertical", "vertical-text", "view-box", "visible", "visibleFill", "visiblePainted",
12134
    "visibleStroke", "visual", "w-resize", "wait", "wave", "wider",
12135
    "window", "windowframe", "windowtext", "words", "wrap", "wrap-reverse", "x-large", "x-small", "xor",
12136
    "xx-large", "xx-small"
12137
  ], valueKeywords = keySet(valueKeywords_);
12138
 
12139
  var allWords = documentTypes_.concat(mediaTypes_).concat(mediaFeatures_).concat(mediaValueKeywords_)
12140
    .concat(propertyKeywords_).concat(nonStandardPropertyKeywords_).concat(colorKeywords_)
12141
    .concat(valueKeywords_);
12142
  CodeMirror.registerHelper("hintWords", "css", allWords);
12143
 
12144
  function tokenCComment(stream, state) {
12145
    var maybeEnd = false, ch;
12146
    while ((ch = stream.next()) != null) {
12147
      if (maybeEnd && ch == "/") {
12148
        state.tokenize = null;
12149
        break;
12150
      }
12151
      maybeEnd = (ch == "*");
12152
    }
12153
    return ["comment", "comment"];
12154
  }
12155
 
12156
  CodeMirror.defineMIME("text/css", {
12157
    documentTypes: documentTypes,
12158
    mediaTypes: mediaTypes,
12159
    mediaFeatures: mediaFeatures,
12160
    mediaValueKeywords: mediaValueKeywords,
12161
    propertyKeywords: propertyKeywords,
12162
    nonStandardPropertyKeywords: nonStandardPropertyKeywords,
12163
    fontProperties: fontProperties,
12164
    counterDescriptors: counterDescriptors,
12165
    colorKeywords: colorKeywords,
12166
    valueKeywords: valueKeywords,
12167
    tokenHooks: {
12168
      "/": function(stream, state) {
12169
        if (!stream.eat("*")) return false;
12170
        state.tokenize = tokenCComment;
12171
        return tokenCComment(stream, state);
12172
      }
12173
    },
12174
    name: "css"
12175
  });
12176
 
12177
  CodeMirror.defineMIME("text/x-scss", {
12178
    mediaTypes: mediaTypes,
12179
    mediaFeatures: mediaFeatures,
12180
    mediaValueKeywords: mediaValueKeywords,
12181
    propertyKeywords: propertyKeywords,
12182
    nonStandardPropertyKeywords: nonStandardPropertyKeywords,
12183
    colorKeywords: colorKeywords,
12184
    valueKeywords: valueKeywords,
12185
    fontProperties: fontProperties,
12186
    allowNested: true,
12187
    lineComment: "//",
12188
    tokenHooks: {
12189
      "/": function(stream, state) {
12190
        if (stream.eat("/")) {
12191
          stream.skipToEnd();
12192
          return ["comment", "comment"];
12193
        } else if (stream.eat("*")) {
12194
          state.tokenize = tokenCComment;
12195
          return tokenCComment(stream, state);
12196
        } else {
12197
          return ["operator", "operator"];
12198
        }
12199
      },
12200
      ":": function(stream) {
12201
        if (stream.match(/^\s*\{/, false))
12202
          return [null, null]
12203
        return false;
12204
      },
12205
      "$": function(stream) {
12206
        stream.match(/^[\w-]+/);
12207
        if (stream.match(/^\s*:/, false))
12208
          return ["variable-2", "variable-definition"];
12209
        return ["variable-2", "variable"];
12210
      },
12211
      "#": function(stream) {
12212
        if (!stream.eat("{")) return false;
12213
        return [null, "interpolation"];
12214
      }
12215
    },
12216
    name: "css",
12217
    helperType: "scss"
12218
  });
12219
 
12220
  CodeMirror.defineMIME("text/x-less", {
12221
    mediaTypes: mediaTypes,
12222
    mediaFeatures: mediaFeatures,
12223
    mediaValueKeywords: mediaValueKeywords,
12224
    propertyKeywords: propertyKeywords,
12225
    nonStandardPropertyKeywords: nonStandardPropertyKeywords,
12226
    colorKeywords: colorKeywords,
12227
    valueKeywords: valueKeywords,
12228
    fontProperties: fontProperties,
12229
    allowNested: true,
12230
    lineComment: "//",
12231
    tokenHooks: {
12232
      "/": function(stream, state) {
12233
        if (stream.eat("/")) {
12234
          stream.skipToEnd();
12235
          return ["comment", "comment"];
12236
        } else if (stream.eat("*")) {
12237
          state.tokenize = tokenCComment;
12238
          return tokenCComment(stream, state);
12239
        } else {
12240
          return ["operator", "operator"];
12241
        }
12242
      },
12243
      "@": function(stream) {
12244
        if (stream.eat("{")) return [null, "interpolation"];
12245
        if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/i, false)) return false;
12246
        stream.eatWhile(/[\w\\\-]/);
12247
        if (stream.match(/^\s*:/, false))
12248
          return ["variable-2", "variable-definition"];
12249
        return ["variable-2", "variable"];
12250
      },
12251
      "&": function() {
12252
        return ["atom", "atom"];
12253
      }
12254
    },
12255
    name: "css",
12256
    helperType: "less"
12257
  });
12258
 
12259
  CodeMirror.defineMIME("text/x-gss", {
12260
    documentTypes: documentTypes,
12261
    mediaTypes: mediaTypes,
12262
    mediaFeatures: mediaFeatures,
12263
    propertyKeywords: propertyKeywords,
12264
    nonStandardPropertyKeywords: nonStandardPropertyKeywords,
12265
    fontProperties: fontProperties,
12266
    counterDescriptors: counterDescriptors,
12267
    colorKeywords: colorKeywords,
12268
    valueKeywords: valueKeywords,
12269
    supportsAtComponent: true,
12270
    tokenHooks: {
12271
      "/": function(stream, state) {
12272
        if (!stream.eat("*")) return false;
12273
        state.tokenize = tokenCComment;
12274
        return tokenCComment(stream, state);
12275
      }
12276
    },
12277
    name: "css",
12278
    helperType: "gss"
12279
  });
12280
 
12281
});
12282
// Actually define CodeMirror in our namespace.
12283
Y.namespace('M.atto_html').CodeMirror = CodeMirror;
12284
 
12285
// Restore the original CodeMirror in case one existed.
12286
window.CodeMirror = _codeMirror;
12287
 
12288
 
12289
}, '@VERSION@', {"requires": ["moodle-atto_html-codemirror-skin"]});