仰望星辰工作室

better-staridc-MNBT

better-staridc-MNBT/ imsetes/codemirror/lib/codemirror.js 392.4 KB · 9,874 行 原始文件
Z zfhsh first commit 2 天前
1// CodeMirror, copyright (c) by Marijn Haverbeke and others
2// Distributed under an MIT license: https://codemirror.net/5/LICENSE
3
4// This is CodeMirror (https://codemirror.net/5), a code editor
5// implemented in JavaScript on top of the browser's DOM.
6//
7// You can find some technical background for some of the code below
8// at http://marijnhaverbeke.nl/blog/#cm-internals .
9
10(function (global, factory) {
11 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
12 typeof define === 'function' && define.amd ? define(factory) :
13 (global = global || self, global.CodeMirror = factory());
14}(this, (function () { 'use strict';
15
16 // Kludges for bugs and behavior differences that can't be feature
17 // detected are enabled based on userAgent etc sniffing.
18 var userAgent = navigator.userAgent;
19 var platform = navigator.platform;
20
21 var gecko = /gecko\/\d/i.test(userAgent);
22 var ie_upto10 = /MSIE \d/.test(userAgent);
23 var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent);
24 var edge = /Edge\/(\d+)/.exec(userAgent);
25 var ie = ie_upto10 || ie_11up || edge;
26 var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1]);
27 var webkit = !edge && /WebKit\//.test(userAgent);
28 var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent);
29 var chrome = !edge && /Chrome\/(\d+)/.exec(userAgent);
30 var chrome_version = chrome && +chrome[1];
31 var presto = /Opera\//.test(userAgent);
32 var safari = /Apple Computer/.test(navigator.vendor);
33 var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent);
34 var phantom = /PhantomJS/.test(userAgent);
35
36 var ios = safari && (/Mobile\/\w+/.test(userAgent) || navigator.maxTouchPoints > 2);
37 var android = /Android/.test(userAgent);
38 // This is woefully incomplete. Suggestions for alternative methods welcome.
39 var mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent);
40 var mac = ios || /Mac/.test(platform);
41 var chromeOS = /\bCrOS\b/.test(userAgent);
42 var windows = /win/i.test(platform);
43
44 var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/);
45 if (presto_version) { presto_version = Number(presto_version[1]); }
46 if (presto_version && presto_version >= 15) { presto = false; webkit = true; }
47 // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
48 var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11));
49 var captureRightClick = gecko || (ie && ie_version >= 9);
50
51 function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") }
52
53 var rmClass = function(node, cls) {
54 var current = node.className;
55 var match = classTest(cls).exec(current);
56 if (match) {
57 var after = current.slice(match.index + match[0].length);
58 node.className = current.slice(0, match.index) + (after ? match[1] + after : "");
59 }
60 };
61
62 function removeChildren(e) {
63 for (var count = e.childNodes.length; count > 0; --count)
64 { e.removeChild(e.firstChild); }
65 return e
66 }
67
68 function removeChildrenAndAdd(parent, e) {
69 return removeChildren(parent).appendChild(e)
70 }
71
72 function elt(tag, content, className, style) {
73 var e = document.createElement(tag);
74 if (className) { e.className = className; }
75 if (style) { e.style.cssText = style; }
76 if (typeof content == "string") { e.appendChild(document.createTextNode(content)); }
77 else if (content) { for (var i = 0; i < content.length; ++i) { e.appendChild(content[i]); } }
78 return e
79 }
80 // wrapper for elt, which removes the elt from the accessibility tree
81 function eltP(tag, content, className, style) {
82 var e = elt(tag, content, className, style);
83 e.setAttribute("role", "presentation");
84 return e
85 }
86
87 var range;
88 if (document.createRange) { range = function(node, start, end, endNode) {
89 var r = document.createRange();
90 r.setEnd(endNode || node, end);
91 r.setStart(node, start);
92 return r
93 }; }
94 else { range = function(node, start, end) {
95 var r = document.body.createTextRange();
96 try { r.moveToElementText(node.parentNode); }
97 catch(e) { return r }
98 r.collapse(true);
99 r.moveEnd("character", end);
100 r.moveStart("character", start);
101 return r
102 }; }
103
104 function contains(parent, child) {
105 if (child.nodeType == 3) // Android browser always returns false when child is a textnode
106 { child = child.parentNode; }
107 if (parent.contains)
108 { return parent.contains(child) }
109 do {
110 if (child.nodeType == 11) { child = child.host; }
111 if (child == parent) { return true }
112 } while (child = child.parentNode)
113 }
114
115 function activeElt(doc) {
116 // IE and Edge may throw an "Unspecified Error" when accessing document.activeElement.
117 // IE < 10 will throw when accessed while the page is loading or in an iframe.
118 // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable.
119 var activeElement;
120 try {
121 activeElement = doc.activeElement;
122 } catch(e) {
123 activeElement = doc.body || null;
124 }
125 while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement)
126 { activeElement = activeElement.shadowRoot.activeElement; }
127 return activeElement
128 }
129
130 function addClass(node, cls) {
131 var current = node.className;
132 if (!classTest(cls).test(current)) { node.className += (current ? " " : "") + cls; }
133 }
134 function joinClasses(a, b) {
135 var as = a.split(" ");
136 for (var i = 0; i < as.length; i++)
137 { if (as[i] && !classTest(as[i]).test(b)) { b += " " + as[i]; } }
138 return b
139 }
140
141 var selectInput = function(node) { node.select(); };
142 if (ios) // Mobile Safari apparently has a bug where select() is broken.
143 { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; }; }
144 else if (ie) // Suppress mysterious IE10 errors
145 { selectInput = function(node) { try { node.select(); } catch(_e) {} }; }
146
147 function doc(cm) { return cm.display.wrapper.ownerDocument }
148
149 function win(cm) { return doc(cm).defaultView }
150
151 function bind(f) {
152 var args = Array.prototype.slice.call(arguments, 1);
153 return function(){return f.apply(null, args)}
154 }
155
156 function copyObj(obj, target, overwrite) {
157 if (!target) { target = {}; }
158 for (var prop in obj)
159 { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
160 { target[prop] = obj[prop]; } }
161 return target
162 }
163
164 // Counts the column offset in a string, taking tabs into account.
165 // Used mostly to find indentation.
166 function countColumn(string, end, tabSize, startIndex, startValue) {
167 if (end == null) {
168 end = string.search(/[^\s\u00a0]/);
169 if (end == -1) { end = string.length; }
170 }
171 for (var i = startIndex || 0, n = startValue || 0;;) {
172 var nextTab = string.indexOf("\t", i);
173 if (nextTab < 0 || nextTab >= end)
174 { return n + (end - i) }
175 n += nextTab - i;
176 n += tabSize - (n % tabSize);
177 i = nextTab + 1;
178 }
179 }
180
181 var Delayed = function() {
182 this.id = null;
183 this.f = null;
184 this.time = 0;
185 this.handler = bind(this.onTimeout, this);
186 };
187 Delayed.prototype.onTimeout = function (self) {
188 self.id = 0;
189 if (self.time <= +new Date) {
190 self.f();
191 } else {
192 setTimeout(self.handler, self.time - +new Date);
193 }
194 };
195 Delayed.prototype.set = function (ms, f) {
196 this.f = f;
197 var time = +new Date + ms;
198 if (!this.id || time < this.time) {
199 clearTimeout(this.id);
200 this.id = setTimeout(this.handler, ms);
201 this.time = time;
202 }
203 };
204
205 function indexOf(array, elt) {
206 for (var i = 0; i < array.length; ++i)
207 { if (array[i] == elt) { return i } }
208 return -1
209 }
210
211 // Number of pixels added to scroller and sizer to hide scrollbar
212 var scrollerGap = 50;
213
214 // Returned or thrown by various protocols to signal 'I'm not
215 // handling this'.
216 var Pass = {toString: function(){return "CodeMirror.Pass"}};
217
218 // Reused option objects for setSelection & friends
219 var sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"};
220
221 // The inverse of countColumn -- find the offset that corresponds to
222 // a particular column.
223 function findColumn(string, goal, tabSize) {
224 for (var pos = 0, col = 0;;) {
225 var nextTab = string.indexOf("\t", pos);
226 if (nextTab == -1) { nextTab = string.length; }
227 var skipped = nextTab - pos;
228 if (nextTab == string.length || col + skipped >= goal)
229 { return pos + Math.min(skipped, goal - col) }
230 col += nextTab - pos;
231 col += tabSize - (col % tabSize);
232 pos = nextTab + 1;
233 if (col >= goal) { return pos }
234 }
235 }
236
237 var spaceStrs = [""];
238 function spaceStr(n) {
239 while (spaceStrs.length <= n)
240 { spaceStrs.push(lst(spaceStrs) + " "); }
241 return spaceStrs[n]
242 }
243
244 function lst(arr) { return arr[arr.length-1] }
245
246 function map(array, f) {
247 var out = [];
248 for (var i = 0; i < array.length; i++) { out[i] = f(array[i], i); }
249 return out
250 }
251
252 function insertSorted(array, value, score) {
253 var pos = 0, priority = score(value);
254 while (pos < array.length && score(array[pos]) <= priority) { pos++; }
255 array.splice(pos, 0, value);
256 }
257
258 function nothing() {}
259
260 function createObj(base, props) {
261 var inst;
262 if (Object.create) {
263 inst = Object.create(base);
264 } else {
265 nothing.prototype = base;
266 inst = new nothing();
267 }
268 if (props) { copyObj(props, inst); }
269 return inst
270 }
271
272 var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
273 function isWordCharBasic(ch) {
274 return /\w/.test(ch) || ch > "\x80" &&
275 (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch))
276 }
277 function isWordChar(ch, helper) {
278 if (!helper) { return isWordCharBasic(ch) }
279 if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) { return true }
280 return helper.test(ch)
281 }
282
283 function isEmpty(obj) {
284 for (var n in obj) { if (obj.hasOwnProperty(n) && obj[n]) { return false } }
285 return true
286 }
287
288 // Extending unicode characters. A series of a non-extending char +
289 // any number of extending chars is treated as a single unit as far
290 // as editing and measuring is concerned. This is not fully correct,
291 // since some scripts/fonts/browsers also treat other configurations
292 // of code points as a group.
293 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]/;
294 function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) }
295
296 // Returns a number from the range [`0`; `str.length`] unless `pos` is outside that range.
297 function skipExtendingChars(str, pos, dir) {
298 while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(pos))) { pos += dir; }
299 return pos
300 }
301
302 // Returns the value from the range [`from`; `to`] that satisfies
303 // `pred` and is closest to `from`. Assumes that at least `to`
304 // satisfies `pred`. Supports `from` being greater than `to`.
305 function findFirst(pred, from, to) {
306 // At any point we are certain `to` satisfies `pred`, don't know
307 // whether `from` does.
308 var dir = from > to ? -1 : 1;
309 for (;;) {
310 if (from == to) { return from }
311 var midF = (from + to) / 2, mid = dir < 0 ? Math.ceil(midF) : Math.floor(midF);
312 if (mid == from) { return pred(mid) ? from : to }
313 if (pred(mid)) { to = mid; }
314 else { from = mid + dir; }
315 }
316 }
317
318 // BIDI HELPERS
319
320 function iterateBidiSections(order, from, to, f) {
321 if (!order) { return f(from, to, "ltr", 0) }
322 var found = false;
323 for (var i = 0; i < order.length; ++i) {
324 var part = order[i];
325 if (part.from < to && part.to > from || from == to && part.to == from) {
326 f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr", i);
327 found = true;
328 }
329 }
330 if (!found) { f(from, to, "ltr"); }
331 }
332
333 var bidiOther = null;
334 function getBidiPartAt(order, ch, sticky) {
335 var found;
336 bidiOther = null;
337 for (var i = 0; i < order.length; ++i) {
338 var cur = order[i];
339 if (cur.from < ch && cur.to > ch) { return i }
340 if (cur.to == ch) {
341 if (cur.from != cur.to && sticky == "before") { found = i; }
342 else { bidiOther = i; }
343 }
344 if (cur.from == ch) {
345 if (cur.from != cur.to && sticky != "before") { found = i; }
346 else { bidiOther = i; }
347 }
348 }
349 return found != null ? found : bidiOther
350 }
351
352 // Bidirectional ordering algorithm
353 // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
354 // that this (partially) implements.
355
356 // One-char codes used for character types:
357 // L (L): Left-to-Right
358 // R (R): Right-to-Left
359 // r (AL): Right-to-Left Arabic
360 // 1 (EN): European Number
361 // + (ES): European Number Separator
362 // % (ET): European Number Terminator
363 // n (AN): Arabic Number
364 // , (CS): Common Number Separator
365 // m (NSM): Non-Spacing Mark
366 // b (BN): Boundary Neutral
367 // s (B): Paragraph Separator
368 // t (S): Segment Separator
369 // w (WS): Whitespace
370 // N (ON): Other Neutrals
371
372 // Returns null if characters are ordered as they appear
373 // (left-to-right), or an array of sections ({from, to, level}
374 // objects) in the order in which they occur visually.
375 var bidiOrdering = (function() {
376 // Character types for codepoints 0 to 0xff
377 var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN";
378 // Character types for codepoints 0x600 to 0x6f9
379 var arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111";
380 function charType(code) {
381 if (code <= 0xf7) { return lowTypes.charAt(code) }
382 else if (0x590 <= code && code <= 0x5f4) { return "R" }
383 else if (0x600 <= code && code <= 0x6f9) { return arabicTypes.charAt(code - 0x600) }
384 else if (0x6ee <= code && code <= 0x8ac) { return "r" }
385 else if (0x2000 <= code && code <= 0x200b) { return "w" }
386 else if (code == 0x200c) { return "b" }
387 else { return "L" }
388 }
389
390 var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
391 var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
392
393 function BidiSpan(level, from, to) {
394 this.level = level;
395 this.from = from; this.to = to;
396 }
397
398 return function(str, direction) {
399 var outerType = direction == "ltr" ? "L" : "R";
400
401 if (str.length == 0 || direction == "ltr" && !bidiRE.test(str)) { return false }
402 var len = str.length, types = [];
403 for (var i = 0; i < len; ++i)
404 { types.push(charType(str.charCodeAt(i))); }
405
406 // W1. Examine each non-spacing mark (NSM) in the level run, and
407 // change the type of the NSM to the type of the previous
408 // character. If the NSM is at the start of the level run, it will
409 // get the type of sor.
410 for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) {
411 var type = types[i$1];
412 if (type == "m") { types[i$1] = prev; }
413 else { prev = type; }
414 }
415
416 // W2. Search backwards from each instance of a European number
417 // until the first strong type (R, L, AL, or sor) is found. If an
418 // AL is found, change the type of the European number to Arabic
419 // number.
420 // W3. Change all ALs to R.
421 for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) {
422 var type$1 = types[i$2];
423 if (type$1 == "1" && cur == "r") { types[i$2] = "n"; }
424 else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == "r") { types[i$2] = "R"; } }
425 }
426
427 // W4. A single European separator between two European numbers
428 // changes to a European number. A single common separator between
429 // two numbers of the same type changes to that type.
430 for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) {
431 var type$2 = types[i$3];
432 if (type$2 == "+" && prev$1 == "1" && types[i$3+1] == "1") { types[i$3] = "1"; }
433 else if (type$2 == "," && prev$1 == types[i$3+1] &&
434 (prev$1 == "1" || prev$1 == "n")) { types[i$3] = prev$1; }
435 prev$1 = type$2;
436 }
437
438 // W5. A sequence of European terminators adjacent to European
439 // numbers changes to all European numbers.
440 // W6. Otherwise, separators and terminators change to Other
441 // Neutral.
442 for (var i$4 = 0; i$4 < len; ++i$4) {
443 var type$3 = types[i$4];
444 if (type$3 == ",") { types[i$4] = "N"; }
445 else if (type$3 == "%") {
446 var end = (void 0);
447 for (end = i$4 + 1; end < len && types[end] == "%"; ++end) {}
448 var replace = (i$4 && types[i$4-1] == "!") || (end < len && types[end] == "1") ? "1" : "N";
449 for (var j = i$4; j < end; ++j) { types[j] = replace; }
450 i$4 = end - 1;
451 }
452 }
453
454 // W7. Search backwards from each instance of a European number
455 // until the first strong type (R, L, or sor) is found. If an L is
456 // found, then change the type of the European number to L.
457 for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) {
458 var type$4 = types[i$5];
459 if (cur$1 == "L" && type$4 == "1") { types[i$5] = "L"; }
460 else if (isStrong.test(type$4)) { cur$1 = type$4; }
461 }
462
463 // N1. A sequence of neutrals takes the direction of the
464 // surrounding strong text if the text on both sides has the same
465 // direction. European and Arabic numbers act as if they were R in
466 // terms of their influence on neutrals. Start-of-level-run (sor)
467 // and end-of-level-run (eor) are used at level run boundaries.
468 // N2. Any remaining neutrals take the embedding direction.
469 for (var i$6 = 0; i$6 < len; ++i$6) {
470 if (isNeutral.test(types[i$6])) {
471 var end$1 = (void 0);
472 for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {}
473 var before = (i$6 ? types[i$6-1] : outerType) == "L";
474 var after = (end$1 < len ? types[end$1] : outerType) == "L";
475 var replace$1 = before == after ? (before ? "L" : "R") : outerType;
476 for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1; }
477 i$6 = end$1 - 1;
478 }
479 }
480
481 // Here we depart from the documented algorithm, in order to avoid
482 // building up an actual levels array. Since there are only three
483 // levels (0, 1, 2) in an implementation that doesn't take
484 // explicit embedding into account, we can build up the order on
485 // the fly, without following the level-based algorithm.
486 var order = [], m;
487 for (var i$7 = 0; i$7 < len;) {
488 if (countsAsLeft.test(types[i$7])) {
489 var start = i$7;
490 for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {}
491 order.push(new BidiSpan(0, start, i$7));
492 } else {
493 var pos = i$7, at = order.length, isRTL = direction == "rtl" ? 1 : 0;
494 for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {}
495 for (var j$2 = pos; j$2 < i$7;) {
496 if (countsAsNum.test(types[j$2])) {
497 if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)); at += isRTL; }
498 var nstart = j$2;
499 for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {}
500 order.splice(at, 0, new BidiSpan(2, nstart, j$2));
501 at += isRTL;
502 pos = j$2;
503 } else { ++j$2; }
504 }
505 if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)); }
506 }
507 }
508 if (direction == "ltr") {
509 if (order[0].level == 1 && (m = str.match(/^\s+/))) {
510 order[0].from = m[0].length;
511 order.unshift(new BidiSpan(0, 0, m[0].length));
512 }
513 if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
514 lst(order).to -= m[0].length;
515 order.push(new BidiSpan(0, len - m[0].length, len));
516 }
517 }
518
519 return direction == "rtl" ? order.reverse() : order
520 }
521 })();
522
523 // Get the bidi ordering for the given line (and cache it). Returns
524 // false for lines that are fully left-to-right, and an array of
525 // BidiSpan objects otherwise.
526 function getOrder(line, direction) {
527 var order = line.order;
528 if (order == null) { order = line.order = bidiOrdering(line.text, direction); }
529 return order
530 }
531
532 // EVENT HANDLING
533
534 // Lightweight event framework. on/off also work on DOM nodes,
535 // registering native DOM handlers.
536
537 var noHandlers = [];
538
539 var on = function(emitter, type, f) {
540 if (emitter.addEventListener) {
541 emitter.addEventListener(type, f, false);
542 } else if (emitter.attachEvent) {
543 emitter.attachEvent("on" + type, f);
544 } else {
545 var map = emitter._handlers || (emitter._handlers = {});
546 map[type] = (map[type] || noHandlers).concat(f);
547 }
548 };
549
550 function getHandlers(emitter, type) {
551 return emitter._handlers && emitter._handlers[type] || noHandlers
552 }
553
554 function off(emitter, type, f) {
555 if (emitter.removeEventListener) {
556 emitter.removeEventListener(type, f, false);
557 } else if (emitter.detachEvent) {
558 emitter.detachEvent("on" + type, f);
559 } else {
560 var map = emitter._handlers, arr = map && map[type];
561 if (arr) {
562 var index = indexOf(arr, f);
563 if (index > -1)
564 { map[type] = arr.slice(0, index).concat(arr.slice(index + 1)); }
565 }
566 }
567 }
568
569 function signal(emitter, type /*, values...*/) {
570 var handlers = getHandlers(emitter, type);
571 if (!handlers.length) { return }
572 var args = Array.prototype.slice.call(arguments, 2);
573 for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args); }
574 }
575
576 // The DOM events that CodeMirror handles can be overridden by
577 // registering a (non-DOM) handler on the editor for the event name,
578 // and preventDefault-ing the event in that handler.
579 function signalDOMEvent(cm, e, override) {
580 if (typeof e == "string")
581 { e = {type: e, preventDefault: function() { this.defaultPrevented = true; }}; }
582 signal(cm, override || e.type, cm, e);
583 return e_defaultPrevented(e) || e.codemirrorIgnore
584 }
585
586 function signalCursorActivity(cm) {
587 var arr = cm._handlers && cm._handlers.cursorActivity;
588 if (!arr) { return }
589 var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []);
590 for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1)
591 { set.push(arr[i]); } }
592 }
593
594 function hasHandler(emitter, type) {
595 return getHandlers(emitter, type).length > 0
596 }
597
598 // Add on and off methods to a constructor's prototype, to make
599 // registering events on such objects more convenient.
600 function eventMixin(ctor) {
601 ctor.prototype.on = function(type, f) {on(this, type, f);};
602 ctor.prototype.off = function(type, f) {off(this, type, f);};
603 }
604
605 // Due to the fact that we still support jurassic IE versions, some
606 // compatibility wrappers are needed.
607
608 function e_preventDefault(e) {
609 if (e.preventDefault) { e.preventDefault(); }
610 else { e.returnValue = false; }
611 }
612 function e_stopPropagation(e) {
613 if (e.stopPropagation) { e.stopPropagation(); }
614 else { e.cancelBubble = true; }
615 }
616 function e_defaultPrevented(e) {
617 return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false
618 }
619 function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
620
621 function e_target(e) {return e.target || e.srcElement}
622 function e_button(e) {
623 var b = e.which;
624 if (b == null) {
625 if (e.button & 1) { b = 1; }
626 else if (e.button & 2) { b = 3; }
627 else if (e.button & 4) { b = 2; }
628 }
629 if (mac && e.ctrlKey && b == 1) { b = 3; }
630 return b
631 }
632
633 // Detect drag-and-drop
634 var dragAndDrop = function() {
635 // There is *some* kind of drag-and-drop support in IE6-8, but I
636 // couldn't get it to work yet.
637 if (ie && ie_version < 9) { return false }
638 var div = elt('div');
639 return "draggable" in div || "dragDrop" in div
640 }();
641
642 var zwspSupported;
643 function zeroWidthElement(measure) {
644 if (zwspSupported == null) {
645 var test = elt("span", "\u200b");
646 removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]));
647 if (measure.firstChild.offsetHeight != 0)
648 { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8); }
649 }
650 var node = zwspSupported ? elt("span", "\u200b") :
651 elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
652 node.setAttribute("cm-text", "");
653 return node
654 }
655
656 // Feature-detect IE's crummy client rect reporting for bidi text
657 var badBidiRects;
658 function hasBadBidiRects(measure) {
659 if (badBidiRects != null) { return badBidiRects }
660 var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA"));
661 var r0 = range(txt, 0, 1).getBoundingClientRect();
662 var r1 = range(txt, 1, 2).getBoundingClientRect();
663 removeChildren(measure);
664 if (!r0 || r0.left == r0.right) { return false } // Safari returns null in some cases (#2780)
665 return badBidiRects = (r1.right - r0.right < 3)
666 }
667
668 // See if "".split is the broken IE version, if so, provide an
669 // alternative way to split lines.
670 var splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function (string) {
671 var pos = 0, result = [], l = string.length;
672 while (pos <= l) {
673 var nl = string.indexOf("\n", pos);
674 if (nl == -1) { nl = string.length; }
675 var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
676 var rt = line.indexOf("\r");
677 if (rt != -1) {
678 result.push(line.slice(0, rt));
679 pos += rt + 1;
680 } else {
681 result.push(line);
682 pos = nl + 1;
683 }
684 }
685 return result
686 } : function (string) { return string.split(/\r\n?|\n/); };
687
688 var hasSelection = window.getSelection ? function (te) {
689 try { return te.selectionStart != te.selectionEnd }
690 catch(e) { return false }
691 } : function (te) {
692 var range;
693 try {range = te.ownerDocument.selection.createRange();}
694 catch(e) {}
695 if (!range || range.parentElement() != te) { return false }
696 return range.compareEndPoints("StartToEnd", range) != 0
697 };
698
699 var hasCopyEvent = (function () {
700 var e = elt("div");
701 if ("oncopy" in e) { return true }
702 e.setAttribute("oncopy", "return;");
703 return typeof e.oncopy == "function"
704 })();
705
706 var badZoomedRects = null;
707 function hasBadZoomedRects(measure) {
708 if (badZoomedRects != null) { return badZoomedRects }
709 var node = removeChildrenAndAdd(measure, elt("span", "x"));
710 var normal = node.getBoundingClientRect();
711 var fromRange = range(node, 0, 1).getBoundingClientRect();
712 return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1
713 }
714
715 // Known modes, by name and by MIME
716 var modes = {}, mimeModes = {};
717
718 // Extra arguments are stored as the mode's dependencies, which is
719 // used by (legacy) mechanisms like loadmode.js to automatically
720 // load a mode. (Preferred mechanism is the require/define calls.)
721 function defineMode(name, mode) {
722 if (arguments.length > 2)
723 { mode.dependencies = Array.prototype.slice.call(arguments, 2); }
724 modes[name] = mode;
725 }
726
727 function defineMIME(mime, spec) {
728 mimeModes[mime] = spec;
729 }
730
731 // Given a MIME type, a {name, ...options} config object, or a name
732 // string, return a mode config object.
733 function resolveMode(spec) {
734 if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
735 spec = mimeModes[spec];
736 } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
737 var found = mimeModes[spec.name];
738 if (typeof found == "string") { found = {name: found}; }
739 spec = createObj(found, spec);
740 spec.name = found.name;
741 } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
742 return resolveMode("application/xml")
743 } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) {
744 return resolveMode("application/json")
745 }
746 if (typeof spec == "string") { return {name: spec} }
747 else { return spec || {name: "null"} }
748 }
749
750 // Given a mode spec (anything that resolveMode accepts), find and
751 // initialize an actual mode object.
752 function getMode(options, spec) {
753 spec = resolveMode(spec);
754 var mfactory = modes[spec.name];
755 if (!mfactory) { return getMode(options, "text/plain") }
756 var modeObj = mfactory(options, spec);
757 if (modeExtensions.hasOwnProperty(spec.name)) {
758 var exts = modeExtensions[spec.name];
759 for (var prop in exts) {
760 if (!exts.hasOwnProperty(prop)) { continue }
761 if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop]; }
762 modeObj[prop] = exts[prop];
763 }
764 }
765 modeObj.name = spec.name;
766 if (spec.helperType) { modeObj.helperType = spec.helperType; }
767 if (spec.modeProps) { for (var prop$1 in spec.modeProps)
768 { modeObj[prop$1] = spec.modeProps[prop$1]; } }
769
770 return modeObj
771 }
772
773 // This can be used to attach properties to mode objects from
774 // outside the actual mode definition.
775 var modeExtensions = {};
776 function extendMode(mode, properties) {
777 var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
778 copyObj(properties, exts);
779 }
780
781 function copyState(mode, state) {
782 if (state === true) { return state }
783 if (mode.copyState) { return mode.copyState(state) }
784 var nstate = {};
785 for (var n in state) {
786 var val = state[n];
787 if (val instanceof Array) { val = val.concat([]); }
788 nstate[n] = val;
789 }
790 return nstate
791 }
792
793 // Given a mode and a state (for that mode), find the inner mode and
794 // state at the position that the state refers to.
795 function innerMode(mode, state) {
796 var info;
797 while (mode.innerMode) {
798 info = mode.innerMode(state);
799 if (!info || info.mode == mode) { break }
800 state = info.state;
801 mode = info.mode;
802 }
803 return info || {mode: mode, state: state}
804 }
805
806 function startState(mode, a1, a2) {
807 return mode.startState ? mode.startState(a1, a2) : true
808 }
809
810 // STRING STREAM
811
812 // Fed to the mode parsers, provides helper functions to make
813 // parsers more succinct.
814
815 var StringStream = function(string, tabSize, lineOracle) {
816 this.pos = this.start = 0;
817 this.string = string;
818 this.tabSize = tabSize || 8;
819 this.lastColumnPos = this.lastColumnValue = 0;
820 this.lineStart = 0;
821 this.lineOracle = lineOracle;
822 };
823
824 StringStream.prototype.eol = function () {return this.pos >= this.string.length};
825 StringStream.prototype.sol = function () {return this.pos == this.lineStart};
826 StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined};
827 StringStream.prototype.next = function () {
828 if (this.pos < this.string.length)
829 { return this.string.charAt(this.pos++) }
830 };
831 StringStream.prototype.eat = function (match) {
832 var ch = this.string.charAt(this.pos);
833 var ok;
834 if (typeof match == "string") { ok = ch == match; }
835 else { ok = ch && (match.test ? match.test(ch) : match(ch)); }
836 if (ok) {++this.pos; return ch}
837 };
838 StringStream.prototype.eatWhile = function (match) {
839 var start = this.pos;
840 while (this.eat(match)){}
841 return this.pos > start
842 };
843 StringStream.prototype.eatSpace = function () {
844 var start = this.pos;
845 while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this.pos; }
846 return this.pos > start
847 };
848 StringStream.prototype.skipToEnd = function () {this.pos = this.string.length;};
849 StringStream.prototype.skipTo = function (ch) {
850 var found = this.string.indexOf(ch, this.pos);
851 if (found > -1) {this.pos = found; return true}
852 };
853 StringStream.prototype.backUp = function (n) {this.pos -= n;};
854 StringStream.prototype.column = function () {
855 if (this.lastColumnPos < this.start) {
856 this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
857 this.lastColumnPos = this.start;
858 }
859 return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
860 };
861 StringStream.prototype.indentation = function () {
862 return countColumn(this.string, null, this.tabSize) -
863 (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
864 };
865 StringStream.prototype.match = function (pattern, consume, caseInsensitive) {
866 if (typeof pattern == "string") {
867 var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; };
868 var substr = this.string.substr(this.pos, pattern.length);
869 if (cased(substr) == cased(pattern)) {
870 if (consume !== false) { this.pos += pattern.length; }
871 return true
872 }
873 } else {
874 var match = this.string.slice(this.pos).match(pattern);
875 if (match && match.index > 0) { return null }
876 if (match && consume !== false) { this.pos += match[0].length; }
877 return match
878 }
879 };
880 StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)};
881 StringStream.prototype.hideFirstChars = function (n, inner) {
882 this.lineStart += n;
883 try { return inner() }
884 finally { this.lineStart -= n; }
885 };
886 StringStream.prototype.lookAhead = function (n) {
887 var oracle = this.lineOracle;
888 return oracle && oracle.lookAhead(n)
889 };
890 StringStream.prototype.baseToken = function () {
891 var oracle = this.lineOracle;
892 return oracle && oracle.baseToken(this.pos)
893 };
894
895 // Find the line object corresponding to the given line number.
896 function getLine(doc, n) {
897 n -= doc.first;
898 if (n < 0 || n >= doc.size) { throw new Error("There is no line " + (n + doc.first) + " in the document.") }
899 var chunk = doc;
900 while (!chunk.lines) {
901 for (var i = 0;; ++i) {
902 var child = chunk.children[i], sz = child.chunkSize();
903 if (n < sz) { chunk = child; break }
904 n -= sz;
905 }
906 }
907 return chunk.lines[n]
908 }
909
910 // Get the part of a document between two positions, as an array of
911 // strings.
912 function getBetween(doc, start, end) {
913 var out = [], n = start.line;
914 doc.iter(start.line, end.line + 1, function (line) {
915 var text = line.text;
916 if (n == end.line) { text = text.slice(0, end.ch); }
917 if (n == start.line) { text = text.slice(start.ch); }
918 out.push(text);
919 ++n;
920 });
921 return out
922 }
923 // Get the lines between from and to, as array of strings.
924 function getLines(doc, from, to) {
925 var out = [];
926 doc.iter(from, to, function (line) { out.push(line.text); }); // iter aborts when callback returns truthy value
927 return out
928 }
929
930 // Update the height of a line, propagating the height change
931 // upwards to parent nodes.
932 function updateLineHeight(line, height) {
933 var diff = height - line.height;
934 if (diff) { for (var n = line; n; n = n.parent) { n.height += diff; } }
935 }
936
937 // Given a line object, find its line number by walking up through
938 // its parent links.
939 function lineNo(line) {
940 if (line.parent == null) { return null }
941 var cur = line.parent, no = indexOf(cur.lines, line);
942 for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
943 for (var i = 0;; ++i) {
944 if (chunk.children[i] == cur) { break }
945 no += chunk.children[i].chunkSize();
946 }
947 }
948 return no + cur.first
949 }
950
951 // Find the line at the given vertical position, using the height
952 // information in the document tree.
953 function lineAtHeight(chunk, h) {
954 var n = chunk.first;
955 outer: do {
956 for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) {
957 var child = chunk.children[i$1], ch = child.height;
958 if (h < ch) { chunk = child; continue outer }
959 h -= ch;
960 n += child.chunkSize();
961 }
962 return n
963 } while (!chunk.lines)
964 var i = 0;
965 for (; i < chunk.lines.length; ++i) {
966 var line = chunk.lines[i], lh = line.height;
967 if (h < lh) { break }
968 h -= lh;
969 }
970 return n + i
971 }
972
973 function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size}
974
975 function lineNumberFor(options, i) {
976 return String(options.lineNumberFormatter(i + options.firstLineNumber))
977 }
978
979 // A Pos instance represents a position within the text.
980 function Pos(line, ch, sticky) {
981 if ( sticky === void 0 ) sticky = null;
982
983 if (!(this instanceof Pos)) { return new Pos(line, ch, sticky) }
984 this.line = line;
985 this.ch = ch;
986 this.sticky = sticky;
987 }
988
989 // Compare two positions, return 0 if they are the same, a negative
990 // number when a is less, and a positive number otherwise.
991 function cmp(a, b) { return a.line - b.line || a.ch - b.ch }
992
993 function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 }
994
995 function copyPos(x) {return Pos(x.line, x.ch)}
996 function maxPos(a, b) { return cmp(a, b) < 0 ? b : a }
997 function minPos(a, b) { return cmp(a, b) < 0 ? a : b }
998
999 // Most of the external API clips given positions to make sure they
1000 // actually exist within the document.
1001 function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))}
1002 function clipPos(doc, pos) {
1003 if (pos.line < doc.first) { return Pos(doc.first, 0) }
1004 var last = doc.first + doc.size - 1;
1005 if (pos.line > last) { return Pos(last, getLine(doc, last).text.length) }
1006 return clipToLen(pos, getLine(doc, pos.line).text.length)
1007 }
1008 function clipToLen(pos, linelen) {
1009 var ch = pos.ch;
1010 if (ch == null || ch > linelen) { return Pos(pos.line, linelen) }
1011 else if (ch < 0) { return Pos(pos.line, 0) }
1012 else { return pos }
1013 }
1014 function clipPosArray(doc, array) {
1015 var out = [];
1016 for (var i = 0; i < array.length; i++) { out[i] = clipPos(doc, array[i]); }
1017 return out
1018 }
1019
1020 var SavedContext = function(state, lookAhead) {
1021 this.state = state;
1022 this.lookAhead = lookAhead;
1023 };
1024
1025 var Context = function(doc, state, line, lookAhead) {
1026 this.state = state;
1027 this.doc = doc;
1028 this.line = line;
1029 this.maxLookAhead = lookAhead || 0;
1030 this.baseTokens = null;
1031 this.baseTokenPos = 1;
1032 };
1033
1034 Context.prototype.lookAhead = function (n) {
1035 var line = this.doc.getLine(this.line + n);
1036 if (line != null && n > this.maxLookAhead) { this.maxLookAhead = n; }
1037 return line
1038 };
1039
1040 Context.prototype.baseToken = function (n) {
1041 if (!this.baseTokens) { return null }
1042 while (this.baseTokens[this.baseTokenPos] <= n)
1043 { this.baseTokenPos += 2; }
1044 var type = this.baseTokens[this.baseTokenPos + 1];
1045 return {type: type && type.replace(/( |^)overlay .*/, ""),
1046 size: this.baseTokens[this.baseTokenPos] - n}
1047 };
1048
1049 Context.prototype.nextLine = function () {
1050 this.line++;
1051 if (this.maxLookAhead > 0) { this.maxLookAhead--; }
1052 };
1053
1054 Context.fromSaved = function (doc, saved, line) {
1055 if (saved instanceof SavedContext)
1056 { return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead) }
1057 else
1058 { return new Context(doc, copyState(doc.mode, saved), line) }
1059 };
1060
1061 Context.prototype.save = function (copy) {
1062 var state = copy !== false ? copyState(this.doc.mode, this.state) : this.state;
1063 return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state
1064 };
1065
1066
1067 // Compute a style array (an array starting with a mode generation
1068 // -- for invalidation -- followed by pairs of end positions and
1069 // style strings), which is used to highlight the tokens on the
1070 // line.
1071 function highlightLine(cm, line, context, forceToEnd) {
1072 // A styles array always starts with a number identifying the
1073 // mode/overlays that it is based on (for easy invalidation).
1074 var st = [cm.state.modeGen], lineClasses = {};
1075 // Compute the base array of styles
1076 runMode(cm, line.text, cm.doc.mode, context, function (end, style) { return st.push(end, style); },
1077 lineClasses, forceToEnd);
1078 var state = context.state;
1079
1080 // Run overlays, adjust style array.
1081 var loop = function ( o ) {
1082 context.baseTokens = st;
1083 var overlay = cm.state.overlays[o], i = 1, at = 0;
1084 context.state = true;
1085 runMode(cm, line.text, overlay.mode, context, function (end, style) {
1086 var start = i;
1087 // Ensure there's a token end at the current position, and that i points at it
1088 while (at < end) {
1089 var i_end = st[i];
1090 if (i_end > end)
1091 { st.splice(i, 1, end, st[i+1], i_end); }
1092 i += 2;
1093 at = Math.min(end, i_end);
1094 }
1095 if (!style) { return }
1096 if (overlay.opaque) {
1097 st.splice(start, i - start, end, "overlay " + style);
1098 i = start + 2;
1099 } else {
1100 for (; start < i; start += 2) {
1101 var cur = st[start+1];
1102 st[start+1] = (cur ? cur + " " : "") + "overlay " + style;
1103 }
1104 }
1105 }, lineClasses);
1106 context.state = state;
1107 context.baseTokens = null;
1108 context.baseTokenPos = 1;
1109 };
1110
1111 for (var o = 0; o < cm.state.overlays.length; ++o) loop( o );
1112
1113 return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null}
1114 }
1115
1116 function getLineStyles(cm, line, updateFrontier) {
1117 if (!line.styles || line.styles[0] != cm.state.modeGen) {
1118 var context = getContextBefore(cm, lineNo(line));
1119 var resetState = line.text.length > cm.options.maxHighlightLength && copyState(cm.doc.mode, context.state);
1120 var result = highlightLine(cm, line, context);
1121 if (resetState) { context.state = resetState; }
1122 line.stateAfter = context.save(!resetState);
1123 line.styles = result.styles;
1124 if (result.classes) { line.styleClasses = result.classes; }
1125 else if (line.styleClasses) { line.styleClasses = null; }
1126 if (updateFrontier === cm.doc.highlightFrontier)
1127 { cm.doc.modeFrontier = Math.max(cm.doc.modeFrontier, ++cm.doc.highlightFrontier); }
1128 }
1129 return line.styles
1130 }
1131
1132 function getContextBefore(cm, n, precise) {
1133 var doc = cm.doc, display = cm.display;
1134 if (!doc.mode.startState) { return new Context(doc, true, n) }
1135 var start = findStartLine(cm, n, precise);
1136 var saved = start > doc.first && getLine(doc, start - 1).stateAfter;
1137 var context = saved ? Context.fromSaved(doc, saved, start) : new Context(doc, startState(doc.mode), start);
1138
1139 doc.iter(start, n, function (line) {
1140 processLine(cm, line.text, context);
1141 var pos = context.line;
1142 line.stateAfter = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo ? context.save() : null;
1143 context.nextLine();
1144 });
1145 if (precise) { doc.modeFrontier = context.line; }
1146 return context
1147 }
1148
1149 // Lightweight form of highlight -- proceed over this line and
1150 // update state, but don't save a style array. Used for lines that
1151 // aren't currently visible.
1152 function processLine(cm, text, context, startAt) {
1153 var mode = cm.doc.mode;
1154 var stream = new StringStream(text, cm.options.tabSize, context);
1155 stream.start = stream.pos = startAt || 0;
1156 if (text == "") { callBlankLine(mode, context.state); }
1157 while (!stream.eol()) {
1158 readToken(mode, stream, context.state);
1159 stream.start = stream.pos;
1160 }
1161 }
1162
1163 function callBlankLine(mode, state) {
1164 if (mode.blankLine) { return mode.blankLine(state) }
1165 if (!mode.innerMode) { return }
1166 var inner = innerMode(mode, state);
1167 if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) }
1168 }
1169
1170 function readToken(mode, stream, state, inner) {
1171 for (var i = 0; i < 10; i++) {
1172 if (inner) { inner[0] = innerMode(mode, state).mode; }
1173 var style = mode.token(stream, state);
1174 if (stream.pos > stream.start) { return style }
1175 }
1176 throw new Error("Mode " + mode.name + " failed to advance stream.")
1177 }
1178
1179 var Token = function(stream, type, state) {
1180 this.start = stream.start; this.end = stream.pos;
1181 this.string = stream.current();
1182 this.type = type || null;
1183 this.state = state;
1184 };
1185
1186 // Utility for getTokenAt and getLineTokens
1187 function takeToken(cm, pos, precise, asArray) {
1188 var doc = cm.doc, mode = doc.mode, style;
1189 pos = clipPos(doc, pos);
1190 var line = getLine(doc, pos.line), context = getContextBefore(cm, pos.line, precise);
1191 var stream = new StringStream(line.text, cm.options.tabSize, context), tokens;
1192 if (asArray) { tokens = []; }
1193 while ((asArray || stream.pos < pos.ch) && !stream.eol()) {
1194 stream.start = stream.pos;
1195 style = readToken(mode, stream, context.state);
1196 if (asArray) { tokens.push(new Token(stream, style, copyState(doc.mode, context.state))); }
1197 }
1198 return asArray ? tokens : new Token(stream, style, context.state)
1199 }
1200
1201 function extractLineClasses(type, output) {
1202 if (type) { for (;;) {
1203 var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/);
1204 if (!lineClass) { break }
1205 type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length);
1206 var prop = lineClass[1] ? "bgClass" : "textClass";
1207 if (output[prop] == null)
1208 { output[prop] = lineClass[2]; }
1209 else if (!(new RegExp("(?:^|\\s)" + lineClass[2] + "(?:$|\\s)")).test(output[prop]))
1210 { output[prop] += " " + lineClass[2]; }
1211 } }
1212 return type
1213 }
1214
1215 // Run the given mode's parser over a line, calling f for each token.
1216 function runMode(cm, text, mode, context, f, lineClasses, forceToEnd) {
1217 var flattenSpans = mode.flattenSpans;
1218 if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans; }
1219 var curStart = 0, curStyle = null;
1220 var stream = new StringStream(text, cm.options.tabSize, context), style;
1221 var inner = cm.options.addModeClass && [null];
1222 if (text == "") { extractLineClasses(callBlankLine(mode, context.state), lineClasses); }
1223 while (!stream.eol()) {
1224 if (stream.pos > cm.options.maxHighlightLength) {
1225 flattenSpans = false;
1226 if (forceToEnd) { processLine(cm, text, context, stream.pos); }
1227 stream.pos = text.length;
1228 style = null;
1229 } else {
1230 style = extractLineClasses(readToken(mode, stream, context.state, inner), lineClasses);
1231 }
1232 if (inner) {
1233 var mName = inner[0].name;
1234 if (mName) { style = "m-" + (style ? mName + " " + style : mName); }
1235 }
1236 if (!flattenSpans || curStyle != style) {
1237 while (curStart < stream.start) {
1238 curStart = Math.min(stream.start, curStart + 5000);
1239 f(curStart, curStyle);
1240 }
1241 curStyle = style;
1242 }
1243 stream.start = stream.pos;
1244 }
1245 while (curStart < stream.pos) {
1246 // Webkit seems to refuse to render text nodes longer than 57444
1247 // characters, and returns inaccurate measurements in nodes
1248 // starting around 5000 chars.
1249 var pos = Math.min(stream.pos, curStart + 5000);
1250 f(pos, curStyle);
1251 curStart = pos;
1252 }
1253 }
1254
1255 // Finds the line to start with when starting a parse. Tries to
1256 // find a line with a stateAfter, so that it can start with a
1257 // valid state. If that fails, it returns the line with the
1258 // smallest indentation, which tends to need the least context to
1259 // parse correctly.
1260 function findStartLine(cm, n, precise) {
1261 var minindent, minline, doc = cm.doc;
1262 var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100);
1263 for (var search = n; search > lim; --search) {
1264 if (search <= doc.first) { return doc.first }
1265 var line = getLine(doc, search - 1), after = line.stateAfter;
1266 if (after && (!precise || search + (after instanceof SavedContext ? after.lookAhead : 0) <= doc.modeFrontier))
1267 { return search }
1268 var indented = countColumn(line.text, null, cm.options.tabSize);
1269 if (minline == null || minindent > indented) {
1270 minline = search - 1;
1271 minindent = indented;
1272 }
1273 }
1274 return minline
1275 }
1276
1277 function retreatFrontier(doc, n) {
1278 doc.modeFrontier = Math.min(doc.modeFrontier, n);
1279 if (doc.highlightFrontier < n - 10) { return }
1280 var start = doc.first;
1281 for (var line = n - 1; line > start; line--) {
1282 var saved = getLine(doc, line).stateAfter;
1283 // change is on 3
1284 // state on line 1 looked ahead 2 -- so saw 3
1285 // test 1 + 2 < 3 should cover this
1286 if (saved && (!(saved instanceof SavedContext) || line + saved.lookAhead < n)) {
1287 start = line + 1;
1288 break
1289 }
1290 }
1291 doc.highlightFrontier = Math.min(doc.highlightFrontier, start);
1292 }
1293
1294 // Optimize some code when these features are not used.
1295 var sawReadOnlySpans = false, sawCollapsedSpans = false;
1296
1297 function seeReadOnlySpans() {
1298 sawReadOnlySpans = true;
1299 }
1300
1301 function seeCollapsedSpans() {
1302 sawCollapsedSpans = true;
1303 }
1304
1305 // TEXTMARKER SPANS
1306
1307 function MarkedSpan(marker, from, to) {
1308 this.marker = marker;
1309 this.from = from; this.to = to;
1310 }
1311
1312 // Search an array of spans for a span matching the given marker.
1313 function getMarkedSpanFor(spans, marker) {
1314 if (spans) { for (var i = 0; i < spans.length; ++i) {
1315 var span = spans[i];
1316 if (span.marker == marker) { return span }
1317 } }
1318 }
1319
1320 // Remove a span from an array, returning undefined if no spans are
1321 // left (we don't store arrays for lines without spans).
1322 function removeMarkedSpan(spans, span) {
1323 var r;
1324 for (var i = 0; i < spans.length; ++i)
1325 { if (spans[i] != span) { (r || (r = [])).push(spans[i]); } }
1326 return r
1327 }
1328
1329 // Add a span to a line.
1330 function addMarkedSpan(line, span, op) {
1331 var inThisOp = op && window.WeakSet && (op.markedSpans || (op.markedSpans = new WeakSet));
1332 if (inThisOp && line.markedSpans && inThisOp.has(line.markedSpans)) {
1333 line.markedSpans.push(span);
1334 } else {
1335 line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
1336 if (inThisOp) { inThisOp.add(line.markedSpans); }
1337 }
1338 span.marker.attachLine(line);
1339 }
1340
1341 // Used for the algorithm that adjusts markers for a change in the
1342 // document. These functions cut an array of spans at a given
1343 // character position, returning an array of remaining chunks (or
1344 // undefined if nothing remains).
1345 function markedSpansBefore(old, startCh, isInsert) {
1346 var nw;
1347 if (old) { for (var i = 0; i < old.length; ++i) {
1348 var span = old[i], marker = span.marker;
1349 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
1350 if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) {
1351 var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh)
1352 ;(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to));
1353 }
1354 } }
1355 return nw
1356 }
1357 function markedSpansAfter(old, endCh, isInsert) {
1358 var nw;
1359 if (old) { for (var i = 0; i < old.length; ++i) {
1360 var span = old[i], marker = span.marker;
1361 var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
1362 if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) {
1363 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh)
1364 ;(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh,
1365 span.to == null ? null : span.to - endCh));
1366 }
1367 } }
1368 return nw
1369 }
1370
1371 // Given a change object, compute the new set of marker spans that
1372 // cover the line in which the change took place. Removes spans
1373 // entirely within the change, reconnects spans belonging to the
1374 // same marker that appear on both sides of the change, and cuts off
1375 // spans partially within the change. Returns an array of span
1376 // arrays with one element for each line in (after) the change.
1377 function stretchSpansOverChange(doc, change) {
1378 if (change.full) { return null }
1379 var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans;
1380 var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans;
1381 if (!oldFirst && !oldLast) { return null }
1382
1383 var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0;
1384 // Get the spans that 'stick out' on both sides
1385 var first = markedSpansBefore(oldFirst, startCh, isInsert);
1386 var last = markedSpansAfter(oldLast, endCh, isInsert);
1387
1388 // Next, merge those two ends
1389 var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0);
1390 if (first) {
1391 // Fix up .to properties of first
1392 for (var i = 0; i < first.length; ++i) {
1393 var span = first[i];
1394 if (span.to == null) {
1395 var found = getMarkedSpanFor(last, span.marker);
1396 if (!found) { span.to = startCh; }
1397 else if (sameLine) { span.to = found.to == null ? null : found.to + offset; }
1398 }
1399 }
1400 }
1401 if (last) {
1402 // Fix up .from in last (or move them into first in case of sameLine)
1403 for (var i$1 = 0; i$1 < last.length; ++i$1) {
1404 var span$1 = last[i$1];
1405 if (span$1.to != null) { span$1.to += offset; }
1406 if (span$1.from == null) {
1407 var found$1 = getMarkedSpanFor(first, span$1.marker);
1408 if (!found$1) {
1409 span$1.from = offset;
1410 if (sameLine) { (first || (first = [])).push(span$1); }
1411 }
1412 } else {
1413 span$1.from += offset;
1414 if (sameLine) { (first || (first = [])).push(span$1); }
1415 }
1416 }
1417 }
1418 // Make sure we didn't create any zero-length spans
1419 if (first) { first = clearEmptySpans(first); }
1420 if (last && last != first) { last = clearEmptySpans(last); }
1421
1422 var newMarkers = [first];
1423 if (!sameLine) {
1424 // Fill gap with whole-line-spans
1425 var gap = change.text.length - 2, gapMarkers;
1426 if (gap > 0 && first)
1427 { for (var i$2 = 0; i$2 < first.length; ++i$2)
1428 { if (first[i$2].to == null)
1429 { (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i$2].marker, null, null)); } } }
1430 for (var i$3 = 0; i$3 < gap; ++i$3)
1431 { newMarkers.push(gapMarkers); }
1432 newMarkers.push(last);
1433 }
1434 return newMarkers
1435 }
1436
1437 // Remove spans that are empty and don't have a clearWhenEmpty
1438 // option of false.
1439 function clearEmptySpans(spans) {
1440 for (var i = 0; i < spans.length; ++i) {
1441 var span = spans[i];
1442 if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)
1443 { spans.splice(i--, 1); }
1444 }
1445 if (!spans.length) { return null }
1446 return spans
1447 }
1448
1449 // Used to 'clip' out readOnly ranges when making a change.
1450 function removeReadOnlyRanges(doc, from, to) {
1451 var markers = null;
1452 doc.iter(from.line, to.line + 1, function (line) {
1453 if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
1454 var mark = line.markedSpans[i].marker;
1455 if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
1456 { (markers || (markers = [])).push(mark); }
1457 } }
1458 });
1459 if (!markers) { return null }
1460 var parts = [{from: from, to: to}];
1461 for (var i = 0; i < markers.length; ++i) {
1462 var mk = markers[i], m = mk.find(0);
1463 for (var j = 0; j < parts.length; ++j) {
1464 var p = parts[j];
1465 if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) { continue }
1466 var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to);
1467 if (dfrom < 0 || !mk.inclusiveLeft && !dfrom)
1468 { newParts.push({from: p.from, to: m.from}); }
1469 if (dto > 0 || !mk.inclusiveRight && !dto)
1470 { newParts.push({from: m.to, to: p.to}); }
1471 parts.splice.apply(parts, newParts);
1472 j += newParts.length - 3;
1473 }
1474 }
1475 return parts
1476 }
1477
1478 // Connect or disconnect spans from a line.
1479 function detachMarkedSpans(line) {
1480 var spans = line.markedSpans;
1481 if (!spans) { return }
1482 for (var i = 0; i < spans.length; ++i)
1483 { spans[i].marker.detachLine(line); }
1484 line.markedSpans = null;
1485 }
1486 function attachMarkedSpans(line, spans) {
1487 if (!spans) { return }
1488 for (var i = 0; i < spans.length; ++i)
1489 { spans[i].marker.attachLine(line); }
1490 line.markedSpans = spans;
1491 }
1492
1493 // Helpers used when computing which overlapping collapsed span
1494 // counts as the larger one.
1495 function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 }
1496 function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 }
1497
1498 // Returns a number indicating which of two overlapping collapsed
1499 // spans is larger (and thus includes the other). Falls back to
1500 // comparing ids when the spans cover exactly the same range.
1501 function compareCollapsedMarkers(a, b) {
1502 var lenDiff = a.lines.length - b.lines.length;
1503 if (lenDiff != 0) { return lenDiff }
1504 var aPos = a.find(), bPos = b.find();
1505 var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b);
1506 if (fromCmp) { return -fromCmp }
1507 var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b);
1508 if (toCmp) { return toCmp }
1509 return b.id - a.id
1510 }
1511
1512 // Find out whether a line ends or starts in a collapsed span. If
1513 // so, return the marker for that span.
1514 function collapsedSpanAtSide(line, start) {
1515 var sps = sawCollapsedSpans && line.markedSpans, found;
1516 if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
1517 sp = sps[i];
1518 if (sp.marker.collapsed && (start ? sp.from : sp.to) == null &&
1519 (!found || compareCollapsedMarkers(found, sp.marker) < 0))
1520 { found = sp.marker; }
1521 } }
1522 return found
1523 }
1524 function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) }
1525 function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) }
1526
1527 function collapsedSpanAround(line, ch) {
1528 var sps = sawCollapsedSpans && line.markedSpans, found;
1529 if (sps) { for (var i = 0; i < sps.length; ++i) {
1530 var sp = sps[i];
1531 if (sp.marker.collapsed && (sp.from == null || sp.from < ch) && (sp.to == null || sp.to > ch) &&
1532 (!found || compareCollapsedMarkers(found, sp.marker) < 0)) { found = sp.marker; }
1533 } }
1534 return found
1535 }
1536
1537 // Test whether there exists a collapsed span that partially
1538 // overlaps (covers the start or end, but not both) of a new span.
1539 // Such overlap is not allowed.
1540 function conflictingCollapsedRange(doc, lineNo, from, to, marker) {
1541 var line = getLine(doc, lineNo);
1542 var sps = sawCollapsedSpans && line.markedSpans;
1543 if (sps) { for (var i = 0; i < sps.length; ++i) {
1544 var sp = sps[i];
1545 if (!sp.marker.collapsed) { continue }
1546 var found = sp.marker.find(0);
1547 var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker);
1548 var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker);
1549 if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue }
1550 if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) ||
1551 fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0))
1552 { return true }
1553 } }
1554 }
1555
1556 // A visual line is a line as drawn on the screen. Folding, for
1557 // example, can cause multiple logical lines to appear on the same
1558 // visual line. This finds the start of the visual line that the
1559 // given line is part of (usually that is the line itself).
1560 function visualLine(line) {
1561 var merged;
1562 while (merged = collapsedSpanAtStart(line))
1563 { line = merged.find(-1, true).line; }
1564 return line
1565 }
1566
1567 function visualLineEnd(line) {
1568 var merged;
1569 while (merged = collapsedSpanAtEnd(line))
1570 { line = merged.find(1, true).line; }
1571 return line
1572 }
1573
1574 // Returns an array of logical lines that continue the visual line
1575 // started by the argument, or undefined if there are no such lines.
1576 function visualLineContinued(line) {
1577 var merged, lines;
1578 while (merged = collapsedSpanAtEnd(line)) {
1579 line = merged.find(1, true).line
1580 ;(lines || (lines = [])).push(line);
1581 }
1582 return lines
1583 }
1584
1585 // Get the line number of the start of the visual line that the
1586 // given line number is part of.
1587 function visualLineNo(doc, lineN) {
1588 var line = getLine(doc, lineN), vis = visualLine(line);
1589 if (line == vis) { return lineN }
1590 return lineNo(vis)
1591 }
1592
1593 // Get the line number of the start of the next visual line after
1594 // the given line.
1595 function visualLineEndNo(doc, lineN) {
1596 if (lineN > doc.lastLine()) { return lineN }
1597 var line = getLine(doc, lineN), merged;
1598 if (!lineIsHidden(doc, line)) { return lineN }
1599 while (merged = collapsedSpanAtEnd(line))
1600 { line = merged.find(1, true).line; }
1601 return lineNo(line) + 1
1602 }
1603
1604 // Compute whether a line is hidden. Lines count as hidden when they
1605 // are part of a visual line that starts with another line, or when
1606 // they are entirely covered by collapsed, non-widget span.
1607 function lineIsHidden(doc, line) {
1608 var sps = sawCollapsedSpans && line.markedSpans;
1609 if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
1610 sp = sps[i];
1611 if (!sp.marker.collapsed) { continue }
1612 if (sp.from == null) { return true }
1613 if (sp.marker.widgetNode) { continue }
1614 if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
1615 { return true }
1616 } }
1617 }
1618 function lineIsHiddenInner(doc, line, span) {
1619 if (span.to == null) {
1620 var end = span.marker.find(1, true);
1621 return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker))
1622 }
1623 if (span.marker.inclusiveRight && span.to == line.text.length)
1624 { return true }
1625 for (var sp = (void 0), i = 0; i < line.markedSpans.length; ++i) {
1626 sp = line.markedSpans[i];
1627 if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to &&
1628 (sp.to == null || sp.to != span.from) &&
1629 (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
1630 lineIsHiddenInner(doc, line, sp)) { return true }
1631 }
1632 }
1633
1634 // Find the height above the given line.
1635 function heightAtLine(lineObj) {
1636 lineObj = visualLine(lineObj);
1637
1638 var h = 0, chunk = lineObj.parent;
1639 for (var i = 0; i < chunk.lines.length; ++i) {
1640 var line = chunk.lines[i];
1641 if (line == lineObj) { break }
1642 else { h += line.height; }
1643 }
1644 for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
1645 for (var i$1 = 0; i$1 < p.children.length; ++i$1) {
1646 var cur = p.children[i$1];
1647 if (cur == chunk) { break }
1648 else { h += cur.height; }
1649 }
1650 }
1651 return h
1652 }
1653
1654 // Compute the character length of a line, taking into account
1655 // collapsed ranges (see markText) that might hide parts, and join
1656 // other lines onto it.
1657 function lineLength(line) {
1658 if (line.height == 0) { return 0 }
1659 var len = line.text.length, merged, cur = line;
1660 while (merged = collapsedSpanAtStart(cur)) {
1661 var found = merged.find(0, true);
1662 cur = found.from.line;
1663 len += found.from.ch - found.to.ch;
1664 }
1665 cur = line;
1666 while (merged = collapsedSpanAtEnd(cur)) {
1667 var found$1 = merged.find(0, true);
1668 len -= cur.text.length - found$1.from.ch;
1669 cur = found$1.to.line;
1670 len += cur.text.length - found$1.to.ch;
1671 }
1672 return len
1673 }
1674
1675 // Find the longest line in the document.
1676 function findMaxLine(cm) {
1677 var d = cm.display, doc = cm.doc;
1678 d.maxLine = getLine(doc, doc.first);
1679 d.maxLineLength = lineLength(d.maxLine);
1680 d.maxLineChanged = true;
1681 doc.iter(function (line) {
1682 var len = lineLength(line);
1683 if (len > d.maxLineLength) {
1684 d.maxLineLength = len;
1685 d.maxLine = line;
1686 }
1687 });
1688 }
1689
1690 // LINE DATA STRUCTURE
1691
1692 // Line objects. These hold state related to a line, including
1693 // highlighting info (the styles array).
1694 var Line = function(text, markedSpans, estimateHeight) {
1695 this.text = text;
1696 attachMarkedSpans(this, markedSpans);
1697 this.height = estimateHeight ? estimateHeight(this) : 1;
1698 };
1699
1700 Line.prototype.lineNo = function () { return lineNo(this) };
1701 eventMixin(Line);
1702
1703 // Change the content (text, markers) of a line. Automatically
1704 // invalidates cached information and tries to re-estimate the
1705 // line's height.
1706 function updateLine(line, text, markedSpans, estimateHeight) {
1707 line.text = text;
1708 if (line.stateAfter) { line.stateAfter = null; }
1709 if (line.styles) { line.styles = null; }
1710 if (line.order != null) { line.order = null; }
1711 detachMarkedSpans(line);
1712 attachMarkedSpans(line, markedSpans);
1713 var estHeight = estimateHeight ? estimateHeight(line) : 1;
1714 if (estHeight != line.height) { updateLineHeight(line, estHeight); }
1715 }
1716
1717 // Detach a line from the document tree and its markers.
1718 function cleanUpLine(line) {
1719 line.parent = null;
1720 detachMarkedSpans(line);
1721 }
1722
1723 // Convert a style as returned by a mode (either null, or a string
1724 // containing one or more styles) to a CSS style. This is cached,
1725 // and also looks for line-wide styles.
1726 var styleToClassCache = {}, styleToClassCacheWithMode = {};
1727 function interpretTokenStyle(style, options) {
1728 if (!style || /^\s*$/.test(style)) { return null }
1729 var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache;
1730 return cache[style] ||
1731 (cache[style] = style.replace(/\S+/g, "cm-$&"))
1732 }
1733
1734 // Render the DOM representation of the text of a line. Also builds
1735 // up a 'line map', which points at the DOM nodes that represent
1736 // specific stretches of text, and is used by the measuring code.
1737 // The returned object contains the DOM node, this map, and
1738 // information about line-wide styles that were set by the mode.
1739 function buildLineContent(cm, lineView) {
1740 // The padding-right forces the element to have a 'border', which
1741 // is needed on Webkit to be able to get line-level bounding
1742 // rectangles for it (in measureChar).
1743 var content = eltP("span", null, null, webkit ? "padding-right: .1px" : null);
1744 var builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: content,
1745 col: 0, pos: 0, cm: cm,
1746 trailingSpace: false,
1747 splitSpaces: cm.getOption("lineWrapping")};
1748 lineView.measure = {};
1749
1750 // Iterate over the logical lines that make up this visual line.
1751 for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {
1752 var line = i ? lineView.rest[i - 1] : lineView.line, order = (void 0);
1753 builder.pos = 0;
1754 builder.addToken = buildToken;
1755 // Optionally wire in some hacks into the token-rendering
1756 // algorithm, to deal with browser quirks.
1757 if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction)))
1758 { builder.addToken = buildTokenBadBidi(builder.addToken, order); }
1759 builder.map = [];
1760 var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line);
1761 insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate));
1762 if (line.styleClasses) {
1763 if (line.styleClasses.bgClass)
1764 { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || ""); }
1765 if (line.styleClasses.textClass)
1766 { builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || ""); }
1767 }
1768
1769 // Ensure at least a single node is present, for measuring.
1770 if (builder.map.length == 0)
1771 { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))); }
1772
1773 // Store the map and a cache object for the current logical line
1774 if (i == 0) {
1775 lineView.measure.map = builder.map;
1776 lineView.measure.cache = {};
1777 } else {
1778 (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map)
1779 ;(lineView.measure.caches || (lineView.measure.caches = [])).push({});
1780 }
1781 }
1782
1783 // See issue #2901
1784 if (webkit) {
1785 var last = builder.content.lastChild;
1786 if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab")))
1787 { builder.content.className = "cm-tab-wrap-hack"; }
1788 }
1789
1790 signal(cm, "renderLine", cm, lineView.line, builder.pre);
1791 if (builder.pre.className)
1792 { builder.textClass = joinClasses(builder.pre.className, builder.textClass || ""); }
1793
1794 return builder
1795 }
1796
1797 function defaultSpecialCharPlaceholder(ch) {
1798 var token = elt("span", "\u2022", "cm-invalidchar");
1799 token.title = "\\u" + ch.charCodeAt(0).toString(16);
1800 token.setAttribute("aria-label", token.title);
1801 return token
1802 }
1803
1804 // Build up the DOM representation for a single token, and add it to
1805 // the line map. Takes care to render special characters separately.
1806 function buildToken(builder, text, style, startStyle, endStyle, css, attributes) {
1807 if (!text) { return }
1808 var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text;
1809 var special = builder.cm.state.specialChars, mustWrap = false;
1810 var content;
1811 if (!special.test(text)) {
1812 builder.col += text.length;
1813 content = document.createTextNode(displayText);
1814 builder.map.push(builder.pos, builder.pos + text.length, content);
1815 if (ie && ie_version < 9) { mustWrap = true; }
1816 builder.pos += text.length;
1817 } else {
1818 content = document.createDocumentFragment();
1819 var pos = 0;
1820 while (true) {
1821 special.lastIndex = pos;
1822 var m = special.exec(text);
1823 var skipped = m ? m.index - pos : text.length - pos;
1824 if (skipped) {
1825 var txt = document.createTextNode(displayText.slice(pos, pos + skipped));
1826 if (ie && ie_version < 9) { content.appendChild(elt("span", [txt])); }
1827 else { content.appendChild(txt); }
1828 builder.map.push(builder.pos, builder.pos + skipped, txt);
1829 builder.col += skipped;
1830 builder.pos += skipped;
1831 }
1832 if (!m) { break }
1833 pos += skipped + 1;
1834 var txt$1 = (void 0);
1835 if (m[0] == "\t") {
1836 var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
1837 txt$1 = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
1838 txt$1.setAttribute("role", "presentation");
1839 txt$1.setAttribute("cm-text", "\t");
1840 builder.col += tabWidth;
1841 } else if (m[0] == "\r" || m[0] == "\n") {
1842 txt$1 = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar"));
1843 txt$1.setAttribute("cm-text", m[0]);
1844 builder.col += 1;
1845 } else {
1846 txt$1 = builder.cm.options.specialCharPlaceholder(m[0]);
1847 txt$1.setAttribute("cm-text", m[0]);
1848 if (ie && ie_version < 9) { content.appendChild(elt("span", [txt$1])); }
1849 else { content.appendChild(txt$1); }
1850 builder.col += 1;
1851 }
1852 builder.map.push(builder.pos, builder.pos + 1, txt$1);
1853 builder.pos++;
1854 }
1855 }
1856 builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32;
1857 if (style || startStyle || endStyle || mustWrap || css || attributes) {
1858 var fullStyle = style || "";
1859 if (startStyle) { fullStyle += startStyle; }
1860 if (endStyle) { fullStyle += endStyle; }
1861 var token = elt("span", [content], fullStyle, css);
1862 if (attributes) {
1863 for (var attr in attributes) { if (attributes.hasOwnProperty(attr) && attr != "style" && attr != "class")
1864 { token.setAttribute(attr, attributes[attr]); } }
1865 }
1866 return builder.content.appendChild(token)
1867 }
1868 builder.content.appendChild(content);
1869 }
1870
1871 // Change some spaces to NBSP to prevent the browser from collapsing
1872 // trailing spaces at the end of a line when rendering text (issue #1362).
1873 function splitSpaces(text, trailingBefore) {
1874 if (text.length > 1 && !/ /.test(text)) { return text }
1875 var spaceBefore = trailingBefore, result = "";
1876 for (var i = 0; i < text.length; i++) {
1877 var ch = text.charAt(i);
1878 if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32))
1879 { ch = "\u00a0"; }
1880 result += ch;
1881 spaceBefore = ch == " ";
1882 }
1883 return result
1884 }
1885
1886 // Work around nonsense dimensions being reported for stretches of
1887 // right-to-left text.
1888 function buildTokenBadBidi(inner, order) {
1889 return function (builder, text, style, startStyle, endStyle, css, attributes) {
1890 style = style ? style + " cm-force-border" : "cm-force-border";
1891 var start = builder.pos, end = start + text.length;
1892 for (;;) {
1893 // Find the part that overlaps with the start of this text
1894 var part = (void 0);
1895 for (var i = 0; i < order.length; i++) {
1896 part = order[i];
1897 if (part.to > start && part.from <= start) { break }
1898 }
1899 if (part.to >= end) { return inner(builder, text, style, startStyle, endStyle, css, attributes) }
1900 inner(builder, text.slice(0, part.to - start), style, startStyle, null, css, attributes);
1901 startStyle = null;
1902 text = text.slice(part.to - start);
1903 start = part.to;
1904 }
1905 }
1906 }
1907
1908 function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
1909 var widget = !ignoreWidget && marker.widgetNode;
1910 if (widget) { builder.map.push(builder.pos, builder.pos + size, widget); }
1911 if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {
1912 if (!widget)
1913 { widget = builder.content.appendChild(document.createElement("span")); }
1914 widget.setAttribute("cm-marker", marker.id);
1915 }
1916 if (widget) {
1917 builder.cm.display.input.setUneditable(widget);
1918 builder.content.appendChild(widget);
1919 }
1920 builder.pos += size;
1921 builder.trailingSpace = false;
1922 }
1923
1924 // Outputs a number of spans to make up a line, taking highlighting
1925 // and marked text into account.
1926 function insertLineContent(line, builder, styles) {
1927 var spans = line.markedSpans, allText = line.text, at = 0;
1928 if (!spans) {
1929 for (var i$1 = 1; i$1 < styles.length; i$1+=2)
1930 { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpretTokenStyle(styles[i$1+1], builder.cm.options)); }
1931 return
1932 }
1933
1934 var len = allText.length, pos = 0, i = 1, text = "", style, css;
1935 var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed, attributes;
1936 for (;;) {
1937 if (nextChange == pos) { // Update current marker set
1938 spanStyle = spanEndStyle = spanStartStyle = css = "";
1939 attributes = null;
1940 collapsed = null; nextChange = Infinity;
1941 var foundBookmarks = [], endStyles = (void 0);
1942 for (var j = 0; j < spans.length; ++j) {
1943 var sp = spans[j], m = sp.marker;
1944 if (m.type == "bookmark" && sp.from == pos && m.widgetNode) {
1945 foundBookmarks.push(m);
1946 } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) {
1947 if (sp.to != null && sp.to != pos && nextChange > sp.to) {
1948 nextChange = sp.to;
1949 spanEndStyle = "";
1950 }
1951 if (m.className) { spanStyle += " " + m.className; }
1952 if (m.css) { css = (css ? css + ";" : "") + m.css; }
1953 if (m.startStyle && sp.from == pos) { spanStartStyle += " " + m.startStyle; }
1954 if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [])).push(m.endStyle, sp.to); }
1955 // support for the old title property
1956 // https://github.com/codemirror/CodeMirror/pull/5673
1957 if (m.title) { (attributes || (attributes = {})).title = m.title; }
1958 if (m.attributes) {
1959 for (var attr in m.attributes)
1960 { (attributes || (attributes = {}))[attr] = m.attributes[attr]; }
1961 }
1962 if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))
1963 { collapsed = sp; }
1964 } else if (sp.from > pos && nextChange > sp.from) {
1965 nextChange = sp.from;
1966 }
1967 }
1968 if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2)
1969 { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += " " + endStyles[j$1]; } } }
1970
1971 if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2)
1972 { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]); } }
1973 if (collapsed && (collapsed.from || 0) == pos) {
1974 buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos,
1975 collapsed.marker, collapsed.from == null);
1976 if (collapsed.to == null) { return }
1977 if (collapsed.to == pos) { collapsed = false; }
1978 }
1979 }
1980 if (pos >= len) { break }
1981
1982 var upto = Math.min(len, nextChange);
1983 while (true) {
1984 if (text) {
1985 var end = pos + text.length;
1986 if (!collapsed) {
1987 var tokenText = end > upto ? text.slice(0, upto - pos) : text;
1988 builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
1989 spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", css, attributes);
1990 }
1991 if (end >= upto) {text = text.slice(upto - pos); pos = upto; break}
1992 pos = end;
1993 spanStartStyle = "";
1994 }
1995 text = allText.slice(at, at = styles[i++]);
1996 style = interpretTokenStyle(styles[i++], builder.cm.options);
1997 }
1998 }
1999 }
2000
2001
2002 // These objects are used to represent the visible (currently drawn)
2003 // part of the document. A LineView may correspond to multiple
2004 // logical lines, if those are connected by collapsed ranges.
2005 function LineView(doc, line, lineN) {
2006 // The starting line
2007 this.line = line;
2008 // Continuing lines, if any
2009 this.rest = visualLineContinued(line);
2010 // Number of logical lines in this visual line
2011 this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1;
2012 this.node = this.text = null;
2013 this.hidden = lineIsHidden(doc, line);
2014 }
2015
2016 // Create a range of LineView objects for the given lines.
2017 function buildViewArray(cm, from, to) {
2018 var array = [], nextPos;
2019 for (var pos = from; pos < to; pos = nextPos) {
2020 var view = new LineView(cm.doc, getLine(cm.doc, pos), pos);
2021 nextPos = pos + view.size;
2022 array.push(view);
2023 }
2024 return array
2025 }
2026
2027 var operationGroup = null;
2028
2029 function pushOperation(op) {
2030 if (operationGroup) {
2031 operationGroup.ops.push(op);
2032 } else {
2033 op.ownsGroup = operationGroup = {
2034 ops: [op],
2035 delayedCallbacks: []
2036 };
2037 }
2038 }
2039
2040 function fireCallbacksForOps(group) {
2041 // Calls delayed callbacks and cursorActivity handlers until no
2042 // new ones appear
2043 var callbacks = group.delayedCallbacks, i = 0;
2044 do {
2045 for (; i < callbacks.length; i++)
2046 { callbacks[i].call(null); }
2047 for (var j = 0; j < group.ops.length; j++) {
2048 var op = group.ops[j];
2049 if (op.cursorActivityHandlers)
2050 { while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
2051 { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm); } }
2052 }
2053 } while (i < callbacks.length)
2054 }
2055
2056 function finishOperation(op, endCb) {
2057 var group = op.ownsGroup;
2058 if (!group) { return }
2059
2060 try { fireCallbacksForOps(group); }
2061 finally {
2062 operationGroup = null;
2063 endCb(group);
2064 }
2065 }
2066
2067 var orphanDelayedCallbacks = null;
2068
2069 // Often, we want to signal events at a point where we are in the
2070 // middle of some work, but don't want the handler to start calling
2071 // other methods on the editor, which might be in an inconsistent
2072 // state or simply not expect any other events to happen.
2073 // signalLater looks whether there are any handlers, and schedules
2074 // them to be executed when the last operation ends, or, if no
2075 // operation is active, when a timeout fires.
2076 function signalLater(emitter, type /*, values...*/) {
2077 var arr = getHandlers(emitter, type);
2078 if (!arr.length) { return }
2079 var args = Array.prototype.slice.call(arguments, 2), list;
2080 if (operationGroup) {
2081 list = operationGroup.delayedCallbacks;
2082 } else if (orphanDelayedCallbacks) {
2083 list = orphanDelayedCallbacks;
2084 } else {
2085 list = orphanDelayedCallbacks = [];
2086 setTimeout(fireOrphanDelayed, 0);
2087 }
2088 var loop = function ( i ) {
2089 list.push(function () { return arr[i].apply(null, args); });
2090 };
2091
2092 for (var i = 0; i < arr.length; ++i)
2093 loop( i );
2094 }
2095
2096 function fireOrphanDelayed() {
2097 var delayed = orphanDelayedCallbacks;
2098 orphanDelayedCallbacks = null;
2099 for (var i = 0; i < delayed.length; ++i) { delayed[i](); }
2100 }
2101
2102 // When an aspect of a line changes, a string is added to
2103 // lineView.changes. This updates the relevant part of the line's
2104 // DOM structure.
2105 function updateLineForChanges(cm, lineView, lineN, dims) {
2106 for (var j = 0; j < lineView.changes.length; j++) {
2107 var type = lineView.changes[j];
2108 if (type == "text") { updateLineText(cm, lineView); }
2109 else if (type == "gutter") { updateLineGutter(cm, lineView, lineN, dims); }
2110 else if (type == "class") { updateLineClasses(cm, lineView); }
2111 else if (type == "widget") { updateLineWidgets(cm, lineView, dims); }
2112 }
2113 lineView.changes = null;
2114 }
2115
2116 // Lines with gutter elements, widgets or a background class need to
2117 // be wrapped, and have the extra elements added to the wrapper div
2118 function ensureLineWrapped(lineView) {
2119 if (lineView.node == lineView.text) {
2120 lineView.node = elt("div", null, null, "position: relative");
2121 if (lineView.text.parentNode)
2122 { lineView.text.parentNode.replaceChild(lineView.node, lineView.text); }
2123 lineView.node.appendChild(lineView.text);
2124 if (ie && ie_version < 8) { lineView.node.style.zIndex = 2; }
2125 }
2126 return lineView.node
2127 }
2128
2129 function updateLineBackground(cm, lineView) {
2130 var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass;
2131 if (cls) { cls += " CodeMirror-linebackground"; }
2132 if (lineView.background) {
2133 if (cls) { lineView.background.className = cls; }
2134 else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; }
2135 } else if (cls) {
2136 var wrap = ensureLineWrapped(lineView);
2137 lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild);
2138 cm.display.input.setUneditable(lineView.background);
2139 }
2140 }
2141
2142 // Wrapper around buildLineContent which will reuse the structure
2143 // in display.externalMeasured when possible.
2144 function getLineContent(cm, lineView) {
2145 var ext = cm.display.externalMeasured;
2146 if (ext && ext.line == lineView.line) {
2147 cm.display.externalMeasured = null;
2148 lineView.measure = ext.measure;
2149 return ext.built
2150 }
2151 return buildLineContent(cm, lineView)
2152 }
2153
2154 // Redraw the line's text. Interacts with the background and text
2155 // classes because the mode may output tokens that influence these
2156 // classes.
2157 function updateLineText(cm, lineView) {
2158 var cls = lineView.text.className;
2159 var built = getLineContent(cm, lineView);
2160 if (lineView.text == lineView.node) { lineView.node = built.pre; }
2161 lineView.text.parentNode.replaceChild(built.pre, lineView.text);
2162 lineView.text = built.pre;
2163 if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {
2164 lineView.bgClass = built.bgClass;
2165 lineView.textClass = built.textClass;
2166 updateLineClasses(cm, lineView);
2167 } else if (cls) {
2168 lineView.text.className = cls;
2169 }
2170 }
2171
2172 function updateLineClasses(cm, lineView) {
2173 updateLineBackground(cm, lineView);
2174 if (lineView.line.wrapClass)
2175 { ensureLineWrapped(lineView).className = lineView.line.wrapClass; }
2176 else if (lineView.node != lineView.text)
2177 { lineView.node.className = ""; }
2178 var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass;
2179 lineView.text.className = textClass || "";
2180 }
2181
2182 function updateLineGutter(cm, lineView, lineN, dims) {
2183 if (lineView.gutter) {
2184 lineView.node.removeChild(lineView.gutter);
2185 lineView.gutter = null;
2186 }
2187 if (lineView.gutterBackground) {
2188 lineView.node.removeChild(lineView.gutterBackground);
2189 lineView.gutterBackground = null;
2190 }
2191 if (lineView.line.gutterClass) {
2192 var wrap = ensureLineWrapped(lineView);
2193 lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass,
2194 ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px; width: " + (dims.gutterTotalWidth) + "px"));
2195 cm.display.input.setUneditable(lineView.gutterBackground);
2196 wrap.insertBefore(lineView.gutterBackground, lineView.text);
2197 }
2198 var markers = lineView.line.gutterMarkers;
2199 if (cm.options.lineNumbers || markers) {
2200 var wrap$1 = ensureLineWrapped(lineView);
2201 var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"));
2202 gutterWrap.setAttribute("aria-hidden", "true");
2203 cm.display.input.setUneditable(gutterWrap);
2204 wrap$1.insertBefore(gutterWrap, lineView.text);
2205 if (lineView.line.gutterClass)
2206 { gutterWrap.className += " " + lineView.line.gutterClass; }
2207 if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
2208 { lineView.lineNumber = gutterWrap.appendChild(
2209 elt("div", lineNumberFor(cm.options, lineN),
2210 "CodeMirror-linenumber CodeMirror-gutter-elt",
2211 ("left: " + (dims.gutterLeft["CodeMirror-linenumbers"]) + "px; width: " + (cm.display.lineNumInnerWidth) + "px"))); }
2212 if (markers) { for (var k = 0; k < cm.display.gutterSpecs.length; ++k) {
2213 var id = cm.display.gutterSpecs[k].className, found = markers.hasOwnProperty(id) && markers[id];
2214 if (found)
2215 { gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt",
2216 ("left: " + (dims.gutterLeft[id]) + "px; width: " + (dims.gutterWidth[id]) + "px"))); }
2217 } }
2218 }
2219 }
2220
2221 function updateLineWidgets(cm, lineView, dims) {
2222 if (lineView.alignable) { lineView.alignable = null; }
2223 var isWidget = classTest("CodeMirror-linewidget");
2224 for (var node = lineView.node.firstChild, next = (void 0); node; node = next) {
2225 next = node.nextSibling;
2226 if (isWidget.test(node.className)) { lineView.node.removeChild(node); }
2227 }
2228 insertLineWidgets(cm, lineView, dims);
2229 }
2230
2231 // Build a line's DOM representation from scratch
2232 function buildLineElement(cm, lineView, lineN, dims) {
2233 var built = getLineContent(cm, lineView);
2234 lineView.text = lineView.node = built.pre;
2235 if (built.bgClass) { lineView.bgClass = built.bgClass; }
2236 if (built.textClass) { lineView.textClass = built.textClass; }
2237
2238 updateLineClasses(cm, lineView);
2239 updateLineGutter(cm, lineView, lineN, dims);
2240 insertLineWidgets(cm, lineView, dims);
2241 return lineView.node
2242 }
2243
2244 // A lineView may contain multiple logical lines (when merged by
2245 // collapsed spans). The widgets for all of them need to be drawn.
2246 function insertLineWidgets(cm, lineView, dims) {
2247 insertLineWidgetsFor(cm, lineView.line, lineView, dims, true);
2248 if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
2249 { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false); } }
2250 }
2251
2252 function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {
2253 if (!line.widgets) { return }
2254 var wrap = ensureLineWrapped(lineView);
2255 for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
2256 var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget" + (widget.className ? " " + widget.className : ""));
2257 if (!widget.handleMouseEvents) { node.setAttribute("cm-ignore-events", "true"); }
2258 positionLineWidget(widget, node, lineView, dims);
2259 cm.display.input.setUneditable(node);
2260 if (allowAbove && widget.above)
2261 { wrap.insertBefore(node, lineView.gutter || lineView.text); }
2262 else
2263 { wrap.appendChild(node); }
2264 signalLater(widget, "redraw");
2265 }
2266 }
2267
2268 function positionLineWidget(widget, node, lineView, dims) {
2269 if (widget.noHScroll) {
2270 (lineView.alignable || (lineView.alignable = [])).push(node);
2271 var width = dims.wrapperWidth;
2272 node.style.left = dims.fixedPos + "px";
2273 if (!widget.coverGutter) {
2274 width -= dims.gutterTotalWidth;
2275 node.style.paddingLeft = dims.gutterTotalWidth + "px";
2276 }
2277 node.style.width = width + "px";
2278 }
2279 if (widget.coverGutter) {
2280 node.style.zIndex = 5;
2281 node.style.position = "relative";
2282 if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + "px"; }
2283 }
2284 }
2285
2286 function widgetHeight(widget) {
2287 if (widget.height != null) { return widget.height }
2288 var cm = widget.doc.cm;
2289 if (!cm) { return 0 }
2290 if (!contains(document.body, widget.node)) {
2291 var parentStyle = "position: relative;";
2292 if (widget.coverGutter)
2293 { parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;"; }
2294 if (widget.noHScroll)
2295 { parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;"; }
2296 removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle));
2297 }
2298 return widget.height = widget.node.parentNode.offsetHeight
2299 }
2300
2301 // Return true when the given mouse event happened in a widget
2302 function eventInWidget(display, e) {
2303 for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
2304 if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") ||
2305 (n.parentNode == display.sizer && n != display.mover))
2306 { return true }
2307 }
2308 }
2309
2310 // POSITION MEASUREMENT
2311
2312 function paddingTop(display) {return display.lineSpace.offsetTop}
2313 function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight}
2314 function paddingH(display) {
2315 if (display.cachedPaddingH) { return display.cachedPaddingH }
2316 var e = removeChildrenAndAdd(display.measure, elt("pre", "x", "CodeMirror-line-like"));
2317 var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle;
2318 var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)};
2319 if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data; }
2320 return data
2321 }
2322
2323 function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth }
2324 function displayWidth(cm) {
2325 return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth
2326 }
2327 function displayHeight(cm) {
2328 return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight
2329 }
2330
2331 // Ensure the lineView.wrapping.heights array is populated. This is
2332 // an array of bottom offsets for the lines that make up a drawn
2333 // line. When lineWrapping is on, there might be more than one
2334 // height.
2335 function ensureLineHeights(cm, lineView, rect) {
2336 var wrapping = cm.options.lineWrapping;
2337 var curWidth = wrapping && displayWidth(cm);
2338 if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {
2339 var heights = lineView.measure.heights = [];
2340 if (wrapping) {
2341 lineView.measure.width = curWidth;
2342 var rects = lineView.text.firstChild.getClientRects();
2343 for (var i = 0; i < rects.length - 1; i++) {
2344 var cur = rects[i], next = rects[i + 1];
2345 if (Math.abs(cur.bottom - next.bottom) > 2)
2346 { heights.push((cur.bottom + next.top) / 2 - rect.top); }
2347 }
2348 }
2349 heights.push(rect.bottom - rect.top);
2350 }
2351 }
2352
2353 // Find a line map (mapping character offsets to text nodes) and a
2354 // measurement cache for the given line number. (A line view might
2355 // contain multiple lines when collapsed ranges are present.)
2356 function mapFromLineView(lineView, line, lineN) {
2357 if (lineView.line == line)
2358 { return {map: lineView.measure.map, cache: lineView.measure.cache} }
2359 if (lineView.rest) {
2360 for (var i = 0; i < lineView.rest.length; i++)
2361 { if (lineView.rest[i] == line)
2362 { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } }
2363 for (var i$1 = 0; i$1 < lineView.rest.length; i$1++)
2364 { if (lineNo(lineView.rest[i$1]) > lineN)
2365 { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } }
2366 }
2367 }
2368
2369 // Render a line into the hidden node display.externalMeasured. Used
2370 // when measurement is needed for a line that's not in the viewport.
2371 function updateExternalMeasurement(cm, line) {
2372 line = visualLine(line);
2373 var lineN = lineNo(line);
2374 var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN);
2375 view.lineN = lineN;
2376 var built = view.built = buildLineContent(cm, view);
2377 view.text = built.pre;
2378 removeChildrenAndAdd(cm.display.lineMeasure, built.pre);
2379 return view
2380 }
2381
2382 // Get a {top, bottom, left, right} box (in line-local coordinates)
2383 // for a given character.
2384 function measureChar(cm, line, ch, bias) {
2385 return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias)
2386 }
2387
2388 // Find a line view that corresponds to the given line number.
2389 function findViewForLine(cm, lineN) {
2390 if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo)
2391 { return cm.display.view[findViewIndex(cm, lineN)] }
2392 var ext = cm.display.externalMeasured;
2393 if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size)
2394 { return ext }
2395 }
2396
2397 // Measurement can be split in two steps, the set-up work that
2398 // applies to the whole line, and the measurement of the actual
2399 // character. Functions like coordsChar, that need to do a lot of
2400 // measurements in a row, can thus ensure that the set-up work is
2401 // only done once.
2402 function prepareMeasureForLine(cm, line) {
2403 var lineN = lineNo(line);
2404 var view = findViewForLine(cm, lineN);
2405 if (view && !view.text) {
2406 view = null;
2407 } else if (view && view.changes) {
2408 updateLineForChanges(cm, view, lineN, getDimensions(cm));
2409 cm.curOp.forceUpdate = true;
2410 }
2411 if (!view)
2412 { view = updateExternalMeasurement(cm, line); }
2413
2414 var info = mapFromLineView(view, line, lineN);
2415 return {
2416 line: line, view: view, rect: null,
2417 map: info.map, cache: info.cache, before: info.before,
2418 hasHeights: false
2419 }
2420 }
2421
2422 // Given a prepared measurement object, measures the position of an
2423 // actual character (or fetches it from the cache).
2424 function measureCharPrepared(cm, prepared, ch, bias, varHeight) {
2425 if (prepared.before) { ch = -1; }
2426 var key = ch + (bias || ""), found;
2427 if (prepared.cache.hasOwnProperty(key)) {
2428 found = prepared.cache[key];
2429 } else {
2430 if (!prepared.rect)
2431 { prepared.rect = prepared.view.text.getBoundingClientRect(); }
2432 if (!prepared.hasHeights) {
2433 ensureLineHeights(cm, prepared.view, prepared.rect);
2434 prepared.hasHeights = true;
2435 }
2436 found = measureCharInner(cm, prepared, ch, bias);
2437 if (!found.bogus) { prepared.cache[key] = found; }
2438 }
2439 return {left: found.left, right: found.right,
2440 top: varHeight ? found.rtop : found.top,
2441 bottom: varHeight ? found.rbottom : found.bottom}
2442 }
2443
2444 var nullRect = {left: 0, right: 0, top: 0, bottom: 0};
2445
2446 function nodeAndOffsetInLineMap(map, ch, bias) {
2447 var node, start, end, collapse, mStart, mEnd;
2448 // First, search the line map for the text node corresponding to,
2449 // or closest to, the target character.
2450 for (var i = 0; i < map.length; i += 3) {
2451 mStart = map[i];
2452 mEnd = map[i + 1];
2453 if (ch < mStart) {
2454 start = 0; end = 1;
2455 collapse = "left";
2456 } else if (ch < mEnd) {
2457 start = ch - mStart;
2458 end = start + 1;
2459 } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) {
2460 end = mEnd - mStart;
2461 start = end - 1;
2462 if (ch >= mEnd) { collapse = "right"; }
2463 }
2464 if (start != null) {
2465 node = map[i + 2];
2466 if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right"))
2467 { collapse = bias; }
2468 if (bias == "left" && start == 0)
2469 { while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) {
2470 node = map[(i -= 3) + 2];
2471 collapse = "left";
2472 } }
2473 if (bias == "right" && start == mEnd - mStart)
2474 { while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].insertLeft) {
2475 node = map[(i += 3) + 2];
2476 collapse = "right";
2477 } }
2478 break
2479 }
2480 }
2481 return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd}
2482 }
2483
2484 function getUsefulRect(rects, bias) {
2485 var rect = nullRect;
2486 if (bias == "left") { for (var i = 0; i < rects.length; i++) {
2487 if ((rect = rects[i]).left != rect.right) { break }
2488 } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) {
2489 if ((rect = rects[i$1]).left != rect.right) { break }
2490 } }
2491 return rect
2492 }
2493
2494 function measureCharInner(cm, prepared, ch, bias) {
2495 var place = nodeAndOffsetInLineMap(prepared.map, ch, bias);
2496 var node = place.node, start = place.start, end = place.end, collapse = place.collapse;
2497
2498 var rect;
2499 if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.
2500 for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonsense rectangles are returned
2501 while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start; }
2502 while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) { ++end; }
2503 if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart)
2504 { rect = node.parentNode.getBoundingClientRect(); }
2505 else
2506 { rect = getUsefulRect(range(node, start, end).getClientRects(), bias); }
2507 if (rect.left || rect.right || start == 0) { break }
2508 end = start;
2509 start = start - 1;
2510 collapse = "right";
2511 }
2512 if (ie && ie_version < 11) { rect = maybeUpdateRectForZooming(cm.display.measure, rect); }
2513 } else { // If it is a widget, simply get the box for the whole widget.
2514 if (start > 0) { collapse = bias = "right"; }
2515 var rects;
2516 if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)
2517 { rect = rects[bias == "right" ? rects.length - 1 : 0]; }
2518 else
2519 { rect = node.getBoundingClientRect(); }
2520 }
2521 if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {
2522 var rSpan = node.parentNode.getClientRects()[0];
2523 if (rSpan)
2524 { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom}; }
2525 else
2526 { rect = nullRect; }
2527 }
2528
2529 var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top;
2530 var mid = (rtop + rbot) / 2;
2531 var heights = prepared.view.measure.heights;
2532 var i = 0;
2533 for (; i < heights.length - 1; i++)
2534 { if (mid < heights[i]) { break } }
2535 var top = i ? heights[i - 1] : 0, bot = heights[i];
2536 var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left,
2537 right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left,
2538 top: top, bottom: bot};
2539 if (!rect.left && !rect.right) { result.bogus = true; }
2540 if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; }
2541
2542 return result
2543 }
2544
2545 // Work around problem with bounding client rects on ranges being
2546 // returned incorrectly when zoomed on IE10 and below.
2547 function maybeUpdateRectForZooming(measure, rect) {
2548 if (!window.screen || screen.logicalXDPI == null ||
2549 screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))
2550 { return rect }
2551 var scaleX = screen.logicalXDPI / screen.deviceXDPI;
2552 var scaleY = screen.logicalYDPI / screen.deviceYDPI;
2553 return {left: rect.left * scaleX, right: rect.right * scaleX,
2554 top: rect.top * scaleY, bottom: rect.bottom * scaleY}
2555 }
2556
2557 function clearLineMeasurementCacheFor(lineView) {
2558 if (lineView.measure) {
2559 lineView.measure.cache = {};
2560 lineView.measure.heights = null;
2561 if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
2562 { lineView.measure.caches[i] = {}; } }
2563 }
2564 }
2565
2566 function clearLineMeasurementCache(cm) {
2567 cm.display.externalMeasure = null;
2568 removeChildren(cm.display.lineMeasure);
2569 for (var i = 0; i < cm.display.view.length; i++)
2570 { clearLineMeasurementCacheFor(cm.display.view[i]); }
2571 }
2572
2573 function clearCaches(cm) {
2574 clearLineMeasurementCache(cm);
2575 cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null;
2576 if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true; }
2577 cm.display.lineNumChars = null;
2578 }
2579
2580 function pageScrollX(doc) {
2581 // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=489206
2582 // which causes page_Offset and bounding client rects to use
2583 // different reference viewports and invalidate our calculations.
2584 if (chrome && android) { return -(doc.body.getBoundingClientRect().left - parseInt(getComputedStyle(doc.body).marginLeft)) }
2585 return doc.defaultView.pageXOffset || (doc.documentElement || doc.body).scrollLeft
2586 }
2587 function pageScrollY(doc) {
2588 if (chrome && android) { return -(doc.body.getBoundingClientRect().top - parseInt(getComputedStyle(doc.body).marginTop)) }
2589 return doc.defaultView.pageYOffset || (doc.documentElement || doc.body).scrollTop
2590 }
2591
2592 function widgetTopHeight(lineObj) {
2593 var ref = visualLine(lineObj);
2594 var widgets = ref.widgets;
2595 var height = 0;
2596 if (widgets) { for (var i = 0; i < widgets.length; ++i) { if (widgets[i].above)
2597 { height += widgetHeight(widgets[i]); } } }
2598 return height
2599 }
2600
2601 // Converts a {top, bottom, left, right} box from line-local
2602 // coordinates into another coordinate system. Context may be one of
2603 // "line", "div" (display.lineDiv), "local"./null (editor), "window",
2604 // or "page".
2605 function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) {
2606 if (!includeWidgets) {
2607 var height = widgetTopHeight(lineObj);
2608 rect.top += height; rect.bottom += height;
2609 }
2610 if (context == "line") { return rect }
2611 if (!context) { context = "local"; }
2612 var yOff = heightAtLine(lineObj);
2613 if (context == "local") { yOff += paddingTop(cm.display); }
2614 else { yOff -= cm.display.viewOffset; }
2615 if (context == "page" || context == "window") {
2616 var lOff = cm.display.lineSpace.getBoundingClientRect();
2617 yOff += lOff.top + (context == "window" ? 0 : pageScrollY(doc(cm)));
2618 var xOff = lOff.left + (context == "window" ? 0 : pageScrollX(doc(cm)));
2619 rect.left += xOff; rect.right += xOff;
2620 }
2621 rect.top += yOff; rect.bottom += yOff;
2622 return rect
2623 }
2624
2625 // Coverts a box from "div" coords to another coordinate system.
2626 // Context may be "window", "page", "div", or "local"./null.
2627 function fromCoordSystem(cm, coords, context) {
2628 if (context == "div") { return coords }
2629 var left = coords.left, top = coords.top;
2630 // First move into "page" coordinate system
2631 if (context == "page") {
2632 left -= pageScrollX(doc(cm));
2633 top -= pageScrollY(doc(cm));
2634 } else if (context == "local" || !context) {
2635 var localBox = cm.display.sizer.getBoundingClientRect();
2636 left += localBox.left;
2637 top += localBox.top;
2638 }
2639
2640 var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect();
2641 return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top}
2642 }
2643
2644 function charCoords(cm, pos, context, lineObj, bias) {
2645 if (!lineObj) { lineObj = getLine(cm.doc, pos.line); }
2646 return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context)
2647 }
2648
2649 // Returns a box for a given cursor position, which may have an
2650 // 'other' property containing the position of the secondary cursor
2651 // on a bidi boundary.
2652 // A cursor Pos(line, char, "before") is on the same visual line as `char - 1`
2653 // and after `char - 1` in writing order of `char - 1`
2654 // A cursor Pos(line, char, "after") is on the same visual line as `char`
2655 // and before `char` in writing order of `char`
2656 // Examples (upper-case letters are RTL, lower-case are LTR):
2657 // Pos(0, 1, ...)
2658 // before after
2659 // ab a|b a|b
2660 // aB a|B aB|
2661 // Ab |Ab A|b
2662 // AB B|A B|A
2663 // Every position after the last character on a line is considered to stick
2664 // to the last character on the line.
2665 function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
2666 lineObj = lineObj || getLine(cm.doc, pos.line);
2667 if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); }
2668 function get(ch, right) {
2669 var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight);
2670 if (right) { m.left = m.right; } else { m.right = m.left; }
2671 return intoCoordSystem(cm, lineObj, m, context)
2672 }
2673 var order = getOrder(lineObj, cm.doc.direction), ch = pos.ch, sticky = pos.sticky;
2674 if (ch >= lineObj.text.length) {
2675 ch = lineObj.text.length;
2676 sticky = "before";
2677 } else if (ch <= 0) {
2678 ch = 0;
2679 sticky = "after";
2680 }
2681 if (!order) { return get(sticky == "before" ? ch - 1 : ch, sticky == "before") }
2682
2683 function getBidi(ch, partPos, invert) {
2684 var part = order[partPos], right = part.level == 1;
2685 return get(invert ? ch - 1 : ch, right != invert)
2686 }
2687 var partPos = getBidiPartAt(order, ch, sticky);
2688 var other = bidiOther;
2689 var val = getBidi(ch, partPos, sticky == "before");
2690 if (other != null) { val.other = getBidi(ch, other, sticky != "before"); }
2691 return val
2692 }
2693
2694 // Used to cheaply estimate the coordinates for a position. Used for
2695 // intermediate scroll updates.
2696 function estimateCoords(cm, pos) {
2697 var left = 0;
2698 pos = clipPos(cm.doc, pos);
2699 if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch; }
2700 var lineObj = getLine(cm.doc, pos.line);
2701 var top = heightAtLine(lineObj) + paddingTop(cm.display);
2702 return {left: left, right: left, top: top, bottom: top + lineObj.height}
2703 }
2704
2705 // Positions returned by coordsChar contain some extra information.
2706 // xRel is the relative x position of the input coordinates compared
2707 // to the found position (so xRel > 0 means the coordinates are to
2708 // the right of the character position, for example). When outside
2709 // is true, that means the coordinates lie outside the line's
2710 // vertical range.
2711 function PosWithInfo(line, ch, sticky, outside, xRel) {
2712 var pos = Pos(line, ch, sticky);
2713 pos.xRel = xRel;
2714 if (outside) { pos.outside = outside; }
2715 return pos
2716 }
2717
2718 // Compute the character position closest to the given coordinates.
2719 // Input must be lineSpace-local ("div" coordinate system).
2720 function coordsChar(cm, x, y) {
2721 var doc = cm.doc;
2722 y += cm.display.viewOffset;
2723 if (y < 0) { return PosWithInfo(doc.first, 0, null, -1, -1) }
2724 var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
2725 if (lineN > last)
2726 { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, 1, 1) }
2727 if (x < 0) { x = 0; }
2728
2729 var lineObj = getLine(doc, lineN);
2730 for (;;) {
2731 var found = coordsCharInner(cm, lineObj, lineN, x, y);
2732 var collapsed = collapsedSpanAround(lineObj, found.ch + (found.xRel > 0 || found.outside > 0 ? 1 : 0));
2733 if (!collapsed) { return found }
2734 var rangeEnd = collapsed.find(1);
2735 if (rangeEnd.line == lineN) { return rangeEnd }
2736 lineObj = getLine(doc, lineN = rangeEnd.line);
2737 }
2738 }
2739
2740 function wrappedLineExtent(cm, lineObj, preparedMeasure, y) {
2741 y -= widgetTopHeight(lineObj);
2742 var end = lineObj.text.length;
2743 var begin = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch - 1).bottom <= y; }, end, 0);
2744 end = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch).top > y; }, begin, end);
2745 return {begin: begin, end: end}
2746 }
2747
2748 function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) {
2749 if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); }
2750 var targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), "line").top;
2751 return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop)
2752 }
2753
2754 // Returns true if the given side of a box is after the given
2755 // coordinates, in top-to-bottom, left-to-right order.
2756 function boxIsAfter(box, x, y, left) {
2757 return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x
2758 }
2759
2760 function coordsCharInner(cm, lineObj, lineNo, x, y) {
2761 // Move y into line-local coordinate space
2762 y -= heightAtLine(lineObj);
2763 var preparedMeasure = prepareMeasureForLine(cm, lineObj);
2764 // When directly calling `measureCharPrepared`, we have to adjust
2765 // for the widgets at this line.
2766 var widgetHeight = widgetTopHeight(lineObj);
2767 var begin = 0, end = lineObj.text.length, ltr = true;
2768
2769 var order = getOrder(lineObj, cm.doc.direction);
2770 // If the line isn't plain left-to-right text, first figure out
2771 // which bidi section the coordinates fall into.
2772 if (order) {
2773 var part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart)
2774 (cm, lineObj, lineNo, preparedMeasure, order, x, y);
2775 ltr = part.level != 1;
2776 // The awkward -1 offsets are needed because findFirst (called
2777 // on these below) will treat its first bound as inclusive,
2778 // second as exclusive, but we want to actually address the
2779 // characters in the part's range
2780 begin = ltr ? part.from : part.to - 1;
2781 end = ltr ? part.to : part.from - 1;
2782 }
2783
2784 // A binary search to find the first character whose bounding box
2785 // starts after the coordinates. If we run across any whose box wrap
2786 // the coordinates, store that.
2787 var chAround = null, boxAround = null;
2788 var ch = findFirst(function (ch) {
2789 var box = measureCharPrepared(cm, preparedMeasure, ch);
2790 box.top += widgetHeight; box.bottom += widgetHeight;
2791 if (!boxIsAfter(box, x, y, false)) { return false }
2792 if (box.top <= y && box.left <= x) {
2793 chAround = ch;
2794 boxAround = box;
2795 }
2796 return true
2797 }, begin, end);
2798
2799 var baseX, sticky, outside = false;
2800 // If a box around the coordinates was found, use that
2801 if (boxAround) {
2802 // Distinguish coordinates nearer to the left or right side of the box
2803 var atLeft = x - boxAround.left < boxAround.right - x, atStart = atLeft == ltr;
2804 ch = chAround + (atStart ? 0 : 1);
2805 sticky = atStart ? "after" : "before";
2806 baseX = atLeft ? boxAround.left : boxAround.right;
2807 } else {
2808 // (Adjust for extended bound, if necessary.)
2809 if (!ltr && (ch == end || ch == begin)) { ch++; }
2810 // To determine which side to associate with, get the box to the
2811 // left of the character and compare it's vertical position to the
2812 // coordinates
2813 sticky = ch == 0 ? "after" : ch == lineObj.text.length ? "before" :
2814 (measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight <= y) == ltr ?
2815 "after" : "before";
2816 // Now get accurate coordinates for this place, in order to get a
2817 // base X position
2818 var coords = cursorCoords(cm, Pos(lineNo, ch, sticky), "line", lineObj, preparedMeasure);
2819 baseX = coords.left;
2820 outside = y < coords.top ? -1 : y >= coords.bottom ? 1 : 0;
2821 }
2822
2823 ch = skipExtendingChars(lineObj.text, ch, 1);
2824 return PosWithInfo(lineNo, ch, sticky, outside, x - baseX)
2825 }
2826
2827 function coordsBidiPart(cm, lineObj, lineNo, preparedMeasure, order, x, y) {
2828 // Bidi parts are sorted left-to-right, and in a non-line-wrapping
2829 // situation, we can take this ordering to correspond to the visual
2830 // ordering. This finds the first part whose end is after the given
2831 // coordinates.
2832 var index = findFirst(function (i) {
2833 var part = order[i], ltr = part.level != 1;
2834 return boxIsAfter(cursorCoords(cm, Pos(lineNo, ltr ? part.to : part.from, ltr ? "before" : "after"),
2835 "line", lineObj, preparedMeasure), x, y, true)
2836 }, 0, order.length - 1);
2837 var part = order[index];
2838 // If this isn't the first part, the part's start is also after
2839 // the coordinates, and the coordinates aren't on the same line as
2840 // that start, move one part back.
2841 if (index > 0) {
2842 var ltr = part.level != 1;
2843 var start = cursorCoords(cm, Pos(lineNo, ltr ? part.from : part.to, ltr ? "after" : "before"),
2844 "line", lineObj, preparedMeasure);
2845 if (boxIsAfter(start, x, y, true) && start.top > y)
2846 { part = order[index - 1]; }
2847 }
2848 return part
2849 }
2850
2851 function coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x, y) {
2852 // In a wrapped line, rtl text on wrapping boundaries can do things
2853 // that don't correspond to the ordering in our `order` array at
2854 // all, so a binary search doesn't work, and we want to return a
2855 // part that only spans one line so that the binary search in
2856 // coordsCharInner is safe. As such, we first find the extent of the
2857 // wrapped line, and then do a flat search in which we discard any
2858 // spans that aren't on the line.
2859 var ref = wrappedLineExtent(cm, lineObj, preparedMeasure, y);
2860 var begin = ref.begin;
2861 var end = ref.end;
2862 if (/\s/.test(lineObj.text.charAt(end - 1))) { end--; }
2863 var part = null, closestDist = null;
2864 for (var i = 0; i < order.length; i++) {
2865 var p = order[i];
2866 if (p.from >= end || p.to <= begin) { continue }
2867 var ltr = p.level != 1;
2868 var endX = measureCharPrepared(cm, preparedMeasure, ltr ? Math.min(end, p.to) - 1 : Math.max(begin, p.from)).right;
2869 // Weigh against spans ending before this, so that they are only
2870 // picked if nothing ends after
2871 var dist = endX < x ? x - endX + 1e9 : endX - x;
2872 if (!part || closestDist > dist) {
2873 part = p;
2874 closestDist = dist;
2875 }
2876 }
2877 if (!part) { part = order[order.length - 1]; }
2878 // Clip the part to the wrapped line.
2879 if (part.from < begin) { part = {from: begin, to: part.to, level: part.level}; }
2880 if (part.to > end) { part = {from: part.from, to: end, level: part.level}; }
2881 return part
2882 }
2883
2884 var measureText;
2885 // Compute the default text height.
2886 function textHeight(display) {
2887 if (display.cachedTextHeight != null) { return display.cachedTextHeight }
2888 if (measureText == null) {
2889 measureText = elt("pre", null, "CodeMirror-line-like");
2890 // Measure a bunch of lines, for browsers that compute
2891 // fractional heights.
2892 for (var i = 0; i < 49; ++i) {
2893 measureText.appendChild(document.createTextNode("x"));
2894 measureText.appendChild(elt("br"));
2895 }
2896 measureText.appendChild(document.createTextNode("x"));
2897 }
2898 removeChildrenAndAdd(display.measure, measureText);
2899 var height = measureText.offsetHeight / 50;
2900 if (height > 3) { display.cachedTextHeight = height; }
2901 removeChildren(display.measure);
2902 return height || 1
2903 }
2904
2905 // Compute the default character width.
2906 function charWidth(display) {
2907 if (display.cachedCharWidth != null) { return display.cachedCharWidth }
2908 var anchor = elt("span", "xxxxxxxxxx");
2909 var pre = elt("pre", [anchor], "CodeMirror-line-like");
2910 removeChildrenAndAdd(display.measure, pre);
2911 var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10;
2912 if (width > 2) { display.cachedCharWidth = width; }
2913 return width || 10
2914 }
2915
2916 // Do a bulk-read of the DOM positions and sizes needed to draw the
2917 // view, so that we don't interleave reading and writing to the DOM.
2918 function getDimensions(cm) {
2919 var d = cm.display, left = {}, width = {};
2920 var gutterLeft = d.gutters.clientLeft;
2921 for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
2922 var id = cm.display.gutterSpecs[i].className;
2923 left[id] = n.offsetLeft + n.clientLeft + gutterLeft;
2924 width[id] = n.clientWidth;
2925 }
2926 return {fixedPos: compensateForHScroll(d),
2927 gutterTotalWidth: d.gutters.offsetWidth,
2928 gutterLeft: left,
2929 gutterWidth: width,
2930 wrapperWidth: d.wrapper.clientWidth}
2931 }
2932
2933 // Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
2934 // but using getBoundingClientRect to get a sub-pixel-accurate
2935 // result.
2936 function compensateForHScroll(display) {
2937 return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left
2938 }
2939
2940 // Returns a function that estimates the height of a line, to use as
2941 // first approximation until the line becomes visible (and is thus
2942 // properly measurable).
2943 function estimateHeight(cm) {
2944 var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
2945 var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
2946 return function (line) {
2947 if (lineIsHidden(cm.doc, line)) { return 0 }
2948
2949 var widgetsHeight = 0;
2950 if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) {
2951 if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height; }
2952 } }
2953
2954 if (wrapping)
2955 { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th }
2956 else
2957 { return widgetsHeight + th }
2958 }
2959 }
2960
2961 function estimateLineHeights(cm) {
2962 var doc = cm.doc, est = estimateHeight(cm);
2963 doc.iter(function (line) {
2964 var estHeight = est(line);
2965 if (estHeight != line.height) { updateLineHeight(line, estHeight); }
2966 });
2967 }
2968
2969 // Given a mouse event, find the corresponding position. If liberal
2970 // is false, it checks whether a gutter or scrollbar was clicked,
2971 // and returns null if it was. forRect is used by rectangular
2972 // selections, and tries to estimate a character position even for
2973 // coordinates beyond the right of the text.
2974 function posFromMouse(cm, e, liberal, forRect) {
2975 var display = cm.display;
2976 if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") { return null }
2977
2978 var x, y, space = display.lineSpace.getBoundingClientRect();
2979 // Fails unpredictably on IE[67] when mouse is dragged around quickly.
2980 try { x = e.clientX - space.left; y = e.clientY - space.top; }
2981 catch (e$1) { return null }
2982 var coords = coordsChar(cm, x, y), line;
2983 if (forRect && coords.xRel > 0 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) {
2984 var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length;
2985 coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff));
2986 }
2987 return coords
2988 }
2989
2990 // Find the view element corresponding to a given line. Return null
2991 // when the line isn't visible.
2992 function findViewIndex(cm, n) {
2993 if (n >= cm.display.viewTo) { return null }
2994 n -= cm.display.viewFrom;
2995 if (n < 0) { return null }
2996 var view = cm.display.view;
2997 for (var i = 0; i < view.length; i++) {
2998 n -= view[i].size;
2999 if (n < 0) { return i }
3000 }
3001 }
3002
3003 // Updates the display.view data structure for a given change to the
3004 // document. From and to are in pre-change coordinates. Lendiff is
3005 // the amount of lines added or subtracted by the change. This is
3006 // used for changes that span multiple lines, or change the way
3007 // lines are divided into visual lines. regLineChange (below)
3008 // registers single-line changes.
3009 function regChange(cm, from, to, lendiff) {
3010 if (from == null) { from = cm.doc.first; }
3011 if (to == null) { to = cm.doc.first + cm.doc.size; }
3012 if (!lendiff) { lendiff = 0; }
3013
3014 var display = cm.display;
3015 if (lendiff && to < display.viewTo &&
3016 (display.updateLineNumbers == null || display.updateLineNumbers > from))
3017 { display.updateLineNumbers = from; }
3018
3019 cm.curOp.viewChanged = true;
3020
3021 if (from >= display.viewTo) { // Change after
3022 if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)
3023 { resetView(cm); }
3024 } else if (to <= display.viewFrom) { // Change before
3025 if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {
3026 resetView(cm);
3027 } else {
3028 display.viewFrom += lendiff;
3029 display.viewTo += lendiff;
3030 }
3031 } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap
3032 resetView(cm);
3033 } else if (from <= display.viewFrom) { // Top overlap
3034 var cut = viewCuttingPoint(cm, to, to + lendiff, 1);
3035 if (cut) {
3036 display.view = display.view.slice(cut.index);
3037 display.viewFrom = cut.lineN;
3038 display.viewTo += lendiff;
3039 } else {
3040 resetView(cm);
3041 }
3042 } else if (to >= display.viewTo) { // Bottom overlap
3043 var cut$1 = viewCuttingPoint(cm, from, from, -1);
3044 if (cut$1) {
3045 display.view = display.view.slice(0, cut$1.index);
3046 display.viewTo = cut$1.lineN;
3047 } else {
3048 resetView(cm);
3049 }
3050 } else { // Gap in the middle
3051 var cutTop = viewCuttingPoint(cm, from, from, -1);
3052 var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1);
3053 if (cutTop && cutBot) {
3054 display.view = display.view.slice(0, cutTop.index)
3055 .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))
3056 .concat(display.view.slice(cutBot.index));
3057 display.viewTo += lendiff;
3058 } else {
3059 resetView(cm);
3060 }
3061 }
3062
3063 var ext = display.externalMeasured;
3064 if (ext) {
3065 if (to < ext.lineN)
3066 { ext.lineN += lendiff; }
3067 else if (from < ext.lineN + ext.size)
3068 { display.externalMeasured = null; }
3069 }
3070 }
3071
3072 // Register a change to a single line. Type must be one of "text",
3073 // "gutter", "class", "widget"
3074 function regLineChange(cm, line, type) {
3075 cm.curOp.viewChanged = true;
3076 var display = cm.display, ext = cm.display.externalMeasured;
3077 if (ext && line >= ext.lineN && line < ext.lineN + ext.size)
3078 { display.externalMeasured = null; }
3079
3080 if (line < display.viewFrom || line >= display.viewTo) { return }
3081 var lineView = display.view[findViewIndex(cm, line)];
3082 if (lineView.node == null) { return }
3083 var arr = lineView.changes || (lineView.changes = []);
3084 if (indexOf(arr, type) == -1) { arr.push(type); }
3085 }
3086
3087 // Clear the view.
3088 function resetView(cm) {
3089 cm.display.viewFrom = cm.display.viewTo = cm.doc.first;
3090 cm.display.view = [];
3091 cm.display.viewOffset = 0;
3092 }
3093
3094 function viewCuttingPoint(cm, oldN, newN, dir) {
3095 var index = findViewIndex(cm, oldN), diff, view = cm.display.view;
3096 if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)
3097 { return {index: index, lineN: newN} }
3098 var n = cm.display.viewFrom;
3099 for (var i = 0; i < index; i++)
3100 { n += view[i].size; }
3101 if (n != oldN) {
3102 if (dir > 0) {
3103 if (index == view.length - 1) { return null }
3104 diff = (n + view[index].size) - oldN;
3105 index++;
3106 } else {
3107 diff = n - oldN;
3108 }
3109 oldN += diff; newN += diff;
3110 }
3111 while (visualLineNo(cm.doc, newN) != newN) {
3112 if (index == (dir < 0 ? 0 : view.length - 1)) { return null }
3113 newN += dir * view[index - (dir < 0 ? 1 : 0)].size;
3114 index += dir;
3115 }
3116 return {index: index, lineN: newN}
3117 }
3118
3119 // Force the view to cover a given range, adding empty view element
3120 // or clipping off existing ones as needed.
3121 function adjustView(cm, from, to) {
3122 var display = cm.display, view = display.view;
3123 if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {
3124 display.view = buildViewArray(cm, from, to);
3125 display.viewFrom = from;
3126 } else {
3127 if (display.viewFrom > from)
3128 { display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view); }
3129 else if (display.viewFrom < from)
3130 { display.view = display.view.slice(findViewIndex(cm, from)); }
3131 display.viewFrom = from;
3132 if (display.viewTo < to)
3133 { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)); }
3134 else if (display.viewTo > to)
3135 { display.view = display.view.slice(0, findViewIndex(cm, to)); }
3136 }
3137 display.viewTo = to;
3138 }
3139
3140 // Count the number of lines in the view whose DOM representation is
3141 // out of date (or nonexistent).
3142 function countDirtyView(cm) {
3143 var view = cm.display.view, dirty = 0;
3144 for (var i = 0; i < view.length; i++) {
3145 var lineView = view[i];
3146 if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty; }
3147 }
3148 return dirty
3149 }
3150
3151 function updateSelection(cm) {
3152 cm.display.input.showSelection(cm.display.input.prepareSelection());
3153 }
3154
3155 function prepareSelection(cm, primary) {
3156 if ( primary === void 0 ) primary = true;
3157
3158 var doc = cm.doc, result = {};
3159 var curFragment = result.cursors = document.createDocumentFragment();
3160 var selFragment = result.selection = document.createDocumentFragment();
3161
3162 var customCursor = cm.options.$customCursor;
3163 if (customCursor) { primary = true; }
3164 for (var i = 0; i < doc.sel.ranges.length; i++) {
3165 if (!primary && i == doc.sel.primIndex) { continue }
3166 var range = doc.sel.ranges[i];
3167 if (range.from().line >= cm.display.viewTo || range.to().line < cm.display.viewFrom) { continue }
3168 var collapsed = range.empty();
3169 if (customCursor) {
3170 var head = customCursor(cm, range);
3171 if (head) { drawSelectionCursor(cm, head, curFragment); }
3172 } else if (collapsed || cm.options.showCursorWhenSelecting) {
3173 drawSelectionCursor(cm, range.head, curFragment);
3174 }
3175 if (!collapsed)
3176 { drawSelectionRange(cm, range, selFragment); }
3177 }
3178 return result
3179 }
3180
3181 // Draws a cursor for the given range
3182 function drawSelectionCursor(cm, head, output) {
3183 var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine);
3184
3185 var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor"));
3186 cursor.style.left = pos.left + "px";
3187 cursor.style.top = pos.top + "px";
3188 cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
3189
3190 if (/\bcm-fat-cursor\b/.test(cm.getWrapperElement().className)) {
3191 var charPos = charCoords(cm, head, "div", null, null);
3192 var width = charPos.right - charPos.left;
3193 cursor.style.width = (width > 0 ? width : cm.defaultCharWidth()) + "px";
3194 }
3195
3196 if (pos.other) {
3197 // Secondary cursor, shown when on a 'jump' in bi-directional text
3198 var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"));
3199 otherCursor.style.display = "";
3200 otherCursor.style.left = pos.other.left + "px";
3201 otherCursor.style.top = pos.other.top + "px";
3202 otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
3203 }
3204 }
3205
3206 function cmpCoords(a, b) { return a.top - b.top || a.left - b.left }
3207
3208 // Draws the given range as a highlighted selection
3209 function drawSelectionRange(cm, range, output) {
3210 var display = cm.display, doc = cm.doc;
3211 var fragment = document.createDocumentFragment();
3212 var padding = paddingH(cm.display), leftSide = padding.left;
3213 var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right;
3214 var docLTR = doc.direction == "ltr";
3215
3216 function add(left, top, width, bottom) {
3217 if (top < 0) { top = 0; }
3218 top = Math.round(top);
3219 bottom = Math.round(bottom);
3220 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")));
3221 }
3222
3223 function drawForLine(line, fromArg, toArg) {
3224 var lineObj = getLine(doc, line);
3225 var lineLen = lineObj.text.length;
3226 var start, end;
3227 function coords(ch, bias) {
3228 return charCoords(cm, Pos(line, ch), "div", lineObj, bias)
3229 }
3230
3231 function wrapX(pos, dir, side) {
3232 var extent = wrappedLineExtentChar(cm, lineObj, null, pos);
3233 var prop = (dir == "ltr") == (side == "after") ? "left" : "right";
3234 var ch = side == "after" ? extent.begin : extent.end - (/\s/.test(lineObj.text.charAt(extent.end - 1)) ? 2 : 1);
3235 return coords(ch, prop)[prop]
3236 }
3237
3238 var order = getOrder(lineObj, doc.direction);
3239 iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir, i) {
3240 var ltr = dir == "ltr";
3241 var fromPos = coords(from, ltr ? "left" : "right");
3242 var toPos = coords(to - 1, ltr ? "right" : "left");
3243
3244 var openStart = fromArg == null && from == 0, openEnd = toArg == null && to == lineLen;
3245 var first = i == 0, last = !order || i == order.length - 1;
3246 if (toPos.top - fromPos.top <= 3) { // Single line
3247 var openLeft = (docLTR ? openStart : openEnd) && first;
3248 var openRight = (docLTR ? openEnd : openStart) && last;
3249 var left = openLeft ? leftSide : (ltr ? fromPos : toPos).left;
3250 var right = openRight ? rightSide : (ltr ? toPos : fromPos).right;
3251 add(left, fromPos.top, right - left, fromPos.bottom);
3252 } else { // Multiple lines
3253 var topLeft, topRight, botLeft, botRight;
3254 if (ltr) {
3255 topLeft = docLTR && openStart && first ? leftSide : fromPos.left;
3256 topRight = docLTR ? rightSide : wrapX(from, dir, "before");
3257 botLeft = docLTR ? leftSide : wrapX(to, dir, "after");
3258 botRight = docLTR && openEnd && last ? rightSide : toPos.right;
3259 } else {
3260 topLeft = !docLTR ? leftSide : wrapX(from, dir, "before");
3261 topRight = !docLTR && openStart && first ? rightSide : fromPos.right;
3262 botLeft = !docLTR && openEnd && last ? leftSide : toPos.left;
3263 botRight = !docLTR ? rightSide : wrapX(to, dir, "after");
3264 }
3265 add(topLeft, fromPos.top, topRight - topLeft, fromPos.bottom);
3266 if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top); }
3267 add(botLeft, toPos.top, botRight - botLeft, toPos.bottom);
3268 }
3269
3270 if (!start || cmpCoords(fromPos, start) < 0) { start = fromPos; }
3271 if (cmpCoords(toPos, start) < 0) { start = toPos; }
3272 if (!end || cmpCoords(fromPos, end) < 0) { end = fromPos; }
3273 if (cmpCoords(toPos, end) < 0) { end = toPos; }
3274 });
3275 return {start: start, end: end}
3276 }
3277
3278 var sFrom = range.from(), sTo = range.to();
3279 if (sFrom.line == sTo.line) {
3280 drawForLine(sFrom.line, sFrom.ch, sTo.ch);
3281 } else {
3282 var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line);
3283 var singleVLine = visualLine(fromLine) == visualLine(toLine);
3284 var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end;
3285 var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start;
3286 if (singleVLine) {
3287 if (leftEnd.top < rightStart.top - 2) {
3288 add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);
3289 add(leftSide, rightStart.top, rightStart.left, rightStart.bottom);
3290 } else {
3291 add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);
3292 }
3293 }
3294 if (leftEnd.bottom < rightStart.top)
3295 { add(leftSide, leftEnd.bottom, null, rightStart.top); }
3296 }
3297
3298 output.appendChild(fragment);
3299 }
3300
3301 // Cursor-blinking
3302 function restartBlink(cm) {
3303 if (!cm.state.focused) { return }
3304 var display = cm.display;
3305 clearInterval(display.blinker);
3306 var on = true;
3307 display.cursorDiv.style.visibility = "";
3308 if (cm.options.cursorBlinkRate > 0)
3309 { display.blinker = setInterval(function () {
3310 if (!cm.hasFocus()) { onBlur(cm); }
3311 display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden";
3312 }, cm.options.cursorBlinkRate); }
3313 else if (cm.options.cursorBlinkRate < 0)
3314 { display.cursorDiv.style.visibility = "hidden"; }
3315 }
3316
3317 function ensureFocus(cm) {
3318 if (!cm.hasFocus()) {
3319 cm.display.input.focus();
3320 if (!cm.state.focused) { onFocus(cm); }
3321 }
3322 }
3323
3324 function delayBlurEvent(cm) {
3325 cm.state.delayingBlurEvent = true;
3326 setTimeout(function () { if (cm.state.delayingBlurEvent) {
3327 cm.state.delayingBlurEvent = false;
3328 if (cm.state.focused) { onBlur(cm); }
3329 } }, 100);
3330 }
3331
3332 function onFocus(cm, e) {
3333 if (cm.state.delayingBlurEvent && !cm.state.draggingText) { cm.state.delayingBlurEvent = false; }
3334
3335 if (cm.options.readOnly == "nocursor") { return }
3336 if (!cm.state.focused) {
3337 signal(cm, "focus", cm, e);
3338 cm.state.focused = true;
3339 addClass(cm.display.wrapper, "CodeMirror-focused");
3340 // This test prevents this from firing when a context
3341 // menu is closed (since the input reset would kill the
3342 // select-all detection hack)
3343 if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {
3344 cm.display.input.reset();
3345 if (webkit) { setTimeout(function () { return cm.display.input.reset(true); }, 20); } // Issue #1730
3346 }
3347 cm.display.input.receivedFocus();
3348 }
3349 restartBlink(cm);
3350 }
3351 function onBlur(cm, e) {
3352 if (cm.state.delayingBlurEvent) { return }
3353
3354 if (cm.state.focused) {
3355 signal(cm, "blur", cm, e);
3356 cm.state.focused = false;
3357 rmClass(cm.display.wrapper, "CodeMirror-focused");
3358 }
3359 clearInterval(cm.display.blinker);
3360 setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false; } }, 150);
3361 }
3362
3363 // Read the actual heights of the rendered lines, and update their
3364 // stored heights to match.
3365 function updateHeightsInViewport(cm) {
3366 var display = cm.display;
3367 var prevBottom = display.lineDiv.offsetTop;
3368 var viewTop = Math.max(0, display.scroller.getBoundingClientRect().top);
3369 var oldHeight = display.lineDiv.getBoundingClientRect().top;
3370 var mustScroll = 0;
3371 for (var i = 0; i < display.view.length; i++) {
3372 var cur = display.view[i], wrapping = cm.options.lineWrapping;
3373 var height = (void 0), width = 0;
3374 if (cur.hidden) { continue }
3375 oldHeight += cur.line.height;
3376 if (ie && ie_version < 8) {
3377 var bot = cur.node.offsetTop + cur.node.offsetHeight;
3378 height = bot - prevBottom;
3379 prevBottom = bot;
3380 } else {
3381 var box = cur.node.getBoundingClientRect();
3382 height = box.bottom - box.top;
3383 // Check that lines don't extend past the right of the current
3384 // editor width
3385 if (!wrapping && cur.text.firstChild)
3386 { width = cur.text.firstChild.getBoundingClientRect().right - box.left - 1; }
3387 }
3388 var diff = cur.line.height - height;
3389 if (diff > .005 || diff < -.005) {
3390 if (oldHeight < viewTop) { mustScroll -= diff; }
3391 updateLineHeight(cur.line, height);
3392 updateWidgetHeight(cur.line);
3393 if (cur.rest) { for (var j = 0; j < cur.rest.length; j++)
3394 { updateWidgetHeight(cur.rest[j]); } }
3395 }
3396 if (width > cm.display.sizerWidth) {
3397 var chWidth = Math.ceil(width / charWidth(cm.display));
3398 if (chWidth > cm.display.maxLineLength) {
3399 cm.display.maxLineLength = chWidth;
3400 cm.display.maxLine = cur.line;
3401 cm.display.maxLineChanged = true;
3402 }
3403 }
3404 }
3405 if (Math.abs(mustScroll) > 2) { display.scroller.scrollTop += mustScroll; }
3406 }
3407
3408 // Read and store the height of line widgets associated with the
3409 // given line.
3410 function updateWidgetHeight(line) {
3411 if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i) {
3412 var w = line.widgets[i], parent = w.node.parentNode;
3413 if (parent) { w.height = parent.offsetHeight; }
3414 } }
3415 }
3416
3417 // Compute the lines that are visible in a given viewport (defaults
3418 // the the current scroll position). viewport may contain top,
3419 // height, and ensure (see op.scrollToPos) properties.
3420 function visibleLines(display, doc, viewport) {
3421 var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop;
3422 top = Math.floor(top - paddingTop(display));
3423 var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight;
3424
3425 var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom);
3426 // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
3427 // forces those lines into the viewport (if possible).
3428 if (viewport && viewport.ensure) {
3429 var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line;
3430 if (ensureFrom < from) {
3431 from = ensureFrom;
3432 to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight);
3433 } else if (Math.min(ensureTo, doc.lastLine()) >= to) {
3434 from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight);
3435 to = ensureTo;
3436 }
3437 }
3438 return {from: from, to: Math.max(to, from + 1)}
3439 }
3440
3441 // SCROLLING THINGS INTO VIEW
3442
3443 // If an editor sits on the top or bottom of the window, partially
3444 // scrolled out of view, this ensures that the cursor is visible.
3445 function maybeScrollWindow(cm, rect) {
3446 if (signalDOMEvent(cm, "scrollCursorIntoView")) { return }
3447
3448 var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;
3449 var doc = display.wrapper.ownerDocument;
3450 if (rect.top + box.top < 0) { doScroll = true; }
3451 else if (rect.bottom + box.top > (doc.defaultView.innerHeight || doc.documentElement.clientHeight)) { doScroll = false; }
3452 if (doScroll != null && !phantom) {
3453 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;"));
3454 cm.display.lineSpace.appendChild(scrollNode);
3455 scrollNode.scrollIntoView(doScroll);
3456 cm.display.lineSpace.removeChild(scrollNode);
3457 }
3458 }
3459
3460 // Scroll a given position into view (immediately), verifying that
3461 // it actually became visible (as line heights are accurately
3462 // measured, the position of something may 'drift' during drawing).
3463 function scrollPosIntoView(cm, pos, end, margin) {
3464 if (margin == null) { margin = 0; }
3465 var rect;
3466 if (!cm.options.lineWrapping && pos == end) {
3467 // Set pos and end to the cursor positions around the character pos sticks to
3468 // If pos.sticky == "before", that is around pos.ch - 1, otherwise around pos.ch
3469 // If pos == Pos(_, 0, "before"), pos and end are unchanged
3470 end = pos.sticky == "before" ? Pos(pos.line, pos.ch + 1, "before") : pos;
3471 pos = pos.ch ? Pos(pos.line, pos.sticky == "before" ? pos.ch - 1 : pos.ch, "after") : pos;
3472 }
3473 for (var limit = 0; limit < 5; limit++) {
3474 var changed = false;
3475 var coords = cursorCoords(cm, pos);
3476 var endCoords = !end || end == pos ? coords : cursorCoords(cm, end);
3477 rect = {left: Math.min(coords.left, endCoords.left),
3478 top: Math.min(coords.top, endCoords.top) - margin,
3479 right: Math.max(coords.left, endCoords.left),
3480 bottom: Math.max(coords.bottom, endCoords.bottom) + margin};
3481 var scrollPos = calculateScrollPos(cm, rect);
3482 var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
3483 if (scrollPos.scrollTop != null) {
3484 updateScrollTop(cm, scrollPos.scrollTop);
3485 if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true; }
3486 }
3487 if (scrollPos.scrollLeft != null) {
3488 setScrollLeft(cm, scrollPos.scrollLeft);
3489 if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true; }
3490 }
3491 if (!changed) { break }
3492 }
3493 return rect
3494 }
3495
3496 // Scroll a given set of coordinates into view (immediately).
3497 function scrollIntoView(cm, rect) {
3498 var scrollPos = calculateScrollPos(cm, rect);
3499 if (scrollPos.scrollTop != null) { updateScrollTop(cm, scrollPos.scrollTop); }
3500 if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft); }
3501 }
3502
3503 // Calculate a new scroll position needed to scroll the given
3504 // rectangle into view. Returns an object with scrollTop and
3505 // scrollLeft properties. When these are undefined, the
3506 // vertical/horizontal position does not need to be adjusted.
3507 function calculateScrollPos(cm, rect) {
3508 var display = cm.display, snapMargin = textHeight(cm.display);
3509 if (rect.top < 0) { rect.top = 0; }
3510 var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop;
3511 var screen = displayHeight(cm), result = {};
3512 if (rect.bottom - rect.top > screen) { rect.bottom = rect.top + screen; }
3513 var docBottom = cm.doc.height + paddingVert(display);
3514 var atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMargin;
3515 if (rect.top < screentop) {
3516 result.scrollTop = atTop ? 0 : rect.top;
3517 } else if (rect.bottom > screentop + screen) {
3518 var newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen);
3519 if (newTop != screentop) { result.scrollTop = newTop; }
3520 }
3521
3522 var gutterSpace = cm.options.fixedGutter ? 0 : display.gutters.offsetWidth;
3523 var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft - gutterSpace;
3524 var screenw = displayWidth(cm) - display.gutters.offsetWidth;
3525 var tooWide = rect.right - rect.left > screenw;
3526 if (tooWide) { rect.right = rect.left + screenw; }
3527 if (rect.left < 10)
3528 { result.scrollLeft = 0; }
3529 else if (rect.left < screenleft)
3530 { result.scrollLeft = Math.max(0, rect.left + gutterSpace - (tooWide ? 0 : 10)); }
3531 else if (rect.right > screenw + screenleft - 3)
3532 { result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw; }
3533 return result
3534 }
3535
3536 // Store a relative adjustment to the scroll position in the current
3537 // operation (to be applied when the operation finishes).
3538 function addToScrollTop(cm, top) {
3539 if (top == null) { return }
3540 resolveScrollToPos(cm);
3541 cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top;
3542 }
3543
3544 // Make sure that at the end of the operation the current cursor is
3545 // shown.
3546 function ensureCursorVisible(cm) {
3547 resolveScrollToPos(cm);
3548 var cur = cm.getCursor();
3549 cm.curOp.scrollToPos = {from: cur, to: cur, margin: cm.options.cursorScrollMargin};
3550 }
3551
3552 function scrollToCoords(cm, x, y) {
3553 if (x != null || y != null) { resolveScrollToPos(cm); }
3554 if (x != null) { cm.curOp.scrollLeft = x; }
3555 if (y != null) { cm.curOp.scrollTop = y; }
3556 }
3557
3558 function scrollToRange(cm, range) {
3559 resolveScrollToPos(cm);
3560 cm.curOp.scrollToPos = range;
3561 }
3562
3563 // When an operation has its scrollToPos property set, and another
3564 // scroll action is applied before the end of the operation, this
3565 // 'simulates' scrolling that position into view in a cheap way, so
3566 // that the effect of intermediate scroll commands is not ignored.
3567 function resolveScrollToPos(cm) {
3568 var range = cm.curOp.scrollToPos;
3569 if (range) {
3570 cm.curOp.scrollToPos = null;
3571 var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to);
3572 scrollToCoordsRange(cm, from, to, range.margin);
3573 }
3574 }
3575
3576 function scrollToCoordsRange(cm, from, to, margin) {
3577 var sPos = calculateScrollPos(cm, {
3578 left: Math.min(from.left, to.left),
3579 top: Math.min(from.top, to.top) - margin,
3580 right: Math.max(from.right, to.right),
3581 bottom: Math.max(from.bottom, to.bottom) + margin
3582 });
3583 scrollToCoords(cm, sPos.scrollLeft, sPos.scrollTop);
3584 }
3585
3586 // Sync the scrollable area and scrollbars, ensure the viewport
3587 // covers the visible area.
3588 function updateScrollTop(cm, val) {
3589 if (Math.abs(cm.doc.scrollTop - val) < 2) { return }
3590 if (!gecko) { updateDisplaySimple(cm, {top: val}); }
3591 setScrollTop(cm, val, true);
3592 if (gecko) { updateDisplaySimple(cm); }
3593 startWorker(cm, 100);
3594 }
3595
3596 function setScrollTop(cm, val, forceScroll) {
3597 val = Math.max(0, Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val));
3598 if (cm.display.scroller.scrollTop == val && !forceScroll) { return }
3599 cm.doc.scrollTop = val;
3600 cm.display.scrollbars.setScrollTop(val);
3601 if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = val; }
3602 }
3603
3604 // Sync scroller and scrollbar, ensure the gutter elements are
3605 // aligned.
3606 function setScrollLeft(cm, val, isScroller, forceScroll) {
3607 val = Math.max(0, Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth));
3608 if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) { return }
3609 cm.doc.scrollLeft = val;
3610 alignHorizontally(cm);
3611 if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val; }
3612 cm.display.scrollbars.setScrollLeft(val);
3613 }
3614
3615 // SCROLLBARS
3616
3617 // Prepare DOM reads needed to update the scrollbars. Done in one
3618 // shot to minimize update/measure roundtrips.
3619 function measureForScrollbars(cm) {
3620 var d = cm.display, gutterW = d.gutters.offsetWidth;
3621 var docH = Math.round(cm.doc.height + paddingVert(cm.display));
3622 return {
3623 clientHeight: d.scroller.clientHeight,
3624 viewHeight: d.wrapper.clientHeight,
3625 scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,
3626 viewWidth: d.wrapper.clientWidth,
3627 barLeft: cm.options.fixedGutter ? gutterW : 0,
3628 docHeight: docH,
3629 scrollHeight: docH + scrollGap(cm) + d.barHeight,
3630 nativeBarWidth: d.nativeBarWidth,
3631 gutterWidth: gutterW
3632 }
3633 }
3634
3635 var NativeScrollbars = function(place, scroll, cm) {
3636 this.cm = cm;
3637 var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar");
3638 var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar");
3639 vert.tabIndex = horiz.tabIndex = -1;
3640 place(vert); place(horiz);
3641
3642 on(vert, "scroll", function () {
3643 if (vert.clientHeight) { scroll(vert.scrollTop, "vertical"); }
3644 });
3645 on(horiz, "scroll", function () {
3646 if (horiz.clientWidth) { scroll(horiz.scrollLeft, "horizontal"); }
3647 });
3648
3649 this.checkedZeroWidth = false;
3650 // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
3651 if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = "18px"; }
3652 };
3653
3654 NativeScrollbars.prototype.update = function (measure) {
3655 var needsH = measure.scrollWidth > measure.clientWidth + 1;
3656 var needsV = measure.scrollHeight > measure.clientHeight + 1;
3657 var sWidth = measure.nativeBarWidth;
3658
3659 if (needsV) {
3660 this.vert.style.display = "block";
3661 this.vert.style.bottom = needsH ? sWidth + "px" : "0";
3662 var totalHeight = measure.viewHeight - (needsH ? sWidth : 0);
3663 // A bug in IE8 can cause this value to be negative, so guard it.
3664 this.vert.firstChild.style.height =
3665 Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px";
3666 } else {
3667 this.vert.scrollTop = 0;
3668 this.vert.style.display = "";
3669 this.vert.firstChild.style.height = "0";
3670 }
3671
3672 if (needsH) {
3673 this.horiz.style.display = "block";
3674 this.horiz.style.right = needsV ? sWidth + "px" : "0";
3675 this.horiz.style.left = measure.barLeft + "px";
3676 var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0);
3677 this.horiz.firstChild.style.width =
3678 Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + "px";
3679 } else {
3680 this.horiz.style.display = "";
3681 this.horiz.firstChild.style.width = "0";
3682 }
3683
3684 if (!this.checkedZeroWidth && measure.clientHeight > 0) {
3685 if (sWidth == 0) { this.zeroWidthHack(); }
3686 this.checkedZeroWidth = true;
3687 }
3688
3689 return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0}
3690 };
3691
3692 NativeScrollbars.prototype.setScrollLeft = function (pos) {
3693 if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos; }
3694 if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz, "horiz"); }
3695 };
3696
3697 NativeScrollbars.prototype.setScrollTop = function (pos) {
3698 if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos; }
3699 if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert, "vert"); }
3700 };
3701
3702 NativeScrollbars.prototype.zeroWidthHack = function () {
3703 var w = mac && !mac_geMountainLion ? "12px" : "18px";
3704 this.horiz.style.height = this.vert.style.width = w;
3705 this.horiz.style.visibility = this.vert.style.visibility = "hidden";
3706 this.disableHoriz = new Delayed;
3707 this.disableVert = new Delayed;
3708 };
3709
3710 NativeScrollbars.prototype.enableZeroWidthBar = function (bar, delay, type) {
3711 bar.style.visibility = "";
3712 function maybeDisable() {
3713 // To find out whether the scrollbar is still visible, we
3714 // check whether the element under the pixel in the bottom
3715 // right corner of the scrollbar box is the scrollbar box
3716 // itself (when the bar is still visible) or its filler child
3717 // (when the bar is hidden). If it is still visible, we keep
3718 // it enabled, if it's hidden, we disable pointer events.
3719 var box = bar.getBoundingClientRect();
3720 var elt = type == "vert" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2)
3721 : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1);
3722 if (elt != bar) { bar.style.visibility = "hidden"; }
3723 else { delay.set(1000, maybeDisable); }
3724 }
3725 delay.set(1000, maybeDisable);
3726 };
3727
3728 NativeScrollbars.prototype.clear = function () {
3729 var parent = this.horiz.parentNode;
3730 parent.removeChild(this.horiz);
3731 parent.removeChild(this.vert);
3732 };
3733
3734 var NullScrollbars = function () {};
3735
3736 NullScrollbars.prototype.update = function () { return {bottom: 0, right: 0} };
3737 NullScrollbars.prototype.setScrollLeft = function () {};
3738 NullScrollbars.prototype.setScrollTop = function () {};
3739 NullScrollbars.prototype.clear = function () {};
3740
3741 function updateScrollbars(cm, measure) {
3742 if (!measure) { measure = measureForScrollbars(cm); }
3743 var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight;
3744 updateScrollbarsInner(cm, measure);
3745 for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) {
3746 if (startWidth != cm.display.barWidth && cm.options.lineWrapping)
3747 { updateHeightsInViewport(cm); }
3748 updateScrollbarsInner(cm, measureForScrollbars(cm));
3749 startWidth = cm.display.barWidth; startHeight = cm.display.barHeight;
3750 }
3751 }
3752
3753 // Re-synchronize the fake scrollbars with the actual size of the
3754 // content.
3755 function updateScrollbarsInner(cm, measure) {
3756 var d = cm.display;
3757 var sizes = d.scrollbars.update(measure);
3758
3759 d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px";
3760 d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px";
3761 d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent";
3762
3763 if (sizes.right && sizes.bottom) {
3764 d.scrollbarFiller.style.display = "block";
3765 d.scrollbarFiller.style.height = sizes.bottom + "px";
3766 d.scrollbarFiller.style.width = sizes.right + "px";
3767 } else { d.scrollbarFiller.style.display = ""; }
3768 if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
3769 d.gutterFiller.style.display = "block";
3770 d.gutterFiller.style.height = sizes.bottom + "px";
3771 d.gutterFiller.style.width = measure.gutterWidth + "px";
3772 } else { d.gutterFiller.style.display = ""; }
3773 }
3774
3775 var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars};
3776
3777 function initScrollbars(cm) {
3778 if (cm.display.scrollbars) {
3779 cm.display.scrollbars.clear();
3780 if (cm.display.scrollbars.addClass)
3781 { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass); }
3782 }
3783
3784 cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) {
3785 cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller);
3786 // Prevent clicks in the scrollbars from killing focus
3787 on(node, "mousedown", function () {
3788 if (cm.state.focused) { setTimeout(function () { return cm.display.input.focus(); }, 0); }
3789 });
3790 node.setAttribute("cm-not-content", "true");
3791 }, function (pos, axis) {
3792 if (axis == "horizontal") { setScrollLeft(cm, pos); }
3793 else { updateScrollTop(cm, pos); }
3794 }, cm);
3795 if (cm.display.scrollbars.addClass)
3796 { addClass(cm.display.wrapper, cm.display.scrollbars.addClass); }
3797 }
3798
3799 // Operations are used to wrap a series of changes to the editor
3800 // state in such a way that each change won't have to update the
3801 // cursor and display (which would be awkward, slow, and
3802 // error-prone). Instead, display updates are batched and then all
3803 // combined and executed at once.
3804
3805 var nextOpId = 0;
3806 // Start a new operation.
3807 function startOperation(cm) {
3808 cm.curOp = {
3809 cm: cm,
3810 viewChanged: false, // Flag that indicates that lines might need to be redrawn
3811 startHeight: cm.doc.height, // Used to detect need to update scrollbar
3812 forceUpdate: false, // Used to force a redraw
3813 updateInput: 0, // Whether to reset the input textarea
3814 typing: false, // Whether this reset should be careful to leave existing text (for compositing)
3815 changeObjs: null, // Accumulated changes, for firing change events
3816 cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on
3817 cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
3818 selectionChanged: false, // Whether the selection needs to be redrawn
3819 updateMaxLine: false, // Set when the widest line needs to be determined anew
3820 scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
3821 scrollToPos: null, // Used to scroll to a specific position
3822 focus: false,
3823 id: ++nextOpId, // Unique ID
3824 markArrays: null // Used by addMarkedSpan
3825 };
3826 pushOperation(cm.curOp);
3827 }
3828
3829 // Finish an operation, updating the display and signalling delayed events
3830 function endOperation(cm) {
3831 var op = cm.curOp;
3832 if (op) { finishOperation(op, function (group) {
3833 for (var i = 0; i < group.ops.length; i++)
3834 { group.ops[i].cm.curOp = null; }
3835 endOperations(group);
3836 }); }
3837 }
3838
3839 // The DOM updates done when an operation finishes are batched so
3840 // that the minimum number of relayouts are required.
3841 function endOperations(group) {
3842 var ops = group.ops;
3843 for (var i = 0; i < ops.length; i++) // Read DOM
3844 { endOperation_R1(ops[i]); }
3845 for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe)
3846 { endOperation_W1(ops[i$1]); }
3847 for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM
3848 { endOperation_R2(ops[i$2]); }
3849 for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe)
3850 { endOperation_W2(ops[i$3]); }
3851 for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM
3852 { endOperation_finish(ops[i$4]); }
3853 }
3854
3855 function endOperation_R1(op) {
3856 var cm = op.cm, display = cm.display;
3857 maybeClipScrollbars(cm);
3858 if (op.updateMaxLine) { findMaxLine(cm); }
3859
3860 op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||
3861 op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
3862 op.scrollToPos.to.line >= display.viewTo) ||
3863 display.maxLineChanged && cm.options.lineWrapping;
3864 op.update = op.mustUpdate &&
3865 new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate);
3866 }
3867
3868 function endOperation_W1(op) {
3869 op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update);
3870 }
3871
3872 function endOperation_R2(op) {
3873 var cm = op.cm, display = cm.display;
3874 if (op.updatedDisplay) { updateHeightsInViewport(cm); }
3875
3876 op.barMeasure = measureForScrollbars(cm);
3877
3878 // If the max line changed since it was last measured, measure it,
3879 // and ensure the document's width matches it.
3880 // updateDisplay_W2 will use these properties to do the actual resizing
3881 if (display.maxLineChanged && !cm.options.lineWrapping) {
3882 op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3;
3883 cm.display.sizerWidth = op.adjustWidthTo;
3884 op.barMeasure.scrollWidth =
3885 Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth);
3886 op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm));
3887 }
3888
3889 if (op.updatedDisplay || op.selectionChanged)
3890 { op.preparedSelection = display.input.prepareSelection(); }
3891 }
3892
3893 function endOperation_W2(op) {
3894 var cm = op.cm;
3895
3896 if (op.adjustWidthTo != null) {
3897 cm.display.sizer.style.minWidth = op.adjustWidthTo + "px";
3898 if (op.maxScrollLeft < cm.doc.scrollLeft)
3899 { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true); }
3900 cm.display.maxLineChanged = false;
3901 }
3902
3903 var takeFocus = op.focus && op.focus == activeElt(doc(cm));
3904 if (op.preparedSelection)
3905 { cm.display.input.showSelection(op.preparedSelection, takeFocus); }
3906 if (op.updatedDisplay || op.startHeight != cm.doc.height)
3907 { updateScrollbars(cm, op.barMeasure); }
3908 if (op.updatedDisplay)
3909 { setDocumentHeight(cm, op.barMeasure); }
3910
3911 if (op.selectionChanged) { restartBlink(cm); }
3912
3913 if (cm.state.focused && op.updateInput)
3914 { cm.display.input.reset(op.typing); }
3915 if (takeFocus) { ensureFocus(op.cm); }
3916 }
3917
3918 function endOperation_finish(op) {
3919 var cm = op.cm, display = cm.display, doc = cm.doc;
3920
3921 if (op.updatedDisplay) { postUpdateDisplay(cm, op.update); }
3922
3923 // Abort mouse wheel delta measurement, when scrolling explicitly
3924 if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))
3925 { display.wheelStartX = display.wheelStartY = null; }
3926
3927 // Propagate the scroll position to the actual DOM scroller
3928 if (op.scrollTop != null) { setScrollTop(cm, op.scrollTop, op.forceScroll); }
3929
3930 if (op.scrollLeft != null) { setScrollLeft(cm, op.scrollLeft, true, true); }
3931 // If we need to scroll a specific position into view, do so.
3932 if (op.scrollToPos) {
3933 var rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
3934 clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin);
3935 maybeScrollWindow(cm, rect);
3936 }
3937
3938 // Fire events for markers that are hidden/unidden by editing or
3939 // undoing
3940 var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
3941 if (hidden) { for (var i = 0; i < hidden.length; ++i)
3942 { if (!hidden[i].lines.length) { signal(hidden[i], "hide"); } } }
3943 if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1)
3944 { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], "unhide"); } } }
3945
3946 if (display.wrapper.offsetHeight)
3947 { doc.scrollTop = cm.display.scroller.scrollTop; }
3948
3949 // Fire change events, and delayed event handlers
3950 if (op.changeObjs)
3951 { signal(cm, "changes", cm, op.changeObjs); }
3952 if (op.update)
3953 { op.update.finish(); }
3954 }
3955
3956 // Run the given function in an operation
3957 function runInOp(cm, f) {
3958 if (cm.curOp) { return f() }
3959 startOperation(cm);
3960 try { return f() }
3961 finally { endOperation(cm); }
3962 }
3963 // Wraps a function in an operation. Returns the wrapped function.
3964 function operation(cm, f) {
3965 return function() {
3966 if (cm.curOp) { return f.apply(cm, arguments) }
3967 startOperation(cm);
3968 try { return f.apply(cm, arguments) }
3969 finally { endOperation(cm); }
3970 }
3971 }
3972 // Used to add methods to editor and doc instances, wrapping them in
3973 // operations.
3974 function methodOp(f) {
3975 return function() {
3976 if (this.curOp) { return f.apply(this, arguments) }
3977 startOperation(this);
3978 try { return f.apply(this, arguments) }
3979 finally { endOperation(this); }
3980 }
3981 }
3982 function docMethodOp(f) {
3983 return function() {
3984 var cm = this.cm;
3985 if (!cm || cm.curOp) { return f.apply(this, arguments) }
3986 startOperation(cm);
3987 try { return f.apply(this, arguments) }
3988 finally { endOperation(cm); }
3989 }
3990 }
3991
3992 // HIGHLIGHT WORKER
3993
3994 function startWorker(cm, time) {
3995 if (cm.doc.highlightFrontier < cm.display.viewTo)
3996 { cm.state.highlight.set(time, bind(highlightWorker, cm)); }
3997 }
3998
3999 function highlightWorker(cm) {
4000 var doc = cm.doc;
4001 if (doc.highlightFrontier >= cm.display.viewTo) { return }
4002 var end = +new Date + cm.options.workTime;
4003 var context = getContextBefore(cm, doc.highlightFrontier);
4004 var changedLines = [];
4005
4006 doc.iter(context.line, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function (line) {
4007 if (context.line >= cm.display.viewFrom) { // Visible
4008 var oldStyles = line.styles;
4009 var resetState = line.text.length > cm.options.maxHighlightLength ? copyState(doc.mode, context.state) : null;
4010 var highlighted = highlightLine(cm, line, context, true);
4011 if (resetState) { context.state = resetState; }
4012 line.styles = highlighted.styles;
4013 var oldCls = line.styleClasses, newCls = highlighted.classes;
4014 if (newCls) { line.styleClasses = newCls; }
4015 else if (oldCls) { line.styleClasses = null; }
4016 var ischange = !oldStyles || oldStyles.length != line.styles.length ||
4017 oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass);
4018 for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldStyles[i] != line.styles[i]; }
4019 if (ischange) { changedLines.push(context.line); }
4020 line.stateAfter = context.save();
4021 context.nextLine();
4022 } else {
4023 if (line.text.length <= cm.options.maxHighlightLength)
4024 { processLine(cm, line.text, context); }
4025 line.stateAfter = context.line % 5 == 0 ? context.save() : null;
4026 context.nextLine();
4027 }
4028 if (+new Date > end) {
4029 startWorker(cm, cm.options.workDelay);
4030 return true
4031 }
4032 });
4033 doc.highlightFrontier = context.line;
4034 doc.modeFrontier = Math.max(doc.modeFrontier, context.line);
4035 if (changedLines.length) { runInOp(cm, function () {
4036 for (var i = 0; i < changedLines.length; i++)
4037 { regLineChange(cm, changedLines[i], "text"); }
4038 }); }
4039 }
4040
4041 // DISPLAY DRAWING
4042
4043 var DisplayUpdate = function(cm, viewport, force) {
4044 var display = cm.display;
4045
4046 this.viewport = viewport;
4047 // Store some values that we'll need later (but don't want to force a relayout for)
4048 this.visible = visibleLines(display, cm.doc, viewport);
4049 this.editorIsHidden = !display.wrapper.offsetWidth;
4050 this.wrapperHeight = display.wrapper.clientHeight;
4051 this.wrapperWidth = display.wrapper.clientWidth;
4052 this.oldDisplayWidth = displayWidth(cm);
4053 this.force = force;
4054 this.dims = getDimensions(cm);
4055 this.events = [];
4056 };
4057
4058 DisplayUpdate.prototype.signal = function (emitter, type) {
4059 if (hasHandler(emitter, type))
4060 { this.events.push(arguments); }
4061 };
4062 DisplayUpdate.prototype.finish = function () {
4063 for (var i = 0; i < this.events.length; i++)
4064 { signal.apply(null, this.events[i]); }
4065 };
4066
4067 function maybeClipScrollbars(cm) {
4068 var display = cm.display;
4069 if (!display.scrollbarsClipped && display.scroller.offsetWidth) {
4070 display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth;
4071 display.heightForcer.style.height = scrollGap(cm) + "px";
4072 display.sizer.style.marginBottom = -display.nativeBarWidth + "px";
4073 display.sizer.style.borderRightWidth = scrollGap(cm) + "px";
4074 display.scrollbarsClipped = true;
4075 }
4076 }
4077
4078 function selectionSnapshot(cm) {
4079 if (cm.hasFocus()) { return null }
4080 var active = activeElt(doc(cm));
4081 if (!active || !contains(cm.display.lineDiv, active)) { return null }
4082 var result = {activeElt: active};
4083 if (window.getSelection) {
4084 var sel = win(cm).getSelection();
4085 if (sel.anchorNode && sel.extend && contains(cm.display.lineDiv, sel.anchorNode)) {
4086 result.anchorNode = sel.anchorNode;
4087 result.anchorOffset = sel.anchorOffset;
4088 result.focusNode = sel.focusNode;
4089 result.focusOffset = sel.focusOffset;
4090 }
4091 }
4092 return result
4093 }
4094
4095 function restoreSelection(snapshot) {
4096 if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt(snapshot.activeElt.ownerDocument)) { return }
4097 snapshot.activeElt.focus();
4098 if (!/^(INPUT|TEXTAREA)$/.test(snapshot.activeElt.nodeName) &&
4099 snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) {
4100 var doc = snapshot.activeElt.ownerDocument;
4101 var sel = doc.defaultView.getSelection(), range = doc.createRange();
4102 range.setEnd(snapshot.anchorNode, snapshot.anchorOffset);
4103 range.collapse(false);
4104 sel.removeAllRanges();
4105 sel.addRange(range);
4106 sel.extend(snapshot.focusNode, snapshot.focusOffset);
4107 }
4108 }
4109
4110 // Does the actual updating of the line display. Bails out
4111 // (returning false) when there is nothing to be done and forced is
4112 // false.
4113 function updateDisplayIfNeeded(cm, update) {
4114 var display = cm.display, doc = cm.doc;
4115
4116 if (update.editorIsHidden) {
4117 resetView(cm);
4118 return false
4119 }
4120
4121 // Bail out if the visible area is already rendered and nothing changed.
4122 if (!update.force &&
4123 update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&
4124 (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&
4125 display.renderedView == display.view && countDirtyView(cm) == 0)
4126 { return false }
4127
4128 if (maybeUpdateLineNumberWidth(cm)) {
4129 resetView(cm);
4130 update.dims = getDimensions(cm);
4131 }
4132
4133 // Compute a suitable new viewport (from & to)
4134 var end = doc.first + doc.size;
4135 var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first);
4136 var to = Math.min(end, update.visible.to + cm.options.viewportMargin);
4137 if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max(doc.first, display.viewFrom); }
4138 if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, display.viewTo); }
4139 if (sawCollapsedSpans) {
4140 from = visualLineNo(cm.doc, from);
4141 to = visualLineEndNo(cm.doc, to);
4142 }
4143
4144 var different = from != display.viewFrom || to != display.viewTo ||
4145 display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth;
4146 adjustView(cm, from, to);
4147
4148 display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom));
4149 // Position the mover div to align with the current scroll position
4150 cm.display.mover.style.top = display.viewOffset + "px";
4151
4152 var toUpdate = countDirtyView(cm);
4153 if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view &&
4154 (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))
4155 { return false }
4156
4157 // For big changes, we hide the enclosing element during the
4158 // update, since that speeds up the operations on most browsers.
4159 var selSnapshot = selectionSnapshot(cm);
4160 if (toUpdate > 4) { display.lineDiv.style.display = "none"; }
4161 patchDisplay(cm, display.updateLineNumbers, update.dims);
4162 if (toUpdate > 4) { display.lineDiv.style.display = ""; }
4163 display.renderedView = display.view;
4164 // There might have been a widget with a focused element that got
4165 // hidden or updated, if so re-focus it.
4166 restoreSelection(selSnapshot);
4167
4168 // Prevent selection and cursors from interfering with the scroll
4169 // width and height.
4170 removeChildren(display.cursorDiv);
4171 removeChildren(display.selectionDiv);
4172 display.gutters.style.height = display.sizer.style.minHeight = 0;
4173
4174 if (different) {
4175 display.lastWrapHeight = update.wrapperHeight;
4176 display.lastWrapWidth = update.wrapperWidth;
4177 startWorker(cm, 400);
4178 }
4179
4180 display.updateLineNumbers = null;
4181
4182 return true
4183 }
4184
4185 function postUpdateDisplay(cm, update) {
4186 var viewport = update.viewport;
4187
4188 for (var first = true;; first = false) {
4189 if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) {
4190 // Clip forced viewport to actual scrollable area.
4191 if (viewport && viewport.top != null)
4192 { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)}; }
4193 // Updated line heights might result in the drawn area not
4194 // actually covering the viewport. Keep looping until it does.
4195 update.visible = visibleLines(cm.display, cm.doc, viewport);
4196 if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)
4197 { break }
4198 } else if (first) {
4199 update.visible = visibleLines(cm.display, cm.doc, viewport);
4200 }
4201 if (!updateDisplayIfNeeded(cm, update)) { break }
4202 updateHeightsInViewport(cm);
4203 var barMeasure = measureForScrollbars(cm);
4204 updateSelection(cm);
4205 updateScrollbars(cm, barMeasure);
4206 setDocumentHeight(cm, barMeasure);
4207 update.force = false;
4208 }
4209
4210 update.signal(cm, "update", cm);
4211 if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {
4212 update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo);
4213 cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo;
4214 }
4215 }
4216
4217 function updateDisplaySimple(cm, viewport) {
4218 var update = new DisplayUpdate(cm, viewport);
4219 if (updateDisplayIfNeeded(cm, update)) {
4220 updateHeightsInViewport(cm);
4221 postUpdateDisplay(cm, update);
4222 var barMeasure = measureForScrollbars(cm);
4223 updateSelection(cm);
4224 updateScrollbars(cm, barMeasure);
4225 setDocumentHeight(cm, barMeasure);
4226 update.finish();
4227 }
4228 }
4229
4230 // Sync the actual display DOM structure with display.view, removing
4231 // nodes for lines that are no longer in view, and creating the ones
4232 // that are not there yet, and updating the ones that are out of
4233 // date.
4234 function patchDisplay(cm, updateNumbersFrom, dims) {
4235 var display = cm.display, lineNumbers = cm.options.lineNumbers;
4236 var container = display.lineDiv, cur = container.firstChild;
4237
4238 function rm(node) {
4239 var next = node.nextSibling;
4240 // Works around a throw-scroll bug in OS X Webkit
4241 if (webkit && mac && cm.display.currentWheelTarget == node)
4242 { node.style.display = "none"; }
4243 else
4244 { node.parentNode.removeChild(node); }
4245 return next
4246 }
4247
4248 var view = display.view, lineN = display.viewFrom;
4249 // Loop over the elements in the view, syncing cur (the DOM nodes
4250 // in display.lineDiv) with the view as we go.
4251 for (var i = 0; i < view.length; i++) {
4252 var lineView = view[i];
4253 if (lineView.hidden) ; else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet
4254 var node = buildLineElement(cm, lineView, lineN, dims);
4255 container.insertBefore(node, cur);
4256 } else { // Already drawn
4257 while (cur != lineView.node) { cur = rm(cur); }
4258 var updateNumber = lineNumbers && updateNumbersFrom != null &&
4259 updateNumbersFrom <= lineN && lineView.lineNumber;
4260 if (lineView.changes) {
4261 if (indexOf(lineView.changes, "gutter") > -1) { updateNumber = false; }
4262 updateLineForChanges(cm, lineView, lineN, dims);
4263 }
4264 if (updateNumber) {
4265 removeChildren(lineView.lineNumber);
4266 lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN)));
4267 }
4268 cur = lineView.node.nextSibling;
4269 }
4270 lineN += lineView.size;
4271 }
4272 while (cur) { cur = rm(cur); }
4273 }
4274
4275 function updateGutterSpace(display) {
4276 var width = display.gutters.offsetWidth;
4277 display.sizer.style.marginLeft = width + "px";
4278 // Send an event to consumers responding to changes in gutter width.
4279 signalLater(display, "gutterChanged", display);
4280 }
4281
4282 function setDocumentHeight(cm, measure) {
4283 cm.display.sizer.style.minHeight = measure.docHeight + "px";
4284 cm.display.heightForcer.style.top = measure.docHeight + "px";
4285 cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px";
4286 }
4287
4288 // Re-align line numbers and gutter marks to compensate for
4289 // horizontal scrolling.
4290 function alignHorizontally(cm) {
4291 var display = cm.display, view = display.view;
4292 if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) { return }
4293 var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;
4294 var gutterW = display.gutters.offsetWidth, left = comp + "px";
4295 for (var i = 0; i < view.length; i++) { if (!view[i].hidden) {
4296 if (cm.options.fixedGutter) {
4297 if (view[i].gutter)
4298 { view[i].gutter.style.left = left; }
4299 if (view[i].gutterBackground)
4300 { view[i].gutterBackground.style.left = left; }
4301 }
4302 var align = view[i].alignable;
4303 if (align) { for (var j = 0; j < align.length; j++)
4304 { align[j].style.left = left; } }
4305 } }
4306 if (cm.options.fixedGutter)
4307 { display.gutters.style.left = (comp + gutterW) + "px"; }
4308 }
4309
4310 // Used to ensure that the line number gutter is still the right
4311 // size for the current document size. Returns true when an update
4312 // is needed.
4313 function maybeUpdateLineNumberWidth(cm) {
4314 if (!cm.options.lineNumbers) { return false }
4315 var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;
4316 if (last.length != display.lineNumChars) {
4317 var test = display.measure.appendChild(elt("div", [elt("div", last)],
4318 "CodeMirror-linenumber CodeMirror-gutter-elt"));
4319 var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
4320 display.lineGutter.style.width = "";
4321 display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1;
4322 display.lineNumWidth = display.lineNumInnerWidth + padding;
4323 display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
4324 display.lineGutter.style.width = display.lineNumWidth + "px";
4325 updateGutterSpace(cm.display);
4326 return true
4327 }
4328 return false
4329 }
4330
4331 function getGutters(gutters, lineNumbers) {
4332 var result = [], sawLineNumbers = false;
4333 for (var i = 0; i < gutters.length; i++) {
4334 var name = gutters[i], style = null;
4335 if (typeof name != "string") { style = name.style; name = name.className; }
4336 if (name == "CodeMirror-linenumbers") {
4337 if (!lineNumbers) { continue }
4338 else { sawLineNumbers = true; }
4339 }
4340 result.push({className: name, style: style});
4341 }
4342 if (lineNumbers && !sawLineNumbers) { result.push({className: "CodeMirror-linenumbers", style: null}); }
4343 return result
4344 }
4345
4346 // Rebuild the gutter elements, ensure the margin to the left of the
4347 // code matches their width.
4348 function renderGutters(display) {
4349 var gutters = display.gutters, specs = display.gutterSpecs;
4350 removeChildren(gutters);
4351 display.lineGutter = null;
4352 for (var i = 0; i < specs.length; ++i) {
4353 var ref = specs[i];
4354 var className = ref.className;
4355 var style = ref.style;
4356 var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + className));
4357 if (style) { gElt.style.cssText = style; }
4358 if (className == "CodeMirror-linenumbers") {
4359 display.lineGutter = gElt;
4360 gElt.style.width = (display.lineNumWidth || 1) + "px";
4361 }
4362 }
4363 gutters.style.display = specs.length ? "" : "none";
4364 updateGutterSpace(display);
4365 }
4366
4367 function updateGutters(cm) {
4368 renderGutters(cm.display);
4369 regChange(cm);
4370 alignHorizontally(cm);
4371 }
4372
4373 // The display handles the DOM integration, both for input reading
4374 // and content drawing. It holds references to DOM nodes and
4375 // display-related state.
4376
4377 function Display(place, doc, input, options) {
4378 var d = this;
4379 this.input = input;
4380
4381 // Covers bottom-right square when both scrollbars are present.
4382 d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
4383 d.scrollbarFiller.setAttribute("cm-not-content", "true");
4384 // Covers bottom of gutter when coverGutterNextToScrollbar is on
4385 // and h scrollbar is present.
4386 d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler");
4387 d.gutterFiller.setAttribute("cm-not-content", "true");
4388 // Will contain the actual code, positioned to cover the viewport.
4389 d.lineDiv = eltP("div", null, "CodeMirror-code");
4390 // Elements are added to these to represent selection and cursors.
4391 d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
4392 d.cursorDiv = elt("div", null, "CodeMirror-cursors");
4393 // A visibility: hidden element used to find the size of things.
4394 d.measure = elt("div", null, "CodeMirror-measure");
4395 // When lines outside of the viewport are measured, they are drawn in this.
4396 d.lineMeasure = elt("div", null, "CodeMirror-measure");
4397 // Wraps everything that needs to exist inside the vertically-padded coordinate system
4398 d.lineSpace = eltP("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],
4399 null, "position: relative; outline: none");
4400 var lines = eltP("div", [d.lineSpace], "CodeMirror-lines");
4401 // Moved around its parent to cover visible view.
4402 d.mover = elt("div", [lines], null, "position: relative");
4403 // Set to the height of the document, allowing scrolling.
4404 d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
4405 d.sizerWidth = null;
4406 // Behavior of elts with overflow: auto and padding is
4407 // inconsistent across browsers. This is used to ensure the
4408 // scrollable area is big enough.
4409 d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;");
4410 // Will contain the gutters, if any.
4411 d.gutters = elt("div", null, "CodeMirror-gutters");
4412 d.lineGutter = null;
4413 // Actual scrollable element.
4414 d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll");
4415 d.scroller.setAttribute("tabIndex", "-1");
4416 // The element in which the editor lives.
4417 d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror");
4418 // See #6982. FIXME remove when this has been fixed for a while in Chrome
4419 if (chrome && chrome_version >= 105) { d.wrapper.style.clipPath = "inset(0px)"; }
4420
4421 // This attribute is respected by automatic translation systems such as Google Translate,
4422 // and may also be respected by tools used by human translators.
4423 d.wrapper.setAttribute('translate', 'no');
4424
4425 // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)
4426 if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
4427 if (!webkit && !(gecko && mobile)) { d.scroller.draggable = true; }
4428
4429 if (place) {
4430 if (place.appendChild) { place.appendChild(d.wrapper); }
4431 else { place(d.wrapper); }
4432 }
4433
4434 // Current rendered range (may be bigger than the view window).
4435 d.viewFrom = d.viewTo = doc.first;
4436 d.reportedViewFrom = d.reportedViewTo = doc.first;
4437 // Information about the rendered lines.
4438 d.view = [];
4439 d.renderedView = null;
4440 // Holds info about a single rendered line when it was rendered
4441 // for measurement, while not in view.
4442 d.externalMeasured = null;
4443 // Empty space (in pixels) above the view
4444 d.viewOffset = 0;
4445 d.lastWrapHeight = d.lastWrapWidth = 0;
4446 d.updateLineNumbers = null;
4447
4448 d.nativeBarWidth = d.barHeight = d.barWidth = 0;
4449 d.scrollbarsClipped = false;
4450
4451 // Used to only resize the line number gutter when necessary (when
4452 // the amount of lines crosses a boundary that makes its width change)
4453 d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
4454 // Set to true when a non-horizontal-scrolling line widget is
4455 // added. As an optimization, line widget aligning is skipped when
4456 // this is false.
4457 d.alignWidgets = false;
4458
4459 d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
4460
4461 // Tracks the maximum line length so that the horizontal scrollbar
4462 // can be kept static when scrolling.
4463 d.maxLine = null;
4464 d.maxLineLength = 0;
4465 d.maxLineChanged = false;
4466
4467 // Used for measuring wheel scrolling granularity
4468 d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
4469
4470 // True when shift is held down.
4471 d.shift = false;
4472
4473 // Used to track whether anything happened since the context menu
4474 // was opened.
4475 d.selForContextMenu = null;
4476
4477 d.activeTouch = null;
4478
4479 d.gutterSpecs = getGutters(options.gutters, options.lineNumbers);
4480 renderGutters(d);
4481
4482 input.init(d);
4483 }
4484
4485 // Since the delta values reported on mouse wheel events are
4486 // unstandardized between browsers and even browser versions, and
4487 // generally horribly unpredictable, this code starts by measuring
4488 // the scroll effect that the first few mouse wheel events have,
4489 // and, from that, detects the way it can convert deltas to pixel
4490 // offsets afterwards.
4491 //
4492 // The reason we want to know the amount a wheel event will scroll
4493 // is that it gives us a chance to update the display before the
4494 // actual scrolling happens, reducing flickering.
4495
4496 var wheelSamples = 0, wheelPixelsPerUnit = null;
4497 // Fill in a browser-detected starting value on browsers where we
4498 // know one. These don't have to be accurate -- the result of them
4499 // being wrong would just be a slight flicker on the first wheel
4500 // scroll (if it is large enough).
4501 if (ie) { wheelPixelsPerUnit = -.53; }
4502 else if (gecko) { wheelPixelsPerUnit = 15; }
4503 else if (chrome) { wheelPixelsPerUnit = -.7; }
4504 else if (safari) { wheelPixelsPerUnit = -1/3; }
4505
4506 function wheelEventDelta(e) {
4507 var dx = e.wheelDeltaX, dy = e.wheelDeltaY;
4508 if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail; }
4509 if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail; }
4510 else if (dy == null) { dy = e.wheelDelta; }
4511 return {x: dx, y: dy}
4512 }
4513 function wheelEventPixels(e) {
4514 var delta = wheelEventDelta(e);
4515 delta.x *= wheelPixelsPerUnit;
4516 delta.y *= wheelPixelsPerUnit;
4517 return delta
4518 }
4519
4520 function onScrollWheel(cm, e) {
4521 // On Chrome 102, viewport updates somehow stop wheel-based
4522 // scrolling. Turning off pointer events during the scroll seems
4523 // to avoid the issue.
4524 if (chrome && chrome_version == 102) {
4525 if (cm.display.chromeScrollHack == null) { cm.display.sizer.style.pointerEvents = "none"; }
4526 else { clearTimeout(cm.display.chromeScrollHack); }
4527 cm.display.chromeScrollHack = setTimeout(function () {
4528 cm.display.chromeScrollHack = null;
4529 cm.display.sizer.style.pointerEvents = "";
4530 }, 100);
4531 }
4532 var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y;
4533 var pixelsPerUnit = wheelPixelsPerUnit;
4534 if (e.deltaMode === 0) {
4535 dx = e.deltaX;
4536 dy = e.deltaY;
4537 pixelsPerUnit = 1;
4538 }
4539
4540 var display = cm.display, scroll = display.scroller;
4541 // Quit if there's nothing to scroll here
4542 var canScrollX = scroll.scrollWidth > scroll.clientWidth;
4543 var canScrollY = scroll.scrollHeight > scroll.clientHeight;
4544 if (!(dx && canScrollX || dy && canScrollY)) { return }
4545
4546 // Webkit browsers on OS X abort momentum scrolls when the target
4547 // of the scroll event is removed from the scrollable element.
4548 // This hack (see related code in patchDisplay) makes sure the
4549 // element is kept around.
4550 if (dy && mac && webkit) {
4551 outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {
4552 for (var i = 0; i < view.length; i++) {
4553 if (view[i].node == cur) {
4554 cm.display.currentWheelTarget = cur;
4555 break outer
4556 }
4557 }
4558 }
4559 }
4560
4561 // On some browsers, horizontal scrolling will cause redraws to
4562 // happen before the gutter has been realigned, causing it to
4563 // wriggle around in a most unseemly way. When we have an
4564 // estimated pixels/delta value, we just handle horizontal
4565 // scrolling entirely here. It'll be slightly off from native, but
4566 // better than glitching out.
4567 if (dx && !gecko && !presto && pixelsPerUnit != null) {
4568 if (dy && canScrollY)
4569 { updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * pixelsPerUnit)); }
4570 setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * pixelsPerUnit));
4571 // Only prevent default scrolling if vertical scrolling is
4572 // actually possible. Otherwise, it causes vertical scroll
4573 // jitter on OSX trackpads when deltaX is small and deltaY
4574 // is large (issue #3579)
4575 if (!dy || (dy && canScrollY))
4576 { e_preventDefault(e); }
4577 display.wheelStartX = null; // Abort measurement, if in progress
4578 return
4579 }
4580
4581 // 'Project' the visible viewport to cover the area that is being
4582 // scrolled into view (if we know enough to estimate it).
4583 if (dy && pixelsPerUnit != null) {
4584 var pixels = dy * pixelsPerUnit;
4585 var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
4586 if (pixels < 0) { top = Math.max(0, top + pixels - 50); }
4587 else { bot = Math.min(cm.doc.height, bot + pixels + 50); }
4588 updateDisplaySimple(cm, {top: top, bottom: bot});
4589 }
4590
4591 if (wheelSamples < 20 && e.deltaMode !== 0) {
4592 if (display.wheelStartX == null) {
4593 display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;
4594 display.wheelDX = dx; display.wheelDY = dy;
4595 setTimeout(function () {
4596 if (display.wheelStartX == null) { return }
4597 var movedX = scroll.scrollLeft - display.wheelStartX;
4598 var movedY = scroll.scrollTop - display.wheelStartY;
4599 var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
4600 (movedX && display.wheelDX && movedX / display.wheelDX);
4601 display.wheelStartX = display.wheelStartY = null;
4602 if (!sample) { return }
4603 wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
4604 ++wheelSamples;
4605 }, 200);
4606 } else {
4607 display.wheelDX += dx; display.wheelDY += dy;
4608 }
4609 }
4610 }
4611
4612 // Selection objects are immutable. A new one is created every time
4613 // the selection changes. A selection is one or more non-overlapping
4614 // (and non-touching) ranges, sorted, and an integer that indicates
4615 // which one is the primary selection (the one that's scrolled into
4616 // view, that getCursor returns, etc).
4617 var Selection = function(ranges, primIndex) {
4618 this.ranges = ranges;
4619 this.primIndex = primIndex;
4620 };
4621
4622 Selection.prototype.primary = function () { return this.ranges[this.primIndex] };
4623
4624 Selection.prototype.equals = function (other) {
4625 if (other == this) { return true }
4626 if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false }
4627 for (var i = 0; i < this.ranges.length; i++) {
4628 var here = this.ranges[i], there = other.ranges[i];
4629 if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) { return false }
4630 }
4631 return true
4632 };
4633
4634 Selection.prototype.deepCopy = function () {
4635 var out = [];
4636 for (var i = 0; i < this.ranges.length; i++)
4637 { out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head)); }
4638 return new Selection(out, this.primIndex)
4639 };
4640
4641 Selection.prototype.somethingSelected = function () {
4642 for (var i = 0; i < this.ranges.length; i++)
4643 { if (!this.ranges[i].empty()) { return true } }
4644 return false
4645 };
4646
4647 Selection.prototype.contains = function (pos, end) {
4648 if (!end) { end = pos; }
4649 for (var i = 0; i < this.ranges.length; i++) {
4650 var range = this.ranges[i];
4651 if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)
4652 { return i }
4653 }
4654 return -1
4655 };
4656
4657 var Range = function(anchor, head) {
4658 this.anchor = anchor; this.head = head;
4659 };
4660
4661 Range.prototype.from = function () { return minPos(this.anchor, this.head) };
4662 Range.prototype.to = function () { return maxPos(this.anchor, this.head) };
4663 Range.prototype.empty = function () { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch };
4664
4665 // Take an unsorted, potentially overlapping set of ranges, and
4666 // build a selection out of it. 'Consumes' ranges array (modifying
4667 // it).
4668 function normalizeSelection(cm, ranges, primIndex) {
4669 var mayTouch = cm && cm.options.selectionsMayTouch;
4670 var prim = ranges[primIndex];
4671 ranges.sort(function (a, b) { return cmp(a.from(), b.from()); });
4672 primIndex = indexOf(ranges, prim);
4673 for (var i = 1; i < ranges.length; i++) {
4674 var cur = ranges[i], prev = ranges[i - 1];
4675 var diff = cmp(prev.to(), cur.from());
4676 if (mayTouch && !cur.empty() ? diff > 0 : diff >= 0) {
4677 var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to());
4678 var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head;
4679 if (i <= primIndex) { --primIndex; }
4680 ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to));
4681 }
4682 }
4683 return new Selection(ranges, primIndex)
4684 }
4685
4686 function simpleSelection(anchor, head) {
4687 return new Selection([new Range(anchor, head || anchor)], 0)
4688 }
4689
4690 // Compute the position of the end of a change (its 'to' property
4691 // refers to the pre-change end).
4692 function changeEnd(change) {
4693 if (!change.text) { return change.to }
4694 return Pos(change.from.line + change.text.length - 1,
4695 lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0))
4696 }
4697
4698 // Adjust a position to refer to the post-change position of the
4699 // same text, or the end of the change if the change covers it.
4700 function adjustForChange(pos, change) {
4701 if (cmp(pos, change.from) < 0) { return pos }
4702 if (cmp(pos, change.to) <= 0) { return changeEnd(change) }
4703
4704 var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;
4705 if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch; }
4706 return Pos(line, ch)
4707 }
4708
4709 function computeSelAfterChange(doc, change) {
4710 var out = [];
4711 for (var i = 0; i < doc.sel.ranges.length; i++) {
4712 var range = doc.sel.ranges[i];
4713 out.push(new Range(adjustForChange(range.anchor, change),
4714 adjustForChange(range.head, change)));
4715 }
4716 return normalizeSelection(doc.cm, out, doc.sel.primIndex)
4717 }
4718
4719 function offsetPos(pos, old, nw) {
4720 if (pos.line == old.line)
4721 { return Pos(nw.line, pos.ch - old.ch + nw.ch) }
4722 else
4723 { return Pos(nw.line + (pos.line - old.line), pos.ch) }
4724 }
4725
4726 // Used by replaceSelections to allow moving the selection to the
4727 // start or around the replaced test. Hint may be "start" or "around".
4728 function computeReplacedSel(doc, changes, hint) {
4729 var out = [];
4730 var oldPrev = Pos(doc.first, 0), newPrev = oldPrev;
4731 for (var i = 0; i < changes.length; i++) {
4732 var change = changes[i];
4733 var from = offsetPos(change.from, oldPrev, newPrev);
4734 var to = offsetPos(changeEnd(change), oldPrev, newPrev);
4735 oldPrev = change.to;
4736 newPrev = to;
4737 if (hint == "around") {
4738 var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0;
4739 out[i] = new Range(inv ? to : from, inv ? from : to);
4740 } else {
4741 out[i] = new Range(from, from);
4742 }
4743 }
4744 return new Selection(out, doc.sel.primIndex)
4745 }
4746
4747 // Used to get the editor into a consistent state again when options change.
4748
4749 function loadMode(cm) {
4750 cm.doc.mode = getMode(cm.options, cm.doc.modeOption);
4751 resetModeState(cm);
4752 }
4753
4754 function resetModeState(cm) {
4755 cm.doc.iter(function (line) {
4756 if (line.stateAfter) { line.stateAfter = null; }
4757 if (line.styles) { line.styles = null; }
4758 });
4759 cm.doc.modeFrontier = cm.doc.highlightFrontier = cm.doc.first;
4760 startWorker(cm, 100);
4761 cm.state.modeGen++;
4762 if (cm.curOp) { regChange(cm); }
4763 }
4764
4765 // DOCUMENT DATA STRUCTURE
4766
4767 // By default, updates that start and end at the beginning of a line
4768 // are treated specially, in order to make the association of line
4769 // widgets and marker elements with the text behave more intuitive.
4770 function isWholeLineUpdate(doc, change) {
4771 return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" &&
4772 (!doc.cm || doc.cm.options.wholeLineUpdateBefore)
4773 }
4774
4775 // Perform a change on the document data structure.
4776 function updateDoc(doc, change, markedSpans, estimateHeight) {
4777 function spansFor(n) {return markedSpans ? markedSpans[n] : null}
4778 function update(line, text, spans) {
4779 updateLine(line, text, spans, estimateHeight);
4780 signalLater(line, "change", line, change);
4781 }
4782 function linesFor(start, end) {
4783 var result = [];
4784 for (var i = start; i < end; ++i)
4785 { result.push(new Line(text[i], spansFor(i), estimateHeight)); }
4786 return result
4787 }
4788
4789 var from = change.from, to = change.to, text = change.text;
4790 var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
4791 var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;
4792
4793 // Adjust the line structure
4794 if (change.full) {
4795 doc.insert(0, linesFor(0, text.length));
4796 doc.remove(text.length, doc.size - text.length);
4797 } else if (isWholeLineUpdate(doc, change)) {
4798 // This is a whole-line replace. Treated specially to make
4799 // sure line objects move the way they are supposed to.
4800 var added = linesFor(0, text.length - 1);
4801 update(lastLine, lastLine.text, lastSpans);
4802 if (nlines) { doc.remove(from.line, nlines); }
4803 if (added.length) { doc.insert(from.line, added); }
4804 } else if (firstLine == lastLine) {
4805 if (text.length == 1) {
4806 update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
4807 } else {
4808 var added$1 = linesFor(1, text.length - 1);
4809 added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight));
4810 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
4811 doc.insert(from.line + 1, added$1);
4812 }
4813 } else if (text.length == 1) {
4814 update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0));
4815 doc.remove(from.line + 1, nlines);
4816 } else {
4817 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
4818 update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
4819 var added$2 = linesFor(1, text.length - 1);
4820 if (nlines > 1) { doc.remove(from.line + 1, nlines - 1); }
4821 doc.insert(from.line + 1, added$2);
4822 }
4823
4824 signalLater(doc, "change", doc, change);
4825 }
4826
4827 // Call f for all linked documents.
4828 function linkedDocs(doc, f, sharedHistOnly) {
4829 function propagate(doc, skip, sharedHist) {
4830 if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) {
4831 var rel = doc.linked[i];
4832 if (rel.doc == skip) { continue }
4833 var shared = sharedHist && rel.sharedHist;
4834 if (sharedHistOnly && !shared) { continue }
4835 f(rel.doc, shared);
4836 propagate(rel.doc, doc, shared);
4837 } }
4838 }
4839 propagate(doc, null, true);
4840 }
4841
4842 // Attach a document to an editor.
4843 function attachDoc(cm, doc) {
4844 if (doc.cm) { throw new Error("This document is already in use.") }
4845 cm.doc = doc;
4846 doc.cm = cm;
4847 estimateLineHeights(cm);
4848 loadMode(cm);
4849 setDirectionClass(cm);
4850 cm.options.direction = doc.direction;
4851 if (!cm.options.lineWrapping) { findMaxLine(cm); }
4852 cm.options.mode = doc.modeOption;
4853 regChange(cm);
4854 }
4855
4856 function setDirectionClass(cm) {
4857 (cm.doc.direction == "rtl" ? addClass : rmClass)(cm.display.lineDiv, "CodeMirror-rtl");
4858 }
4859
4860 function directionChanged(cm) {
4861 runInOp(cm, function () {
4862 setDirectionClass(cm);
4863 regChange(cm);
4864 });
4865 }
4866
4867 function History(prev) {
4868 // Arrays of change events and selections. Doing something adds an
4869 // event to done and clears undo. Undoing moves events from done
4870 // to undone, redoing moves them in the other direction.
4871 this.done = []; this.undone = [];
4872 this.undoDepth = prev ? prev.undoDepth : Infinity;
4873 // Used to track when changes can be merged into a single undo
4874 // event
4875 this.lastModTime = this.lastSelTime = 0;
4876 this.lastOp = this.lastSelOp = null;
4877 this.lastOrigin = this.lastSelOrigin = null;
4878 // Used by the isClean() method
4879 this.generation = this.maxGeneration = prev ? prev.maxGeneration : 1;
4880 }
4881
4882 // Create a history change event from an updateDoc-style change
4883 // object.
4884 function historyChangeFromChange(doc, change) {
4885 var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
4886 attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
4887 linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); }, true);
4888 return histChange
4889 }
4890
4891 // Pop all selection events off the end of a history array. Stop at
4892 // a change event.
4893 function clearSelectionEvents(array) {
4894 while (array.length) {
4895 var last = lst(array);
4896 if (last.ranges) { array.pop(); }
4897 else { break }
4898 }
4899 }
4900
4901 // Find the top change event in the history. Pop off selection
4902 // events that are in the way.
4903 function lastChangeEvent(hist, force) {
4904 if (force) {
4905 clearSelectionEvents(hist.done);
4906 return lst(hist.done)
4907 } else if (hist.done.length && !lst(hist.done).ranges) {
4908 return lst(hist.done)
4909 } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) {
4910 hist.done.pop();
4911 return lst(hist.done)
4912 }
4913 }
4914
4915 // Register a change in the history. Merges changes that are within
4916 // a single operation, or are close together with an origin that
4917 // allows merging (starting with "+") into a single event.
4918 function addChangeToHistory(doc, change, selAfter, opId) {
4919 var hist = doc.history;
4920 hist.undone.length = 0;
4921 var time = +new Date, cur;
4922 var last;
4923
4924 if ((hist.lastOp == opId ||
4925 hist.lastOrigin == change.origin && change.origin &&
4926 ((change.origin.charAt(0) == "+" && hist.lastModTime > time - (doc.cm ? doc.cm.options.historyEventDelay : 500)) ||
4927 change.origin.charAt(0) == "*")) &&
4928 (cur = lastChangeEvent(hist, hist.lastOp == opId))) {
4929 // Merge this change into the last event
4930 last = lst(cur.changes);
4931 if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) {
4932 // Optimized case for simple insertion -- don't want to add
4933 // new changesets for every character typed
4934 last.to = changeEnd(change);
4935 } else {
4936 // Add new sub-event
4937 cur.changes.push(historyChangeFromChange(doc, change));
4938 }
4939 } else {
4940 // Can not be merged, start a new event.
4941 var before = lst(hist.done);
4942 if (!before || !before.ranges)
4943 { pushSelectionToHistory(doc.sel, hist.done); }
4944 cur = {changes: [historyChangeFromChange(doc, change)],
4945 generation: hist.generation};
4946 hist.done.push(cur);
4947 while (hist.done.length > hist.undoDepth) {
4948 hist.done.shift();
4949 if (!hist.done[0].ranges) { hist.done.shift(); }
4950 }
4951 }
4952 hist.done.push(selAfter);
4953 hist.generation = ++hist.maxGeneration;
4954 hist.lastModTime = hist.lastSelTime = time;
4955 hist.lastOp = hist.lastSelOp = opId;
4956 hist.lastOrigin = hist.lastSelOrigin = change.origin;
4957
4958 if (!last) { signal(doc, "historyAdded"); }
4959 }
4960
4961 function selectionEventCanBeMerged(doc, origin, prev, sel) {
4962 var ch = origin.charAt(0);
4963 return ch == "*" ||
4964 ch == "+" &&
4965 prev.ranges.length == sel.ranges.length &&
4966 prev.somethingSelected() == sel.somethingSelected() &&
4967 new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500)
4968 }
4969
4970 // Called whenever the selection changes, sets the new selection as
4971 // the pending selection in the history, and pushes the old pending
4972 // selection into the 'done' array when it was significantly
4973 // different (in number of selected ranges, emptiness, or time).
4974 function addSelectionToHistory(doc, sel, opId, options) {
4975 var hist = doc.history, origin = options && options.origin;
4976
4977 // A new event is started when the previous origin does not match
4978 // the current, or the origins don't allow matching. Origins
4979 // starting with * are always merged, those starting with + are
4980 // merged when similar and close together in time.
4981 if (opId == hist.lastSelOp ||
4982 (origin && hist.lastSelOrigin == origin &&
4983 (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||
4984 selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))
4985 { hist.done[hist.done.length - 1] = sel; }
4986 else
4987 { pushSelectionToHistory(sel, hist.done); }
4988
4989 hist.lastSelTime = +new Date;
4990 hist.lastSelOrigin = origin;
4991 hist.lastSelOp = opId;
4992 if (options && options.clearRedo !== false)
4993 { clearSelectionEvents(hist.undone); }
4994 }
4995
4996 function pushSelectionToHistory(sel, dest) {
4997 var top = lst(dest);
4998 if (!(top && top.ranges && top.equals(sel)))
4999 { dest.push(sel); }
5000 }
…文件过长,仅显示前 5000 行