PluginProbe
HurryTimer – An Scarcity and Urgency Countdown Timer for WordPress & WooCommerce / 2.1.6
HurryTimer – An Scarcity and Urgency Countdown Timer for WordPress & WooCommerce v2.1.6
2.15.0 trunk 1.0.0 1.0.1 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.1.2 2.1.3 2.1.5 2.1.6 2.1.7 2.1.8 2.10.0 All 62 releases
hurrytimer / assets / js / codemirror.js

codemirror.js in HurryTimer – An Scarcity and Urgency Countdown Timer for WordPress & WooCommerce 2.1.6, at assets/js/codemirror.js

9,731 lines 384.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: https://codemirror.net/LICENSE
3
4 // This is CodeMirror (https://codemirror.net), a code editor
5 // implemented in JavaScript on top of the browser's DOM.
6 //
7 // You can find some technical background for some of the code below
8 // at http://marijnhaverbeke.nl/blog/#cm-internals .
9
10 (function (global, factory) {
11 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
12 typeof define === 'function' && define.amd ? define(factory) :
13 (global.CodeMirror = factory());
14 }(this, (function () { 'use strict';
15
16 // Kludges for bugs and behavior differences that can't be feature
17 // detected are enabled based on userAgent etc sniffing.
18 var userAgent = navigator.userAgent;
19 var platform = navigator.platform;
20
21 var gecko = /gecko\/\d/i.test(userAgent);
22 var ie_upto10 = /MSIE \d/.test(userAgent);
23 var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent);
24 var edge = /Edge\/(\d+)/.exec(userAgent);
25 var ie = ie_upto10 || ie_11up || edge;
26 var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1]);
27 var webkit = !edge && /WebKit\//.test(userAgent);
28 var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent);
29 var chrome = !edge && /Chrome\//.test(userAgent);
30 var presto = /Opera\//.test(userAgent);
31 var safari = /Apple Computer/.test(navigator.vendor);
32 var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent);
33 var phantom = /PhantomJS/.test(userAgent);
34
35 var ios = !edge && /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent);
36 var android = /Android/.test(userAgent);
37 // This is woefully incomplete. Suggestions for alternative methods welcome.
38 var mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent);
39 var mac = ios || /Mac/.test(platform);
40 var chromeOS = /\bCrOS\b/.test(userAgent);
41 var windows = /win/i.test(platform);
42
43 var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/);
44 if (presto_version) { presto_version = Number(presto_version[1]); }
45 if (presto_version && presto_version >= 15) { presto = false; webkit = true; }
46 // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
47 var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11));
48 var captureRightClick = gecko || (ie && ie_version >= 9);
49
50 function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") }
51
52 var rmClass = function(node, cls) {
53 var current = node.className;
54 var match = classTest(cls).exec(current);
55 if (match) {
56 var after = current.slice(match.index + match[0].length);
57 node.className = current.slice(0, match.index) + (after ? match[1] + after : "");
58 }
59 };
60
61 function removeChildren(e) {
62 for (var count = e.childNodes.length; count > 0; --count)
63 { e.removeChild(e.firstChild); }
64 return e
65 }
66
67 function removeChildrenAndAdd(parent, e) {
68 return removeChildren(parent).appendChild(e)
69 }
70
71 function elt(tag, content, className, style) {
72 var e = document.createElement(tag);
73 if (className) { e.className = className; }
74 if (style) { e.style.cssText = style; }
75 if (typeof content == "string") { e.appendChild(document.createTextNode(content)); }
76 else if (content) { for (var i = 0; i < content.length; ++i) { e.appendChild(content[i]); } }
77 return e
78 }
79 // wrapper for elt, which removes the elt from the accessibility tree
80 function eltP(tag, content, className, style) {
81 var e = elt(tag, content, className, style);
82 e.setAttribute("role", "presentation");
83 return e
84 }
85
86 var range;
87 if (document.createRange) { range = function(node, start, end, endNode) {
88 var r = document.createRange();
89 r.setEnd(endNode || node, end);
90 r.setStart(node, start);
91 return r
92 }; }
93 else { range = function(node, start, end) {
94 var r = document.body.createTextRange();
95 try { r.moveToElementText(node.parentNode); }
96 catch(e) { return r }
97 r.collapse(true);
98 r.moveEnd("character", end);
99 r.moveStart("character", start);
100 return r
101 }; }
102
103 function contains(parent, child) {
104 if (child.nodeType == 3) // Android browser always returns false when child is a textnode
105 { child = child.parentNode; }
106 if (parent.contains)
107 { return parent.contains(child) }
108 do {
109 if (child.nodeType == 11) { child = child.host; }
110 if (child == parent) { return true }
111 } while (child = child.parentNode)
112 }
113
114 function activeElt() {
115 // IE and Edge may throw an "Unspecified Error" when accessing document.activeElement.
116 // IE < 10 will throw when accessed while the page is loading or in an iframe.
117 // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable.
118 var activeElement;
119 try {
120 activeElement = document.activeElement;
121 } catch(e) {
122 activeElement = document.body || null;
123 }
124 while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement)
125 { activeElement = activeElement.shadowRoot.activeElement; }
126 return activeElement
127 }
128
129 function addClass(node, cls) {
130 var current = node.className;
131 if (!classTest(cls).test(current)) { node.className += (current ? " " : "") + cls; }
132 }
133 function joinClasses(a, b) {
134 var as = a.split(" ");
135 for (var i = 0; i < as.length; i++)
136 { if (as[i] && !classTest(as[i]).test(b)) { b += " " + as[i]; } }
137 return b
138 }
139
140 var selectInput = function(node) { node.select(); };
141 if (ios) // Mobile Safari apparently has a bug where select() is broken.
142 { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; }; }
143 else if (ie) // Suppress mysterious IE10 errors
144 { selectInput = function(node) { try { node.select(); } catch(_e) {} }; }
145
146 function bind(f) {
147 var args = Array.prototype.slice.call(arguments, 1);
148 return function(){return f.apply(null, args)}
149 }
150
151 function copyObj(obj, target, overwrite) {
152 if (!target) { target = {}; }
153 for (var prop in obj)
154 { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
155 { target[prop] = obj[prop]; } }
156 return target
157 }
158
159 // Counts the column offset in a string, taking tabs into account.
160 // Used mostly to find indentation.
161 function countColumn(string, end, tabSize, startIndex, startValue) {
162 if (end == null) {
163 end = string.search(/[^\s\u00a0]/);
164 if (end == -1) { end = string.length; }
165 }
166 for (var i = startIndex || 0, n = startValue || 0;;) {
167 var nextTab = string.indexOf("\t", i);
168 if (nextTab < 0 || nextTab >= end)
169 { return n + (end - i) }
170 n += nextTab - i;
171 n += tabSize - (n % tabSize);
172 i = nextTab + 1;
173 }
174 }
175
176 var Delayed = function() {this.id = null;};
177 Delayed.prototype.set = function (ms, f) {
178 clearTimeout(this.id);
179 this.id = setTimeout(f, ms);
180 };
181
182 function indexOf(array, elt) {
183 for (var i = 0; i < array.length; ++i)
184 { if (array[i] == elt) { return i } }
185 return -1
186 }
187
188 // Number of pixels added to scroller and sizer to hide scrollbar
189 var scrollerGap = 30;
190
191 // Returned or thrown by various protocols to signal 'I'm not
192 // handling this'.
193 var Pass = {toString: function(){return "CodeMirror.Pass"}};
194
195 // Reused option objects for setSelection & friends
196 var sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"};
197
198 // The inverse of countColumn -- find the offset that corresponds to
199 // a particular column.
200 function findColumn(string, goal, tabSize) {
201 for (var pos = 0, col = 0;;) {
202 var nextTab = string.indexOf("\t", pos);
203 if (nextTab == -1) { nextTab = string.length; }
204 var skipped = nextTab - pos;
205 if (nextTab == string.length || col + skipped >= goal)
206 { return pos + Math.min(skipped, goal - col) }
207 col += nextTab - pos;
208 col += tabSize - (col % tabSize);
209 pos = nextTab + 1;
210 if (col >= goal) { return pos }
211 }
212 }
213
214 var spaceStrs = [""];
215 function spaceStr(n) {
216 while (spaceStrs.length <= n)
217 { spaceStrs.push(lst(spaceStrs) + " "); }
218 return spaceStrs[n]
219 }
220
221 function lst(arr) { return arr[arr.length-1] }
222
223 function map(array, f) {
224 var out = [];
225 for (var i = 0; i < array.length; i++) { out[i] = f(array[i], i); }
226 return out
227 }
228
229 function insertSorted(array, value, score) {
230 var pos = 0, priority = score(value);
231 while (pos < array.length && score(array[pos]) <= priority) { pos++; }
232 array.splice(pos, 0, value);
233 }
234
235 function nothing() {}
236
237 function createObj(base, props) {
238 var inst;
239 if (Object.create) {
240 inst = Object.create(base);
241 } else {
242 nothing.prototype = base;
243 inst = new nothing();
244 }
245 if (props) { copyObj(props, inst); }
246 return inst
247 }
248
249 var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
250 function isWordCharBasic(ch) {
251 return /\w/.test(ch) || ch > "\x80" &&
252 (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch))
253 }
254 function isWordChar(ch, helper) {
255 if (!helper) { return isWordCharBasic(ch) }
256 if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) { return true }
257 return helper.test(ch)
258 }
259
260 function isEmpty(obj) {
261 for (var n in obj) { if (obj.hasOwnProperty(n) && obj[n]) { return false } }
262 return true
263 }
264
265 // Extending unicode characters. A series of a non-extending char +
266 // any number of extending chars is treated as a single unit as far
267 // as editing and measuring is concerned. This is not fully correct,
268 // since some scripts/fonts/browsers also treat other configurations
269 // of code points as a group.
270 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]/;
271 function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) }
272
273 // Returns a number from the range [`0`; `str.length`] unless `pos` is outside that range.
274 function skipExtendingChars(str, pos, dir) {
275 while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(pos))) { pos += dir; }
276 return pos
277 }
278
279 // Returns the value from the range [`from`; `to`] that satisfies
280 // `pred` and is closest to `from`. Assumes that at least `to`
281 // satisfies `pred`. Supports `from` being greater than `to`.
282 function findFirst(pred, from, to) {
283 // At any point we are certain `to` satisfies `pred`, don't know
284 // whether `from` does.
285 var dir = from > to ? -1 : 1;
286 for (;;) {
287 if (from == to) { return from }
288 var midF = (from + to) / 2, mid = dir < 0 ? Math.ceil(midF) : Math.floor(midF);
289 if (mid == from) { return pred(mid) ? from : to }
290 if (pred(mid)) { to = mid; }
291 else { from = mid + dir; }
292 }
293 }
294
295 // The display handles the DOM integration, both for input reading
296 // and content drawing. It holds references to DOM nodes and
297 // display-related state.
298
299 function Display(place, doc, input) {
300 var d = this;
301 this.input = input;
302
303 // Covers bottom-right square when both scrollbars are present.
304 d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
305 d.scrollbarFiller.setAttribute("cm-not-content", "true");
306 // Covers bottom of gutter when coverGutterNextToScrollbar is on
307 // and h scrollbar is present.
308 d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler");
309 d.gutterFiller.setAttribute("cm-not-content", "true");
310 // Will contain the actual code, positioned to cover the viewport.
311 d.lineDiv = eltP("div", null, "CodeMirror-code");
312 // Elements are added to these to represent selection and cursors.
313 d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
314 d.cursorDiv = elt("div", null, "CodeMirror-cursors");
315 // A visibility: hidden element used to find the size of things.
316 d.measure = elt("div", null, "CodeMirror-measure");
317 // When lines outside of the viewport are measured, they are drawn in this.
318 d.lineMeasure = elt("div", null, "CodeMirror-measure");
319 // Wraps everything that needs to exist inside the vertically-padded coordinate system
320 d.lineSpace = eltP("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],
321 null, "position: relative; outline: none");
322 var lines = eltP("div", [d.lineSpace], "CodeMirror-lines");
323 // Moved around its parent to cover visible view.
324 d.mover = elt("div", [lines], null, "position: relative");
325 // Set to the height of the document, allowing scrolling.
326 d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
327 d.sizerWidth = null;
328 // Behavior of elts with overflow: auto and padding is
329 // inconsistent across browsers. This is used to ensure the
330 // scrollable area is big enough.
331 d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;");
332 // Will contain the gutters, if any.
333 d.gutters = elt("div", null, "CodeMirror-gutters");
334 d.lineGutter = null;
335 // Actual scrollable element.
336 d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll");
337 d.scroller.setAttribute("tabIndex", "-1");
338 // The element in which the editor lives.
339 d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror");
340
341 // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)
342 if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
343 if (!webkit && !(gecko && mobile)) { d.scroller.draggable = true; }
344
345 if (place) {
346 if (place.appendChild) { place.appendChild(d.wrapper); }
347 else { place(d.wrapper); }
348 }
349
350 // Current rendered range (may be bigger than the view window).
351 d.viewFrom = d.viewTo = doc.first;
352 d.reportedViewFrom = d.reportedViewTo = doc.first;
353 // Information about the rendered lines.
354 d.view = [];
355 d.renderedView = null;
356 // Holds info about a single rendered line when it was rendered
357 // for measurement, while not in view.
358 d.externalMeasured = null;
359 // Empty space (in pixels) above the view
360 d.viewOffset = 0;
361 d.lastWrapHeight = d.lastWrapWidth = 0;
362 d.updateLineNumbers = null;
363
364 d.nativeBarWidth = d.barHeight = d.barWidth = 0;
365 d.scrollbarsClipped = false;
366
367 // Used to only resize the line number gutter when necessary (when
368 // the amount of lines crosses a boundary that makes its width change)
369 d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
370 // Set to true when a non-horizontal-scrolling line widget is
371 // added. As an optimization, line widget aligning is skipped when
372 // this is false.
373 d.alignWidgets = false;
374
375 d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
376
377 // Tracks the maximum line length so that the horizontal scrollbar
378 // can be kept static when scrolling.
379 d.maxLine = null;
380 d.maxLineLength = 0;
381 d.maxLineChanged = false;
382
383 // Used for measuring wheel scrolling granularity
384 d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
385
386 // True when shift is held down.
387 d.shift = false;
388
389 // Used to track whether anything happened since the context menu
390 // was opened.
391 d.selForContextMenu = null;
392
393 d.activeTouch = null;
394
395 input.init(d);
396 }
397
398 // Find the line object corresponding to the given line number.
399 function getLine(doc, n) {
400 n -= doc.first;
401 if (n < 0 || n >= doc.size) { throw new Error("There is no line " + (n + doc.first) + " in the document.") }
402 var chunk = doc;
403 while (!chunk.lines) {
404 for (var i = 0;; ++i) {
405 var child = chunk.children[i], sz = child.chunkSize();
406 if (n < sz) { chunk = child; break }
407 n -= sz;
408 }
409 }
410 return chunk.lines[n]
411 }
412
413 // Get the part of a document between two positions, as an array of
414 // strings.
415 function getBetween(doc, start, end) {
416 var out = [], n = start.line;
417 doc.iter(start.line, end.line + 1, function (line) {
418 var text = line.text;
419 if (n == end.line) { text = text.slice(0, end.ch); }
420 if (n == start.line) { text = text.slice(start.ch); }
421 out.push(text);
422 ++n;
423 });
424 return out
425 }
426 // Get the lines between from and to, as array of strings.
427 function getLines(doc, from, to) {
428 var out = [];
429 doc.iter(from, to, function (line) { out.push(line.text); }); // iter aborts when callback returns truthy value
430 return out
431 }
432
433 // Update the height of a line, propagating the height change
434 // upwards to parent nodes.
435 function updateLineHeight(line, height) {
436 var diff = height - line.height;
437 if (diff) { for (var n = line; n; n = n.parent) { n.height += diff; } }
438 }
439
440 // Given a line object, find its line number by walking up through
441 // its parent links.
442 function lineNo(line) {
443 if (line.parent == null) { return null }
444 var cur = line.parent, no = indexOf(cur.lines, line);
445 for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
446 for (var i = 0;; ++i) {
447 if (chunk.children[i] == cur) { break }
448 no += chunk.children[i].chunkSize();
449 }
450 }
451 return no + cur.first
452 }
453
454 // Find the line at the given vertical position, using the height
455 // information in the document tree.
456 function lineAtHeight(chunk, h) {
457 var n = chunk.first;
458 outer: do {
459 for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) {
460 var child = chunk.children[i$1], ch = child.height;
461 if (h < ch) { chunk = child; continue outer }
462 h -= ch;
463 n += child.chunkSize();
464 }
465 return n
466 } while (!chunk.lines)
467 var i = 0;
468 for (; i < chunk.lines.length; ++i) {
469 var line = chunk.lines[i], lh = line.height;
470 if (h < lh) { break }
471 h -= lh;
472 }
473 return n + i
474 }
475
476 function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size}
477
478 function lineNumberFor(options, i) {
479 return String(options.lineNumberFormatter(i + options.firstLineNumber))
480 }
481
482 // A Pos instance represents a position within the text.
483 function Pos(line, ch, sticky) {
484 if ( sticky === void 0 ) sticky = null;
485
486 if (!(this instanceof Pos)) { return new Pos(line, ch, sticky) }
487 this.line = line;
488 this.ch = ch;
489 this.sticky = sticky;
490 }
491
492 // Compare two positions, return 0 if they are the same, a negative
493 // number when a is less, and a positive number otherwise.
494 function cmp(a, b) { return a.line - b.line || a.ch - b.ch }
495
496 function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 }
497
498 function copyPos(x) {return Pos(x.line, x.ch)}
499 function maxPos(a, b) { return cmp(a, b) < 0 ? b : a }
500 function minPos(a, b) { return cmp(a, b) < 0 ? a : b }
501
502 // Most of the external API clips given positions to make sure they
503 // actually exist within the document.
504 function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))}
505 function clipPos(doc, pos) {
506 if (pos.line < doc.first) { return Pos(doc.first, 0) }
507 var last = doc.first + doc.size - 1;
508 if (pos.line > last) { return Pos(last, getLine(doc, last).text.length) }
509 return clipToLen(pos, getLine(doc, pos.line).text.length)
510 }
511 function clipToLen(pos, linelen) {
512 var ch = pos.ch;
513 if (ch == null || ch > linelen) { return Pos(pos.line, linelen) }
514 else if (ch < 0) { return Pos(pos.line, 0) }
515 else { return pos }
516 }
517 function clipPosArray(doc, array) {
518 var out = [];
519 for (var i = 0; i < array.length; i++) { out[i] = clipPos(doc, array[i]); }
520 return out
521 }
522
523 // Optimize some code when these features are not used.
524 var sawReadOnlySpans = false, sawCollapsedSpans = false;
525
526 function seeReadOnlySpans() {
527 sawReadOnlySpans = true;
528 }
529
530 function seeCollapsedSpans() {
531 sawCollapsedSpans = true;
532 }
533
534 // TEXTMARKER SPANS
535
536 function MarkedSpan(marker, from, to) {
537 this.marker = marker;
538 this.from = from; this.to = to;
539 }
540
541 // Search an array of spans for a span matching the given marker.
542 function getMarkedSpanFor(spans, marker) {
543 if (spans) { for (var i = 0; i < spans.length; ++i) {
544 var span = spans[i];
545 if (span.marker == marker) { return span }
546 } }
547 }
548 // Remove a span from an array, returning undefined if no spans are
549 // left (we don't store arrays for lines without spans).
550 function removeMarkedSpan(spans, span) {
551 var r;
552 for (var i = 0; i < spans.length; ++i)
553 { if (spans[i] != span) { (r || (r = [])).push(spans[i]); } }
554 return r
555 }
556 // Add a span to a line.
557 function addMarkedSpan(line, span) {
558 line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
559 span.marker.attachLine(line);
560 }
561
562 // Used for the algorithm that adjusts markers for a change in the
563 // document. These functions cut an array of spans at a given
564 // character position, returning an array of remaining chunks (or
565 // undefined if nothing remains).
566 function markedSpansBefore(old, startCh, isInsert) {
567 var nw;
568 if (old) { for (var i = 0; i < old.length; ++i) {
569 var span = old[i], marker = span.marker;
570 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
571 if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) {
572 var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh)
573 ;(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to));
574 }
575 } }
576 return nw
577 }
578 function markedSpansAfter(old, endCh, isInsert) {
579 var nw;
580 if (old) { for (var i = 0; i < old.length; ++i) {
581 var span = old[i], marker = span.marker;
582 var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
583 if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) {
584 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh)
585 ;(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh,
586 span.to == null ? null : span.to - endCh));
587 }
588 } }
589 return nw
590 }
591
592 // Given a change object, compute the new set of marker spans that
593 // cover the line in which the change took place. Removes spans
594 // entirely within the change, reconnects spans belonging to the
595 // same marker that appear on both sides of the change, and cuts off
596 // spans partially within the change. Returns an array of span
597 // arrays with one element for each line in (after) the change.
598 function stretchSpansOverChange(doc, change) {
599 if (change.full) { return null }
600 var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans;
601 var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans;
602 if (!oldFirst && !oldLast) { return null }
603
604 var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0;
605 // Get the spans that 'stick out' on both sides
606 var first = markedSpansBefore(oldFirst, startCh, isInsert);
607 var last = markedSpansAfter(oldLast, endCh, isInsert);
608
609 // Next, merge those two ends
610 var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0);
611 if (first) {
612 // Fix up .to properties of first
613 for (var i = 0; i < first.length; ++i) {
614 var span = first[i];
615 if (span.to == null) {
616 var found = getMarkedSpanFor(last, span.marker);
617 if (!found) { span.to = startCh; }
618 else if (sameLine) { span.to = found.to == null ? null : found.to + offset; }
619 }
620 }
621 }
622 if (last) {
623 // Fix up .from in last (or move them into first in case of sameLine)
624 for (var i$1 = 0; i$1 < last.length; ++i$1) {
625 var span$1 = last[i$1];
626 if (span$1.to != null) { span$1.to += offset; }
627 if (span$1.from == null) {
628 var found$1 = getMarkedSpanFor(first, span$1.marker);
629 if (!found$1) {
630 span$1.from = offset;
631 if (sameLine) { (first || (first = [])).push(span$1); }
632 }
633 } else {
634 span$1.from += offset;
635 if (sameLine) { (first || (first = [])).push(span$1); }
636 }
637 }
638 }
639 // Make sure we didn't create any zero-length spans
640 if (first) { first = clearEmptySpans(first); }
641 if (last && last != first) { last = clearEmptySpans(last); }
642
643 var newMarkers = [first];
644 if (!sameLine) {
645 // Fill gap with whole-line-spans
646 var gap = change.text.length - 2, gapMarkers;
647 if (gap > 0 && first)
648 { for (var i$2 = 0; i$2 < first.length; ++i$2)
649 { if (first[i$2].to == null)
650 { (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i$2].marker, null, null)); } } }
651 for (var i$3 = 0; i$3 < gap; ++i$3)
652 { newMarkers.push(gapMarkers); }
653 newMarkers.push(last);
654 }
655 return newMarkers
656 }
657
658 // Remove spans that are empty and don't have a clearWhenEmpty
659 // option of false.
660 function clearEmptySpans(spans) {
661 for (var i = 0; i < spans.length; ++i) {
662 var span = spans[i];
663 if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)
664 { spans.splice(i--, 1); }
665 }
666 if (!spans.length) { return null }
667 return spans
668 }
669
670 // Used to 'clip' out readOnly ranges when making a change.
671 function removeReadOnlyRanges(doc, from, to) {
672 var markers = null;
673 doc.iter(from.line, to.line + 1, function (line) {
674 if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
675 var mark = line.markedSpans[i].marker;
676 if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
677 { (markers || (markers = [])).push(mark); }
678 } }
679 });
680 if (!markers) { return null }
681 var parts = [{from: from, to: to}];
682 for (var i = 0; i < markers.length; ++i) {
683 var mk = markers[i], m = mk.find(0);
684 for (var j = 0; j < parts.length; ++j) {
685 var p = parts[j];
686 if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) { continue }
687 var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to);
688 if (dfrom < 0 || !mk.inclusiveLeft && !dfrom)
689 { newParts.push({from: p.from, to: m.from}); }
690 if (dto > 0 || !mk.inclusiveRight && !dto)
691 { newParts.push({from: m.to, to: p.to}); }
692 parts.splice.apply(parts, newParts);
693 j += newParts.length - 3;
694 }
695 }
696 return parts
697 }
698
699 // Connect or disconnect spans from a line.
700 function detachMarkedSpans(line) {
701 var spans = line.markedSpans;
702 if (!spans) { return }
703 for (var i = 0; i < spans.length; ++i)
704 { spans[i].marker.detachLine(line); }
705 line.markedSpans = null;
706 }
707 function attachMarkedSpans(line, spans) {
708 if (!spans) { return }
709 for (var i = 0; i < spans.length; ++i)
710 { spans[i].marker.attachLine(line); }
711 line.markedSpans = spans;
712 }
713
714 // Helpers used when computing which overlapping collapsed span
715 // counts as the larger one.
716 function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 }
717 function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 }
718
719 // Returns a number indicating which of two overlapping collapsed
720 // spans is larger (and thus includes the other). Falls back to
721 // comparing ids when the spans cover exactly the same range.
722 function compareCollapsedMarkers(a, b) {
723 var lenDiff = a.lines.length - b.lines.length;
724 if (lenDiff != 0) { return lenDiff }
725 var aPos = a.find(), bPos = b.find();
726 var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b);
727 if (fromCmp) { return -fromCmp }
728 var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b);
729 if (toCmp) { return toCmp }
730 return b.id - a.id
731 }
732
733 // Find out whether a line ends or starts in a collapsed span. If
734 // so, return the marker for that span.
735 function collapsedSpanAtSide(line, start) {
736 var sps = sawCollapsedSpans && line.markedSpans, found;
737 if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
738 sp = sps[i];
739 if (sp.marker.collapsed && (start ? sp.from : sp.to) == null &&
740 (!found || compareCollapsedMarkers(found, sp.marker) < 0))
741 { found = sp.marker; }
742 } }
743 return found
744 }
745 function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) }
746 function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) }
747
748 function collapsedSpanAround(line, ch) {
749 var sps = sawCollapsedSpans && line.markedSpans, found;
750 if (sps) { for (var i = 0; i < sps.length; ++i) {
751 var sp = sps[i];
752 if (sp.marker.collapsed && (sp.from == null || sp.from < ch) && (sp.to == null || sp.to > ch) &&
753 (!found || compareCollapsedMarkers(found, sp.marker) < 0)) { found = sp.marker; }
754 } }
755 return found
756 }
757
758 // Test whether there exists a collapsed span that partially
759 // overlaps (covers the start or end, but not both) of a new span.
760 // Such overlap is not allowed.
761 function conflictingCollapsedRange(doc, lineNo$$1, from, to, marker) {
762 var line = getLine(doc, lineNo$$1);
763 var sps = sawCollapsedSpans && line.markedSpans;
764 if (sps) { for (var i = 0; i < sps.length; ++i) {
765 var sp = sps[i];
766 if (!sp.marker.collapsed) { continue }
767 var found = sp.marker.find(0);
768 var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker);
769 var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker);
770 if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue }
771 if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) ||
772 fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0))
773 { return true }
774 } }
775 }
776
777 // A visual line is a line as drawn on the screen. Folding, for
778 // example, can cause multiple logical lines to appear on the same
779 // visual line. This finds the start of the visual line that the
780 // given line is part of (usually that is the line itself).
781 function visualLine(line) {
782 var merged;
783 while (merged = collapsedSpanAtStart(line))
784 { line = merged.find(-1, true).line; }
785 return line
786 }
787
788 function visualLineEnd(line) {
789 var merged;
790 while (merged = collapsedSpanAtEnd(line))
791 { line = merged.find(1, true).line; }
792 return line
793 }
794
795 // Returns an array of logical lines that continue the visual line
796 // started by the argument, or undefined if there are no such lines.
797 function visualLineContinued(line) {
798 var merged, lines;
799 while (merged = collapsedSpanAtEnd(line)) {
800 line = merged.find(1, true).line
801 ;(lines || (lines = [])).push(line);
802 }
803 return lines
804 }
805
806 // Get the line number of the start of the visual line that the
807 // given line number is part of.
808 function visualLineNo(doc, lineN) {
809 var line = getLine(doc, lineN), vis = visualLine(line);
810 if (line == vis) { return lineN }
811 return lineNo(vis)
812 }
813
814 // Get the line number of the start of the next visual line after
815 // the given line.
816 function visualLineEndNo(doc, lineN) {
817 if (lineN > doc.lastLine()) { return lineN }
818 var line = getLine(doc, lineN), merged;
819 if (!lineIsHidden(doc, line)) { return lineN }
820 while (merged = collapsedSpanAtEnd(line))
821 { line = merged.find(1, true).line; }
822 return lineNo(line) + 1
823 }
824
825 // Compute whether a line is hidden. Lines count as hidden when they
826 // are part of a visual line that starts with another line, or when
827 // they are entirely covered by collapsed, non-widget span.
828 function lineIsHidden(doc, line) {
829 var sps = sawCollapsedSpans && line.markedSpans;
830 if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
831 sp = sps[i];
832 if (!sp.marker.collapsed) { continue }
833 if (sp.from == null) { return true }
834 if (sp.marker.widgetNode) { continue }
835 if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
836 { return true }
837 } }
838 }
839 function lineIsHiddenInner(doc, line, span) {
840 if (span.to == null) {
841 var end = span.marker.find(1, true);
842 return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker))
843 }
844 if (span.marker.inclusiveRight && span.to == line.text.length)
845 { return true }
846 for (var sp = (void 0), i = 0; i < line.markedSpans.length; ++i) {
847 sp = line.markedSpans[i];
848 if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to &&
849 (sp.to == null || sp.to != span.from) &&
850 (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
851 lineIsHiddenInner(doc, line, sp)) { return true }
852 }
853 }
854
855 // Find the height above the given line.
856 function heightAtLine(lineObj) {
857 lineObj = visualLine(lineObj);
858
859 var h = 0, chunk = lineObj.parent;
860 for (var i = 0; i < chunk.lines.length; ++i) {
861 var line = chunk.lines[i];
862 if (line == lineObj) { break }
863 else { h += line.height; }
864 }
865 for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
866 for (var i$1 = 0; i$1 < p.children.length; ++i$1) {
867 var cur = p.children[i$1];
868 if (cur == chunk) { break }
869 else { h += cur.height; }
870 }
871 }
872 return h
873 }
874
875 // Compute the character length of a line, taking into account
876 // collapsed ranges (see markText) that might hide parts, and join
877 // other lines onto it.
878 function lineLength(line) {
879 if (line.height == 0) { return 0 }
880 var len = line.text.length, merged, cur = line;
881 while (merged = collapsedSpanAtStart(cur)) {
882 var found = merged.find(0, true);
883 cur = found.from.line;
884 len += found.from.ch - found.to.ch;
885 }
886 cur = line;
887 while (merged = collapsedSpanAtEnd(cur)) {
888 var found$1 = merged.find(0, true);
889 len -= cur.text.length - found$1.from.ch;
890 cur = found$1.to.line;
891 len += cur.text.length - found$1.to.ch;
892 }
893 return len
894 }
895
896 // Find the longest line in the document.
897 function findMaxLine(cm) {
898 var d = cm.display, doc = cm.doc;
899 d.maxLine = getLine(doc, doc.first);
900 d.maxLineLength = lineLength(d.maxLine);
901 d.maxLineChanged = true;
902 doc.iter(function (line) {
903 var len = lineLength(line);
904 if (len > d.maxLineLength) {
905 d.maxLineLength = len;
906 d.maxLine = line;
907 }
908 });
909 }
910
911 // BIDI HELPERS
912
913 function iterateBidiSections(order, from, to, f) {
914 if (!order) { return f(from, to, "ltr", 0) }
915 var found = false;
916 for (var i = 0; i < order.length; ++i) {
917 var part = order[i];
918 if (part.from < to && part.to > from || from == to && part.to == from) {
919 f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr", i);
920 found = true;
921 }
922 }
923 if (!found) { f(from, to, "ltr"); }
924 }
925
926 var bidiOther = null;
927 function getBidiPartAt(order, ch, sticky) {
928 var found;
929 bidiOther = null;
930 for (var i = 0; i < order.length; ++i) {
931 var cur = order[i];
932 if (cur.from < ch && cur.to > ch) { return i }
933 if (cur.to == ch) {
934 if (cur.from != cur.to && sticky == "before") { found = i; }
935 else { bidiOther = i; }
936 }
937 if (cur.from == ch) {
938 if (cur.from != cur.to && sticky != "before") { found = i; }
939 else { bidiOther = i; }
940 }
941 }
942 return found != null ? found : bidiOther
943 }
944
945 // Bidirectional ordering algorithm
946 // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
947 // that this (partially) implements.
948
949 // One-char codes used for character types:
950 // L (L): Left-to-Right
951 // R (R): Right-to-Left
952 // r (AL): Right-to-Left Arabic
953 // 1 (EN): European Number
954 // + (ES): European Number Separator
955 // % (ET): European Number Terminator
956 // n (AN): Arabic Number
957 // , (CS): Common Number Separator
958 // m (NSM): Non-Spacing Mark
959 // b (BN): Boundary Neutral
960 // s (B): Paragraph Separator
961 // t (S): Segment Separator
962 // w (WS): Whitespace
963 // N (ON): Other Neutrals
964
965 // Returns null if characters are ordered as they appear
966 // (left-to-right), or an array of sections ({from, to, level}
967 // objects) in the order in which they occur visually.
968 var bidiOrdering = (function() {
969 // Character types for codepoints 0 to 0xff
970 var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN";
971 // Character types for codepoints 0x600 to 0x6f9
972 var arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111";
973 function charType(code) {
974 if (code <= 0xf7) { return lowTypes.charAt(code) }
975 else if (0x590 <= code && code <= 0x5f4) { return "R" }
976 else if (0x600 <= code && code <= 0x6f9) { return arabicTypes.charAt(code - 0x600) }
977 else if (0x6ee <= code && code <= 0x8ac) { return "r" }
978 else if (0x2000 <= code && code <= 0x200b) { return "w" }
979 else if (code == 0x200c) { return "b" }
980 else { return "L" }
981 }
982
983 var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
984 var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
985
986 function BidiSpan(level, from, to) {
987 this.level = level;
988 this.from = from; this.to = to;
989 }
990
991 return function(str, direction) {
992 var outerType = direction == "ltr" ? "L" : "R";
993
994 if (str.length == 0 || direction == "ltr" && !bidiRE.test(str)) { return false }
995 var len = str.length, types = [];
996 for (var i = 0; i < len; ++i)
997 { types.push(charType(str.charCodeAt(i))); }
998
999 // W1. Examine each non-spacing mark (NSM) in the level run, and
1000 // change the type of the NSM to the type of the previous
1001 // character. If the NSM is at the start of the level run, it will
1002 // get the type of sor.
1003 for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) {
1004 var type = types[i$1];
1005 if (type == "m") { types[i$1] = prev; }
1006 else { prev = type; }
1007 }
1008
1009 // W2. Search backwards from each instance of a European number
1010 // until the first strong type (R, L, AL, or sor) is found. If an
1011 // AL is found, change the type of the European number to Arabic
1012 // number.
1013 // W3. Change all ALs to R.
1014 for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) {
1015 var type$1 = types[i$2];
1016 if (type$1 == "1" && cur == "r") { types[i$2] = "n"; }
1017 else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == "r") { types[i$2] = "R"; } }
1018 }
1019
1020 // W4. A single European separator between two European numbers
1021 // changes to a European number. A single common separator between
1022 // two numbers of the same type changes to that type.
1023 for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) {
1024 var type$2 = types[i$3];
1025 if (type$2 == "+" && prev$1 == "1" && types[i$3+1] == "1") { types[i$3] = "1"; }
1026 else if (type$2 == "," && prev$1 == types[i$3+1] &&
1027 (prev$1 == "1" || prev$1 == "n")) { types[i$3] = prev$1; }
1028 prev$1 = type$2;
1029 }
1030
1031 // W5. A sequence of European terminators adjacent to European
1032 // numbers changes to all European numbers.
1033 // W6. Otherwise, separators and terminators change to Other
1034 // Neutral.
1035 for (var i$4 = 0; i$4 < len; ++i$4) {
1036 var type$3 = types[i$4];
1037 if (type$3 == ",") { types[i$4] = "N"; }
1038 else if (type$3 == "%") {
1039 var end = (void 0);
1040 for (end = i$4 + 1; end < len && types[end] == "%"; ++end) {}
1041 var replace = (i$4 && types[i$4-1] == "!") || (end < len && types[end] == "1") ? "1" : "N";
1042 for (var j = i$4; j < end; ++j) { types[j] = replace; }
1043 i$4 = end - 1;
1044 }
1045 }
1046
1047 // W7. Search backwards from each instance of a European number
1048 // until the first strong type (R, L, or sor) is found. If an L is
1049 // found, then change the type of the European number to L.
1050 for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) {
1051 var type$4 = types[i$5];
1052 if (cur$1 == "L" && type$4 == "1") { types[i$5] = "L"; }
1053 else if (isStrong.test(type$4)) { cur$1 = type$4; }
1054 }
1055
1056 // N1. A sequence of neutrals takes the direction of the
1057 // surrounding strong text if the text on both sides has the same
1058 // direction. European and Arabic numbers act as if they were R in
1059 // terms of their influence on neutrals. Start-of-level-run (sor)
1060 // and end-of-level-run (eor) are used at level run boundaries.
1061 // N2. Any remaining neutrals take the embedding direction.
1062 for (var i$6 = 0; i$6 < len; ++i$6) {
1063 if (isNeutral.test(types[i$6])) {
1064 var end$1 = (void 0);
1065 for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {}
1066 var before = (i$6 ? types[i$6-1] : outerType) == "L";
1067 var after = (end$1 < len ? types[end$1] : outerType) == "L";
1068 var replace$1 = before == after ? (before ? "L" : "R") : outerType;
1069 for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1; }
1070 i$6 = end$1 - 1;
1071 }
1072 }
1073
1074 // Here we depart from the documented algorithm, in order to avoid
1075 // building up an actual levels array. Since there are only three
1076 // levels (0, 1, 2) in an implementation that doesn't take
1077 // explicit embedding into account, we can build up the order on
1078 // the fly, without following the level-based algorithm.
1079 var order = [], m;
1080 for (var i$7 = 0; i$7 < len;) {
1081 if (countsAsLeft.test(types[i$7])) {
1082 var start = i$7;
1083 for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {}
1084 order.push(new BidiSpan(0, start, i$7));
1085 } else {
1086 var pos = i$7, at = order.length;
1087 for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {}
1088 for (var j$2 = pos; j$2 < i$7;) {
1089 if (countsAsNum.test(types[j$2])) {
1090 if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)); }
1091 var nstart = j$2;
1092 for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {}
1093 order.splice(at, 0, new BidiSpan(2, nstart, j$2));
1094 pos = j$2;
1095 } else { ++j$2; }
1096 }
1097 if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)); }
1098 }
1099 }
1100 if (direction == "ltr") {
1101 if (order[0].level == 1 && (m = str.match(/^\s+/))) {
1102 order[0].from = m[0].length;
1103 order.unshift(new BidiSpan(0, 0, m[0].length));
1104 }
1105 if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
1106 lst(order).to -= m[0].length;
1107 order.push(new BidiSpan(0, len - m[0].length, len));
1108 }
1109 }
1110
1111 return direction == "rtl" ? order.reverse() : order
1112 }
1113 })();
1114
1115 // Get the bidi ordering for the given line (and cache it). Returns
1116 // false for lines that are fully left-to-right, and an array of
1117 // BidiSpan objects otherwise.
1118 function getOrder(line, direction) {
1119 var order = line.order;
1120 if (order == null) { order = line.order = bidiOrdering(line.text, direction); }
1121 return order
1122 }
1123
1124 // EVENT HANDLING
1125
1126 // Lightweight event framework. on/off also work on DOM nodes,
1127 // registering native DOM handlers.
1128
1129 var noHandlers = [];
1130
1131 var on = function(emitter, type, f) {
1132 if (emitter.addEventListener) {
1133 emitter.addEventListener(type, f, false);
1134 } else if (emitter.attachEvent) {
1135 emitter.attachEvent("on" + type, f);
1136 } else {
1137 var map$$1 = emitter._handlers || (emitter._handlers = {});
1138 map$$1[type] = (map$$1[type] || noHandlers).concat(f);
1139 }
1140 };
1141
1142 function getHandlers(emitter, type) {
1143 return emitter._handlers && emitter._handlers[type] || noHandlers
1144 }
1145
1146 function off(emitter, type, f) {
1147 if (emitter.removeEventListener) {
1148 emitter.removeEventListener(type, f, false);
1149 } else if (emitter.detachEvent) {
1150 emitter.detachEvent("on" + type, f);
1151 } else {
1152 var map$$1 = emitter._handlers, arr = map$$1 && map$$1[type];
1153 if (arr) {
1154 var index = indexOf(arr, f);
1155 if (index > -1)
1156 { map$$1[type] = arr.slice(0, index).concat(arr.slice(index + 1)); }
1157 }
1158 }
1159 }
1160
1161 function signal(emitter, type /*, values...*/) {
1162 var handlers = getHandlers(emitter, type);
1163 if (!handlers.length) { return }
1164 var args = Array.prototype.slice.call(arguments, 2);
1165 for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args); }
1166 }
1167
1168 // The DOM events that CodeMirror handles can be overridden by
1169 // registering a (non-DOM) handler on the editor for the event name,
1170 // and preventDefault-ing the event in that handler.
1171 function signalDOMEvent(cm, e, override) {
1172 if (typeof e == "string")
1173 { e = {type: e, preventDefault: function() { this.defaultPrevented = true; }}; }
1174 signal(cm, override || e.type, cm, e);
1175 return e_defaultPrevented(e) || e.codemirrorIgnore
1176 }
1177
1178 function signalCursorActivity(cm) {
1179 var arr = cm._handlers && cm._handlers.cursorActivity;
1180 if (!arr) { return }
1181 var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []);
1182 for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1)
1183 { set.push(arr[i]); } }
1184 }
1185
1186 function hasHandler(emitter, type) {
1187 return getHandlers(emitter, type).length > 0
1188 }
1189
1190 // Add on and off methods to a constructor's prototype, to make
1191 // registering events on such objects more convenient.
1192 function eventMixin(ctor) {
1193 ctor.prototype.on = function(type, f) {on(this, type, f);};
1194 ctor.prototype.off = function(type, f) {off(this, type, f);};
1195 }
1196
1197 // Due to the fact that we still support jurassic IE versions, some
1198 // compatibility wrappers are needed.
1199
1200 function e_preventDefault(e) {
1201 if (e.preventDefault) { e.preventDefault(); }
1202 else { e.returnValue = false; }
1203 }
1204 function e_stopPropagation(e) {
1205 if (e.stopPropagation) { e.stopPropagation(); }
1206 else { e.cancelBubble = true; }
1207 }
1208 function e_defaultPrevented(e) {
1209 return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false
1210 }
1211 function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
1212
1213 function e_target(e) {return e.target || e.srcElement}
1214 function e_button(e) {
1215 var b = e.which;
1216 if (b == null) {
1217 if (e.button & 1) { b = 1; }
1218 else if (e.button & 2) { b = 3; }
1219 else if (e.button & 4) { b = 2; }
1220 }
1221 if (mac && e.ctrlKey && b == 1) { b = 3; }
1222 return b
1223 }
1224
1225 // Detect drag-and-drop
1226 var dragAndDrop = function() {
1227 // There is *some* kind of drag-and-drop support in IE6-8, but I
1228 // couldn't get it to work yet.
1229 if (ie && ie_version < 9) { return false }
1230 var div = elt('div');
1231 return "draggable" in div || "dragDrop" in div
1232 }();
1233
1234 var zwspSupported;
1235 function zeroWidthElement(measure) {
1236 if (zwspSupported == null) {
1237 var test = elt("span", "\u200b");
1238 removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]));
1239 if (measure.firstChild.offsetHeight != 0)
1240 { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8); }
1241 }
1242 var node = zwspSupported ? elt("span", "\u200b") :
1243 elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
1244 node.setAttribute("cm-text", "");
1245 return node
1246 }
1247
1248 // Feature-detect IE's crummy client rect reporting for bidi text
1249 var badBidiRects;
1250 function hasBadBidiRects(measure) {
1251 if (badBidiRects != null) { return badBidiRects }
1252 var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA"));
1253 var r0 = range(txt, 0, 1).getBoundingClientRect();
1254 var r1 = range(txt, 1, 2).getBoundingClientRect();
1255 removeChildren(measure);
1256 if (!r0 || r0.left == r0.right) { return false } // Safari returns null in some cases (#2780)
1257 return badBidiRects = (r1.right - r0.right < 3)
1258 }
1259
1260 // See if "".split is the broken IE version, if so, provide an
1261 // alternative way to split lines.
1262 var splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function (string) {
1263 var pos = 0, result = [], l = string.length;
1264 while (pos <= l) {
1265 var nl = string.indexOf("\n", pos);
1266 if (nl == -1) { nl = string.length; }
1267 var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
1268 var rt = line.indexOf("\r");
1269 if (rt != -1) {
1270 result.push(line.slice(0, rt));
1271 pos += rt + 1;
1272 } else {
1273 result.push(line);
1274 pos = nl + 1;
1275 }
1276 }
1277 return result
1278 } : function (string) { return string.split(/\r\n?|\n/); };
1279
1280 var hasSelection = window.getSelection ? function (te) {
1281 try { return te.selectionStart != te.selectionEnd }
1282 catch(e) { return false }
1283 } : function (te) {
1284 var range$$1;
1285 try {range$$1 = te.ownerDocument.selection.createRange();}
1286 catch(e) {}
1287 if (!range$$1 || range$$1.parentElement() != te) { return false }
1288 return range$$1.compareEndPoints("StartToEnd", range$$1) != 0
1289 };
1290
1291 var hasCopyEvent = (function () {
1292 var e = elt("div");
1293 if ("oncopy" in e) { return true }
1294 e.setAttribute("oncopy", "return;");
1295 return typeof e.oncopy == "function"
1296 })();
1297
1298 var badZoomedRects = null;
1299 function hasBadZoomedRects(measure) {
1300 if (badZoomedRects != null) { return badZoomedRects }
1301 var node = removeChildrenAndAdd(measure, elt("span", "x"));
1302 var normal = node.getBoundingClientRect();
1303 var fromRange = range(node, 0, 1).getBoundingClientRect();
1304 return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1
1305 }
1306
1307 // Known modes, by name and by MIME
1308 var modes = {}, mimeModes = {};
1309
1310 // Extra arguments are stored as the mode's dependencies, which is
1311 // used by (legacy) mechanisms like loadmode.js to automatically
1312 // load a mode. (Preferred mechanism is the require/define calls.)
1313 function defineMode(name, mode) {
1314 if (arguments.length > 2)
1315 { mode.dependencies = Array.prototype.slice.call(arguments, 2); }
1316 modes[name] = mode;
1317 }
1318
1319 function defineMIME(mime, spec) {
1320 mimeModes[mime] = spec;
1321 }
1322
1323 // Given a MIME type, a {name, ...options} config object, or a name
1324 // string, return a mode config object.
1325 function resolveMode(spec) {
1326 if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
1327 spec = mimeModes[spec];
1328 } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
1329 var found = mimeModes[spec.name];
1330 if (typeof found == "string") { found = {name: found}; }
1331 spec = createObj(found, spec);
1332 spec.name = found.name;
1333 } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
1334 return resolveMode("application/xml")
1335 } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) {
1336 return resolveMode("application/json")
1337 }
1338 if (typeof spec == "string") { return {name: spec} }
1339 else { return spec || {name: "null"} }
1340 }
1341
1342 // Given a mode spec (anything that resolveMode accepts), find and
1343 // initialize an actual mode object.
1344 function getMode(options, spec) {
1345 spec = resolveMode(spec);
1346 var mfactory = modes[spec.name];
1347 if (!mfactory) { return getMode(options, "text/plain") }
1348 var modeObj = mfactory(options, spec);
1349 if (modeExtensions.hasOwnProperty(spec.name)) {
1350 var exts = modeExtensions[spec.name];
1351 for (var prop in exts) {
1352 if (!exts.hasOwnProperty(prop)) { continue }
1353 if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop]; }
1354 modeObj[prop] = exts[prop];
1355 }
1356 }
1357 modeObj.name = spec.name;
1358 if (spec.helperType) { modeObj.helperType = spec.helperType; }
1359 if (spec.modeProps) { for (var prop$1 in spec.modeProps)
1360 { modeObj[prop$1] = spec.modeProps[prop$1]; } }
1361
1362 return modeObj
1363 }
1364
1365 // This can be used to attach properties to mode objects from
1366 // outside the actual mode definition.
1367 var modeExtensions = {};
1368 function extendMode(mode, properties) {
1369 var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
1370 copyObj(properties, exts);
1371 }
1372
1373 function copyState(mode, state) {
1374 if (state === true) { return state }
1375 if (mode.copyState) { return mode.copyState(state) }
1376 var nstate = {};
1377 for (var n in state) {
1378 var val = state[n];
1379 if (val instanceof Array) { val = val.concat([]); }
1380 nstate[n] = val;
1381 }
1382 return nstate
1383 }
1384
1385 // Given a mode and a state (for that mode), find the inner mode and
1386 // state at the position that the state refers to.
1387 function innerMode(mode, state) {
1388 var info;
1389 while (mode.innerMode) {
1390 info = mode.innerMode(state);
1391 if (!info || info.mode == mode) { break }
1392 state = info.state;
1393 mode = info.mode;
1394 }
1395 return info || {mode: mode, state: state}
1396 }
1397
1398 function startState(mode, a1, a2) {
1399 return mode.startState ? mode.startState(a1, a2) : true
1400 }
1401
1402 // STRING STREAM
1403
1404 // Fed to the mode parsers, provides helper functions to make
1405 // parsers more succinct.
1406
1407 var StringStream = function(string, tabSize, lineOracle) {
1408 this.pos = this.start = 0;
1409 this.string = string;
1410 this.tabSize = tabSize || 8;
1411 this.lastColumnPos = this.lastColumnValue = 0;
1412 this.lineStart = 0;
1413 this.lineOracle = lineOracle;
1414 };
1415
1416 StringStream.prototype.eol = function () {return this.pos >= this.string.length};
1417 StringStream.prototype.sol = function () {return this.pos == this.lineStart};
1418 StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined};
1419 StringStream.prototype.next = function () {
1420 if (this.pos < this.string.length)
1421 { return this.string.charAt(this.pos++) }
1422 };
1423 StringStream.prototype.eat = function (match) {
1424 var ch = this.string.charAt(this.pos);
1425 var ok;
1426 if (typeof match == "string") { ok = ch == match; }
1427 else { ok = ch && (match.test ? match.test(ch) : match(ch)); }
1428 if (ok) {++this.pos; return ch}
1429 };
1430 StringStream.prototype.eatWhile = function (match) {
1431 var start = this.pos;
1432 while (this.eat(match)){}
1433 return this.pos > start
1434 };
1435 StringStream.prototype.eatSpace = function () {
1436 var this$1 = this;
1437
1438 var start = this.pos;
1439 while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this$1.pos; }
1440 return this.pos > start
1441 };
1442 StringStream.prototype.skipToEnd = function () {this.pos = this.string.length;};
1443 StringStream.prototype.skipTo = function (ch) {
1444 var found = this.string.indexOf(ch, this.pos);
1445 if (found > -1) {this.pos = found; return true}
1446 };
1447 StringStream.prototype.backUp = function (n) {this.pos -= n;};
1448 StringStream.prototype.column = function () {
1449 if (this.lastColumnPos < this.start) {
1450 this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
1451 this.lastColumnPos = this.start;
1452 }
1453 return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
1454 };
1455 StringStream.prototype.indentation = function () {
1456 return countColumn(this.string, null, this.tabSize) -
1457 (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
1458 };
1459 StringStream.prototype.match = function (pattern, consume, caseInsensitive) {
1460 if (typeof pattern == "string") {
1461 var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; };
1462 var substr = this.string.substr(this.pos, pattern.length);
1463 if (cased(substr) == cased(pattern)) {
1464 if (consume !== false) { this.pos += pattern.length; }
1465 return true
1466 }
1467 } else {
1468 var match = this.string.slice(this.pos).match(pattern);
1469 if (match && match.index > 0) { return null }
1470 if (match && consume !== false) { this.pos += match[0].length; }
1471 return match
1472 }
1473 };
1474 StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)};
1475 StringStream.prototype.hideFirstChars = function (n, inner) {
1476 this.lineStart += n;
1477 try { return inner() }
1478 finally { this.lineStart -= n; }
1479 };
1480 StringStream.prototype.lookAhead = function (n) {
1481 var oracle = this.lineOracle;
1482 return oracle && oracle.lookAhead(n)
1483 };
1484 StringStream.prototype.baseToken = function () {
1485 var oracle = this.lineOracle;
1486 return oracle && oracle.baseToken(this.pos)
1487 };
1488
1489 var SavedContext = function(state, lookAhead) {
1490 this.state = state;
1491 this.lookAhead = lookAhead;
1492 };
1493
1494 var Context = function(doc, state, line, lookAhead) {
1495 this.state = state;
1496 this.doc = doc;
1497 this.line = line;
1498 this.maxLookAhead = lookAhead || 0;
1499 this.baseTokens = null;
1500 this.baseTokenPos = 1;
1501 };
1502
1503 Context.prototype.lookAhead = function (n) {
1504 var line = this.doc.getLine(this.line + n);
1505 if (line != null && n > this.maxLookAhead) { this.maxLookAhead = n; }
1506 return line
1507 };
1508
1509 Context.prototype.baseToken = function (n) {
1510 var this$1 = this;
1511
1512 if (!this.baseTokens) { return null }
1513 while (this.baseTokens[this.baseTokenPos] <= n)
1514 { this$1.baseTokenPos += 2; }
1515 var type = this.baseTokens[this.baseTokenPos + 1];
1516 return {type: type && type.replace(/( |^)overlay .*/, ""),
1517 size: this.baseTokens[this.baseTokenPos] - n}
1518 };
1519
1520 Context.prototype.nextLine = function () {
1521 this.line++;
1522 if (this.maxLookAhead > 0) { this.maxLookAhead--; }
1523 };
1524
1525 Context.fromSaved = function (doc, saved, line) {
1526 if (saved instanceof SavedContext)
1527 { return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead) }
1528 else
1529 { return new Context(doc, copyState(doc.mode, saved), line) }
1530 };
1531
1532 Context.prototype.save = function (copy) {
1533 var state = copy !== false ? copyState(this.doc.mode, this.state) : this.state;
1534 return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state
1535 };
1536
1537
1538 // Compute a style array (an array starting with a mode generation
1539 // -- for invalidation -- followed by pairs of end positions and
1540 // style strings), which is used to highlight the tokens on the
1541 // line.
1542 function highlightLine(cm, line, context, forceToEnd) {
1543 // A styles array always starts with a number identifying the
1544 // mode/overlays that it is based on (for easy invalidation).
1545 var st = [cm.state.modeGen], lineClasses = {};
1546 // Compute the base array of styles
1547 runMode(cm, line.text, cm.doc.mode, context, function (end, style) { return st.push(end, style); },
1548 lineClasses, forceToEnd);
1549 var state = context.state;
1550
1551 // Run overlays, adjust style array.
1552 var loop = function ( o ) {
1553 context.baseTokens = st;
1554 var overlay = cm.state.overlays[o], i = 1, at = 0;
1555 context.state = true;
1556 runMode(cm, line.text, overlay.mode, context, function (end, style) {
1557 var start = i;
1558 // Ensure there's a token end at the current position, and that i points at it
1559 while (at < end) {
1560 var i_end = st[i];
1561 if (i_end > end)
1562 { st.splice(i, 1, end, st[i+1], i_end); }
1563 i += 2;
1564 at = Math.min(end, i_end);
1565 }
1566 if (!style) { return }
1567 if (overlay.opaque) {
1568 st.splice(start, i - start, end, "overlay " + style);
1569 i = start + 2;
1570 } else {
1571 for (; start < i; start += 2) {
1572 var cur = st[start+1];
1573 st[start+1] = (cur ? cur + " " : "") + "overlay " + style;
1574 }
1575 }
1576 }, lineClasses);
1577 context.state = state;
1578 context.baseTokens = null;
1579 context.baseTokenPos = 1;
1580 };
1581
1582 for (var o = 0; o < cm.state.overlays.length; ++o) loop( o );
1583
1584 return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null}
1585 }
1586
1587 function getLineStyles(cm, line, updateFrontier) {
1588 if (!line.styles || line.styles[0] != cm.state.modeGen) {
1589 var context = getContextBefore(cm, lineNo(line));
1590 var resetState = line.text.length > cm.options.maxHighlightLength && copyState(cm.doc.mode, context.state);
1591 var result = highlightLine(cm, line, context);
1592 if (resetState) { context.state = resetState; }
1593 line.stateAfter = context.save(!resetState);
1594 line.styles = result.styles;
1595 if (result.classes) { line.styleClasses = result.classes; }
1596 else if (line.styleClasses) { line.styleClasses = null; }
1597 if (updateFrontier === cm.doc.highlightFrontier)
1598 { cm.doc.modeFrontier = Math.max(cm.doc.modeFrontier, ++cm.doc.highlightFrontier); }
1599 }
1600 return line.styles
1601 }
1602
1603 function getContextBefore(cm, n, precise) {
1604 var doc = cm.doc, display = cm.display;
1605 if (!doc.mode.startState) { return new Context(doc, true, n) }
1606 var start = findStartLine(cm, n, precise);
1607 var saved = start > doc.first && getLine(doc, start - 1).stateAfter;
1608 var context = saved ? Context.fromSaved(doc, saved, start) : new Context(doc, startState(doc.mode), start);
1609
1610 doc.iter(start, n, function (line) {
1611 processLine(cm, line.text, context);
1612 var pos = context.line;
1613 line.stateAfter = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo ? context.save() : null;
1614 context.nextLine();
1615 });
1616 if (precise) { doc.modeFrontier = context.line; }
1617 return context
1618 }
1619
1620 // Lightweight form of highlight -- proceed over this line and
1621 // update state, but don't save a style array. Used for lines that
1622 // aren't currently visible.
1623 function processLine(cm, text, context, startAt) {
1624 var mode = cm.doc.mode;
1625 var stream = new StringStream(text, cm.options.tabSize, context);
1626 stream.start = stream.pos = startAt || 0;
1627 if (text == "") { callBlankLine(mode, context.state); }
1628 while (!stream.eol()) {
1629 readToken(mode, stream, context.state);
1630 stream.start = stream.pos;
1631 }
1632 }
1633
1634 function callBlankLine(mode, state) {
1635 if (mode.blankLine) { return mode.blankLine(state) }
1636 if (!mode.innerMode) { return }
1637 var inner = innerMode(mode, state);
1638 if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) }
1639 }
1640
1641 function readToken(mode, stream, state, inner) {
1642 for (var i = 0; i < 10; i++) {
1643 if (inner) { inner[0] = innerMode(mode, state).mode; }
1644 var style = mode.token(stream, state);
1645 if (stream.pos > stream.start) { return style }
1646 }
1647 throw new Error("Mode " + mode.name + " failed to advance stream.")
1648 }
1649
1650 var Token = function(stream, type, state) {
1651 this.start = stream.start; this.end = stream.pos;
1652 this.string = stream.current();
1653 this.type = type || null;
1654 this.state = state;
1655 };
1656
1657 // Utility for getTokenAt and getLineTokens
1658 function takeToken(cm, pos, precise, asArray) {
1659 var doc = cm.doc, mode = doc.mode, style;
1660 pos = clipPos(doc, pos);
1661 var line = getLine(doc, pos.line), context = getContextBefore(cm, pos.line, precise);
1662 var stream = new StringStream(line.text, cm.options.tabSize, context), tokens;
1663 if (asArray) { tokens = []; }
1664 while ((asArray || stream.pos < pos.ch) && !stream.eol()) {
1665 stream.start = stream.pos;
1666 style = readToken(mode, stream, context.state);
1667 if (asArray) { tokens.push(new Token(stream, style, copyState(doc.mode, context.state))); }
1668 }
1669 return asArray ? tokens : new Token(stream, style, context.state)
1670 }
1671
1672 function extractLineClasses(type, output) {
1673 if (type) { for (;;) {
1674 var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/);
1675 if (!lineClass) { break }
1676 type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length);
1677 var prop = lineClass[1] ? "bgClass" : "textClass";
1678 if (output[prop] == null)
1679 { output[prop] = lineClass[2]; }
1680 else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop]))
1681 { output[prop] += " " + lineClass[2]; }
1682 } }
1683 return type
1684 }
1685
1686 // Run the given mode's parser over a line, calling f for each token.
1687 function runMode(cm, text, mode, context, f, lineClasses, forceToEnd) {
1688 var flattenSpans = mode.flattenSpans;
1689 if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans; }
1690 var curStart = 0, curStyle = null;
1691 var stream = new StringStream(text, cm.options.tabSize, context), style;
1692 var inner = cm.options.addModeClass && [null];
1693 if (text == "") { extractLineClasses(callBlankLine(mode, context.state), lineClasses); }
1694 while (!stream.eol()) {
1695 if (stream.pos > cm.options.maxHighlightLength) {
1696 flattenSpans = false;
1697 if (forceToEnd) { processLine(cm, text, context, stream.pos); }
1698 stream.pos = text.length;
1699 style = null;
1700 } else {
1701 style = extractLineClasses(readToken(mode, stream, context.state, inner), lineClasses);
1702 }
1703 if (inner) {
1704 var mName = inner[0].name;
1705 if (mName) { style = "m-" + (style ? mName + " " + style : mName); }
1706 }
1707 if (!flattenSpans || curStyle != style) {
1708 while (curStart < stream.start) {
1709 curStart = Math.min(stream.start, curStart + 5000);
1710 f(curStart, curStyle);
1711 }
1712 curStyle = style;
1713 }
1714 stream.start = stream.pos;
1715 }
1716 while (curStart < stream.pos) {
1717 // Webkit seems to refuse to render text nodes longer than 57444
1718 // characters, and returns inaccurate measurements in nodes
1719 // starting around 5000 chars.
1720 var pos = Math.min(stream.pos, curStart + 5000);
1721 f(pos, curStyle);
1722 curStart = pos;
1723 }
1724 }
1725
1726 // Finds the line to start with when starting a parse. Tries to
1727 // find a line with a stateAfter, so that it can start with a
1728 // valid state. If that fails, it returns the line with the
1729 // smallest indentation, which tends to need the least context to
1730 // parse correctly.
1731 function findStartLine(cm, n, precise) {
1732 var minindent, minline, doc = cm.doc;
1733 var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100);
1734 for (var search = n; search > lim; --search) {
1735 if (search <= doc.first) { return doc.first }
1736 var line = getLine(doc, search - 1), after = line.stateAfter;
1737 if (after && (!precise || search + (after instanceof SavedContext ? after.lookAhead : 0) <= doc.modeFrontier))
1738 { return search }
1739 var indented = countColumn(line.text, null, cm.options.tabSize);
1740 if (minline == null || minindent > indented) {
1741 minline = search - 1;
1742 minindent = indented;
1743 }
1744 }
1745 return minline
1746 }
1747
1748 function retreatFrontier(doc, n) {
1749 doc.modeFrontier = Math.min(doc.modeFrontier, n);
1750 if (doc.highlightFrontier < n - 10) { return }
1751 var start = doc.first;
1752 for (var line = n - 1; line > start; line--) {
1753 var saved = getLine(doc, line).stateAfter;
1754 // change is on 3
1755 // state on line 1 looked ahead 2 -- so saw 3
1756 // test 1 + 2 < 3 should cover this
1757 if (saved && (!(saved instanceof SavedContext) || line + saved.lookAhead < n)) {
1758 start = line + 1;
1759 break
1760 }
1761 }
1762 doc.highlightFrontier = Math.min(doc.highlightFrontier, start);
1763 }
1764
1765 // LINE DATA STRUCTURE
1766
1767 // Line objects. These hold state related to a line, including
1768 // highlighting info (the styles array).
1769 var Line = function(text, markedSpans, estimateHeight) {
1770 this.text = text;
1771 attachMarkedSpans(this, markedSpans);
1772 this.height = estimateHeight ? estimateHeight(this) : 1;
1773 };
1774
1775 Line.prototype.lineNo = function () { return lineNo(this) };
1776 eventMixin(Line);
1777
1778 // Change the content (text, markers) of a line. Automatically
1779 // invalidates cached information and tries to re-estimate the
1780 // line's height.
1781 function updateLine(line, text, markedSpans, estimateHeight) {
1782 line.text = text;
1783 if (line.stateAfter) { line.stateAfter = null; }
1784 if (line.styles) { line.styles = null; }
1785 if (line.order != null) { line.order = null; }
1786 detachMarkedSpans(line);
1787 attachMarkedSpans(line, markedSpans);
1788 var estHeight = estimateHeight ? estimateHeight(line) : 1;
1789 if (estHeight != line.height) { updateLineHeight(line, estHeight); }
1790 }
1791
1792 // Detach a line from the document tree and its markers.
1793 function cleanUpLine(line) {
1794 line.parent = null;
1795 detachMarkedSpans(line);
1796 }
1797
1798 // Convert a style as returned by a mode (either null, or a string
1799 // containing one or more styles) to a CSS style. This is cached,
1800 // and also looks for line-wide styles.
1801 var styleToClassCache = {}, styleToClassCacheWithMode = {};
1802 function interpretTokenStyle(style, options) {
1803 if (!style || /^\s*$/.test(style)) { return null }
1804 var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache;
1805 return cache[style] ||
1806 (cache[style] = style.replace(/\S+/g, "cm-$&"))
1807 }
1808
1809 // Render the DOM representation of the text of a line. Also builds
1810 // up a 'line map', which points at the DOM nodes that represent
1811 // specific stretches of text, and is used by the measuring code.
1812 // The returned object contains the DOM node, this map, and
1813 // information about line-wide styles that were set by the mode.
1814 function buildLineContent(cm, lineView) {
1815 // The padding-right forces the element to have a 'border', which
1816 // is needed on Webkit to be able to get line-level bounding
1817 // rectangles for it (in measureChar).
1818 var content = eltP("span", null, null, webkit ? "padding-right: .1px" : null);
1819 var builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: content,
1820 col: 0, pos: 0, cm: cm,
1821 trailingSpace: false,
1822 splitSpaces: cm.getOption("lineWrapping")};
1823 lineView.measure = {};
1824
1825 // Iterate over the logical lines that make up this visual line.
1826 for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {
1827 var line = i ? lineView.rest[i - 1] : lineView.line, order = (void 0);
1828 builder.pos = 0;
1829 builder.addToken = buildToken;
1830 // Optionally wire in some hacks into the token-rendering
1831 // algorithm, to deal with browser quirks.
1832 if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction)))
1833 { builder.addToken = buildTokenBadBidi(builder.addToken, order); }
1834 builder.map = [];
1835 var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line);
1836 insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate));
1837 if (line.styleClasses) {
1838 if (line.styleClasses.bgClass)
1839 { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || ""); }
1840 if (line.styleClasses.textClass)
1841 { builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || ""); }
1842 }
1843
1844 // Ensure at least a single node is present, for measuring.
1845 if (builder.map.length == 0)
1846 { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))); }
1847
1848 // Store the map and a cache object for the current logical line
1849 if (i == 0) {
1850 lineView.measure.map = builder.map;
1851 lineView.measure.cache = {};
1852 } else {
1853 (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map)
1854 ;(lineView.measure.caches || (lineView.measure.caches = [])).push({});
1855 }
1856 }
1857
1858 // See issue #2901
1859 if (webkit) {
1860 var last = builder.content.lastChild;
1861 if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab")))
1862 { builder.content.className = "cm-tab-wrap-hack"; }
1863 }
1864
1865 signal(cm, "renderLine", cm, lineView.line, builder.pre);
1866 if (builder.pre.className)
1867 { builder.textClass = joinClasses(builder.pre.className, builder.textClass || ""); }
1868
1869 return builder
1870 }
1871
1872 function defaultSpecialCharPlaceholder(ch) {
1873 var token = elt("span", "\u2022", "cm-invalidchar");
1874 token.title = "\\u" + ch.charCodeAt(0).toString(16);
1875 token.setAttribute("aria-label", token.title);
1876 return token
1877 }
1878
1879 // Build up the DOM representation for a single token, and add it to
1880 // the line map. Takes care to render special characters separately.
1881 function buildToken(builder, text, style, startStyle, endStyle, css, attributes) {
1882 if (!text) { return }
1883 var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text;
1884 var special = builder.cm.state.specialChars, mustWrap = false;
1885 var content;
1886 if (!special.test(text)) {
1887 builder.col += text.length;
1888 content = document.createTextNode(displayText);
1889 builder.map.push(builder.pos, builder.pos + text.length, content);
1890 if (ie && ie_version < 9) { mustWrap = true; }
1891 builder.pos += text.length;
1892 } else {
1893 content = document.createDocumentFragment();
1894 var pos = 0;
1895 while (true) {
1896 special.lastIndex = pos;
1897 var m = special.exec(text);
1898 var skipped = m ? m.index - pos : text.length - pos;
1899 if (skipped) {
1900 var txt = document.createTextNode(displayText.slice(pos, pos + skipped));
1901 if (ie && ie_version < 9) { content.appendChild(elt("span", [txt])); }
1902 else { content.appendChild(txt); }
1903 builder.map.push(builder.pos, builder.pos + skipped, txt);
1904 builder.col += skipped;
1905 builder.pos += skipped;
1906 }
1907 if (!m) { break }
1908 pos += skipped + 1;
1909 var txt$1 = (void 0);
1910 if (m[0] == "\t") {
1911 var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
1912 txt$1 = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
1913 txt$1.setAttribute("role", "presentation");
1914 txt$1.setAttribute("cm-text", "\t");
1915 builder.col += tabWidth;
1916 } else if (m[0] == "\r" || m[0] == "\n") {
1917 txt$1 = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar"));
1918 txt$1.setAttribute("cm-text", m[0]);
1919 builder.col += 1;
1920 } else {
1921 txt$1 = builder.cm.options.specialCharPlaceholder(m[0]);
1922 txt$1.setAttribute("cm-text", m[0]);
1923 if (ie && ie_version < 9) { content.appendChild(elt("span", [txt$1])); }
1924 else { content.appendChild(txt$1); }
1925 builder.col += 1;
1926 }
1927 builder.map.push(builder.pos, builder.pos + 1, txt$1);
1928 builder.pos++;
1929 }
1930 }
1931 builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32;
1932 if (style || startStyle || endStyle || mustWrap || css) {
1933 var fullStyle = style || "";
1934 if (startStyle) { fullStyle += startStyle; }
1935 if (endStyle) { fullStyle += endStyle; }
1936 var token = elt("span", [content], fullStyle, css);
1937 if (attributes) {
1938 for (var attr in attributes) { if (attributes.hasOwnProperty(attr) && attr != "style" && attr != "class")
1939 { token.setAttribute(attr, attributes[attr]); } }
1940 }
1941 return builder.content.appendChild(token)
1942 }
1943 builder.content.appendChild(content);
1944 }
1945
1946 // Change some spaces to NBSP to prevent the browser from collapsing
1947 // trailing spaces at the end of a line when rendering text (issue #1362).
1948 function splitSpaces(text, trailingBefore) {
1949 if (text.length > 1 && !/ /.test(text)) { return text }
1950 var spaceBefore = trailingBefore, result = "";
1951 for (var i = 0; i < text.length; i++) {
1952 var ch = text.charAt(i);
1953 if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32))
1954 { ch = "\u00a0"; }
1955 result += ch;
1956 spaceBefore = ch == " ";
1957 }
1958 return result
1959 }
1960
1961 // Work around nonsense dimensions being reported for stretches of
1962 // right-to-left text.
1963 function buildTokenBadBidi(inner, order) {
1964 return function (builder, text, style, startStyle, endStyle, css, attributes) {
1965 style = style ? style + " cm-force-border" : "cm-force-border";
1966 var start = builder.pos, end = start + text.length;
1967 for (;;) {
1968 // Find the part that overlaps with the start of this text
1969 var part = (void 0);
1970 for (var i = 0; i < order.length; i++) {
1971 part = order[i];
1972 if (part.to > start && part.from <= start) { break }
1973 }
1974 if (part.to >= end) { return inner(builder, text, style, startStyle, endStyle, css, attributes) }
1975 inner(builder, text.slice(0, part.to - start), style, startStyle, null, css, attributes);
1976 startStyle = null;
1977 text = text.slice(part.to - start);
1978 start = part.to;
1979 }
1980 }
1981 }
1982
1983 function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
1984 var widget = !ignoreWidget && marker.widgetNode;
1985 if (widget) { builder.map.push(builder.pos, builder.pos + size, widget); }
1986 if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {
1987 if (!widget)
1988 { widget = builder.content.appendChild(document.createElement("span")); }
1989 widget.setAttribute("cm-marker", marker.id);
1990 }
1991 if (widget) {
1992 builder.cm.display.input.setUneditable(widget);
1993 builder.content.appendChild(widget);
1994 }
1995 builder.pos += size;
1996 builder.trailingSpace = false;
1997 }
1998
1999 // Outputs a number of spans to make up a line, taking highlighting
2000 // and marked text into account.
2001 function insertLineContent(line, builder, styles) {
2002 var spans = line.markedSpans, allText = line.text, at = 0;
2003 if (!spans) {
2004 for (var i$1 = 1; i$1 < styles.length; i$1+=2)
2005 { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpretTokenStyle(styles[i$1+1], builder.cm.options)); }
2006 return
2007 }
2008
2009 var len = allText.length, pos = 0, i = 1, text = "", style, css;
2010 var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed, attributes;
2011 for (;;) {
2012 if (nextChange == pos) { // Update current marker set
2013 spanStyle = spanEndStyle = spanStartStyle = css = "";
2014 attributes = null;
2015 collapsed = null; nextChange = Infinity;
2016 var foundBookmarks = [], endStyles = (void 0);
2017 for (var j = 0; j < spans.length; ++j) {
2018 var sp = spans[j], m = sp.marker;
2019 if (m.type == "bookmark" && sp.from == pos && m.widgetNode) {
2020 foundBookmarks.push(m);
2021 } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) {
2022 if (sp.to != null && sp.to != pos && nextChange > sp.to) {
2023 nextChange = sp.to;
2024 spanEndStyle = "";
2025 }
2026 if (m.className) { spanStyle += " " + m.className; }
2027 if (m.css) { css = (css ? css + ";" : "") + m.css; }
2028 if (m.startStyle && sp.from == pos) { spanStartStyle += " " + m.startStyle; }
2029 if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [])).push(m.endStyle, sp.to); }
2030 // support for the old title property
2031 // https://github.com/codemirror/CodeMirror/pull/5673
2032 if (m.title) { (attributes || (attributes = {})).title = m.title; }
2033 if (m.attributes) {
2034 for (var attr in m.attributes)
2035 { (attributes || (attributes = {}))[attr] = m.attributes[attr]; }
2036 }
2037 if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))
2038 { collapsed = sp; }
2039 } else if (sp.from > pos && nextChange > sp.from) {
2040 nextChange = sp.from;
2041 }
2042 }
2043 if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2)
2044 { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += " " + endStyles[j$1]; } } }
2045
2046 if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2)
2047 { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]); } }
2048 if (collapsed && (collapsed.from || 0) == pos) {
2049 buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos,
2050 collapsed.marker, collapsed.from == null);
2051 if (collapsed.to == null) { return }
2052 if (collapsed.to == pos) { collapsed = false; }
2053 }
2054 }
2055 if (pos >= len) { break }
2056
2057 var upto = Math.min(len, nextChange);
2058 while (true) {
2059 if (text) {
2060 var end = pos + text.length;
2061 if (!collapsed) {
2062 var tokenText = end > upto ? text.slice(0, upto - pos) : text;
2063 builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
2064 spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", css, attributes);
2065 }
2066 if (end >= upto) {text = text.slice(upto - pos); pos = upto; break}
2067 pos = end;
2068 spanStartStyle = "";
2069 }
2070 text = allText.slice(at, at = styles[i++]);
2071 style = interpretTokenStyle(styles[i++], builder.cm.options);
2072 }
2073 }
2074 }
2075
2076
2077 // These objects are used to represent the visible (currently drawn)
2078 // part of the document. A LineView may correspond to multiple
2079 // logical lines, if those are connected by collapsed ranges.
2080 function LineView(doc, line, lineN) {
2081 // The starting line
2082 this.line = line;
2083 // Continuing lines, if any
2084 this.rest = visualLineContinued(line);
2085 // Number of logical lines in this visual line
2086 this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1;
2087 this.node = this.text = null;
2088 this.hidden = lineIsHidden(doc, line);
2089 }
2090
2091 // Create a range of LineView objects for the given lines.
2092 function buildViewArray(cm, from, to) {
2093 var array = [], nextPos;
2094 for (var pos = from; pos < to; pos = nextPos) {
2095 var view = new LineView(cm.doc, getLine(cm.doc, pos), pos);
2096 nextPos = pos + view.size;
2097 array.push(view);
2098 }
2099 return array
2100 }
2101
2102 var operationGroup = null;
2103
2104 function pushOperation(op) {
2105 if (operationGroup) {
2106 operationGroup.ops.push(op);
2107 } else {
2108 op.ownsGroup = operationGroup = {
2109 ops: [op],
2110 delayedCallbacks: []
2111 };
2112 }
2113 }
2114
2115 function fireCallbacksForOps(group) {
2116 // Calls delayed callbacks and cursorActivity handlers until no
2117 // new ones appear
2118 var callbacks = group.delayedCallbacks, i = 0;
2119 do {
2120 for (; i < callbacks.length; i++)
2121 { callbacks[i].call(null); }
2122 for (var j = 0; j < group.ops.length; j++) {
2123 var op = group.ops[j];
2124 if (op.cursorActivityHandlers)
2125 { while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
2126 { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm); } }
2127 }
2128 } while (i < callbacks.length)
2129 }
2130
2131 function finishOperation(op, endCb) {
2132 var group = op.ownsGroup;
2133 if (!group) { return }
2134
2135 try { fireCallbacksForOps(group); }
2136 finally {
2137 operationGroup = null;
2138 endCb(group);
2139 }
2140 }
2141
2142 var orphanDelayedCallbacks = null;
2143
2144 // Often, we want to signal events at a point where we are in the
2145 // middle of some work, but don't want the handler to start calling
2146 // other methods on the editor, which might be in an inconsistent
2147 // state or simply not expect any other events to happen.
2148 // signalLater looks whether there are any handlers, and schedules
2149 // them to be executed when the last operation ends, or, if no
2150 // operation is active, when a timeout fires.
2151 function signalLater(emitter, type /*, values...*/) {
2152 var arr = getHandlers(emitter, type);
2153 if (!arr.length) { return }
2154 var args = Array.prototype.slice.call(arguments, 2), list;
2155 if (operationGroup) {
2156 list = operationGroup.delayedCallbacks;
2157 } else if (orphanDelayedCallbacks) {
2158 list = orphanDelayedCallbacks;
2159 } else {
2160 list = orphanDelayedCallbacks = [];
2161 setTimeout(fireOrphanDelayed, 0);
2162 }
2163 var loop = function ( i ) {
2164 list.push(function () { return arr[i].apply(null, args); });
2165 };
2166
2167 for (var i = 0; i < arr.length; ++i)
2168 loop( i );
2169 }
2170
2171 function fireOrphanDelayed() {
2172 var delayed = orphanDelayedCallbacks;
2173 orphanDelayedCallbacks = null;
2174 for (var i = 0; i < delayed.length; ++i) { delayed[i](); }
2175 }
2176
2177 // When an aspect of a line changes, a string is added to
2178 // lineView.changes. This updates the relevant part of the line's
2179 // DOM structure.
2180 function updateLineForChanges(cm, lineView, lineN, dims) {
2181 for (var j = 0; j < lineView.changes.length; j++) {
2182 var type = lineView.changes[j];
2183 if (type == "text") { updateLineText(cm, lineView); }
2184 else if (type == "gutter") { updateLineGutter(cm, lineView, lineN, dims); }
2185 else if (type == "class") { updateLineClasses(cm, lineView); }
2186 else if (type == "widget") { updateLineWidgets(cm, lineView, dims); }
2187 }
2188 lineView.changes = null;
2189 }
2190
2191 // Lines with gutter elements, widgets or a background class need to
2192 // be wrapped, and have the extra elements added to the wrapper div
2193 function ensureLineWrapped(lineView) {
2194 if (lineView.node == lineView.text) {
2195 lineView.node = elt("div", null, null, "position: relative");
2196 if (lineView.text.parentNode)
2197 { lineView.text.parentNode.replaceChild(lineView.node, lineView.text); }
2198 lineView.node.appendChild(lineView.text);
2199 if (ie && ie_version < 8) { lineView.node.style.zIndex = 2; }
2200 }
2201 return lineView.node
2202 }
2203
2204 function updateLineBackground(cm, lineView) {
2205 var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass;
2206 if (cls) { cls += " CodeMirror-linebackground"; }
2207 if (lineView.background) {
2208 if (cls) { lineView.background.className = cls; }
2209 else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; }
2210 } else if (cls) {
2211 var wrap = ensureLineWrapped(lineView);
2212 lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild);
2213 cm.display.input.setUneditable(lineView.background);
2214 }
2215 }
2216
2217 // Wrapper around buildLineContent which will reuse the structure
2218 // in display.externalMeasured when possible.
2219 function getLineContent(cm, lineView) {
2220 var ext = cm.display.externalMeasured;
2221 if (ext && ext.line == lineView.line) {
2222 cm.display.externalMeasured = null;
2223 lineView.measure = ext.measure;
2224 return ext.built
2225 }
2226 return buildLineContent(cm, lineView)
2227 }
2228
2229 // Redraw the line's text. Interacts with the background and text
2230 // classes because the mode may output tokens that influence these
2231 // classes.
2232 function updateLineText(cm, lineView) {
2233 var cls = lineView.text.className;
2234 var built = getLineContent(cm, lineView);
2235 if (lineView.text == lineView.node) { lineView.node = built.pre; }
2236 lineView.text.parentNode.replaceChild(built.pre, lineView.text);
2237 lineView.text = built.pre;
2238 if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {
2239 lineView.bgClass = built.bgClass;
2240 lineView.textClass = built.textClass;
2241 updateLineClasses(cm, lineView);
2242 } else if (cls) {
2243 lineView.text.className = cls;
2244 }
2245 }
2246
2247 function updateLineClasses(cm, lineView) {
2248 updateLineBackground(cm, lineView);
2249 if (lineView.line.wrapClass)
2250 { ensureLineWrapped(lineView).className = lineView.line.wrapClass; }
2251 else if (lineView.node != lineView.text)
2252 { lineView.node.className = ""; }
2253 var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass;
2254 lineView.text.className = textClass || "";
2255 }
2256
2257 function updateLineGutter(cm, lineView, lineN, dims) {
2258 if (lineView.gutter) {
2259 lineView.node.removeChild(lineView.gutter);
2260 lineView.gutter = null;
2261 }
2262 if (lineView.gutterBackground) {
2263 lineView.node.removeChild(lineView.gutterBackground);
2264 lineView.gutterBackground = null;
2265 }
2266 if (lineView.line.gutterClass) {
2267 var wrap = ensureLineWrapped(lineView);
2268 lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass,
2269 ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px; width: " + (dims.gutterTotalWidth) + "px"));
2270 cm.display.input.setUneditable(lineView.gutterBackground);
2271 wrap.insertBefore(lineView.gutterBackground, lineView.text);
2272 }
2273 var markers = lineView.line.gutterMarkers;
2274 if (cm.options.lineNumbers || markers) {
2275 var wrap$1 = ensureLineWrapped(lineView);
2276 var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"));
2277 cm.display.input.setUneditable(gutterWrap);
2278 wrap$1.insertBefore(gutterWrap, lineView.text);
2279 if (lineView.line.gutterClass)
2280 { gutterWrap.className += " " + lineView.line.gutterClass; }
2281 if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
2282 { lineView.lineNumber = gutterWrap.appendChild(
2283 elt("div", lineNumberFor(cm.options, lineN),
2284 "CodeMirror-linenumber CodeMirror-gutter-elt",
2285 ("left: " + (dims.gutterLeft["CodeMirror-linenumbers"]) + "px; width: " + (cm.display.lineNumInnerWidth) + "px"))); }
2286 if (markers) { for (var k = 0; k < cm.options.gutters.length; ++k) {
2287 var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id];
2288 if (found)
2289 { gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt",
2290 ("left: " + (dims.gutterLeft[id]) + "px; width: " + (dims.gutterWidth[id]) + "px"))); }
2291 } }
2292 }
2293 }
2294
2295 function updateLineWidgets(cm, lineView, dims) {
2296 if (lineView.alignable) { lineView.alignable = null; }
2297 for (var node = lineView.node.firstChild, next = (void 0); node; node = next) {
2298 next = node.nextSibling;
2299 if (node.className == "CodeMirror-linewidget")
2300 { lineView.node.removeChild(node); }
2301 }
2302 insertLineWidgets(cm, lineView, dims);
2303 }
2304
2305 // Build a line's DOM representation from scratch
2306 function buildLineElement(cm, lineView, lineN, dims) {
2307 var built = getLineContent(cm, lineView);
2308 lineView.text = lineView.node = built.pre;
2309 if (built.bgClass) { lineView.bgClass = built.bgClass; }
2310 if (built.textClass) { lineView.textClass = built.textClass; }
2311
2312 updateLineClasses(cm, lineView);
2313 updateLineGutter(cm, lineView, lineN, dims);
2314 insertLineWidgets(cm, lineView, dims);
2315 return lineView.node
2316 }
2317
2318 // A lineView may contain multiple logical lines (when merged by
2319 // collapsed spans). The widgets for all of them need to be drawn.
2320 function insertLineWidgets(cm, lineView, dims) {
2321 insertLineWidgetsFor(cm, lineView.line, lineView, dims, true);
2322 if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
2323 { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false); } }
2324 }
2325
2326 function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {
2327 if (!line.widgets) { return }
2328 var wrap = ensureLineWrapped(lineView);
2329 for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
2330 var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
2331 if (!widget.handleMouseEvents) { node.setAttribute("cm-ignore-events", "true"); }
2332 positionLineWidget(widget, node, lineView, dims);
2333 cm.display.input.setUneditable(node);
2334 if (allowAbove && widget.above)
2335 { wrap.insertBefore(node, lineView.gutter || lineView.text); }
2336 else
2337 { wrap.appendChild(node); }
2338 signalLater(widget, "redraw");
2339 }
2340 }
2341
2342 function positionLineWidget(widget, node, lineView, dims) {
2343 if (widget.noHScroll) {
2344 (lineView.alignable || (lineView.alignable = [])).push(node);
2345 var width = dims.wrapperWidth;
2346 node.style.left = dims.fixedPos + "px";
2347 if (!widget.coverGutter) {
2348 width -= dims.gutterTotalWidth;
2349 node.style.paddingLeft = dims.gutterTotalWidth + "px";
2350 }
2351 node.style.width = width + "px";
2352 }
2353 if (widget.coverGutter) {
2354 node.style.zIndex = 5;
2355 node.style.position = "relative";
2356 if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + "px"; }
2357 }
2358 }
2359
2360 function widgetHeight(widget) {
2361 if (widget.height != null) { return widget.height }
2362 var cm = widget.doc.cm;
2363 if (!cm) { return 0 }
2364 if (!contains(document.body, widget.node)) {
2365 var parentStyle = "position: relative;";
2366 if (widget.coverGutter)
2367 { parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;"; }
2368 if (widget.noHScroll)
2369 { parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;"; }
2370 removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle));
2371 }
2372 return widget.height = widget.node.parentNode.offsetHeight
2373 }
2374
2375 // Return true when the given mouse event happened in a widget
2376 function eventInWidget(display, e) {
2377 for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
2378 if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") ||
2379 (n.parentNode == display.sizer && n != display.mover))
2380 { return true }
2381 }
2382 }
2383
2384 // POSITION MEASUREMENT
2385
2386 function paddingTop(display) {return display.lineSpace.offsetTop}
2387 function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight}
2388 function paddingH(display) {
2389 if (display.cachedPaddingH) { return display.cachedPaddingH }
2390 var e = removeChildrenAndAdd(display.measure, elt("pre", "x"));
2391 var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle;
2392 var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)};
2393 if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data; }
2394 return data
2395 }
2396
2397 function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth }
2398 function displayWidth(cm) {
2399 return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth
2400 }
2401 function displayHeight(cm) {
2402 return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight
2403 }
2404
2405 // Ensure the lineView.wrapping.heights array is populated. This is
2406 // an array of bottom offsets for the lines that make up a drawn
2407 // line. When lineWrapping is on, there might be more than one
2408 // height.
2409 function ensureLineHeights(cm, lineView, rect) {
2410 var wrapping = cm.options.lineWrapping;
2411 var curWidth = wrapping && displayWidth(cm);
2412 if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {
2413 var heights = lineView.measure.heights = [];
2414 if (wrapping) {
2415 lineView.measure.width = curWidth;
2416 var rects = lineView.text.firstChild.getClientRects();
2417 for (var i = 0; i < rects.length - 1; i++) {
2418 var cur = rects[i], next = rects[i + 1];
2419 if (Math.abs(cur.bottom - next.bottom) > 2)
2420 { heights.push((cur.bottom + next.top) / 2 - rect.top); }
2421 }
2422 }
2423 heights.push(rect.bottom - rect.top);
2424 }
2425 }
2426
2427 // Find a line map (mapping character offsets to text nodes) and a
2428 // measurement cache for the given line number. (A line view might
2429 // contain multiple lines when collapsed ranges are present.)
2430 function mapFromLineView(lineView, line, lineN) {
2431 if (lineView.line == line)
2432 { return {map: lineView.measure.map, cache: lineView.measure.cache} }
2433 for (var i = 0; i < lineView.rest.length; i++)
2434 { if (lineView.rest[i] == line)
2435 { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } }
2436 for (var i$1 = 0; i$1 < lineView.rest.length; i$1++)
2437 { if (lineNo(lineView.rest[i$1]) > lineN)
2438 { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } }
2439 }
2440
2441 // Render a line into the hidden node display.externalMeasured. Used
2442 // when measurement is needed for a line that's not in the viewport.
2443 function updateExternalMeasurement(cm, line) {
2444 line = visualLine(line);
2445 var lineN = lineNo(line);
2446 var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN);
2447 view.lineN = lineN;
2448 var built = view.built = buildLineContent(cm, view);
2449 view.text = built.pre;
2450 removeChildrenAndAdd(cm.display.lineMeasure, built.pre);
2451 return view
2452 }
2453
2454 // Get a {top, bottom, left, right} box (in line-local coordinates)
2455 // for a given character.
2456 function measureChar(cm, line, ch, bias) {
2457 return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias)
2458 }
2459
2460 // Find a line view that corresponds to the given line number.
2461 function findViewForLine(cm, lineN) {
2462 if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo)
2463 { return cm.display.view[findViewIndex(cm, lineN)] }
2464 var ext = cm.display.externalMeasured;
2465 if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size)
2466 { return ext }
2467 }
2468
2469 // Measurement can be split in two steps, the set-up work that
2470 // applies to the whole line, and the measurement of the actual
2471 // character. Functions like coordsChar, that need to do a lot of
2472 // measurements in a row, can thus ensure that the set-up work is
2473 // only done once.
2474 function prepareMeasureForLine(cm, line) {
2475 var lineN = lineNo(line);
2476 var view = findViewForLine(cm, lineN);
2477 if (view && !view.text) {
2478 view = null;
2479 } else if (view && view.changes) {
2480 updateLineForChanges(cm, view, lineN, getDimensions(cm));
2481 cm.curOp.forceUpdate = true;
2482 }
2483 if (!view)
2484 { view = updateExternalMeasurement(cm, line); }
2485
2486 var info = mapFromLineView(view, line, lineN);
2487 return {
2488 line: line, view: view, rect: null,
2489 map: info.map, cache: info.cache, before: info.before,
2490 hasHeights: false
2491 }
2492 }
2493
2494 // Given a prepared measurement object, measures the position of an
2495 // actual character (or fetches it from the cache).
2496 function measureCharPrepared(cm, prepared, ch, bias, varHeight) {
2497 if (prepared.before) { ch = -1; }
2498 var key = ch + (bias || ""), found;
2499 if (prepared.cache.hasOwnProperty(key)) {
2500 found = prepared.cache[key];
2501 } else {
2502 if (!prepared.rect)
2503 { prepared.rect = prepared.view.text.getBoundingClientRect(); }
2504 if (!prepared.hasHeights) {
2505 ensureLineHeights(cm, prepared.view, prepared.rect);
2506 prepared.hasHeights = true;
2507 }
2508 found = measureCharInner(cm, prepared, ch, bias);
2509 if (!found.bogus) { prepared.cache[key] = found; }
2510 }
2511 return {left: found.left, right: found.right,
2512 top: varHeight ? found.rtop : found.top,
2513 bottom: varHeight ? found.rbottom : found.bottom}
2514 }
2515
2516 var nullRect = {left: 0, right: 0, top: 0, bottom: 0};
2517
2518 function nodeAndOffsetInLineMap(map$$1, ch, bias) {
2519 var node, start, end, collapse, mStart, mEnd;
2520 // First, search the line map for the text node corresponding to,
2521 // or closest to, the target character.
2522 for (var i = 0; i < map$$1.length; i += 3) {
2523 mStart = map$$1[i];
2524 mEnd = map$$1[i + 1];
2525 if (ch < mStart) {
2526 start = 0; end = 1;
2527 collapse = "left";
2528 } else if (ch < mEnd) {
2529 start = ch - mStart;
2530 end = start + 1;
2531 } else if (i == map$$1.length - 3 || ch == mEnd && map$$1[i + 3] > ch) {
2532 end = mEnd - mStart;
2533 start = end - 1;
2534 if (ch >= mEnd) { collapse = "right"; }
2535 }
2536 if (start != null) {
2537 node = map$$1[i + 2];
2538 if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right"))
2539 { collapse = bias; }
2540 if (bias == "left" && start == 0)
2541 { while (i && map$$1[i - 2] == map$$1[i - 3] && map$$1[i - 1].insertLeft) {
2542 node = map$$1[(i -= 3) + 2];
2543 collapse = "left";
2544 } }
2545 if (bias == "right" && start == mEnd - mStart)
2546 { while (i < map$$1.length - 3 && map$$1[i + 3] == map$$1[i + 4] && !map$$1[i + 5].insertLeft) {
2547 node = map$$1[(i += 3) + 2];
2548 collapse = "right";
2549 } }
2550 break
2551 }
2552 }
2553 return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd}
2554 }
2555
2556 function getUsefulRect(rects, bias) {
2557 var rect = nullRect;
2558 if (bias == "left") { for (var i = 0; i < rects.length; i++) {
2559 if ((rect = rects[i]).left != rect.right) { break }
2560 } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) {
2561 if ((rect = rects[i$1]).left != rect.right) { break }
2562 } }
2563 return rect
2564 }
2565
2566 function measureCharInner(cm, prepared, ch, bias) {
2567 var place = nodeAndOffsetInLineMap(prepared.map, ch, bias);
2568 var node = place.node, start = place.start, end = place.end, collapse = place.collapse;
2569
2570 var rect;
2571 if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.
2572 for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonsense rectangles are returned
2573 while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start; }
2574 while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) { ++end; }
2575 if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart)
2576 { rect = node.parentNode.getBoundingClientRect(); }
2577 else
2578 { rect = getUsefulRect(range(node, start, end).getClientRects(), bias); }
2579 if (rect.left || rect.right || start == 0) { break }
2580 end = start;
2581 start = start - 1;
2582 collapse = "right";
2583 }
2584 if (ie && ie_version < 11) { rect = maybeUpdateRectForZooming(cm.display.measure, rect); }
2585 } else { // If it is a widget, simply get the box for the whole widget.
2586 if (start > 0) { collapse = bias = "right"; }
2587 var rects;
2588 if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)
2589 { rect = rects[bias == "right" ? rects.length - 1 : 0]; }
2590 else
2591 { rect = node.getBoundingClientRect(); }
2592 }
2593 if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {
2594 var rSpan = node.parentNode.getClientRects()[0];
2595 if (rSpan)
2596 { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom}; }
2597 else
2598 { rect = nullRect; }
2599 }
2600
2601 var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top;
2602 var mid = (rtop + rbot) / 2;
2603 var heights = prepared.view.measure.heights;
2604 var i = 0;
2605 for (; i < heights.length - 1; i++)
2606 { if (mid < heights[i]) { break } }
2607 var top = i ? heights[i - 1] : 0, bot = heights[i];
2608 var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left,
2609 right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left,
2610 top: top, bottom: bot};
2611 if (!rect.left && !rect.right) { result.bogus = true; }
2612 if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; }
2613
2614 return result
2615 }
2616
2617 // Work around problem with bounding client rects on ranges being
2618 // returned incorrectly when zoomed on IE10 and below.
2619 function maybeUpdateRectForZooming(measure, rect) {
2620 if (!window.screen || screen.logicalXDPI == null ||
2621 screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))
2622 { return rect }
2623 var scaleX = screen.logicalXDPI / screen.deviceXDPI;
2624 var scaleY = screen.logicalYDPI / screen.deviceYDPI;
2625 return {left: rect.left * scaleX, right: rect.right * scaleX,
2626 top: rect.top * scaleY, bottom: rect.bottom * scaleY}
2627 }
2628
2629 function clearLineMeasurementCacheFor(lineView) {
2630 if (lineView.measure) {
2631 lineView.measure.cache = {};
2632 lineView.measure.heights = null;
2633 if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
2634 { lineView.measure.caches[i] = {}; } }
2635 }
2636 }
2637
2638 function clearLineMeasurementCache(cm) {
2639 cm.display.externalMeasure = null;
2640 removeChildren(cm.display.lineMeasure);
2641 for (var i = 0; i < cm.display.view.length; i++)
2642 { clearLineMeasurementCacheFor(cm.display.view[i]); }
2643 }
2644
2645 function clearCaches(cm) {
2646 clearLineMeasurementCache(cm);
2647 cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null;
2648 if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true; }
2649 cm.display.lineNumChars = null;
2650 }
2651
2652 function pageScrollX() {
2653 // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=489206
2654 // which causes page_Offset and bounding client rects to use
2655 // different reference viewports and invalidate our calculations.
2656 if (chrome && android) { return -(document.body.getBoundingClientRect().left - parseInt(getComputedStyle(document.body).marginLeft)) }
2657 return window.pageXOffset || (document.documentElement || document.body).scrollLeft
2658 }
2659 function pageScrollY() {
2660 if (chrome && android) { return -(document.body.getBoundingClientRect().top - parseInt(getComputedStyle(document.body).marginTop)) }
2661 return window.pageYOffset || (document.documentElement || document.body).scrollTop
2662 }
2663
2664 function widgetTopHeight(lineObj) {
2665 var height = 0;
2666 if (lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above)
2667 { height += widgetHeight(lineObj.widgets[i]); } } }
2668 return height
2669 }
2670
2671 // Converts a {top, bottom, left, right} box from line-local
2672 // coordinates into another coordinate system. Context may be one of
2673 // "line", "div" (display.lineDiv), "local"./null (editor), "window",
2674 // or "page".
2675 function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) {
2676 if (!includeWidgets) {
2677 var height = widgetTopHeight(lineObj);
2678 rect.top += height; rect.bottom += height;
2679 }
2680 if (context == "line") { return rect }
2681 if (!context) { context = "local"; }
2682 var yOff = heightAtLine(lineObj);
2683 if (context == "local") { yOff += paddingTop(cm.display); }
2684 else { yOff -= cm.display.viewOffset; }
2685 if (context == "page" || context == "window") {
2686 var lOff = cm.display.lineSpace.getBoundingClientRect();
2687 yOff += lOff.top + (context == "window" ? 0 : pageScrollY());
2688 var xOff = lOff.left + (context == "window" ? 0 : pageScrollX());
2689 rect.left += xOff; rect.right += xOff;
2690 }
2691 rect.top += yOff; rect.bottom += yOff;
2692 return rect
2693 }
2694
2695 // Coverts a box from "div" coords to another coordinate system.
2696 // Context may be "window", "page", "div", or "local"./null.
2697 function fromCoordSystem(cm, coords, context) {
2698 if (context == "div") { return coords }
2699 var left = coords.left, top = coords.top;
2700 // First move into "page" coordinate system
2701 if (context == "page") {
2702 left -= pageScrollX();
2703 top -= pageScrollY();
2704 } else if (context == "local" || !context) {
2705 var localBox = cm.display.sizer.getBoundingClientRect();
2706 left += localBox.left;
2707 top += localBox.top;
2708 }
2709
2710 var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect();
2711 return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top}
2712 }
2713
2714 function charCoords(cm, pos, context, lineObj, bias) {
2715 if (!lineObj) { lineObj = getLine(cm.doc, pos.line); }
2716 return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context)
2717 }
2718
2719 // Returns a box for a given cursor position, which may have an
2720 // 'other' property containing the position of the secondary cursor
2721 // on a bidi boundary.
2722 // A cursor Pos(line, char, "before") is on the same visual line as `char - 1`
2723 // and after `char - 1` in writing order of `char - 1`
2724 // A cursor Pos(line, char, "after") is on the same visual line as `char`
2725 // and before `char` in writing order of `char`
2726 // Examples (upper-case letters are RTL, lower-case are LTR):
2727 // Pos(0, 1, ...)
2728 // before after
2729 // ab a|b a|b
2730 // aB a|B aB|
2731 // Ab |Ab A|b
2732 // AB B|A B|A
2733 // Every position after the last character on a line is considered to stick
2734 // to the last character on the line.
2735 function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
2736 lineObj = lineObj || getLine(cm.doc, pos.line);
2737 if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); }
2738 function get(ch, right) {
2739 var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight);
2740 if (right) { m.left = m.right; } else { m.right = m.left; }
2741 return intoCoordSystem(cm, lineObj, m, context)
2742 }
2743 var order = getOrder(lineObj, cm.doc.direction), ch = pos.ch, sticky = pos.sticky;
2744 if (ch >= lineObj.text.length) {
2745 ch = lineObj.text.length;
2746 sticky = "before";
2747 } else if (ch <= 0) {
2748 ch = 0;
2749 sticky = "after";
2750 }
2751 if (!order) { return get(sticky == "before" ? ch - 1 : ch, sticky == "before") }
2752
2753 function getBidi(ch, partPos, invert) {
2754 var part = order[partPos], right = part.level == 1;
2755 return get(invert ? ch - 1 : ch, right != invert)
2756 }
2757 var partPos = getBidiPartAt(order, ch, sticky);
2758 var other = bidiOther;
2759 var val = getBidi(ch, partPos, sticky == "before");
2760 if (other != null) { val.other = getBidi(ch, other, sticky != "before"); }
2761 return val
2762 }
2763
2764 // Used to cheaply estimate the coordinates for a position. Used for
2765 // intermediate scroll updates.
2766 function estimateCoords(cm, pos) {
2767 var left = 0;
2768 pos = clipPos(cm.doc, pos);
2769 if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch; }
2770 var lineObj = getLine(cm.doc, pos.line);
2771 var top = heightAtLine(lineObj) + paddingTop(cm.display);
2772 return {left: left, right: left, top: top, bottom: top + lineObj.height}
2773 }
2774
2775 // Positions returned by coordsChar contain some extra information.
2776 // xRel is the relative x position of the input coordinates compared
2777 // to the found position (so xRel > 0 means the coordinates are to
2778 // the right of the character position, for example). When outside
2779 // is true, that means the coordinates lie outside the line's
2780 // vertical range.
2781 function PosWithInfo(line, ch, sticky, outside, xRel) {
2782 var pos = Pos(line, ch, sticky);
2783 pos.xRel = xRel;
2784 if (outside) { pos.outside = true; }
2785 return pos
2786 }
2787
2788 // Compute the character position closest to the given coordinates.
2789 // Input must be lineSpace-local ("div" coordinate system).
2790 function coordsChar(cm, x, y) {
2791 var doc = cm.doc;
2792 y += cm.display.viewOffset;
2793 if (y < 0) { return PosWithInfo(doc.first, 0, null, true, -1) }
2794 var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
2795 if (lineN > last)
2796 { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, true, 1) }
2797 if (x < 0) { x = 0; }
2798
2799 var lineObj = getLine(doc, lineN);
2800 for (;;) {
2801 var found = coordsCharInner(cm, lineObj, lineN, x, y);
2802 var collapsed = collapsedSpanAround(lineObj, found.ch + (found.xRel > 0 ? 1 : 0));
2803 if (!collapsed) { return found }
2804 var rangeEnd = collapsed.find(1);
2805 if (rangeEnd.line == lineN) { return rangeEnd }
2806 lineObj = getLine(doc, lineN = rangeEnd.line);
2807 }
2808 }
2809
2810 function wrappedLineExtent(cm, lineObj, preparedMeasure, y) {
2811 y -= widgetTopHeight(lineObj);
2812 var end = lineObj.text.length;
2813 var begin = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch - 1).bottom <= y; }, end, 0);
2814 end = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch).top > y; }, begin, end);
2815 return {begin: begin, end: end}
2816 }
2817
2818 function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) {
2819 if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); }
2820 var targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), "line").top;
2821 return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop)
2822 }
2823
2824 // Returns true if the given side of a box is after the given
2825 // coordinates, in top-to-bottom, left-to-right order.
2826 function boxIsAfter(box, x, y, left) {
2827 return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x
2828 }
2829
2830 function coordsCharInner(cm, lineObj, lineNo$$1, x, y) {
2831 // Move y into line-local coordinate space
2832 y -= heightAtLine(lineObj);
2833 var preparedMeasure = prepareMeasureForLine(cm, lineObj);
2834 // When directly calling `measureCharPrepared`, we have to adjust
2835 // for the widgets at this line.
2836 var widgetHeight$$1 = widgetTopHeight(lineObj);
2837 var begin = 0, end = lineObj.text.length, ltr = true;
2838
2839 var order = getOrder(lineObj, cm.doc.direction);
2840 // If the line isn't plain left-to-right text, first figure out
2841 // which bidi section the coordinates fall into.
2842 if (order) {
2843 var part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart)
2844 (cm, lineObj, lineNo$$1, preparedMeasure, order, x, y);
2845 ltr = part.level != 1;
2846 // The awkward -1 offsets are needed because findFirst (called
2847 // on these below) will treat its first bound as inclusive,
2848 // second as exclusive, but we want to actually address the
2849 // characters in the part's range
2850 begin = ltr ? part.from : part.to - 1;
2851 end = ltr ? part.to : part.from - 1;
2852 }
2853
2854 // A binary search to find the first character whose bounding box
2855 // starts after the coordinates. If we run across any whose box wrap
2856 // the coordinates, store that.
2857 var chAround = null, boxAround = null;
2858 var ch = findFirst(function (ch) {
2859 var box = measureCharPrepared(cm, preparedMeasure, ch);
2860 box.top += widgetHeight$$1; box.bottom += widgetHeight$$1;
2861 if (!boxIsAfter(box, x, y, false)) { return false }
2862 if (box.top <= y && box.left <= x) {
2863 chAround = ch;
2864 boxAround = box;
2865 }
2866 return true
2867 }, begin, end);
2868
2869 var baseX, sticky, outside = false;
2870 // If a box around the coordinates was found, use that
2871 if (boxAround) {
2872 // Distinguish coordinates nearer to the left or right side of the box
2873 var atLeft = x - boxAround.left < boxAround.right - x, atStart = atLeft == ltr;
2874 ch = chAround + (atStart ? 0 : 1);
2875 sticky = atStart ? "after" : "before";
2876 baseX = atLeft ? boxAround.left : boxAround.right;
2877 } else {
2878 // (Adjust for extended bound, if necessary.)
2879 if (!ltr && (ch == end || ch == begin)) { ch++; }
2880 // To determine which side to associate with, get the box to the
2881 // left of the character and compare it's vertical position to the
2882 // coordinates
2883 sticky = ch == 0 ? "after" : ch == lineObj.text.length ? "before" :
2884 (measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight$$1 <= y) == ltr ?
2885 "after" : "before";
2886 // Now get accurate coordinates for this place, in order to get a
2887 // base X position
2888 var coords = cursorCoords(cm, Pos(lineNo$$1, ch, sticky), "line", lineObj, preparedMeasure);
2889 baseX = coords.left;
2890 outside = y < coords.top || y >= coords.bottom;
2891 }
2892
2893 ch = skipExtendingChars(lineObj.text, ch, 1);
2894 return PosWithInfo(lineNo$$1, ch, sticky, outside, x - baseX)
2895 }
2896
2897 function coordsBidiPart(cm, lineObj, lineNo$$1, preparedMeasure, order, x, y) {
2898 // Bidi parts are sorted left-to-right, and in a non-line-wrapping
2899 // situation, we can take this ordering to correspond to the visual
2900 // ordering. This finds the first part whose end is after the given
2901 // coordinates.
2902 var index = findFirst(function (i) {
2903 var part = order[i], ltr = part.level != 1;
2904 return boxIsAfter(cursorCoords(cm, Pos(lineNo$$1, ltr ? part.to : part.from, ltr ? "before" : "after"),
2905 "line", lineObj, preparedMeasure), x, y, true)
2906 }, 0, order.length - 1);
2907 var part = order[index];
2908 // If this isn't the first part, the part's start is also after
2909 // the coordinates, and the coordinates aren't on the same line as
2910 // that start, move one part back.
2911 if (index > 0) {
2912 var ltr = part.level != 1;
2913 var start = cursorCoords(cm, Pos(lineNo$$1, ltr ? part.from : part.to, ltr ? "after" : "before"),
2914 "line", lineObj, preparedMeasure);
2915 if (boxIsAfter(start, x, y, true) && start.top > y)
2916 { part = order[index - 1]; }
2917 }
2918 return part
2919 }
2920
2921 function coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x, y) {
2922 // In a wrapped line, rtl text on wrapping boundaries can do things
2923 // that don't correspond to the ordering in our `order` array at
2924 // all, so a binary search doesn't work, and we want to return a
2925 // part that only spans one line so that the binary search in
2926 // coordsCharInner is safe. As such, we first find the extent of the
2927 // wrapped line, and then do a flat search in which we discard any
2928 // spans that aren't on the line.
2929 var ref = wrappedLineExtent(cm, lineObj, preparedMeasure, y);
2930 var begin = ref.begin;
2931 var end = ref.end;
2932 if (/\s/.test(lineObj.text.charAt(end - 1))) { end--; }
2933 var part = null, closestDist = null;
2934 for (var i = 0; i < order.length; i++) {
2935 var p = order[i];
2936 if (p.from >= end || p.to <= begin) { continue }
2937 var ltr = p.level != 1;
2938 var endX = measureCharPrepared(cm, preparedMeasure, ltr ? Math.min(end, p.to) - 1 : Math.max(begin, p.from)).right;
2939 // Weigh against spans ending before this, so that they are only
2940 // picked if nothing ends after
2941 var dist = endX < x ? x - endX + 1e9 : endX - x;
2942 if (!part || closestDist > dist) {
2943 part = p;
2944 closestDist = dist;
2945 }
2946 }
2947 if (!part) { part = order[order.length - 1]; }
2948 // Clip the part to the wrapped line.
2949 if (part.from < begin) { part = {from: begin, to: part.to, level: part.level}; }
2950 if (part.to > end) { part = {from: part.from, to: end, level: part.level}; }
2951 return part
2952 }
2953
2954 var measureText;
2955 // Compute the default text height.
2956 function textHeight(display) {
2957 if (display.cachedTextHeight != null) { return display.cachedTextHeight }
2958 if (measureText == null) {
2959 measureText = elt("pre");
2960 // Measure a bunch of lines, for browsers that compute
2961 // fractional heights.
2962 for (var i = 0; i < 49; ++i) {
2963 measureText.appendChild(document.createTextNode("x"));
2964 measureText.appendChild(elt("br"));
2965 }
2966 measureText.appendChild(document.createTextNode("x"));
2967 }
2968 removeChildrenAndAdd(display.measure, measureText);
2969 var height = measureText.offsetHeight / 50;
2970 if (height > 3) { display.cachedTextHeight = height; }
2971 removeChildren(display.measure);
2972 return height || 1
2973 }
2974
2975 // Compute the default character width.
2976 function charWidth(display) {
2977 if (display.cachedCharWidth != null) { return display.cachedCharWidth }
2978 var anchor = elt("span", "xxxxxxxxxx");
2979 var pre = elt("pre", [anchor]);
2980 removeChildrenAndAdd(display.measure, pre);
2981 var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10;
2982 if (width > 2) { display.cachedCharWidth = width; }
2983 return width || 10
2984 }
2985
2986 // Do a bulk-read of the DOM positions and sizes needed to draw the
2987 // view, so that we don't interleave reading and writing to the DOM.
2988 function getDimensions(cm) {
2989 var d = cm.display, left = {}, width = {};
2990 var gutterLeft = d.gutters.clientLeft;
2991 for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
2992 left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft;
2993 width[cm.options.gutters[i]] = n.clientWidth;
2994 }
2995 return {fixedPos: compensateForHScroll(d),
2996 gutterTotalWidth: d.gutters.offsetWidth,
2997 gutterLeft: left,
2998 gutterWidth: width,
2999 wrapperWidth: d.wrapper.clientWidth}
3000 }
3001
3002 // Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
3003 // but using getBoundingClientRect to get a sub-pixel-accurate
3004 // result.
3005 function compensateForHScroll(display) {
3006 return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left
3007 }
3008
3009 // Returns a function that estimates the height of a line, to use as
3010 // first approximation until the line becomes visible (and is thus
3011 // properly measurable).
3012 function estimateHeight(cm) {
3013 var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
3014 var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
3015 return function (line) {
3016 if (lineIsHidden(cm.doc, line)) { return 0 }
3017
3018 var widgetsHeight = 0;
3019 if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) {
3020 if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height; }
3021 } }
3022
3023 if (wrapping)
3024 { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th }
3025 else
3026 { return widgetsHeight + th }
3027 }
3028 }
3029
3030 function estimateLineHeights(cm) {
3031 var doc = cm.doc, est = estimateHeight(cm);
3032 doc.iter(function (line) {
3033 var estHeight = est(line);
3034 if (estHeight != line.height) { updateLineHeight(line, estHeight); }
3035 });
3036 }
3037
3038 // Given a mouse event, find the corresponding position. If liberal
3039 // is false, it checks whether a gutter or scrollbar was clicked,
3040 // and returns null if it was. forRect is used by rectangular
3041 // selections, and tries to estimate a character position even for
3042 // coordinates beyond the right of the text.
3043 function posFromMouse(cm, e, liberal, forRect) {
3044 var display = cm.display;
3045 if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") { return null }
3046
3047 var x, y, space = display.lineSpace.getBoundingClientRect();
3048 // Fails unpredictably on IE[67] when mouse is dragged around quickly.
3049 try { x = e.clientX - space.left; y = e.clientY - space.top; }
3050 catch (e) { return null }
3051 var coords = coordsChar(cm, x, y), line;
3052 if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) {
3053 var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length;
3054 coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff));
3055 }
3056 return coords
3057 }
3058
3059 // Find the view element corresponding to a given line. Return null
3060 // when the line isn't visible.
3061 function findViewIndex(cm, n) {
3062 if (n >= cm.display.viewTo) { return null }
3063 n -= cm.display.viewFrom;
3064 if (n < 0) { return null }
3065 var view = cm.display.view;
3066 for (var i = 0; i < view.length; i++) {
3067 n -= view[i].size;
3068 if (n < 0) { return i }
3069 }
3070 }
3071
3072 function updateSelection(cm) {
3073 cm.display.input.showSelection(cm.display.input.prepareSelection());
3074 }
3075
3076 function prepareSelection(cm, primary) {
3077 if ( primary === void 0 ) primary = true;
3078
3079 var doc = cm.doc, result = {};
3080 var curFragment = result.cursors = document.createDocumentFragment();
3081 var selFragment = result.selection = document.createDocumentFragment();
3082
3083 for (var i = 0; i < doc.sel.ranges.length; i++) {
3084 if (!primary && i == doc.sel.primIndex) { continue }
3085 var range$$1 = doc.sel.ranges[i];
3086 if (range$$1.from().line >= cm.display.viewTo || range$$1.to().line < cm.display.viewFrom) { continue }
3087 var collapsed = range$$1.empty();
3088 if (collapsed || cm.options.showCursorWhenSelecting)
3089 { drawSelectionCursor(cm, range$$1.head, curFragment); }
3090 if (!collapsed)
3091 { drawSelectionRange(cm, range$$1, selFragment); }
3092 }
3093 return result
3094 }
3095
3096 // Draws a cursor for the given range
3097 function drawSelectionCursor(cm, head, output) {
3098 var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine);
3099
3100 var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor"));
3101 cursor.style.left = pos.left + "px";
3102 cursor.style.top = pos.top + "px";
3103 cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
3104
3105 if (pos.other) {
3106 // Secondary cursor, shown when on a 'jump' in bi-directional text
3107 var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"));
3108 otherCursor.style.display = "";
3109 otherCursor.style.left = pos.other.left + "px";
3110 otherCursor.style.top = pos.other.top + "px";
3111 otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
3112 }
3113 }
3114
3115 function cmpCoords(a, b) { return a.top - b.top || a.left - b.left }
3116
3117 // Draws the given range as a highlighted selection
3118 function drawSelectionRange(cm, range$$1, output) {
3119 var display = cm.display, doc = cm.doc;
3120 var fragment = document.createDocumentFragment();
3121 var padding = paddingH(cm.display), leftSide = padding.left;
3122 var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right;
3123 var docLTR = doc.direction == "ltr";
3124
3125 function add(left, top, width, bottom) {
3126 if (top < 0) { top = 0; }
3127 top = Math.round(top);
3128 bottom = Math.round(bottom);
3129 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")));
3130 }
3131
3132 function drawForLine(line, fromArg, toArg) {
3133 var lineObj = getLine(doc, line);
3134 var lineLen = lineObj.text.length;
3135 var start, end;
3136 function coords(ch, bias) {
3137 return charCoords(cm, Pos(line, ch), "div", lineObj, bias)
3138 }
3139
3140 function wrapX(pos, dir, side) {
3141 var extent = wrappedLineExtentChar(cm, lineObj, null, pos);
3142 var prop = (dir == "ltr") == (side == "after") ? "left" : "right";
3143 var ch = side == "after" ? extent.begin : extent.end - (/\s/.test(lineObj.text.charAt(extent.end - 1)) ? 2 : 1);
3144 return coords(ch, prop)[prop]
3145 }
3146
3147 var order = getOrder(lineObj, doc.direction);
3148 iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir, i) {
3149 var ltr = dir == "ltr";
3150 var fromPos = coords(from, ltr ? "left" : "right");
3151 var toPos = coords(to - 1, ltr ? "right" : "left");
3152
3153 var openStart = fromArg == null && from == 0, openEnd = toArg == null && to == lineLen;
3154 var first = i == 0, last = !order || i == order.length - 1;
3155 if (toPos.top - fromPos.top <= 3) { // Single line
3156 var openLeft = (docLTR ? openStart : openEnd) && first;
3157 var openRight = (docLTR ? openEnd : openStart) && last;
3158 var left = openLeft ? leftSide : (ltr ? fromPos : toPos).left;
3159 var right = openRight ? rightSide : (ltr ? toPos : fromPos).right;
3160 add(left, fromPos.top, right - left, fromPos.bottom);
3161 } else { // Multiple lines
3162 var topLeft, topRight, botLeft, botRight;
3163 if (ltr) {
3164 topLeft = docLTR && openStart && first ? leftSide : fromPos.left;
3165 topRight = docLTR ? rightSide : wrapX(from, dir, "before");
3166 botLeft = docLTR ? leftSide : wrapX(to, dir, "after");
3167 botRight = docLTR && openEnd && last ? rightSide : toPos.right;
3168 } else {
3169 topLeft = !docLTR ? leftSide : wrapX(from, dir, "before");
3170 topRight = !docLTR && openStart && first ? rightSide : fromPos.right;
3171 botLeft = !docLTR && openEnd && last ? leftSide : toPos.left;
3172 botRight = !docLTR ? rightSide : wrapX(to, dir, "after");
3173 }
3174 add(topLeft, fromPos.top, topRight - topLeft, fromPos.bottom);
3175 if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top); }
3176 add(botLeft, toPos.top, botRight - botLeft, toPos.bottom);
3177 }
3178
3179 if (!start || cmpCoords(fromPos, start) < 0) { start = fromPos; }
3180 if (cmpCoords(toPos, start) < 0) { start = toPos; }
3181 if (!end || cmpCoords(fromPos, end) < 0) { end = fromPos; }
3182 if (cmpCoords(toPos, end) < 0) { end = toPos; }
3183 });
3184 return {start: start, end: end}
3185 }
3186
3187 var sFrom = range$$1.from(), sTo = range$$1.to();
3188 if (sFrom.line == sTo.line) {
3189 drawForLine(sFrom.line, sFrom.ch, sTo.ch);
3190 } else {
3191 var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line);
3192 var singleVLine = visualLine(fromLine) == visualLine(toLine);
3193 var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end;
3194 var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start;
3195 if (singleVLine) {
3196 if (leftEnd.top < rightStart.top - 2) {
3197 add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);
3198 add(leftSide, rightStart.top, rightStart.left, rightStart.bottom);
3199 } else {
3200 add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);
3201 }
3202 }
3203 if (leftEnd.bottom < rightStart.top)
3204 { add(leftSide, leftEnd.bottom, null, rightStart.top); }
3205 }
3206
3207 output.appendChild(fragment);
3208 }
3209
3210 // Cursor-blinking
3211 function restartBlink(cm) {
3212 if (!cm.state.focused) { return }
3213 var display = cm.display;
3214 clearInterval(display.blinker);
3215 var on = true;
3216 display.cursorDiv.style.visibility = "";
3217 if (cm.options.cursorBlinkRate > 0)
3218 { display.blinker = setInterval(function () { return display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; },
3219 cm.options.cursorBlinkRate); }
3220 else if (cm.options.cursorBlinkRate < 0)
3221 { display.cursorDiv.style.visibility = "hidden"; }
3222 }
3223
3224 function ensureFocus(cm) {
3225 if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); }
3226 }
3227
3228 function delayBlurEvent(cm) {
3229 cm.state.delayingBlurEvent = true;
3230 setTimeout(function () { if (cm.state.delayingBlurEvent) {
3231 cm.state.delayingBlurEvent = false;
3232 onBlur(cm);
3233 } }, 100);
3234 }
3235
3236 function onFocus(cm, e) {
3237 if (cm.state.delayingBlurEvent) { cm.state.delayingBlurEvent = false; }
3238
3239 if (cm.options.readOnly == "nocursor") { return }
3240 if (!cm.state.focused) {
3241 signal(cm, "focus", cm, e);
3242 cm.state.focused = true;
3243 addClass(cm.display.wrapper, "CodeMirror-focused");
3244 // This test prevents this from firing when a context
3245 // menu is closed (since the input reset would kill the
3246 // select-all detection hack)
3247 if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {
3248 cm.display.input.reset();
3249 if (webkit) { setTimeout(function () { return cm.display.input.reset(true); }, 20); } // Issue #1730
3250 }
3251 cm.display.input.receivedFocus();
3252 }
3253 restartBlink(cm);
3254 }
3255 function onBlur(cm, e) {
3256 if (cm.state.delayingBlurEvent) { return }
3257
3258 if (cm.state.focused) {
3259 signal(cm, "blur", cm, e);
3260 cm.state.focused = false;
3261 rmClass(cm.display.wrapper, "CodeMirror-focused");
3262 }
3263 clearInterval(cm.display.blinker);
3264 setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false; } }, 150);
3265 }
3266
3267 // Read the actual heights of the rendered lines, and update their
3268 // stored heights to match.
3269 function updateHeightsInViewport(cm) {
3270 var display = cm.display;
3271 var prevBottom = display.lineDiv.offsetTop;
3272 for (var i = 0; i < display.view.length; i++) {
3273 var cur = display.view[i], wrapping = cm.options.lineWrapping;
3274 var height = (void 0), width = 0;
3275 if (cur.hidden) { continue }
3276 if (ie && ie_version < 8) {
3277 var bot = cur.node.offsetTop + cur.node.offsetHeight;
3278 height = bot - prevBottom;
3279 prevBottom = bot;
3280 } else {
3281 var box = cur.node.getBoundingClientRect();
3282 height = box.bottom - box.top;
3283 // Check that lines don't extend past the right of the current
3284 // editor width
3285 if (!wrapping && cur.text.firstChild)
3286 { width = cur.text.firstChild.getBoundingClientRect().right - box.left - 1; }
3287 }
3288 var diff = cur.line.height - height;
3289 if (height < 2) { height = textHeight(display); }
3290 if (diff > .005 || diff < -.005) {
3291 updateLineHeight(cur.line, height);
3292 updateWidgetHeight(cur.line);
3293 if (cur.rest) { for (var j = 0; j < cur.rest.length; j++)
3294 { updateWidgetHeight(cur.rest[j]); } }
3295 }
3296 if (width > cm.display.sizerWidth) {
3297 var chWidth = Math.ceil(width / charWidth(cm.display));
3298 if (chWidth > cm.display.maxLineLength) {
3299 cm.display.maxLineLength = chWidth;
3300 cm.display.maxLine = cur.line;
3301 cm.display.maxLineChanged = true;
3302 }
3303 }
3304 }
3305 }
3306
3307 // Read and store the height of line widgets associated with the
3308 // given line.
3309 function updateWidgetHeight(line) {
3310 if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i) {
3311 var w = line.widgets[i], parent = w.node.parentNode;
3312 if (parent) { w.height = parent.offsetHeight; }
3313 } }
3314 }
3315
3316 // Compute the lines that are visible in a given viewport (defaults
3317 // the the current scroll position). viewport may contain top,
3318 // height, and ensure (see op.scrollToPos) properties.
3319 function visibleLines(display, doc, viewport) {
3320 var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop;
3321 top = Math.floor(top - paddingTop(display));
3322 var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight;
3323
3324 var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom);
3325 // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
3326 // forces those lines into the viewport (if possible).
3327 if (viewport && viewport.ensure) {
3328 var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line;
3329 if (ensureFrom < from) {
3330 from = ensureFrom;
3331 to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight);
3332 } else if (Math.min(ensureTo, doc.lastLine()) >= to) {
3333 from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight);
3334 to = ensureTo;
3335 }
3336 }
3337 return {from: from, to: Math.max(to, from + 1)}
3338 }
3339
3340 // Re-align line numbers and gutter marks to compensate for
3341 // horizontal scrolling.
3342 function alignHorizontally(cm) {
3343 var display = cm.display, view = display.view;
3344 if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) { return }
3345 var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;
3346 var gutterW = display.gutters.offsetWidth, left = comp + "px";
3347 for (var i = 0; i < view.length; i++) { if (!view[i].hidden) {
3348 if (cm.options.fixedGutter) {
3349 if (view[i].gutter)
3350 { view[i].gutter.style.left = left; }
3351 if (view[i].gutterBackground)
3352 { view[i].gutterBackground.style.left = left; }
3353 }
3354 var align = view[i].alignable;
3355 if (align) { for (var j = 0; j < align.length; j++)
3356 { align[j].style.left = left; } }
3357 } }
3358 if (cm.options.fixedGutter)
3359 { display.gutters.style.left = (comp + gutterW) + "px"; }
3360 }
3361
3362 // Used to ensure that the line number gutter is still the right
3363 // size for the current document size. Returns true when an update
3364 // is needed.
3365 function maybeUpdateLineNumberWidth(cm) {
3366 if (!cm.options.lineNumbers) { return false }
3367 var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;
3368 if (last.length != display.lineNumChars) {
3369 var test = display.measure.appendChild(elt("div", [elt("div", last)],
3370 "CodeMirror-linenumber CodeMirror-gutter-elt"));
3371 var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
3372 display.lineGutter.style.width = "";
3373 display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1;
3374 display.lineNumWidth = display.lineNumInnerWidth + padding;
3375 display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
3376 display.lineGutter.style.width = display.lineNumWidth + "px";
3377 updateGutterSpace(cm);
3378 return true
3379 }
3380 return false
3381 }
3382
3383 // SCROLLING THINGS INTO VIEW
3384
3385 // If an editor sits on the top or bottom of the window, partially
3386 // scrolled out of view, this ensures that the cursor is visible.
3387 function maybeScrollWindow(cm, rect) {
3388 if (signalDOMEvent(cm, "scrollCursorIntoView")) { return }
3389
3390 var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;
3391 if (rect.top + box.top < 0) { doScroll = true; }
3392 else if (rect.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) { doScroll = false; }
3393 if (doScroll != null && !phantom) {
3394 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;"));
3395 cm.display.lineSpace.appendChild(scrollNode);
3396 scrollNode.scrollIntoView(doScroll);
3397 cm.display.lineSpace.removeChild(scrollNode);
3398 }
3399 }
3400
3401 // Scroll a given position into view (immediately), verifying that
3402 // it actually became visible (as line heights are accurately
3403 // measured, the position of something may 'drift' during drawing).
3404 function scrollPosIntoView(cm, pos, end, margin) {
3405 if (margin == null) { margin = 0; }
3406 var rect;
3407 if (!cm.options.lineWrapping && pos == end) {
3408 // Set pos and end to the cursor positions around the character pos sticks to
3409 // If pos.sticky == "before", that is around pos.ch - 1, otherwise around pos.ch
3410 // If pos == Pos(_, 0, "before"), pos and end are unchanged
3411 pos = pos.ch ? Pos(pos.line, pos.sticky == "before" ? pos.ch - 1 : pos.ch, "after") : pos;
3412 end = pos.sticky == "before" ? Pos(pos.line, pos.ch + 1, "before") : pos;
3413 }
3414 for (var limit = 0; limit < 5; limit++) {
3415 var changed = false;
3416 var coords = cursorCoords(cm, pos);
3417 var endCoords = !end || end == pos ? coords : cursorCoords(cm, end);
3418 rect = {left: Math.min(coords.left, endCoords.left),
3419 top: Math.min(coords.top, endCoords.top) - margin,
3420 right: Math.max(coords.left, endCoords.left),
3421 bottom: Math.max(coords.bottom, endCoords.bottom) + margin};
3422 var scrollPos = calculateScrollPos(cm, rect);
3423 var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
3424 if (scrollPos.scrollTop != null) {
3425 updateScrollTop(cm, scrollPos.scrollTop);
3426 if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true; }
3427 }
3428 if (scrollPos.scrollLeft != null) {
3429 setScrollLeft(cm, scrollPos.scrollLeft);
3430 if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true; }
3431 }
3432 if (!changed) { break }
3433 }
3434 return rect
3435 }
3436
3437 // Scroll a given set of coordinates into view (immediately).
3438 function scrollIntoView(cm, rect) {
3439 var scrollPos = calculateScrollPos(cm, rect);
3440 if (scrollPos.scrollTop != null) { updateScrollTop(cm, scrollPos.scrollTop); }
3441 if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft); }
3442 }
3443
3444 // Calculate a new scroll position needed to scroll the given
3445 // rectangle into view. Returns an object with scrollTop and
3446 // scrollLeft properties. When these are undefined, the
3447 // vertical/horizontal position does not need to be adjusted.
3448 function calculateScrollPos(cm, rect) {
3449 var display = cm.display, snapMargin = textHeight(cm.display);
3450 if (rect.top < 0) { rect.top = 0; }
3451 var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop;
3452 var screen = displayHeight(cm), result = {};
3453 if (rect.bottom - rect.top > screen) { rect.bottom = rect.top + screen; }
3454 var docBottom = cm.doc.height + paddingVert(display);
3455 var atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMargin;
3456 if (rect.top < screentop) {
3457 result.scrollTop = atTop ? 0 : rect.top;
3458 } else if (rect.bottom > screentop + screen) {
3459 var newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen);
3460 if (newTop != screentop) { result.scrollTop = newTop; }
3461 }
3462
3463 var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft;
3464 var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0);
3465 var tooWide = rect.right - rect.left > screenw;
3466 if (tooWide) { rect.right = rect.left + screenw; }
3467 if (rect.left < 10)
3468 { result.scrollLeft = 0; }
3469 else if (rect.left < screenleft)
3470 { result.scrollLeft = Math.max(0, rect.left - (tooWide ? 0 : 10)); }
3471 else if (rect.right > screenw + screenleft - 3)
3472 { result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw; }
3473 return result
3474 }
3475
3476 // Store a relative adjustment to the scroll position in the current
3477 // operation (to be applied when the operation finishes).
3478 function addToScrollTop(cm, top) {
3479 if (top == null) { return }
3480 resolveScrollToPos(cm);
3481 cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top;
3482 }
3483
3484 // Make sure that at the end of the operation the current cursor is
3485 // shown.
3486 function ensureCursorVisible(cm) {
3487 resolveScrollToPos(cm);
3488 var cur = cm.getCursor();
3489 cm.curOp.scrollToPos = {from: cur, to: cur, margin: cm.options.cursorScrollMargin};
3490 }
3491
3492 function scrollToCoords(cm, x, y) {
3493 if (x != null || y != null) { resolveScrollToPos(cm); }
3494 if (x != null) { cm.curOp.scrollLeft = x; }
3495 if (y != null) { cm.curOp.scrollTop = y; }
3496 }
3497
3498 function scrollToRange(cm, range$$1) {
3499 resolveScrollToPos(cm);
3500 cm.curOp.scrollToPos = range$$1;
3501 }
3502
3503 // When an operation has its scrollToPos property set, and another
3504 // scroll action is applied before the end of the operation, this
3505 // 'simulates' scrolling that position into view in a cheap way, so
3506 // that the effect of intermediate scroll commands is not ignored.
3507 function resolveScrollToPos(cm) {
3508 var range$$1 = cm.curOp.scrollToPos;
3509 if (range$$1) {
3510 cm.curOp.scrollToPos = null;
3511 var from = estimateCoords(cm, range$$1.from), to = estimateCoords(cm, range$$1.to);
3512 scrollToCoordsRange(cm, from, to, range$$1.margin);
3513 }
3514 }
3515
3516 function scrollToCoordsRange(cm, from, to, margin) {
3517 var sPos = calculateScrollPos(cm, {
3518 left: Math.min(from.left, to.left),
3519 top: Math.min(from.top, to.top) - margin,
3520 right: Math.max(from.right, to.right),
3521 bottom: Math.max(from.bottom, to.bottom) + margin
3522 });
3523 scrollToCoords(cm, sPos.scrollLeft, sPos.scrollTop);
3524 }
3525
3526 // Sync the scrollable area and scrollbars, ensure the viewport
3527 // covers the visible area.
3528 function updateScrollTop(cm, val) {
3529 if (Math.abs(cm.doc.scrollTop - val) < 2) { return }
3530 if (!gecko) { updateDisplaySimple(cm, {top: val}); }
3531 setScrollTop(cm, val, true);
3532 if (gecko) { updateDisplaySimple(cm); }
3533 startWorker(cm, 100);
3534 }
3535
3536 function setScrollTop(cm, val, forceScroll) {
3537 val = Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val);
3538 if (cm.display.scroller.scrollTop == val && !forceScroll) { return }
3539 cm.doc.scrollTop = val;
3540 cm.display.scrollbars.setScrollTop(val);
3541 if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = val; }
3542 }
3543
3544 // Sync scroller and scrollbar, ensure the gutter elements are
3545 // aligned.
3546 function setScrollLeft(cm, val, isScroller, forceScroll) {
3547 val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth);
3548 if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) { return }
3549 cm.doc.scrollLeft = val;
3550 alignHorizontally(cm);
3551 if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val; }
3552 cm.display.scrollbars.setScrollLeft(val);
3553 }
3554
3555 // SCROLLBARS
3556
3557 // Prepare DOM reads needed to update the scrollbars. Done in one
3558 // shot to minimize update/measure roundtrips.
3559 function measureForScrollbars(cm) {
3560 var d = cm.display, gutterW = d.gutters.offsetWidth;
3561 var docH = Math.round(cm.doc.height + paddingVert(cm.display));
3562 return {
3563 clientHeight: d.scroller.clientHeight,
3564 viewHeight: d.wrapper.clientHeight,
3565 scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,
3566 viewWidth: d.wrapper.clientWidth,
3567 barLeft: cm.options.fixedGutter ? gutterW : 0,
3568 docHeight: docH,
3569 scrollHeight: docH + scrollGap(cm) + d.barHeight,
3570 nativeBarWidth: d.nativeBarWidth,
3571 gutterWidth: gutterW
3572 }
3573 }
3574
3575 var NativeScrollbars = function(place, scroll, cm) {
3576 this.cm = cm;
3577 var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar");
3578 var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar");
3579 vert.tabIndex = horiz.tabIndex = -1;
3580 place(vert); place(horiz);
3581
3582 on(vert, "scroll", function () {
3583 if (vert.clientHeight) { scroll(vert.scrollTop, "vertical"); }
3584 });
3585 on(horiz, "scroll", function () {
3586 if (horiz.clientWidth) { scroll(horiz.scrollLeft, "horizontal"); }
3587 });
3588
3589 this.checkedZeroWidth = false;
3590 // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
3591 if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = "18px"; }
3592 };
3593
3594 NativeScrollbars.prototype.update = function (measure) {
3595 var needsH = measure.scrollWidth > measure.clientWidth + 1;
3596 var needsV = measure.scrollHeight > measure.clientHeight + 1;
3597 var sWidth = measure.nativeBarWidth;
3598
3599 if (needsV) {
3600 this.vert.style.display = "block";
3601 this.vert.style.bottom = needsH ? sWidth + "px" : "0";
3602 var totalHeight = measure.viewHeight - (needsH ? sWidth : 0);
3603 // A bug in IE8 can cause this value to be negative, so guard it.
3604 this.vert.firstChild.style.height =
3605 Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px";
3606 } else {
3607 this.vert.style.display = "";
3608 this.vert.firstChild.style.height = "0";
3609 }
3610
3611 if (needsH) {
3612 this.horiz.style.display = "block";
3613 this.horiz.style.right = needsV ? sWidth + "px" : "0";
3614 this.horiz.style.left = measure.barLeft + "px";
3615 var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0);
3616 this.horiz.firstChild.style.width =
3617 Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + "px";
3618 } else {
3619 this.horiz.style.display = "";
3620 this.horiz.firstChild.style.width = "0";
3621 }
3622
3623 if (!this.checkedZeroWidth && measure.clientHeight > 0) {
3624 if (sWidth == 0) { this.zeroWidthHack(); }
3625 this.checkedZeroWidth = true;
3626 }
3627
3628 return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0}
3629 };
3630
3631 NativeScrollbars.prototype.setScrollLeft = function (pos) {
3632 if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos; }
3633 if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz, "horiz"); }
3634 };
3635
3636 NativeScrollbars.prototype.setScrollTop = function (pos) {
3637 if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos; }
3638 if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert, "vert"); }
3639 };
3640
3641 NativeScrollbars.prototype.zeroWidthHack = function () {
3642 var w = mac && !mac_geMountainLion ? "12px" : "18px";
3643 this.horiz.style.height = this.vert.style.width = w;
3644 this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none";
3645 this.disableHoriz = new Delayed;
3646 this.disableVert = new Delayed;
3647 };
3648
3649 NativeScrollbars.prototype.enableZeroWidthBar = function (bar, delay, type) {
3650 bar.style.pointerEvents = "auto";
3651 function maybeDisable() {
3652 // To find out whether the scrollbar is still visible, we
3653 // check whether the element under the pixel in the bottom
3654 // right corner of the scrollbar box is the scrollbar box
3655 // itself (when the bar is still visible) or its filler child
3656 // (when the bar is hidden). If it is still visible, we keep
3657 // it enabled, if it's hidden, we disable pointer events.
3658 var box = bar.getBoundingClientRect();
3659 var elt$$1 = type == "vert" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2)
3660 : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1);
3661 if (elt$$1 != bar) { bar.style.pointerEvents = "none"; }
3662 else { delay.set(1000, maybeDisable); }
3663 }
3664 delay.set(1000, maybeDisable);
3665 };
3666
3667 NativeScrollbars.prototype.clear = function () {
3668 var parent = this.horiz.parentNode;
3669 parent.removeChild(this.horiz);
3670 parent.removeChild(this.vert);
3671 };
3672
3673 var NullScrollbars = function () {};
3674
3675 NullScrollbars.prototype.update = function () { return {bottom: 0, right: 0} };
3676 NullScrollbars.prototype.setScrollLeft = function () {};
3677 NullScrollbars.prototype.setScrollTop = function () {};
3678 NullScrollbars.prototype.clear = function () {};
3679
3680 function updateScrollbars(cm, measure) {
3681 if (!measure) { measure = measureForScrollbars(cm); }
3682 var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight;
3683 updateScrollbarsInner(cm, measure);
3684 for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) {
3685 if (startWidth != cm.display.barWidth && cm.options.lineWrapping)
3686 { updateHeightsInViewport(cm); }
3687 updateScrollbarsInner(cm, measureForScrollbars(cm));
3688 startWidth = cm.display.barWidth; startHeight = cm.display.barHeight;
3689 }
3690 }
3691
3692 // Re-synchronize the fake scrollbars with the actual size of the
3693 // content.
3694 function updateScrollbarsInner(cm, measure) {
3695 var d = cm.display;
3696 var sizes = d.scrollbars.update(measure);
3697
3698 d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px";
3699 d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px";
3700 d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent";
3701
3702 if (sizes.right && sizes.bottom) {
3703 d.scrollbarFiller.style.display = "block";
3704 d.scrollbarFiller.style.height = sizes.bottom + "px";
3705 d.scrollbarFiller.style.width = sizes.right + "px";
3706 } else { d.scrollbarFiller.style.display = ""; }
3707 if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
3708 d.gutterFiller.style.display = "block";
3709 d.gutterFiller.style.height = sizes.bottom + "px";
3710 d.gutterFiller.style.width = measure.gutterWidth + "px";
3711 } else { d.gutterFiller.style.display = ""; }
3712 }
3713
3714 var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars};
3715
3716 function initScrollbars(cm) {
3717 if (cm.display.scrollbars) {
3718 cm.display.scrollbars.clear();
3719 if (cm.display.scrollbars.addClass)
3720 { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass); }
3721 }
3722
3723 cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) {
3724 cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller);
3725 // Prevent clicks in the scrollbars from killing focus
3726 on(node, "mousedown", function () {
3727 if (cm.state.focused) { setTimeout(function () { return cm.display.input.focus(); }, 0); }
3728 });
3729 node.setAttribute("cm-not-content", "true");
3730 }, function (pos, axis) {
3731 if (axis == "horizontal") { setScrollLeft(cm, pos); }
3732 else { updateScrollTop(cm, pos); }
3733 }, cm);
3734 if (cm.display.scrollbars.addClass)
3735 { addClass(cm.display.wrapper, cm.display.scrollbars.addClass); }
3736 }
3737
3738 // Operations are used to wrap a series of changes to the editor
3739 // state in such a way that each change won't have to update the
3740 // cursor and display (which would be awkward, slow, and
3741 // error-prone). Instead, display updates are batched and then all
3742 // combined and executed at once.
3743
3744 var nextOpId = 0;
3745 // Start a new operation.
3746 function startOperation(cm) {
3747 cm.curOp = {
3748 cm: cm,
3749 viewChanged: false, // Flag that indicates that lines might need to be redrawn
3750 startHeight: cm.doc.height, // Used to detect need to update scrollbar
3751 forceUpdate: false, // Used to force a redraw
3752 updateInput: 0, // Whether to reset the input textarea
3753 typing: false, // Whether this reset should be careful to leave existing text (for compositing)
3754 changeObjs: null, // Accumulated changes, for firing change events
3755 cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on
3756 cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
3757 selectionChanged: false, // Whether the selection needs to be redrawn
3758 updateMaxLine: false, // Set when the widest line needs to be determined anew
3759 scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
3760 scrollToPos: null, // Used to scroll to a specific position
3761 focus: false,
3762 id: ++nextOpId // Unique ID
3763 };
3764 pushOperation(cm.curOp);
3765 }
3766
3767 // Finish an operation, updating the display and signalling delayed events
3768 function endOperation(cm) {
3769 var op = cm.curOp;
3770 if (op) { finishOperation(op, function (group) {
3771 for (var i = 0; i < group.ops.length; i++)
3772 { group.ops[i].cm.curOp = null; }
3773 endOperations(group);
3774 }); }
3775 }
3776
3777 // The DOM updates done when an operation finishes are batched so
3778 // that the minimum number of relayouts are required.
3779 function endOperations(group) {
3780 var ops = group.ops;
3781 for (var i = 0; i < ops.length; i++) // Read DOM
3782 { endOperation_R1(ops[i]); }
3783 for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe)
3784 { endOperation_W1(ops[i$1]); }
3785 for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM
3786 { endOperation_R2(ops[i$2]); }
3787 for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe)
3788 { endOperation_W2(ops[i$3]); }
3789 for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM
3790 { endOperation_finish(ops[i$4]); }
3791 }
3792
3793 function endOperation_R1(op) {
3794 var cm = op.cm, display = cm.display;
3795 maybeClipScrollbars(cm);
3796 if (op.updateMaxLine) { findMaxLine(cm); }
3797
3798 op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||
3799 op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
3800 op.scrollToPos.to.line >= display.viewTo) ||
3801 display.maxLineChanged && cm.options.lineWrapping;
3802 op.update = op.mustUpdate &&
3803 new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate);
3804 }
3805
3806 function endOperation_W1(op) {
3807 op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update);
3808 }
3809
3810 function endOperation_R2(op) {
3811 var cm = op.cm, display = cm.display;
3812 if (op.updatedDisplay) { updateHeightsInViewport(cm); }
3813
3814 op.barMeasure = measureForScrollbars(cm);
3815
3816 // If the max line changed since it was last measured, measure it,
3817 // and ensure the document's width matches it.
3818 // updateDisplay_W2 will use these properties to do the actual resizing
3819 if (display.maxLineChanged && !cm.options.lineWrapping) {
3820 op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3;
3821 cm.display.sizerWidth = op.adjustWidthTo;
3822 op.barMeasure.scrollWidth =
3823 Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth);
3824 op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm));
3825 }
3826
3827 if (op.updatedDisplay || op.selectionChanged)
3828 { op.preparedSelection = display.input.prepareSelection(); }
3829 }
3830
3831 function endOperation_W2(op) {
3832 var cm = op.cm;
3833
3834 if (op.adjustWidthTo != null) {
3835 cm.display.sizer.style.minWidth = op.adjustWidthTo + "px";
3836 if (op.maxScrollLeft < cm.doc.scrollLeft)
3837 { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true); }
3838 cm.display.maxLineChanged = false;
3839 }
3840
3841 var takeFocus = op.focus && op.focus == activeElt();
3842 if (op.preparedSelection)
3843 { cm.display.input.showSelection(op.preparedSelection, takeFocus); }
3844 if (op.updatedDisplay || op.startHeight != cm.doc.height)
3845 { updateScrollbars(cm, op.barMeasure); }
3846 if (op.updatedDisplay)
3847 { setDocumentHeight(cm, op.barMeasure); }
3848
3849 if (op.selectionChanged) { restartBlink(cm); }
3850
3851 if (cm.state.focused && op.updateInput)
3852 { cm.display.input.reset(op.typing); }
3853 if (takeFocus) { ensureFocus(op.cm); }
3854 }
3855
3856 function endOperation_finish(op) {
3857 var cm = op.cm, display = cm.display, doc = cm.doc;
3858
3859 if (op.updatedDisplay) { postUpdateDisplay(cm, op.update); }
3860
3861 // Abort mouse wheel delta measurement, when scrolling explicitly
3862 if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))
3863 { display.wheelStartX = display.wheelStartY = null; }
3864
3865 // Propagate the scroll position to the actual DOM scroller
3866 if (op.scrollTop != null) { setScrollTop(cm, op.scrollTop, op.forceScroll); }
3867
3868 if (op.scrollLeft != null) { setScrollLeft(cm, op.scrollLeft, true, true); }
3869 // If we need to scroll a specific position into view, do so.
3870 if (op.scrollToPos) {
3871 var rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
3872 clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin);
3873 maybeScrollWindow(cm, rect);
3874 }
3875
3876 // Fire events for markers that are hidden/unidden by editing or
3877 // undoing
3878 var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
3879 if (hidden) { for (var i = 0; i < hidden.length; ++i)
3880 { if (!hidden[i].lines.length) { signal(hidden[i], "hide"); } } }
3881 if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1)
3882 { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], "unhide"); } } }
3883
3884 if (display.wrapper.offsetHeight)
3885 { doc.scrollTop = cm.display.scroller.scrollTop; }
3886
3887 // Fire change events, and delayed event handlers
3888 if (op.changeObjs)
3889 { signal(cm, "changes", cm, op.changeObjs); }
3890 if (op.update)
3891 { op.update.finish(); }
3892 }
3893
3894 // Run the given function in an operation
3895 function runInOp(cm, f) {
3896 if (cm.curOp) { return f() }
3897 startOperation(cm);
3898 try { return f() }
3899 finally { endOperation(cm); }
3900 }
3901 // Wraps a function in an operation. Returns the wrapped function.
3902 function operation(cm, f) {
3903 return function() {
3904 if (cm.curOp) { return f.apply(cm, arguments) }
3905 startOperation(cm);
3906 try { return f.apply(cm, arguments) }
3907 finally { endOperation(cm); }
3908 }
3909 }
3910 // Used to add methods to editor and doc instances, wrapping them in
3911 // operations.
3912 function methodOp(f) {
3913 return function() {
3914 if (this.curOp) { return f.apply(this, arguments) }
3915 startOperation(this);
3916 try { return f.apply(this, arguments) }
3917 finally { endOperation(this); }
3918 }
3919 }
3920 function docMethodOp(f) {
3921 return function() {
3922 var cm = this.cm;
3923 if (!cm || cm.curOp) { return f.apply(this, arguments) }
3924 startOperation(cm);
3925 try { return f.apply(this, arguments) }
3926 finally { endOperation(cm); }
3927 }
3928 }
3929
3930 // Updates the display.view data structure for a given change to the
3931 // document. From and to are in pre-change coordinates. Lendiff is
3932 // the amount of lines added or subtracted by the change. This is
3933 // used for changes that span multiple lines, or change the way
3934 // lines are divided into visual lines. regLineChange (below)
3935 // registers single-line changes.
3936 function regChange(cm, from, to, lendiff) {
3937 if (from == null) { from = cm.doc.first; }
3938 if (to == null) { to = cm.doc.first + cm.doc.size; }
3939 if (!lendiff) { lendiff = 0; }
3940
3941 var display = cm.display;
3942 if (lendiff && to < display.viewTo &&
3943 (display.updateLineNumbers == null || display.updateLineNumbers > from))
3944 { display.updateLineNumbers = from; }
3945
3946 cm.curOp.viewChanged = true;
3947
3948 if (from >= display.viewTo) { // Change after
3949 if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)
3950 { resetView(cm); }
3951 } else if (to <= display.viewFrom) { // Change before
3952 if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {
3953 resetView(cm);
3954 } else {
3955 display.viewFrom += lendiff;
3956 display.viewTo += lendiff;
3957 }
3958 } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap
3959 resetView(cm);
3960 } else if (from <= display.viewFrom) { // Top overlap
3961 var cut = viewCuttingPoint(cm, to, to + lendiff, 1);
3962 if (cut) {
3963 display.view = display.view.slice(cut.index);
3964 display.viewFrom = cut.lineN;
3965 display.viewTo += lendiff;
3966 } else {
3967 resetView(cm);
3968 }
3969 } else if (to >= display.viewTo) { // Bottom overlap
3970 var cut$1 = viewCuttingPoint(cm, from, from, -1);
3971 if (cut$1) {
3972 display.view = display.view.slice(0, cut$1.index);
3973 display.viewTo = cut$1.lineN;
3974 } else {
3975 resetView(cm);
3976 }
3977 } else { // Gap in the middle
3978 var cutTop = viewCuttingPoint(cm, from, from, -1);
3979 var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1);
3980 if (cutTop && cutBot) {
3981 display.view = display.view.slice(0, cutTop.index)
3982 .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))
3983 .concat(display.view.slice(cutBot.index));
3984 display.viewTo += lendiff;
3985 } else {
3986 resetView(cm);
3987 }
3988 }
3989
3990 var ext = display.externalMeasured;
3991 if (ext) {
3992 if (to < ext.lineN)
3993 { ext.lineN += lendiff; }
3994 else if (from < ext.lineN + ext.size)
3995 { display.externalMeasured = null; }
3996 }
3997 }
3998
3999 // Register a change to a single line. Type must be one of "text",
4000 // "gutter", "class", "widget"
4001 function regLineChange(cm, line, type) {
4002 cm.curOp.viewChanged = true;
4003 var display = cm.display, ext = cm.display.externalMeasured;
4004 if (ext && line >= ext.lineN && line < ext.lineN + ext.size)
4005 { display.externalMeasured = null; }
4006
4007 if (line < display.viewFrom || line >= display.viewTo) { return }
4008 var lineView = display.view[findViewIndex(cm, line)];
4009 if (lineView.node == null) { return }
4010 var arr = lineView.changes || (lineView.changes = []);
4011 if (indexOf(arr, type) == -1) { arr.push(type); }
4012 }
4013
4014 // Clear the view.
4015 function resetView(cm) {
4016 cm.display.viewFrom = cm.display.viewTo = cm.doc.first;
4017 cm.display.view = [];
4018 cm.display.viewOffset = 0;
4019 }
4020
4021 function viewCuttingPoint(cm, oldN, newN, dir) {
4022 var index = findViewIndex(cm, oldN), diff, view = cm.display.view;
4023 if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)
4024 { return {index: index, lineN: newN} }
4025 var n = cm.display.viewFrom;
4026 for (var i = 0; i < index; i++)
4027 { n += view[i].size; }
4028 if (n != oldN) {
4029 if (dir > 0) {
4030 if (index == view.length - 1) { return null }
4031 diff = (n + view[index].size) - oldN;
4032 index++;
4033 } else {
4034 diff = n - oldN;
4035 }
4036 oldN += diff; newN += diff;
4037 }
4038 while (visualLineNo(cm.doc, newN) != newN) {
4039 if (index == (dir < 0 ? 0 : view.length - 1)) { return null }
4040 newN += dir * view[index - (dir < 0 ? 1 : 0)].size;
4041 index += dir;
4042 }
4043 return {index: index, lineN: newN}
4044 }
4045
4046 // Force the view to cover a given range, adding empty view element
4047 // or clipping off existing ones as needed.
4048 function adjustView(cm, from, to) {
4049 var display = cm.display, view = display.view;
4050 if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {
4051 display.view = buildViewArray(cm, from, to);
4052 display.viewFrom = from;
4053 } else {
4054 if (display.viewFrom > from)
4055 { display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view); }
4056 else if (display.viewFrom < from)
4057 { display.view = display.view.slice(findViewIndex(cm, from)); }
4058 display.viewFrom = from;
4059 if (display.viewTo < to)
4060 { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)); }
4061 else if (display.viewTo > to)
4062 { display.view = display.view.slice(0, findViewIndex(cm, to)); }
4063 }
4064 display.viewTo = to;
4065 }
4066
4067 // Count the number of lines in the view whose DOM representation is
4068 // out of date (or nonexistent).
4069 function countDirtyView(cm) {
4070 var view = cm.display.view, dirty = 0;
4071 for (var i = 0; i < view.length; i++) {
4072 var lineView = view[i];
4073 if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty; }
4074 }
4075 return dirty
4076 }
4077
4078 // HIGHLIGHT WORKER
4079
4080 function startWorker(cm, time) {
4081 if (cm.doc.highlightFrontier < cm.display.viewTo)
4082 { cm.state.highlight.set(time, bind(highlightWorker, cm)); }
4083 }
4084
4085 function highlightWorker(cm) {
4086 var doc = cm.doc;
4087 if (doc.highlightFrontier >= cm.display.viewTo) { return }
4088 var end = +new Date + cm.options.workTime;
4089 var context = getContextBefore(cm, doc.highlightFrontier);
4090 var changedLines = [];
4091
4092 doc.iter(context.line, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function (line) {
4093 if (context.line >= cm.display.viewFrom) { // Visible
4094 var oldStyles = line.styles;
4095 var resetState = line.text.length > cm.options.maxHighlightLength ? copyState(doc.mode, context.state) : null;
4096 var highlighted = highlightLine(cm, line, context, true);
4097 if (resetState) { context.state = resetState; }
4098 line.styles = highlighted.styles;
4099 var oldCls = line.styleClasses, newCls = highlighted.classes;
4100 if (newCls) { line.styleClasses = newCls; }
4101 else if (oldCls) { line.styleClasses = null; }
4102 var ischange = !oldStyles || oldStyles.length != line.styles.length ||
4103 oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass);
4104 for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldStyles[i] != line.styles[i]; }
4105 if (ischange) { changedLines.push(context.line); }
4106 line.stateAfter = context.save();
4107 context.nextLine();
4108 } else {
4109 if (line.text.length <= cm.options.maxHighlightLength)
4110 { processLine(cm, line.text, context); }
4111 line.stateAfter = context.line % 5 == 0 ? context.save() : null;
4112 context.nextLine();
4113 }
4114 if (+new Date > end) {
4115 startWorker(cm, cm.options.workDelay);
4116 return true
4117 }
4118 });
4119 doc.highlightFrontier = context.line;
4120 doc.modeFrontier = Math.max(doc.modeFrontier, context.line);
4121 if (changedLines.length) { runInOp(cm, function () {
4122 for (var i = 0; i < changedLines.length; i++)
4123 { regLineChange(cm, changedLines[i], "text"); }
4124 }); }
4125 }
4126
4127 // DISPLAY DRAWING
4128
4129 var DisplayUpdate = function(cm, viewport, force) {
4130 var display = cm.display;
4131
4132 this.viewport = viewport;
4133 // Store some values that we'll need later (but don't want to force a relayout for)
4134 this.visible = visibleLines(display, cm.doc, viewport);
4135 this.editorIsHidden = !display.wrapper.offsetWidth;
4136 this.wrapperHeight = display.wrapper.clientHeight;
4137 this.wrapperWidth = display.wrapper.clientWidth;
4138 this.oldDisplayWidth = displayWidth(cm);
4139 this.force = force;
4140 this.dims = getDimensions(cm);
4141 this.events = [];
4142 };
4143
4144 DisplayUpdate.prototype.signal = function (emitter, type) {
4145 if (hasHandler(emitter, type))
4146 { this.events.push(arguments); }
4147 };
4148 DisplayUpdate.prototype.finish = function () {
4149 var this$1 = this;
4150
4151 for (var i = 0; i < this.events.length; i++)
4152 { signal.apply(null, this$1.events[i]); }
4153 };
4154
4155 function maybeClipScrollbars(cm) {
4156 var display = cm.display;
4157 if (!display.scrollbarsClipped && display.scroller.offsetWidth) {
4158 display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth;
4159 display.heightForcer.style.height = scrollGap(cm) + "px";
4160 display.sizer.style.marginBottom = -display.nativeBarWidth + "px";
4161 display.sizer.style.borderRightWidth = scrollGap(cm) + "px";
4162 display.scrollbarsClipped = true;
4163 }
4164 }
4165
4166 function selectionSnapshot(cm) {
4167 if (cm.hasFocus()) { return null }
4168 var active = activeElt();
4169 if (!active || !contains(cm.display.lineDiv, active)) { return null }
4170 var result = {activeElt: active};
4171 if (window.getSelection) {
4172 var sel = window.getSelection();
4173 if (sel.anchorNode && sel.extend && contains(cm.display.lineDiv, sel.anchorNode)) {
4174 result.anchorNode = sel.anchorNode;
4175 result.anchorOffset = sel.anchorOffset;
4176 result.focusNode = sel.focusNode;
4177 result.focusOffset = sel.focusOffset;
4178 }
4179 }
4180 return result
4181 }
4182
4183 function restoreSelection(snapshot) {
4184 if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt()) { return }
4185 snapshot.activeElt.focus();
4186 if (snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) {
4187 var sel = window.getSelection(), range$$1 = document.createRange();
4188 range$$1.setEnd(snapshot.anchorNode, snapshot.anchorOffset);
4189 range$$1.collapse(false);
4190 sel.removeAllRanges();
4191 sel.addRange(range$$1);
4192 sel.extend(snapshot.focusNode, snapshot.focusOffset);
4193 }
4194 }
4195
4196 // Does the actual updating of the line display. Bails out
4197 // (returning false) when there is nothing to be done and forced is
4198 // false.
4199 function updateDisplayIfNeeded(cm, update) {
4200 var display = cm.display, doc = cm.doc;
4201
4202 if (update.editorIsHidden) {
4203 resetView(cm);
4204 return false
4205 }
4206
4207 // Bail out if the visible area is already rendered and nothing changed.
4208 if (!update.force &&
4209 update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&
4210 (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&
4211 display.renderedView == display.view && countDirtyView(cm) == 0)
4212 { return false }
4213
4214 if (maybeUpdateLineNumberWidth(cm)) {
4215 resetView(cm);
4216 update.dims = getDimensions(cm);
4217 }
4218
4219 // Compute a suitable new viewport (from & to)
4220 var end = doc.first + doc.size;
4221 var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first);
4222 var to = Math.min(end, update.visible.to + cm.options.viewportMargin);
4223 if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max(doc.first, display.viewFrom); }
4224 if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, display.viewTo); }
4225 if (sawCollapsedSpans) {
4226 from = visualLineNo(cm.doc, from);
4227 to = visualLineEndNo(cm.doc, to);
4228 }
4229
4230 var different = from != display.viewFrom || to != display.viewTo ||
4231 display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth;
4232 adjustView(cm, from, to);
4233
4234 display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom));
4235 // Position the mover div to align with the current scroll position
4236 cm.display.mover.style.top = display.viewOffset + "px";
4237
4238 var toUpdate = countDirtyView(cm);
4239 if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view &&
4240 (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))
4241 { return false }
4242
4243 // For big changes, we hide the enclosing element during the
4244 // update, since that speeds up the operations on most browsers.
4245 var selSnapshot = selectionSnapshot(cm);
4246 if (toUpdate > 4) { display.lineDiv.style.display = "none"; }
4247 patchDisplay(cm, display.updateLineNumbers, update.dims);
4248 if (toUpdate > 4) { display.lineDiv.style.display = ""; }
4249 display.renderedView = display.view;
4250 // There might have been a widget with a focused element that got
4251 // hidden or updated, if so re-focus it.
4252 restoreSelection(selSnapshot);
4253
4254 // Prevent selection and cursors from interfering with the scroll
4255 // width and height.
4256 removeChildren(display.cursorDiv);
4257 removeChildren(display.selectionDiv);
4258 display.gutters.style.height = display.sizer.style.minHeight = 0;
4259
4260 if (different) {
4261 display.lastWrapHeight = update.wrapperHeight;
4262 display.lastWrapWidth = update.wrapperWidth;
4263 startWorker(cm, 400);
4264 }
4265
4266 display.updateLineNumbers = null;
4267
4268 return true
4269 }
4270
4271 function postUpdateDisplay(cm, update) {
4272 var viewport = update.viewport;
4273
4274 for (var first = true;; first = false) {
4275 if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) {
4276 // Clip forced viewport to actual scrollable area.
4277 if (viewport && viewport.top != null)
4278 { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)}; }
4279 // Updated line heights might result in the drawn area not
4280 // actually covering the viewport. Keep looping until it does.
4281 update.visible = visibleLines(cm.display, cm.doc, viewport);
4282 if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)
4283 { break }
4284 }
4285 if (!updateDisplayIfNeeded(cm, update)) { break }
4286 updateHeightsInViewport(cm);
4287 var barMeasure = measureForScrollbars(cm);
4288 updateSelection(cm);
4289 updateScrollbars(cm, barMeasure);
4290 setDocumentHeight(cm, barMeasure);
4291 update.force = false;
4292 }
4293
4294 update.signal(cm, "update", cm);
4295 if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {
4296 update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo);
4297 cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo;
4298 }
4299 }
4300
4301 function updateDisplaySimple(cm, viewport) {
4302 var update = new DisplayUpdate(cm, viewport);
4303 if (updateDisplayIfNeeded(cm, update)) {
4304 updateHeightsInViewport(cm);
4305 postUpdateDisplay(cm, update);
4306 var barMeasure = measureForScrollbars(cm);
4307 updateSelection(cm);
4308 updateScrollbars(cm, barMeasure);
4309 setDocumentHeight(cm, barMeasure);
4310 update.finish();
4311 }
4312 }
4313
4314 // Sync the actual display DOM structure with display.view, removing
4315 // nodes for lines that are no longer in view, and creating the ones
4316 // that are not there yet, and updating the ones that are out of
4317 // date.
4318 function patchDisplay(cm, updateNumbersFrom, dims) {
4319 var display = cm.display, lineNumbers = cm.options.lineNumbers;
4320 var container = display.lineDiv, cur = container.firstChild;
4321
4322 function rm(node) {
4323 var next = node.nextSibling;
4324 // Works around a throw-scroll bug in OS X Webkit
4325 if (webkit && mac && cm.display.currentWheelTarget == node)
4326 { node.style.display = "none"; }
4327 else
4328 { node.parentNode.removeChild(node); }
4329 return next
4330 }
4331
4332 var view = display.view, lineN = display.viewFrom;
4333 // Loop over the elements in the view, syncing cur (the DOM nodes
4334 // in display.lineDiv) with the view as we go.
4335 for (var i = 0; i < view.length; i++) {
4336 var lineView = view[i];
4337 if (lineView.hidden) ; else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet
4338 var node = buildLineElement(cm, lineView, lineN, dims);
4339 container.insertBefore(node, cur);
4340 } else { // Already drawn
4341 while (cur != lineView.node) { cur = rm(cur); }
4342 var updateNumber = lineNumbers && updateNumbersFrom != null &&
4343 updateNumbersFrom <= lineN && lineView.lineNumber;
4344 if (lineView.changes) {
4345 if (indexOf(lineView.changes, "gutter") > -1) { updateNumber = false; }
4346 updateLineForChanges(cm, lineView, lineN, dims);
4347 }
4348 if (updateNumber) {
4349 removeChildren(lineView.lineNumber);
4350 lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN)));
4351 }
4352 cur = lineView.node.nextSibling;
4353 }
4354 lineN += lineView.size;
4355 }
4356 while (cur) { cur = rm(cur); }
4357 }
4358
4359 function updateGutterSpace(cm) {
4360 var width = cm.display.gutters.offsetWidth;
4361 cm.display.sizer.style.marginLeft = width + "px";
4362 }
4363
4364 function setDocumentHeight(cm, measure) {
4365 cm.display.sizer.style.minHeight = measure.docHeight + "px";
4366 cm.display.heightForcer.style.top = measure.docHeight + "px";
4367 cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px";
4368 }
4369
4370 // Rebuild the gutter elements, ensure the margin to the left of the
4371 // code matches their width.
4372 function updateGutters(cm) {
4373 var gutters = cm.display.gutters, specs = cm.options.gutters;
4374 removeChildren(gutters);
4375 var i = 0;
4376 for (; i < specs.length; ++i) {
4377 var gutterClass = specs[i];
4378 var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass));
4379 if (gutterClass == "CodeMirror-linenumbers") {
4380 cm.display.lineGutter = gElt;
4381 gElt.style.width = (cm.display.lineNumWidth || 1) + "px";
4382 }
4383 }
4384 gutters.style.display = i ? "" : "none";
4385 updateGutterSpace(cm);
4386 }
4387
4388 // Make sure the gutters options contains the element
4389 // "CodeMirror-linenumbers" when the lineNumbers option is true.
4390 function setGuttersForLineNumbers(options) {
4391 var found = indexOf(options.gutters, "CodeMirror-linenumbers");
4392 if (found == -1 && options.lineNumbers) {
4393 options.gutters = options.gutters.concat(["CodeMirror-linenumbers"]);
4394 } else if (found > -1 && !options.lineNumbers) {
4395 options.gutters = options.gutters.slice(0);
4396 options.gutters.splice(found, 1);
4397 }
4398 }
4399
4400 // Since the delta values reported on mouse wheel events are
4401 // unstandardized between browsers and even browser versions, and
4402 // generally horribly unpredictable, this code starts by measuring
4403 // the scroll effect that the first few mouse wheel events have,
4404 // and, from that, detects the way it can convert deltas to pixel
4405 // offsets afterwards.
4406 //
4407 // The reason we want to know the amount a wheel event will scroll
4408 // is that it gives us a chance to update the display before the
4409 // actual scrolling happens, reducing flickering.
4410
4411 var wheelSamples = 0, wheelPixelsPerUnit = null;
4412 // Fill in a browser-detected starting value on browsers where we
4413 // know one. These don't have to be accurate -- the result of them
4414 // being wrong would just be a slight flicker on the first wheel
4415 // scroll (if it is large enough).
4416 if (ie) { wheelPixelsPerUnit = -.53; }
4417 else if (gecko) { wheelPixelsPerUnit = 15; }
4418 else if (chrome) { wheelPixelsPerUnit = -.7; }
4419 else if (safari) { wheelPixelsPerUnit = -1/3; }
4420
4421 function wheelEventDelta(e) {
4422 var dx = e.wheelDeltaX, dy = e.wheelDeltaY;
4423 if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail; }
4424 if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail; }
4425 else if (dy == null) { dy = e.wheelDelta; }
4426 return {x: dx, y: dy}
4427 }
4428 function wheelEventPixels(e) {
4429 var delta = wheelEventDelta(e);
4430 delta.x *= wheelPixelsPerUnit;
4431 delta.y *= wheelPixelsPerUnit;
4432 return delta
4433 }
4434
4435 function onScrollWheel(cm, e) {
4436 var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y;
4437
4438 var display = cm.display, scroll = display.scroller;
4439 // Quit if there's nothing to scroll here
4440 var canScrollX = scroll.scrollWidth > scroll.clientWidth;
4441 var canScrollY = scroll.scrollHeight > scroll.clientHeight;
4442 if (!(dx && canScrollX || dy && canScrollY)) { return }
4443
4444 // Webkit browsers on OS X abort momentum scrolls when the target
4445 // of the scroll event is removed from the scrollable element.
4446 // This hack (see related code in patchDisplay) makes sure the
4447 // element is kept around.
4448 if (dy && mac && webkit) {
4449 outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {
4450 for (var i = 0; i < view.length; i++) {
4451 if (view[i].node == cur) {
4452 cm.display.currentWheelTarget = cur;
4453 break outer
4454 }
4455 }
4456 }
4457 }
4458
4459 // On some browsers, horizontal scrolling will cause redraws to
4460 // happen before the gutter has been realigned, causing it to
4461 // wriggle around in a most unseemly way. When we have an
4462 // estimated pixels/delta value, we just handle horizontal
4463 // scrolling entirely here. It'll be slightly off from native, but
4464 // better than glitching out.
4465 if (dx && !gecko && !presto && wheelPixelsPerUnit != null) {
4466 if (dy && canScrollY)
4467 { updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * wheelPixelsPerUnit)); }
4468 setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * wheelPixelsPerUnit));
4469 // Only prevent default scrolling if vertical scrolling is
4470 // actually possible. Otherwise, it causes vertical scroll
4471 // jitter on OSX trackpads when deltaX is small and deltaY
4472 // is large (issue #3579)
4473 if (!dy || (dy && canScrollY))
4474 { e_preventDefault(e); }
4475 display.wheelStartX = null; // Abort measurement, if in progress
4476 return
4477 }
4478
4479 // 'Project' the visible viewport to cover the area that is being
4480 // scrolled into view (if we know enough to estimate it).
4481 if (dy && wheelPixelsPerUnit != null) {
4482 var pixels = dy * wheelPixelsPerUnit;
4483 var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
4484 if (pixels < 0) { top = Math.max(0, top + pixels - 50); }
4485 else { bot = Math.min(cm.doc.height, bot + pixels + 50); }
4486 updateDisplaySimple(cm, {top: top, bottom: bot});
4487 }
4488
4489 if (wheelSamples < 20) {
4490 if (display.wheelStartX == null) {
4491 display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;
4492 display.wheelDX = dx; display.wheelDY = dy;
4493 setTimeout(function () {
4494 if (display.wheelStartX == null) { return }
4495 var movedX = scroll.scrollLeft - display.wheelStartX;
4496 var movedY = scroll.scrollTop - display.wheelStartY;
4497 var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
4498 (movedX && display.wheelDX && movedX / display.wheelDX);
4499 display.wheelStartX = display.wheelStartY = null;
4500 if (!sample) { return }
4501 wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
4502 ++wheelSamples;
4503 }, 200);
4504 } else {
4505 display.wheelDX += dx; display.wheelDY += dy;
4506 }
4507 }
4508 }
4509
4510 // Selection objects are immutable. A new one is created every time
4511 // the selection changes. A selection is one or more non-overlapping
4512 // (and non-touching) ranges, sorted, and an integer that indicates
4513 // which one is the primary selection (the one that's scrolled into
4514 // view, that getCursor returns, etc).
4515 var Selection = function(ranges, primIndex) {
4516 this.ranges = ranges;
4517 this.primIndex = primIndex;
4518 };
4519
4520 Selection.prototype.primary = function () { return this.ranges[this.primIndex] };
4521
4522 Selection.prototype.equals = function (other) {
4523 var this$1 = this;
4524
4525 if (other == this) { return true }
4526 if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false }
4527 for (var i = 0; i < this.ranges.length; i++) {
4528 var here = this$1.ranges[i], there = other.ranges[i];
4529 if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) { return false }
4530 }
4531 return true
4532 };
4533
4534 Selection.prototype.deepCopy = function () {
4535 var this$1 = this;
4536
4537 var out = [];
4538 for (var i = 0; i < this.ranges.length; i++)
4539 { out[i] = new Range(copyPos(this$1.ranges[i].anchor), copyPos(this$1.ranges[i].head)); }
4540 return new Selection(out, this.primIndex)
4541 };
4542
4543 Selection.prototype.somethingSelected = function () {
4544 var this$1 = this;
4545
4546 for (var i = 0; i < this.ranges.length; i++)
4547 { if (!this$1.ranges[i].empty()) { return true } }
4548 return false
4549 };
4550
4551 Selection.prototype.contains = function (pos, end) {
4552 var this$1 = this;
4553
4554 if (!end) { end = pos; }
4555 for (var i = 0; i < this.ranges.length; i++) {
4556 var range = this$1.ranges[i];
4557 if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)
4558 { return i }
4559 }
4560 return -1
4561 };
4562
4563 var Range = function(anchor, head) {
4564 this.anchor = anchor; this.head = head;
4565 };
4566
4567 Range.prototype.from = function () { return minPos(this.anchor, this.head) };
4568 Range.prototype.to = function () { return maxPos(this.anchor, this.head) };
4569 Range.prototype.empty = function () { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch };
4570
4571 // Take an unsorted, potentially overlapping set of ranges, and
4572 // build a selection out of it. 'Consumes' ranges array (modifying
4573 // it).
4574 function normalizeSelection(cm, ranges, primIndex) {
4575 var mayTouch = cm && cm.options.selectionsMayTouch;
4576 var prim = ranges[primIndex];
4577 ranges.sort(function (a, b) { return cmp(a.from(), b.from()); });
4578 primIndex = indexOf(ranges, prim);
4579 for (var i = 1; i < ranges.length; i++) {
4580 var cur = ranges[i], prev = ranges[i - 1];
4581 var diff = cmp(prev.to(), cur.from());
4582 if (mayTouch && !cur.empty() ? diff > 0 : diff >= 0) {
4583 var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to());
4584 var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head;
4585 if (i <= primIndex) { --primIndex; }
4586 ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to));
4587 }
4588 }
4589 return new Selection(ranges, primIndex)
4590 }
4591
4592 function simpleSelection(anchor, head) {
4593 return new Selection([new Range(anchor, head || anchor)], 0)
4594 }
4595
4596 // Compute the position of the end of a change (its 'to' property
4597 // refers to the pre-change end).
4598 function changeEnd(change) {
4599 if (!change.text) { return change.to }
4600 return Pos(change.from.line + change.text.length - 1,
4601 lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0))
4602 }
4603
4604 // Adjust a position to refer to the post-change position of the
4605 // same text, or the end of the change if the change covers it.
4606 function adjustForChange(pos, change) {
4607 if (cmp(pos, change.from) < 0) { return pos }
4608 if (cmp(pos, change.to) <= 0) { return changeEnd(change) }
4609
4610 var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;
4611 if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch; }
4612 return Pos(line, ch)
4613 }
4614
4615 function computeSelAfterChange(doc, change) {
4616 var out = [];
4617 for (var i = 0; i < doc.sel.ranges.length; i++) {
4618 var range = doc.sel.ranges[i];
4619 out.push(new Range(adjustForChange(range.anchor, change),
4620 adjustForChange(range.head, change)));
4621 }
4622 return normalizeSelection(doc.cm, out, doc.sel.primIndex)
4623 }
4624
4625 function offsetPos(pos, old, nw) {
4626 if (pos.line == old.line)
4627 { return Pos(nw.line, pos.ch - old.ch + nw.ch) }
4628 else
4629 { return Pos(nw.line + (pos.line - old.line), pos.ch) }
4630 }
4631
4632 // Used by replaceSelections to allow moving the selection to the
4633 // start or around the replaced test. Hint may be "start" or "around".
4634 function computeReplacedSel(doc, changes, hint) {
4635 var out = [];
4636 var oldPrev = Pos(doc.first, 0), newPrev = oldPrev;
4637 for (var i = 0; i < changes.length; i++) {
4638 var change = changes[i];
4639 var from = offsetPos(change.from, oldPrev, newPrev);
4640 var to = offsetPos(changeEnd(change), oldPrev, newPrev);
4641 oldPrev = change.to;
4642 newPrev = to;
4643 if (hint == "around") {
4644 var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0;
4645 out[i] = new Range(inv ? to : from, inv ? from : to);
4646 } else {
4647 out[i] = new Range(from, from);
4648 }
4649 }
4650 return new Selection(out, doc.sel.primIndex)
4651 }
4652
4653 // Used to get the editor into a consistent state again when options change.
4654
4655 function loadMode(cm) {
4656 cm.doc.mode = getMode(cm.options, cm.doc.modeOption);
4657 resetModeState(cm);
4658 }
4659
4660 function resetModeState(cm) {
4661 cm.doc.iter(function (line) {
4662 if (line.stateAfter) { line.stateAfter = null; }
4663 if (line.styles) { line.styles = null; }
4664 });
4665 cm.doc.modeFrontier = cm.doc.highlightFrontier = cm.doc.first;
4666 startWorker(cm, 100);
4667 cm.state.modeGen++;
4668 if (cm.curOp) { regChange(cm); }
4669 }
4670
4671 // DOCUMENT DATA STRUCTURE
4672
4673 // By default, updates that start and end at the beginning of a line
4674 // are treated specially, in order to make the association of line
4675 // widgets and marker elements with the text behave more intuitive.
4676 function isWholeLineUpdate(doc, change) {
4677 return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" &&
4678 (!doc.cm || doc.cm.options.wholeLineUpdateBefore)
4679 }
4680
4681 // Perform a change on the document data structure.
4682 function updateDoc(doc, change, markedSpans, estimateHeight$$1) {
4683 function spansFor(n) {return markedSpans ? markedSpans[n] : null}
4684 function update(line, text, spans) {
4685 updateLine(line, text, spans, estimateHeight$$1);
4686 signalLater(line, "change", line, change);
4687 }
4688 function linesFor(start, end) {
4689 var result = [];
4690 for (var i = start; i < end; ++i)
4691 { result.push(new Line(text[i], spansFor(i), estimateHeight$$1)); }
4692 return result
4693 }
4694
4695 var from = change.from, to = change.to, text = change.text;
4696 var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
4697 var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;
4698
4699 // Adjust the line structure
4700 if (change.full) {
4701 doc.insert(0, linesFor(0, text.length));
4702 doc.remove(text.length, doc.size - text.length);
4703 } else if (isWholeLineUpdate(doc, change)) {
4704 // This is a whole-line replace. Treated specially to make
4705 // sure line objects move the way they are supposed to.
4706 var added = linesFor(0, text.length - 1);
4707 update(lastLine, lastLine.text, lastSpans);
4708 if (nlines) { doc.remove(from.line, nlines); }
4709 if (added.length) { doc.insert(from.line, added); }
4710 } else if (firstLine == lastLine) {
4711 if (text.length == 1) {
4712 update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
4713 } else {
4714 var added$1 = linesFor(1, text.length - 1);
4715 added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight$$1));
4716 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
4717 doc.insert(from.line + 1, added$1);
4718 }
4719 } else if (text.length == 1) {
4720 update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0));
4721 doc.remove(from.line + 1, nlines);
4722 } else {
4723 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
4724 update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
4725 var added$2 = linesFor(1, text.length - 1);
4726 if (nlines > 1) { doc.remove(from.line + 1, nlines - 1); }
4727 doc.insert(from.line + 1, added$2);
4728 }
4729
4730 signalLater(doc, "change", doc, change);
4731 }
4732
4733 // Call f for all linked documents.
4734 function linkedDocs(doc, f, sharedHistOnly) {
4735 function propagate(doc, skip, sharedHist) {
4736 if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) {
4737 var rel = doc.linked[i];
4738 if (rel.doc == skip) { continue }
4739 var shared = sharedHist && rel.sharedHist;
4740 if (sharedHistOnly && !shared) { continue }
4741 f(rel.doc, shared);
4742 propagate(rel.doc, doc, shared);
4743 } }
4744 }
4745 propagate(doc, null, true);
4746 }
4747
4748 // Attach a document to an editor.
4749 function attachDoc(cm, doc) {
4750 if (doc.cm) { throw new Error("This document is already in use.") }
4751 cm.doc = doc;
4752 doc.cm = cm;
4753 estimateLineHeights(cm);
4754 loadMode(cm);
4755 setDirectionClass(cm);
4756 if (!cm.options.lineWrapping) { findMaxLine(cm); }
4757 cm.options.mode = doc.modeOption;
4758 regChange(cm);
4759 }
4760
4761 function setDirectionClass(cm) {
4762 (cm.doc.direction == "rtl" ? addClass : rmClass)(cm.display.lineDiv, "CodeMirror-rtl");
4763 }
4764
4765 function directionChanged(cm) {
4766 runInOp(cm, function () {
4767 setDirectionClass(cm);
4768 regChange(cm);
4769 });
4770 }
4771
4772 function History(startGen) {
4773 // Arrays of change events and selections. Doing something adds an
4774 // event to done and clears undo. Undoing moves events from done
4775 // to undone, redoing moves them in the other direction.
4776 this.done = []; this.undone = [];
4777 this.undoDepth = Infinity;
4778 // Used to track when changes can be merged into a single undo
4779 // event
4780 this.lastModTime = this.lastSelTime = 0;
4781 this.lastOp = this.lastSelOp = null;
4782 this.lastOrigin = this.lastSelOrigin = null;
4783 // Used by the isClean() method
4784 this.generation = this.maxGeneration = startGen || 1;
4785 }
4786
4787 // Create a history change event from an updateDoc-style change
4788 // object.
4789 function historyChangeFromChange(doc, change) {
4790 var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
4791 attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
4792 linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); }, true);
4793 return histChange
4794 }
4795
4796 // Pop all selection events off the end of a history array. Stop at
4797 // a change event.
4798 function clearSelectionEvents(array) {
4799 while (array.length) {
4800 var last = lst(array);
4801 if (last.ranges) { array.pop(); }
4802 else { break }
4803 }
4804 }
4805
4806 // Find the top change event in the history. Pop off selection
4807 // events that are in the way.
4808 function lastChangeEvent(hist, force) {
4809 if (force) {
4810 clearSelectionEvents(hist.done);
4811 return lst(hist.done)
4812 } else if (hist.done.length && !lst(hist.done).ranges) {
4813 return lst(hist.done)
4814 } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) {
4815 hist.done.pop();
4816 return lst(hist.done)
4817 }
4818 }
4819
4820 // Register a change in the history. Merges changes that are within
4821 // a single operation, or are close together with an origin that
4822 // allows merging (starting with "+") into a single event.
4823 function addChangeToHistory(doc, change, selAfter, opId) {
4824 var hist = doc.history;
4825 hist.undone.length = 0;
4826 var time = +new Date, cur;
4827 var last;
4828
4829 if ((hist.lastOp == opId ||
4830 hist.lastOrigin == change.origin && change.origin &&
4831 ((change.origin.charAt(0) == "+" && hist.lastModTime > time - (doc.cm ? doc.cm.options.historyEventDelay : 500)) ||
4832 change.origin.charAt(0) == "*")) &&
4833 (cur = lastChangeEvent(hist, hist.lastOp == opId))) {
4834 // Merge this change into the last event
4835 last = lst(cur.changes);
4836 if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) {
4837 // Optimized case for simple insertion -- don't want to add
4838 // new changesets for every character typed
4839 last.to = changeEnd(change);
4840 } else {
4841 // Add new sub-event
4842 cur.changes.push(historyChangeFromChange(doc, change));
4843 }
4844 } else {
4845 // Can not be merged, start a new event.
4846 var before = lst(hist.done);
4847 if (!before || !before.ranges)
4848 { pushSelectionToHistory(doc.sel, hist.done); }
4849 cur = {changes: [historyChangeFromChange(doc, change)],
4850 generation: hist.generation};
4851 hist.done.push(cur);
4852 while (hist.done.length > hist.undoDepth) {
4853 hist.done.shift();
4854 if (!hist.done[0].ranges) { hist.done.shift(); }
4855 }
4856 }
4857 hist.done.push(selAfter);
4858 hist.generation = ++hist.maxGeneration;
4859 hist.lastModTime = hist.lastSelTime = time;
4860 hist.lastOp = hist.lastSelOp = opId;
4861 hist.lastOrigin = hist.lastSelOrigin = change.origin;
4862
4863 if (!last) { signal(doc, "historyAdded"); }
4864 }
4865
4866 function selectionEventCanBeMerged(doc, origin, prev, sel) {
4867 var ch = origin.charAt(0);
4868 return ch == "*" ||
4869 ch == "+" &&
4870 prev.ranges.length == sel.ranges.length &&
4871 prev.somethingSelected() == sel.somethingSelected() &&
4872 new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500)
4873 }
4874
4875 // Called whenever the selection changes, sets the new selection as
4876 // the pending selection in the history, and pushes the old pending
4877 // selection into the 'done' array when it was significantly
4878 // different (in number of selected ranges, emptiness, or time).
4879 function addSelectionToHistory(doc, sel, opId, options) {
4880 var hist = doc.history, origin = options && options.origin;
4881
4882 // A new event is started when the previous origin does not match
4883 // the current, or the origins don't allow matching. Origins
4884 // starting with * are always merged, those starting with + are
4885 // merged when similar and close together in time.
4886 if (opId == hist.lastSelOp ||
4887 (origin && hist.lastSelOrigin == origin &&
4888 (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||
4889 selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))
4890 { hist.done[hist.done.length - 1] = sel; }
4891 else
4892 { pushSelectionToHistory(sel, hist.done); }
4893
4894 hist.lastSelTime = +new Date;
4895 hist.lastSelOrigin = origin;
4896 hist.lastSelOp = opId;
4897 if (options && options.clearRedo !== false)
4898 { clearSelectionEvents(hist.undone); }
4899 }
4900
4901 function pushSelectionToHistory(sel, dest) {
4902 var top = lst(dest);
4903 if (!(top && top.ranges && top.equals(sel)))
4904 { dest.push(sel); }
4905 }
4906
4907 // Used to store marked span information in the history.
4908 function attachLocalSpans(doc, change, from, to) {
4909 var existing = change["spans_" + doc.id], n = 0;
4910 doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function (line) {
4911 if (line.markedSpans)
4912 { (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans; }
4913 ++n;
4914 });
4915 }
4916
4917 // When un/re-doing restores text containing marked spans, those
4918 // that have been explicitly cleared should not be restored.
4919 function removeClearedSpans(spans) {
4920 if (!spans) { return null }
4921 var out;
4922 for (var i = 0; i < spans.length; ++i) {
4923 if (spans[i].marker.explicitlyCleared) { if (!out) { out = spans.slice(0, i); } }
4924 else if (out) { out.push(spans[i]); }
4925 }
4926 return !out ? spans : out.length ? out : null
4927 }
4928
4929 // Retrieve and filter the old marked spans stored in a change event.
4930 function getOldSpans(doc, change) {
4931 var found = change["spans_" + doc.id];
4932 if (!found) { return null }
4933 var nw = [];
4934 for (var i = 0; i < change.text.length; ++i)
4935 { nw.push(removeClearedSpans(found[i])); }
4936 return nw
4937 }
4938
4939 // Used for un/re-doing changes from the history. Combines the
4940 // result of computing the existing spans with the set of spans that
4941 // existed in the history (so that deleting around a span and then
4942 // undoing brings back the span).
4943 function mergeOldSpans(doc, change) {
4944 var old = getOldSpans(doc, change);
4945 var stretched = stretchSpansOverChange(doc, change);
4946 if (!old) { return stretched }
4947 if (!stretched) { return old }
4948
4949 for (var i = 0; i < old.length; ++i) {
4950 var oldCur = old[i], stretchCur = stretched[i];
4951 if (oldCur && stretchCur) {
4952 spans: for (var j = 0; j < stretchCur.length; ++j) {
4953 var span = stretchCur[j];
4954 for (var k = 0; k < oldCur.length; ++k)
4955 { if (oldCur[k].marker == span.marker) { continue spans } }
4956 oldCur.push(span);
4957 }
4958 } else if (stretchCur) {
4959 old[i] = stretchCur;
4960 }
4961 }
4962 return old
4963 }
4964
4965 // Used both to provide a JSON-safe object in .getHistory, and, when
4966 // detaching a document, to split the history in two
4967 function copyHistoryArray(events, newGroup, instantiateSel) {
4968 var copy = [];
4969 for (var i = 0; i < events.length; ++i) {
4970 var event = events[i];
4971 if (event.ranges) {
4972 copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event);
4973 continue
4974 }
4975 var changes = event.changes, newChanges = [];
4976 copy.push({changes: newChanges});
4977 for (var j = 0; j < changes.length; ++j) {
4978 var change = changes[j], m = (void 0);
4979 newChanges.push({from: change.from, to: change.to, text: change.text});
4980 if (newGroup) { for (var prop in change) { if (m = prop.match(/^spans_(\d+)$/)) {
4981 if (indexOf(newGroup, Number(m[1])) > -1) {
4982 lst(newChanges)[prop] = change[prop];
4983 delete change[prop];
4984 }
4985 } } }
4986 }
4987 }
4988 return copy
4989 }
4990
4991 // The 'scroll' parameter given to many of these indicated whether
4992 // the new cursor position should be scrolled into view after
4993 // modifying the selection.
4994
4995 // If shift is held or the extend flag is set, extends a range to
4996 // include a given position (and optionally a second position).
4997 // Otherwise, simply returns the range between the given positions.
4998 // Used for cursor motion and such.
4999 function extendRange(range, head, other, extend) {
5000 if (extend) {
5001 var anchor = range.anchor;
5002 if (other) {
5003 var posBefore = cmp(head, anchor) < 0;
5004 if (posBefore != (cmp(other, anchor) < 0)) {
5005 anchor = head;
5006 head = other;
5007 } else if (posBefore != (cmp(head, other) < 0)) {
5008 head = other;
5009 }
5010 }
5011 return new Range(anchor, head)
5012 } else {
5013 return new Range(other || head, head)
5014 }
5015 }
5016
5017 // Extend the primary selection range, discard the rest.
5018 function extendSelection(doc, head, other, options, extend) {
5019 if (extend == null) { extend = doc.cm && (doc.cm.display.shift || doc.extend); }
5020 setSelection(doc, new Selection([extendRange(doc.sel.primary(), head, other, extend)], 0), options);
5021 }
5022
5023 // Extend all selections (pos is an array of selections with length
5024 // equal the number of selections)
5025 function extendSelections(doc, heads, options) {
5026 var out = [];
5027 var extend = doc.cm && (doc.cm.display.shift || doc.extend);
5028 for (var i = 0; i < doc.sel.ranges.length; i++)
5029 { out[i] = extendRange(doc.sel.ranges[i], heads[i], null, extend); }
5030 var newSel = normalizeSelection(doc.cm, out, doc.sel.primIndex);
5031 setSelection(doc, newSel, options);
5032 }
5033
5034 // Updates a single range in the selection.
5035 function replaceOneSelection(doc, i, range, options) {
5036 var ranges = doc.sel.ranges.slice(0);
5037 ranges[i] = range;
5038 setSelection(doc, normalizeSelection(doc.cm, ranges, doc.sel.primIndex), options);
5039 }
5040
5041 // Reset the selection to a single range.
5042 function setSimpleSelection(doc, anchor, head, options) {
5043 setSelection(doc, simpleSelection(anchor, head), options);
5044 }
5045
5046 // Give beforeSelectionChange handlers a change to influence a
5047 // selection update.
5048 function filterSelectionChange(doc, sel, options) {
5049 var obj = {
5050 ranges: sel.ranges,
5051 update: function(ranges) {
5052 var this$1 = this;
5053
5054 this.ranges = [];
5055 for (var i = 0; i < ranges.length; i++)
5056 { this$1.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),
5057 clipPos(doc, ranges[i].head)); }
5058 },
5059 origin: options && options.origin
5060 };
5061 signal(doc, "beforeSelectionChange", doc, obj);
5062 if (doc.cm) { signal(doc.cm, "beforeSelectionChange", doc.cm, obj); }
5063 if (obj.ranges != sel.ranges) { return normalizeSelection(doc.cm, obj.ranges, obj.ranges.length - 1) }
5064 else { return sel }
5065 }
5066
5067 function setSelectionReplaceHistory(doc, sel, options) {
5068 var done = doc.history.done, last = lst(done);
5069 if (last && last.ranges) {
5070 done[done.length - 1] = sel;
5071 setSelectionNoUndo(doc, sel, options);
5072 } else {
5073 setSelection(doc, sel, options);
5074 }
5075 }
5076
5077 // Set a new selection.
5078 function setSelection(doc, sel, options) {
5079 setSelectionNoUndo(doc, sel, options);
5080 addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options);
5081 }
5082
5083 function setSelectionNoUndo(doc, sel, options) {
5084 if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange"))
5085 { sel = filterSelectionChange(doc, sel, options); }
5086
5087 var bias = options && options.bias ||
5088 (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1);
5089 setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true));
5090
5091 if (!(options && options.scroll === false) && doc.cm)
5092 { ensureCursorVisible(doc.cm); }
5093 }
5094
5095 function setSelectionInner(doc, sel) {
5096 if (sel.equals(doc.sel)) { return }
5097
5098 doc.sel = sel;
5099
5100 if (doc.cm) {
5101 doc.cm.curOp.updateInput = 1;
5102 doc.cm.curOp.selectionChanged = true;
5103 signalCursorActivity(doc.cm);
5104 }
5105 signalLater(doc, "cursorActivity", doc);
5106 }
5107
5108 // Verify that the selection does not partially select any atomic
5109 // marked ranges.
5110 function reCheckSelection(doc) {
5111 setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false));
5112 }
5113
5114 // Return a selection that does not partially select any atomic
5115 // ranges.
5116 function skipAtomicInSelection(doc, sel, bias, mayClear) {
5117 var out;
5118 for (var i = 0; i < sel.ranges.length; i++) {
5119 var range = sel.ranges[i];
5120 var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i];
5121 var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear);
5122 var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear);
5123 if (out || newAnchor != range.anchor || newHead != range.head) {
5124 if (!out) { out = sel.ranges.slice(0, i); }
5125 out[i] = new Range(newAnchor, newHead);
5126 }
5127 }
5128 return out ? normalizeSelection(doc.cm, out, sel.primIndex) : sel
5129 }
5130
5131 function skipAtomicInner(doc, pos, oldPos, dir, mayClear) {
5132 var line = getLine(doc, pos.line);
5133 if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
5134 var sp = line.markedSpans[i], m = sp.marker;
5135 if ((sp.from == null || (m.inclusiveLeft ? sp.from <= pos.ch : sp.from < pos.ch)) &&
5136 (sp.to == null || (m.inclusiveRight ? sp.to >= pos.ch : sp.to > pos.ch))) {
5137 if (mayClear) {
5138 signal(m, "beforeCursorEnter");
5139 if (m.explicitlyCleared) {
5140 if (!line.markedSpans) { break }
5141 else {--i; continue}
5142 }
5143 }
5144 if (!m.atomic) { continue }
5145
5146 if (oldPos) {
5147 var near = m.find(dir < 0 ? 1 : -1), diff = (void 0);
5148 if (dir < 0 ? m.inclusiveRight : m.inclusiveLeft)
5149 { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null); }
5150 if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0))
5151 { return skipAtomicInner(doc, near, pos, dir, mayClear) }
5152 }
5153
5154 var far = m.find(dir < 0 ? -1 : 1);
5155 if (dir < 0 ? m.inclusiveLeft : m.inclusiveRight)
5156 { far = movePos(doc, far, dir, far.line == pos.line ? line : null); }
5157 return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null
5158 }
5159 } }
5160 return pos
5161 }
5162
5163 // Ensure a given position is not inside an atomic range.
5164 function skipAtomic(doc, pos, oldPos, bias, mayClear) {
5165 var dir = bias || 1;
5166 var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) ||
5167 (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) ||
5168 skipAtomicInner(doc, pos, oldPos, -dir, mayClear) ||
5169 (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true));
5170 if (!found) {
5171 doc.cantEdit = true;
5172 return Pos(doc.first, 0)
5173 }
5174 return found
5175 }
5176
5177 function movePos(doc, pos, dir, line) {
5178 if (dir < 0 && pos.ch == 0) {
5179 if (pos.line > doc.first) { return clipPos(doc, Pos(pos.line - 1)) }
5180 else { return null }
5181 } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) {
5182 if (pos.line < doc.first + doc.size - 1) { return Pos(pos.line + 1, 0) }
5183 else { return null }
5184 } else {
5185 return new Pos(pos.line, pos.ch + dir)
5186 }
5187 }
5188
5189 function selectAll(cm) {
5190 cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll);
5191 }
5192
5193 // UPDATING
5194
5195 // Allow "beforeChange" event handlers to influence a change
5196 function filterChange(doc, change, update) {
5197 var obj = {
5198 canceled: false,
5199 from: change.from,
5200 to: change.to,
5201 text: change.text,
5202 origin: change.origin,
5203 cancel: function () { return obj.canceled = true; }
5204 };
5205 if (update) { obj.update = function (from, to, text, origin) {
5206 if (from) { obj.from = clipPos(doc, from); }
5207 if (to) { obj.to = clipPos(doc, to); }
5208 if (text) { obj.text = text; }
5209 if (origin !== undefined) { obj.origin = origin; }
5210 }; }
5211 signal(doc, "beforeChange", doc, obj);
5212 if (doc.cm) { signal(doc.cm, "beforeChange", doc.cm, obj); }
5213
5214 if (obj.canceled) {
5215 if (doc.cm) { doc.cm.curOp.updateInput = 2; }
5216 return null
5217 }
5218 return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin}
5219 }
5220
5221 // Apply a change to a document, and add it to the document's
5222 // history, and propagating it to all linked documents.
5223 function makeChange(doc, change, ignoreReadOnly) {
5224 if (doc.cm) {
5225 if (!doc.cm.curOp) { return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) }
5226 if (doc.cm.state.suppressEdits) { return }
5227 }
5228
5229 if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
5230 change = filterChange(doc, change, true);
5231 if (!change) { return }
5232 }
5233
5234 // Possibly split or suppress the update based on the presence
5235 // of read-only spans in its range.
5236 var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);
5237 if (split) {
5238 for (var i = split.length - 1; i >= 0; --i)
5239 { makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text, origin: change.origin}); }
5240 } else {
5241 makeChangeInner(doc, change);
5242 }
5243 }
5244
5245 function makeChangeInner(doc, change) {
5246 if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) { return }
5247 var selAfter = computeSelAfterChange(doc, change);
5248 addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);
5249
5250 makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));
5251 var rebased = [];
5252
5253 linkedDocs(doc, function (doc, sharedHist) {
5254 if (!sharedHist && indexOf(rebased, doc.history) == -1) {
5255 rebaseHist(doc.history, change);
5256 rebased.push(doc.history);
5257 }
5258 makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change));
5259 });
5260 }
5261
5262 // Revert a change stored in a document's history.
5263 function makeChangeFromHistory(doc, type, allowSelectionOnly) {
5264 var suppress = doc.cm && doc.cm.state.suppressEdits;
5265 if (suppress && !allowSelectionOnly) { return }
5266
5267 var hist = doc.history, event, selAfter = doc.sel;
5268 var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done;
5269
5270 // Verify that there is a useable event (so that ctrl-z won't
5271 // needlessly clear selection events)
5272 var i = 0;
5273 for (; i < source.length; i++) {
5274 event = source[i];
5275 if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges)
5276 { break }
5277 }
5278 if (i == source.length) { return }
5279 hist.lastOrigin = hist.lastSelOrigin = null;
5280
5281 for (;;) {
5282 event = source.pop();
5283 if (event.ranges) {
5284 pushSelectionToHistory(event, dest);
5285 if (allowSelectionOnly && !event.equals(doc.sel)) {
5286 setSelection(doc, event, {clearRedo: false});
5287 return
5288 }
5289 selAfter = event;
5290 } else if (suppress) {
5291 source.push(event);
5292 return
5293 } else { break }
5294 }
5295
5296 // Build up a reverse change object to add to the opposite history
5297 // stack (redo when undoing, and vice versa).
5298 var antiChanges = [];
5299 pushSelectionToHistory(selAfter, dest);
5300 dest.push({changes: antiChanges, generation: hist.generation});
5301 hist.generation = event.generation || ++hist.maxGeneration;
5302
5303 var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange");
5304
5305 var loop = function ( i ) {
5306 var change = event.changes[i];
5307 change.origin = type;
5308 if (filter && !filterChange(doc, change, false)) {
5309 source.length = 0;
5310 return {}
5311 }
5312
5313 antiChanges.push(historyChangeFromChange(doc, change));
5314
5315 var after = i ? computeSelAfterChange(doc, change) : lst(source);
5316 makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
5317 if (!i && doc.cm) { doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}); }
5318 var rebased = [];
5319
5320 // Propagate to the linked documents
5321 linkedDocs(doc, function (doc, sharedHist) {
5322 if (!sharedHist && indexOf(rebased, doc.history) == -1) {
5323 rebaseHist(doc.history, change);
5324 rebased.push(doc.history);
5325 }
5326 makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change));
5327 });
5328 };
5329
5330 for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) {
5331 var returned = loop( i$1 );
5332
5333 if ( returned ) return returned.v;
5334 }
5335 }
5336
5337 // Sub-views need their line numbers shifted when text is added
5338 // above or below them in the parent document.
5339 function shiftDoc(doc, distance) {
5340 if (distance == 0) { return }
5341 doc.first += distance;
5342 doc.sel = new Selection(map(doc.sel.ranges, function (range) { return new Range(
5343 Pos(range.anchor.line + distance, range.anchor.ch),
5344 Pos(range.head.line + distance, range.head.ch)
5345 ); }), doc.sel.primIndex);
5346 if (doc.cm) {
5347 regChange(doc.cm, doc.first, doc.first - distance, distance);
5348 for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++)
5349 { regLineChange(doc.cm, l, "gutter"); }
5350 }
5351 }
5352
5353 // More lower-level change function, handling only a single document
5354 // (not linked ones).
5355 function makeChangeSingleDoc(doc, change, selAfter, spans) {
5356 if (doc.cm && !doc.cm.curOp)
5357 { return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) }
5358
5359 if (change.to.line < doc.first) {
5360 shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line));
5361 return
5362 }
5363 if (change.from.line > doc.lastLine()) { return }
5364
5365 // Clip the change to the size of this doc
5366 if (change.from.line < doc.first) {
5367 var shift = change.text.length - 1 - (doc.first - change.from.line);
5368 shiftDoc(doc, shift);
5369 change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
5370 text: [lst(change.text)], origin: change.origin};
5371 }
5372 var last = doc.lastLine();
5373 if (change.to.line > last) {
5374 change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
5375 text: [change.text[0]], origin: change.origin};
5376 }
5377
5378 change.removed = getBetween(doc, change.from, change.to);
5379
5380 if (!selAfter) { selAfter = computeSelAfterChange(doc, change); }
5381 if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans); }
5382 else { updateDoc(doc, change, spans); }
5383 setSelectionNoUndo(doc, selAfter, sel_dontScroll);
5384 }
5385
5386 // Handle the interaction of a change to a document with the editor
5387 // that this document is part of.
5388 function makeChangeSingleDocInEditor(cm, change, spans) {
5389 var doc = cm.doc, display = cm.display, from = change.from, to = change.to;
5390
5391 var recomputeMaxLength = false, checkWidthStart = from.line;
5392 if (!cm.options.lineWrapping) {
5393 checkWidthStart = lineNo(visualLine(getLine(doc, from.line)));
5394 doc.iter(checkWidthStart, to.line + 1, function (line) {
5395 if (line == display.maxLine) {
5396 recomputeMaxLength = true;
5397 return true
5398 }
5399 });
5400 }
5401
5402 if (doc.sel.contains(change.from, change.to) > -1)
5403 { signalCursorActivity(cm); }
5404
5405 updateDoc(doc, change, spans, estimateHeight(cm));
5406
5407 if (!cm.options.lineWrapping) {
5408 doc.iter(checkWidthStart, from.line + change.text.length, function (line) {
5409 var len = lineLength(line);
5410 if (len > display.maxLineLength) {
5411 display.maxLine = line;
5412 display.maxLineLength = len;
5413 display.maxLineChanged = true;
5414 recomputeMaxLength = false;
5415 }
5416 });
5417 if (recomputeMaxLength) { cm.curOp.updateMaxLine = true; }
5418 }
5419
5420 retreatFrontier(doc, from.line);
5421 startWorker(cm, 400);
5422
5423 var lendiff = change.text.length - (to.line - from.line) - 1;
5424 // Remember that these lines changed, for updating the display
5425 if (change.full)
5426 { regChange(cm); }
5427 else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change))
5428 { regLineChange(cm, from.line, "text"); }
5429 else
5430 { regChange(cm, from.line, to.line + 1, lendiff); }
5431
5432 var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change");
5433 if (changeHandler || changesHandler) {
5434 var obj = {
5435 from: from, to: to,
5436 text: change.text,
5437 removed: change.removed,
5438 origin: change.origin
5439 };
5440 if (changeHandler) { signalLater(cm, "change", cm, obj); }
5441 if (changesHandler) { (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj); }
5442 }
5443 cm.display.selForContextMenu = null;
5444 }
5445
5446 function replaceRange(doc, code, from, to, origin) {
5447 var assign;
5448
5449 if (!to) { to = from; }
5450 if (cmp(to, from) < 0) { (assign = [to, from], from = assign[0], to = assign[1]); }
5451 if (typeof code == "string") { code = doc.splitLines(code); }
5452 makeChange(doc, {from: from, to: to, text: code, origin: origin});
5453 }
5454
5455 // Rebasing/resetting history to deal with externally-sourced changes
5456
5457 function rebaseHistSelSingle(pos, from, to, diff) {
5458 if (to < pos.line) {
5459 pos.line += diff;
5460 } else if (from < pos.line) {
5461 pos.line = from;
5462 pos.ch = 0;
5463 }
5464 }
5465
5466 // Tries to rebase an array of history events given a change in the
5467 // document. If the change touches the same lines as the event, the
5468 // event, and everything 'behind' it, is discarded. If the change is
5469 // before the event, the event's positions are updated. Uses a
5470 // copy-on-write scheme for the positions, to avoid having to
5471 // reallocate them all on every rebase, but also avoid problems with
5472 // shared position objects being unsafely updated.
5473 function rebaseHistArray(array, from, to, diff) {
5474 for (var i = 0; i < array.length; ++i) {
5475 var sub = array[i], ok = true;
5476 if (sub.ranges) {
5477 if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; }
5478 for (var j = 0; j < sub.ranges.length; j++) {
5479 rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff);
5480 rebaseHistSelSingle(sub.ranges[j].head, from, to, diff);
5481 }
5482 continue
5483 }
5484 for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) {
5485 var cur = sub.changes[j$1];
5486 if (to < cur.from.line) {
5487 cur.from = Pos(cur.from.line + diff, cur.from.ch);
5488 cur.to = Pos(cur.to.line + diff, cur.to.ch);
5489 } else if (from <= cur.to.line) {
5490 ok = false;
5491 break
5492 }
5493 }
5494 if (!ok) {
5495 array.splice(0, i + 1);
5496 i = 0;
5497 }
5498 }
5499 }
5500
5501 function rebaseHist(hist, change) {
5502 var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1;
5503 rebaseHistArray(hist.done, from, to, diff);
5504 rebaseHistArray(hist.undone, from, to, diff);
5505 }
5506
5507 // Utility for applying a change to a line by handle or number,
5508 // returning the number and optionally registering the line as
5509 // changed.
5510 function changeLine(doc, handle, changeType, op) {
5511 var no = handle, line = handle;
5512 if (typeof handle == "number") { line = getLine(doc, clipLine(doc, handle)); }
5513 else { no = lineNo(handle); }
5514 if (no == null) { return null }
5515 if (op(line, no) && doc.cm) { regLineChange(doc.cm, no, changeType); }
5516 return line
5517 }
5518
5519 // The document is represented as a BTree consisting of leaves, with
5520 // chunk of lines in them, and branches, with up to ten leaves or
5521 // other branch nodes below them. The top node is always a branch
5522 // node, and is the document object itself (meaning it has
5523 // additional methods and properties).
5524 //
5525 // All nodes have parent links. The tree is used both to go from
5526 // line numbers to line objects, and to go from objects to numbers.
5527 // It also indexes by height, and is used to convert between height
5528 // and line object, and to find the total height of the document.
5529 //
5530 // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html
5531
5532 function LeafChunk(lines) {
5533 var this$1 = this;
5534
5535 this.lines = lines;
5536 this.parent = null;
5537 var height = 0;
5538 for (var i = 0; i < lines.length; ++i) {
5539 lines[i].parent = this$1;
5540 height += lines[i].height;
5541 }
5542 this.height = height;
5543 }
5544
5545 LeafChunk.prototype = {
5546 chunkSize: function() { return this.lines.length },
5547
5548 // Remove the n lines at offset 'at'.
5549 removeInner: function(at, n) {
5550 var this$1 = this;
5551
5552 for (var i = at, e = at + n; i < e; ++i) {
5553 var line = this$1.lines[i];
5554 this$1.height -= line.height;
5555 cleanUpLine(line);
5556 signalLater(line, "delete");
5557 }
5558 this.lines.splice(at, n);
5559 },
5560
5561 // Helper used to collapse a small branch into a single leaf.
5562 collapse: function(lines) {
5563 lines.push.apply(lines, this.lines);
5564 },
5565
5566 // Insert the given array of lines at offset 'at', count them as
5567 // having the given height.
5568 insertInner: function(at, lines, height) {
5569 var this$1 = this;
5570
5571 this.height += height;
5572 this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
5573 for (var i = 0; i < lines.length; ++i) { lines[i].parent = this$1; }
5574 },
5575
5576 // Used to iterate over a part of the tree.
5577 iterN: function(at, n, op) {
5578 var this$1 = this;
5579
5580 for (var e = at + n; at < e; ++at)
5581 { if (op(this$1.lines[at])) { return true } }
5582 }
5583 };
5584
5585 function BranchChunk(children) {
5586 var this$1 = this;
5587
5588 this.children = children;
5589 var size = 0, height = 0;
5590 for (var i = 0; i < children.length; ++i) {
5591 var ch = children[i];
5592 size += ch.chunkSize(); height += ch.height;
5593 ch.parent = this$1;
5594 }
5595 this.size = size;
5596 this.height = height;
5597 this.parent = null;
5598 }
5599
5600 BranchChunk.prototype = {
5601 chunkSize: function() { return this.size },
5602
5603 removeInner: function(at, n) {
5604 var this$1 = this;
5605
5606 this.size -= n;
5607 for (var i = 0; i < this.children.length; ++i) {
5608 var child = this$1.children[i], sz = child.chunkSize();
5609 if (at < sz) {
5610 var rm = Math.min(n, sz - at), oldHeight = child.height;
5611 child.removeInner(at, rm);
5612 this$1.height -= oldHeight - child.height;
5613 if (sz == rm) { this$1.children.splice(i--, 1); child.parent = null; }
5614 if ((n -= rm) == 0) { break }
5615 at = 0;
5616 } else { at -= sz; }
5617 }
5618 // If the result is smaller than 25 lines, ensure that it is a
5619 // single leaf node.
5620 if (this.size - n < 25 &&
5621 (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) {
5622 var lines = [];
5623 this.collapse(lines);
5624 this.children = [new LeafChunk(lines)];
5625 this.children[0].parent = this;
5626 }
5627 },
5628
5629 collapse: function(lines) {
5630 var this$1 = this;
5631
5632 for (var i = 0; i < this.children.length; ++i) { this$1.children[i].collapse(lines); }
5633 },
5634
5635 insertInner: function(at, lines, height) {
5636 var this$1 = this;
5637
5638 this.size += lines.length;
5639 this.height += height;
5640 for (var i = 0; i < this.children.length; ++i) {
5641 var child = this$1.children[i], sz = child.chunkSize();
5642 if (at <= sz) {
5643 child.insertInner(at, lines, height);
5644 if (child.lines && child.lines.length > 50) {
5645 // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced.
5646 // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest.
5647 var remaining = child.lines.length % 25 + 25;
5648 for (var pos = remaining; pos < child.lines.length;) {
5649 var leaf = new LeafChunk(child.lines.slice(pos, pos += 25));
5650 child.height -= leaf.height;
5651 this$1.children.splice(++i, 0, leaf);
5652 leaf.parent = this$1;
5653 }
5654 child.lines = child.lines.slice(0, remaining);
5655 this$1.maybeSpill();
5656 }
5657 break
5658 }
5659 at -= sz;
5660 }
5661 },
5662
5663 // When a node has grown, check whether it should be split.
5664 maybeSpill: function() {
5665 if (this.children.length <= 10) { return }
5666 var me = this;
5667 do {
5668 var spilled = me.children.splice(me.children.length - 5, 5);
5669 var sibling = new BranchChunk(spilled);
5670 if (!me.parent) { // Become the parent node
5671 var copy = new BranchChunk(me.children);
5672 copy.parent = me;
5673 me.children = [copy, sibling];
5674 me = copy;
5675 } else {
5676 me.size -= sibling.size;
5677 me.height -= sibling.height;
5678 var myIndex = indexOf(me.parent.children, me);
5679 me.parent.children.splice(myIndex + 1, 0, sibling);
5680 }
5681 sibling.parent = me.parent;
5682 } while (me.children.length > 10)
5683 me.parent.maybeSpill();
5684 },
5685
5686 iterN: function(at, n, op) {
5687 var this$1 = this;
5688
5689 for (var i = 0; i < this.children.length; ++i) {
5690 var child = this$1.children[i], sz = child.chunkSize();
5691 if (at < sz) {
5692 var used = Math.min(n, sz - at);
5693 if (child.iterN(at, used, op)) { return true }
5694 if ((n -= used) == 0) { break }
5695 at = 0;
5696 } else { at -= sz; }
5697 }
5698 }
5699 };
5700
5701 // Line widgets are block elements displayed above or below a line.
5702
5703 var LineWidget = function(doc, node, options) {
5704 var this$1 = this;
5705
5706 if (options) { for (var opt in options) { if (options.hasOwnProperty(opt))
5707 { this$1[opt] = options[opt]; } } }
5708 this.doc = doc;
5709 this.node = node;
5710 };
5711
5712 LineWidget.prototype.clear = function () {
5713 var this$1 = this;
5714
5715 var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line);
5716 if (no == null || !ws) { return }
5717 for (var i = 0; i < ws.length; ++i) { if (ws[i] == this$1) { ws.splice(i--, 1); } }
5718 if (!ws.length) { line.widgets = null; }
5719 var height = widgetHeight(this);
5720 updateLineHeight(line, Math.max(0, line.height - height));
5721 if (cm) {
5722 runInOp(cm, function () {
5723 adjustScrollWhenAboveVisible(cm, line, -height);
5724 regLineChange(cm, no, "widget");
5725 });
5726 signalLater(cm, "lineWidgetCleared", cm, this, no);
5727 }
5728 };
5729
5730 LineWidget.prototype.changed = function () {
5731 var this$1 = this;
5732
5733 var oldH = this.height, cm = this.doc.cm, line = this.line;
5734 this.height = null;
5735 var diff = widgetHeight(this) - oldH;
5736 if (!diff) { return }
5737 if (!lineIsHidden(this.doc, line)) { updateLineHeight(line, line.height + diff); }
5738 if (cm) {
5739 runInOp(cm, function () {
5740 cm.curOp.forceUpdate = true;
5741 adjustScrollWhenAboveVisible(cm, line, diff);
5742 signalLater(cm, "lineWidgetChanged", cm, this$1, lineNo(line));
5743 });
5744 }
5745 };
5746 eventMixin(LineWidget);
5747
5748 function adjustScrollWhenAboveVisible(cm, line, diff) {
5749 if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))
5750 { addToScrollTop(cm, diff); }
5751 }
5752
5753 function addLineWidget(doc, handle, node, options) {
5754 var widget = new LineWidget(doc, node, options);
5755 var cm = doc.cm;
5756 if (cm && widget.noHScroll) { cm.display.alignWidgets = true; }
5757 changeLine(doc, handle, "widget", function (line) {
5758 var widgets = line.widgets || (line.widgets = []);
5759 if (widget.insertAt == null) { widgets.push(widget); }
5760 else { widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget); }
5761 widget.line = line;
5762 if (cm && !lineIsHidden(doc, line)) {
5763 var aboveVisible = heightAtLine(line) < doc.scrollTop;
5764 updateLineHeight(line, line.height + widgetHeight(widget));
5765 if (aboveVisible) { addToScrollTop(cm, widget.height); }
5766 cm.curOp.forceUpdate = true;
5767 }
5768 return true
5769 });
5770 if (cm) { signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle)); }
5771 return widget
5772 }
5773
5774 // TEXTMARKERS
5775
5776 // Created with markText and setBookmark methods. A TextMarker is a
5777 // handle that can be used to clear or find a marked position in the
5778 // document. Line objects hold arrays (markedSpans) containing
5779 // {from, to, marker} object pointing to such marker objects, and
5780 // indicating that such a marker is present on that line. Multiple
5781 // lines may point to the same marker when it spans across lines.
5782 // The spans will have null for their from/to properties when the
5783 // marker continues beyond the start/end of the line. Markers have
5784 // links back to the lines they currently touch.
5785
5786 // Collapsed markers have unique ids, in order to be able to order
5787 // them, which is needed for uniquely determining an outer marker
5788 // when they overlap (they may nest, but not partially overlap).
5789 var nextMarkerId = 0;
5790
5791 var TextMarker = function(doc, type) {
5792 this.lines = [];
5793 this.type = type;
5794 this.doc = doc;
5795 this.id = ++nextMarkerId;
5796 };
5797
5798 // Clear the marker.
5799 TextMarker.prototype.clear = function () {
5800 var this$1 = this;
5801
5802 if (this.explicitlyCleared) { return }
5803 var cm = this.doc.cm, withOp = cm && !cm.curOp;
5804 if (withOp) { startOperation(cm); }
5805 if (hasHandler(this, "clear")) {
5806 var found = this.find();
5807 if (found) { signalLater(this, "clear", found.from, found.to); }
5808 }
5809 var min = null, max = null;
5810 for (var i = 0; i < this.lines.length; ++i) {
5811 var line = this$1.lines[i];
5812 var span = getMarkedSpanFor(line.markedSpans, this$1);
5813 if (cm && !this$1.collapsed) { regLineChange(cm, lineNo(line), "text"); }
5814 else if (cm) {
5815 if (span.to != null) { max = lineNo(line); }
5816 if (span.from != null) { min = lineNo(line); }
5817 }
5818 line.markedSpans = removeMarkedSpan(line.markedSpans, span);
5819 if (span.from == null && this$1.collapsed && !lineIsHidden(this$1.doc, line) && cm)
5820 { updateLineHeight(line, textHeight(cm.display)); }
5821 }
5822 if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) {
5823 var visual = visualLine(this$1.lines[i$1]), len = lineLength(visual);
5824 if (len > cm.display.maxLineLength) {
5825 cm.display.maxLine = visual;
5826 cm.display.maxLineLength = len;
5827 cm.display.maxLineChanged = true;
5828 }
5829 } }
5830
5831 if (min != null && cm && this.collapsed) { regChange(cm, min, max + 1); }
5832 this.lines.length = 0;
5833 this.explicitlyCleared = true;
5834 if (this.atomic && this.doc.cantEdit) {
5835 this.doc.cantEdit = false;
5836 if (cm) { reCheckSelection(cm.doc); }
5837 }
5838 if (cm) { signalLater(cm, "markerCleared", cm, this, min, max); }
5839 if (withOp) { endOperation(cm); }
5840 if (this.parent) { this.parent.clear(); }
5841 };
5842
5843 // Find the position of the marker in the document. Returns a {from,
5844 // to} object by default. Side can be passed to get a specific side
5845 // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the
5846 // Pos objects returned contain a line object, rather than a line
5847 // number (used to prevent looking up the same line twice).
5848 TextMarker.prototype.find = function (side, lineObj) {
5849 var this$1 = this;
5850
5851 if (side == null && this.type == "bookmark") { side = 1; }
5852 var from, to;
5853 for (var i = 0; i < this.lines.length; ++i) {
5854 var line = this$1.lines[i];
5855 var span = getMarkedSpanFor(line.markedSpans, this$1);
5856 if (span.from != null) {
5857 from = Pos(lineObj ? line : lineNo(line), span.from);
5858 if (side == -1) { return from }
5859 }
5860 if (span.to != null) {
5861 to = Pos(lineObj ? line : lineNo(line), span.to);
5862 if (side == 1) { return to }
5863 }
5864 }
5865 return from && {from: from, to: to}
5866 };
5867
5868 // Signals that the marker's widget changed, and surrounding layout
5869 // should be recomputed.
5870 TextMarker.prototype.changed = function () {
5871 var this$1 = this;
5872
5873 var pos = this.find(-1, true), widget = this, cm = this.doc.cm;
5874 if (!pos || !cm) { return }
5875 runInOp(cm, function () {
5876 var line = pos.line, lineN = lineNo(pos.line);
5877 var view = findViewForLine(cm, lineN);
5878 if (view) {
5879 clearLineMeasurementCacheFor(view);
5880 cm.curOp.selectionChanged = cm.curOp.forceUpdate = true;
5881 }
5882 cm.curOp.updateMaxLine = true;
5883 if (!lineIsHidden(widget.doc, line) && widget.height != null) {
5884 var oldHeight = widget.height;
5885 widget.height = null;
5886 var dHeight = widgetHeight(widget) - oldHeight;
5887 if (dHeight)
5888 { updateLineHeight(line, line.height + dHeight); }
5889 }
5890 signalLater(cm, "markerChanged", cm, this$1);
5891 });
5892 };
5893
5894 TextMarker.prototype.attachLine = function (line) {
5895 if (!this.lines.length && this.doc.cm) {
5896 var op = this.doc.cm.curOp;
5897 if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
5898 { (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this); }
5899 }
5900 this.lines.push(line);
5901 };
5902
5903 TextMarker.prototype.detachLine = function (line) {
5904 this.lines.splice(indexOf(this.lines, line), 1);
5905 if (!this.lines.length && this.doc.cm) {
5906 var op = this.doc.cm.curOp
5907 ;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this);
5908 }
5909 };
5910 eventMixin(TextMarker);
5911
5912 // Create a marker, wire it up to the right lines, and
5913 function markText(doc, from, to, options, type) {
5914 // Shared markers (across linked documents) are handled separately
5915 // (markTextShared will call out to this again, once per
5916 // document).
5917 if (options && options.shared) { return markTextShared(doc, from, to, options, type) }
5918 // Ensure we are in an operation.
5919 if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, markText)(doc, from, to, options, type) }
5920
5921 var marker = new TextMarker(doc, type), diff = cmp(from, to);
5922 if (options) { copyObj(options, marker, false); }
5923 // Don't connect empty markers unless clearWhenEmpty is false
5924 if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false)
5925 { return marker }
5926 if (marker.replacedWith) {
5927 // Showing up as a widget implies collapsed (widget replaces text)
5928 marker.collapsed = true;
5929 marker.widgetNode = eltP("span", [marker.replacedWith], "CodeMirror-widget");
5930 if (!options.handleMouseEvents) { marker.widgetNode.setAttribute("cm-ignore-events", "true"); }
5931 if (options.insertLeft) { marker.widgetNode.insertLeft = true; }
5932 }
5933 if (marker.collapsed) {
5934 if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||
5935 from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker))
5936 { throw new Error("Inserting collapsed marker partially overlapping an existing one") }
5937 seeCollapsedSpans();
5938 }
5939
5940 if (marker.addToHistory)
5941 { addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN); }
5942
5943 var curLine = from.line, cm = doc.cm, updateMaxLine;
5944 doc.iter(curLine, to.line + 1, function (line) {
5945 if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine)
5946 { updateMaxLine = true; }
5947 if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0); }
5948 addMarkedSpan(line, new MarkedSpan(marker,
5949 curLine == from.line ? from.ch : null,
5950 curLine == to.line ? to.ch : null));
5951 ++curLine;
5952 });
5953 // lineIsHidden depends on the presence of the spans, so needs a second pass
5954 if (marker.collapsed) { doc.iter(from.line, to.line + 1, function (line) {
5955 if (lineIsHidden(doc, line)) { updateLineHeight(line, 0); }
5956 }); }
5957
5958 if (marker.clearOnEnter) { on(marker, "beforeCursorEnter", function () { return marker.clear(); }); }
5959
5960 if (marker.readOnly) {
5961 seeReadOnlySpans();
5962 if (doc.history.done.length || doc.history.undone.length)
5963 { doc.clearHistory(); }
5964 }
5965 if (marker.collapsed) {
5966 marker.id = ++nextMarkerId;
5967 marker.atomic = true;
5968 }
5969 if (cm) {
5970 // Sync editor state
5971 if (updateMaxLine) { cm.curOp.updateMaxLine = true; }
5972 if (marker.collapsed)
5973 { regChange(cm, from.line, to.line + 1); }
5974 else if (marker.className || marker.startStyle || marker.endStyle || marker.css ||
5975 marker.attributes || marker.title)
5976 { for (var i = from.line; i <= to.line; i++) { regLineChange(cm, i, "text"); } }
5977 if (marker.atomic) { reCheckSelection(cm.doc); }
5978 signalLater(cm, "markerAdded", cm, marker);
5979 }
5980 return marker
5981 }
5982
5983 // SHARED TEXTMARKERS
5984
5985 // A shared marker spans multiple linked documents. It is
5986 // implemented as a meta-marker-object controlling multiple normal
5987 // markers.
5988 var SharedTextMarker = function(markers, primary) {
5989 var this$1 = this;
5990
5991 this.markers = markers;
5992 this.primary = primary;
5993 for (var i = 0; i < markers.length; ++i)
5994 { markers[i].parent = this$1; }
5995 };
5996
5997 SharedTextMarker.prototype.clear = function () {
5998 var this$1 = this;
5999
6000 if (this.explicitlyCleared) { return }
6001 this.explicitlyCleared = true;
6002 for (var i = 0; i < this.markers.length; ++i)
6003 { this$1.markers[i].clear(); }
6004 signalLater(this, "clear");
6005 };
6006
6007 SharedTextMarker.prototype.find = function (side, lineObj) {
6008 return this.primary.find(side, lineObj)
6009 };
6010 eventMixin(SharedTextMarker);
6011
6012 function markTextShared(doc, from, to, options, type) {
6013 options = copyObj(options);
6014 options.shared = false;
6015 var markers = [markText(doc, from, to, options, type)], primary = markers[0];
6016 var widget = options.widgetNode;
6017 linkedDocs(doc, function (doc) {
6018 if (widget) { options.widgetNode = widget.cloneNode(true); }
6019 markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));
6020 for (var i = 0; i < doc.linked.length; ++i)
6021 { if (doc.linked[i].isParent) { return } }
6022 primary = lst(markers);
6023 });
6024 return new SharedTextMarker(markers, primary)
6025 }
6026
6027 function findSharedMarkers(doc) {
6028 return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function (m) { return m.parent; })
6029 }
6030
6031 function copySharedMarkers(doc, markers) {
6032 for (var i = 0; i < markers.length; i++) {
6033 var marker = markers[i], pos = marker.find();
6034 var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to);
6035 if (cmp(mFrom, mTo)) {
6036 var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type);
6037 marker.markers.push(subMark);
6038 subMark.parent = marker;
6039 }
6040 }
6041 }
6042
6043 function detachSharedMarkers(markers) {
6044 var loop = function ( i ) {
6045 var marker = markers[i], linked = [marker.primary.doc];
6046 linkedDocs(marker.primary.doc, function (d) { return linked.push(d); });
6047 for (var j = 0; j < marker.markers.length; j++) {
6048 var subMarker = marker.markers[j];
6049 if (indexOf(linked, subMarker.doc) == -1) {
6050 subMarker.parent = null;
6051 marker.markers.splice(j--, 1);
6052 }
6053 }
6054 };
6055
6056 for (var i = 0; i < markers.length; i++) loop( i );
6057 }
6058
6059 var nextDocId = 0;
6060 var Doc = function(text, mode, firstLine, lineSep, direction) {
6061 if (!(this instanceof Doc)) { return new Doc(text, mode, firstLine, lineSep, direction) }
6062 if (firstLine == null) { firstLine = 0; }
6063
6064 BranchChunk.call(this, [new LeafChunk([new Line("", null)])]);
6065 this.first = firstLine;
6066 this.scrollTop = this.scrollLeft = 0;
6067 this.cantEdit = false;
6068 this.cleanGeneration = 1;
6069 this.modeFrontier = this.highlightFrontier = firstLine;
6070 var start = Pos(firstLine, 0);
6071 this.sel = simpleSelection(start);
6072 this.history = new History(null);
6073 this.id = ++nextDocId;
6074 this.modeOption = mode;
6075 this.lineSep = lineSep;
6076 this.direction = (direction == "rtl") ? "rtl" : "ltr";
6077 this.extend = false;
6078
6079 if (typeof text == "string") { text = this.splitLines(text); }
6080 updateDoc(this, {from: start, to: start, text: text});
6081 setSelection(this, simpleSelection(start), sel_dontScroll);
6082 };
6083
6084 Doc.prototype = createObj(BranchChunk.prototype, {
6085 constructor: Doc,
6086 // Iterate over the document. Supports two forms -- with only one
6087 // argument, it calls that for each line in the document. With
6088 // three, it iterates over the range given by the first two (with
6089 // the second being non-inclusive).
6090 iter: function(from, to, op) {
6091 if (op) { this.iterN(from - this.first, to - from, op); }
6092 else { this.iterN(this.first, this.first + this.size, from); }
6093 },
6094
6095 // Non-public interface for adding and removing lines.
6096 insert: function(at, lines) {
6097 var height = 0;
6098 for (var i = 0; i < lines.length; ++i) { height += lines[i].height; }
6099 this.insertInner(at - this.first, lines, height);
6100 },
6101 remove: function(at, n) { this.removeInner(at - this.first, n); },
6102
6103 // From here, the methods are part of the public interface. Most
6104 // are also available from CodeMirror (editor) instances.
6105
6106 getValue: function(lineSep) {
6107 var lines = getLines(this, this.first, this.first + this.size);
6108 if (lineSep === false) { return lines }
6109 return lines.join(lineSep || this.lineSeparator())
6110 },
6111 setValue: docMethodOp(function(code) {
6112 var top = Pos(this.first, 0), last = this.first + this.size - 1;
6113 makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
6114 text: this.splitLines(code), origin: "setValue", full: true}, true);
6115 if (this.cm) { scrollToCoords(this.cm, 0, 0); }
6116 setSelection(this, simpleSelection(top), sel_dontScroll);
6117 }),
6118 replaceRange: function(code, from, to, origin) {
6119 from = clipPos(this, from);
6120 to = to ? clipPos(this, to) : from;
6121 replaceRange(this, code, from, to, origin);
6122 },
6123 getRange: function(from, to, lineSep) {
6124 var lines = getBetween(this, clipPos(this, from), clipPos(this, to));
6125 if (lineSep === false) { return lines }
6126 return lines.join(lineSep || this.lineSeparator())
6127 },
6128
6129 getLine: function(line) {var l = this.getLineHandle(line); return l && l.text},
6130
6131 getLineHandle: function(line) {if (isLine(this, line)) { return getLine(this, line) }},
6132 getLineNumber: function(line) {return lineNo(line)},
6133
6134 getLineHandleVisualStart: function(line) {
6135 if (typeof line == "number") { line = getLine(this, line); }
6136 return visualLine(line)
6137 },
6138
6139 lineCount: function() {return this.size},
6140 firstLine: function() {return this.first},
6141 lastLine: function() {return this.first + this.size - 1},
6142
6143 clipPos: function(pos) {return clipPos(this, pos)},
6144
6145 getCursor: function(start) {
6146 var range$$1 = this.sel.primary(), pos;
6147 if (start == null || start == "head") { pos = range$$1.head; }
6148 else if (start == "anchor") { pos = range$$1.anchor; }
6149 else if (start == "end" || start == "to" || start === false) { pos = range$$1.to(); }
6150 else { pos = range$$1.from(); }
6151 return pos
6152 },
6153 listSelections: function() { return this.sel.ranges },
6154 somethingSelected: function() {return this.sel.somethingSelected()},
6155
6156 setCursor: docMethodOp(function(line, ch, options) {
6157 setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options);
6158 }),
6159 setSelection: docMethodOp(function(anchor, head, options) {
6160 setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options);
6161 }),
6162 extendSelection: docMethodOp(function(head, other, options) {
6163 extendSelection(this, clipPos(this, head), other && clipPos(this, other), options);
6164 }),
6165 extendSelections: docMethodOp(function(heads, options) {
6166 extendSelections(this, clipPosArray(this, heads), options);
6167 }),
6168 extendSelectionsBy: docMethodOp(function(f, options) {
6169 var heads = map(this.sel.ranges, f);
6170 extendSelections(this, clipPosArray(this, heads), options);
6171 }),
6172 setSelections: docMethodOp(function(ranges, primary, options) {
6173 var this$1 = this;
6174
6175 if (!ranges.length) { return }
6176 var out = [];
6177 for (var i = 0; i < ranges.length; i++)
6178 { out[i] = new Range(clipPos(this$1, ranges[i].anchor),
6179 clipPos(this$1, ranges[i].head)); }
6180 if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex); }
6181 setSelection(this, normalizeSelection(this.cm, out, primary), options);
6182 }),
6183 addSelection: docMethodOp(function(anchor, head, options) {
6184 var ranges = this.sel.ranges.slice(0);
6185 ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)));
6186 setSelection(this, normalizeSelection(this.cm, ranges, ranges.length - 1), options);
6187 }),
6188
6189 getSelection: function(lineSep) {
6190 var this$1 = this;
6191
6192 var ranges = this.sel.ranges, lines;
6193 for (var i = 0; i < ranges.length; i++) {
6194 var sel = getBetween(this$1, ranges[i].from(), ranges[i].to());
6195 lines = lines ? lines.concat(sel) : sel;
6196 }
6197 if (lineSep === false) { return lines }
6198 else { return lines.join(lineSep || this.lineSeparator()) }
6199 },
6200 getSelections: function(lineSep) {
6201 var this$1 = this;
6202
6203 var parts = [], ranges = this.sel.ranges;
6204 for (var i = 0; i < ranges.length; i++) {
6205 var sel = getBetween(this$1, ranges[i].from(), ranges[i].to());
6206 if (lineSep !== false) { sel = sel.join(lineSep || this$1.lineSeparator()); }
6207 parts[i] = sel;
6208 }
6209 return parts
6210 },
6211 replaceSelection: function(code, collapse, origin) {
6212 var dup = [];
6213 for (var i = 0; i < this.sel.ranges.length; i++)
6214 { dup[i] = code; }
6215 this.replaceSelections(dup, collapse, origin || "+input");
6216 },
6217 replaceSelections: docMethodOp(function(code, collapse, origin) {
6218 var this$1 = this;
6219
6220 var changes = [], sel = this.sel;
6221 for (var i = 0; i < sel.ranges.length; i++) {
6222 var range$$1 = sel.ranges[i];
6223 changes[i] = {from: range$$1.from(), to: range$$1.to(), text: this$1.splitLines(code[i]), origin: origin};
6224 }
6225 var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse);
6226 for (var i$1 = changes.length - 1; i$1 >= 0; i$1--)
6227 { makeChange(this$1, changes[i$1]); }
6228 if (newSel) { setSelectionReplaceHistory(this, newSel); }
6229 else if (this.cm) { ensureCursorVisible(this.cm); }
6230 }),
6231 undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}),
6232 redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}),
6233 undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}),
6234 redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true);}),
6235
6236 setExtending: function(val) {this.extend = val;},
6237 getExtending: function() {return this.extend},
6238
6239 historySize: function() {
6240 var hist = this.history, done = 0, undone = 0;
6241 for (var i = 0; i < hist.done.length; i++) { if (!hist.done[i].ranges) { ++done; } }
6242 for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].ranges) { ++undone; } }
6243 return {undo: done, redo: undone}
6244 },
6245 clearHistory: function() {this.history = new History(this.history.maxGeneration);},
6246
6247 markClean: function() {
6248 this.cleanGeneration = this.changeGeneration(true);
6249 },
6250 changeGeneration: function(forceSplit) {
6251 if (forceSplit)
6252 { this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null; }
6253 return this.history.generation
6254 },
6255 isClean: function (gen) {
6256 return this.history.generation == (gen || this.cleanGeneration)
6257 },
6258
6259 getHistory: function() {
6260 return {done: copyHistoryArray(this.history.done),
6261 undone: copyHistoryArray(this.history.undone)}
6262 },
6263 setHistory: function(histData) {
6264 var hist = this.history = new History(this.history.maxGeneration);
6265 hist.done = copyHistoryArray(histData.done.slice(0), null, true);
6266 hist.undone = copyHistoryArray(histData.undone.slice(0), null, true);
6267 },
6268
6269 setGutterMarker: docMethodOp(function(line, gutterID, value) {
6270 return changeLine(this, line, "gutter", function (line) {
6271 var markers = line.gutterMarkers || (line.gutterMarkers = {});
6272 markers[gutterID] = value;
6273 if (!value && isEmpty(markers)) { line.gutterMarkers = null; }
6274 return true
6275 })
6276 }),
6277
6278 clearGutter: docMethodOp(function(gutterID) {
6279 var this$1 = this;
6280
6281 this.iter(function (line) {
6282 if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
6283 changeLine(this$1, line, "gutter", function () {
6284 line.gutterMarkers[gutterID] = null;
6285 if (isEmpty(line.gutterMarkers)) { line.gutterMarkers = null; }
6286 return true
6287 });
6288 }
6289 });
6290 }),
6291
6292 lineInfo: function(line) {
6293 var n;
6294 if (typeof line == "number") {
6295 if (!isLine(this, line)) { return null }
6296 n = line;
6297 line = getLine(this, line);
6298 if (!line) { return null }
6299 } else {
6300 n = lineNo(line);
6301 if (n == null) { return null }
6302 }
6303 return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
6304 textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
6305 widgets: line.widgets}
6306 },
6307
6308 addLineClass: docMethodOp(function(handle, where, cls) {
6309 return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
6310 var prop = where == "text" ? "textClass"
6311 : where == "background" ? "bgClass"
6312 : where == "gutter" ? "gutterClass" : "wrapClass";
6313 if (!line[prop]) { line[prop] = cls; }
6314 else if (classTest(cls).test(line[prop])) { return false }
6315 else { line[prop] += " " + cls; }
6316 return true
6317 })
6318 }),
6319 removeLineClass: docMethodOp(function(handle, where, cls) {
6320 return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
6321 var prop = where == "text" ? "textClass"
6322 : where == "background" ? "bgClass"
6323 : where == "gutter" ? "gutterClass" : "wrapClass";
6324 var cur = line[prop];
6325 if (!cur) { return false }
6326 else if (cls == null) { line[prop] = null; }
6327 else {
6328 var found = cur.match(classTest(cls));
6329 if (!found) { return false }
6330 var end = found.index + found[0].length;
6331 line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null;
6332 }
6333 return true
6334 })
6335 }),
6336
6337 addLineWidget: docMethodOp(function(handle, node, options) {
6338 return addLineWidget(this, handle, node, options)
6339 }),
6340 removeLineWidget: function(widget) { widget.clear(); },
6341
6342 markText: function(from, to, options) {
6343 return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range")
6344 },
6345 setBookmark: function(pos, options) {
6346 var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
6347 insertLeft: options && options.insertLeft,
6348 clearWhenEmpty: false, shared: options && options.shared,
6349 handleMouseEvents: options && options.handleMouseEvents};
6350 pos = clipPos(this, pos);
6351 return markText(this, pos, pos, realOpts, "bookmark")
6352 },
6353 findMarksAt: function(pos) {
6354 pos = clipPos(this, pos);
6355 var markers = [], spans = getLine(this, pos.line).markedSpans;
6356 if (spans) { for (var i = 0; i < spans.length; ++i) {
6357 var span = spans[i];
6358 if ((span.from == null || span.from <= pos.ch) &&
6359 (span.to == null || span.to >= pos.ch))
6360 { markers.push(span.marker.parent || span.marker); }
6361 } }
6362 return markers
6363 },
6364 findMarks: function(from, to, filter) {
6365 from = clipPos(this, from); to = clipPos(this, to);
6366 var found = [], lineNo$$1 = from.line;
6367 this.iter(from.line, to.line + 1, function (line) {
6368 var spans = line.markedSpans;
6369 if (spans) { for (var i = 0; i < spans.length; i++) {
6370 var span = spans[i];
6371 if (!(span.to != null && lineNo$$1 == from.line && from.ch >= span.to ||
6372 span.from == null && lineNo$$1 != from.line ||
6373 span.from != null && lineNo$$1 == to.line && span.from >= to.ch) &&
6374 (!filter || filter(span.marker)))
6375 { found.push(span.marker.parent || span.marker); }
6376 } }
6377 ++lineNo$$1;
6378 });
6379 return found
6380 },
6381 getAllMarks: function() {
6382 var markers = [];
6383 this.iter(function (line) {
6384 var sps = line.markedSpans;
6385 if (sps) { for (var i = 0; i < sps.length; ++i)
6386 { if (sps[i].from != null) { markers.push(sps[i].marker); } } }
6387 });
6388 return markers
6389 },
6390
6391 posFromIndex: function(off) {
6392 var ch, lineNo$$1 = this.first, sepSize = this.lineSeparator().length;
6393 this.iter(function (line) {
6394 var sz = line.text.length + sepSize;
6395 if (sz > off) { ch = off; return true }
6396 off -= sz;
6397 ++lineNo$$1;
6398 });
6399 return clipPos(this, Pos(lineNo$$1, ch))
6400 },
6401 indexFromPos: function (coords) {
6402 coords = clipPos(this, coords);
6403 var index = coords.ch;
6404 if (coords.line < this.first || coords.ch < 0) { return 0 }
6405 var sepSize = this.lineSeparator().length;
6406 this.iter(this.first, coords.line, function (line) { // iter aborts when callback returns a truthy value
6407 index += line.text.length + sepSize;
6408 });
6409 return index
6410 },
6411
6412 copy: function(copyHistory) {
6413 var doc = new Doc(getLines(this, this.first, this.first + this.size),
6414 this.modeOption, this.first, this.lineSep, this.direction);
6415 doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;
6416 doc.sel = this.sel;
6417 doc.extend = false;
6418 if (copyHistory) {
6419 doc.history.undoDepth = this.history.undoDepth;
6420 doc.setHistory(this.getHistory());
6421 }
6422 return doc
6423 },
6424
6425 linkedDoc: function(options) {
6426 if (!options) { options = {}; }
6427 var from = this.first, to = this.first + this.size;
6428 if (options.from != null && options.from > from) { from = options.from; }
6429 if (options.to != null && options.to < to) { to = options.to; }
6430 var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep, this.direction);
6431 if (options.sharedHist) { copy.history = this.history
6432 ; }(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});
6433 copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];
6434 copySharedMarkers(copy, findSharedMarkers(this));
6435 return copy
6436 },
6437 unlinkDoc: function(other) {
6438 var this$1 = this;
6439
6440 if (other instanceof CodeMirror) { other = other.doc; }
6441 if (this.linked) { for (var i = 0; i < this.linked.length; ++i) {
6442 var link = this$1.linked[i];
6443 if (link.doc != other) { continue }
6444 this$1.linked.splice(i, 1);
6445 other.unlinkDoc(this$1);
6446 detachSharedMarkers(findSharedMarkers(this$1));
6447 break
6448 } }
6449 // If the histories were shared, split them again
6450 if (other.history == this.history) {
6451 var splitIds = [other.id];
6452 linkedDocs(other, function (doc) { return splitIds.push(doc.id); }, true);
6453 other.history = new History(null);
6454 other.history.done = copyHistoryArray(this.history.done, splitIds);
6455 other.history.undone = copyHistoryArray(this.history.undone, splitIds);
6456 }
6457 },
6458 iterLinkedDocs: function(f) {linkedDocs(this, f);},
6459
6460 getMode: function() {return this.mode},
6461 getEditor: function() {return this.cm},
6462
6463 splitLines: function(str) {
6464 if (this.lineSep) { return str.split(this.lineSep) }
6465 return splitLinesAuto(str)
6466 },
6467 lineSeparator: function() { return this.lineSep || "\n" },
6468
6469 setDirection: docMethodOp(function (dir) {
6470 if (dir != "rtl") { dir = "ltr"; }
6471 if (dir == this.direction) { return }
6472 this.direction = dir;
6473 this.iter(function (line) { return line.order = null; });
6474 if (this.cm) { directionChanged(this.cm); }
6475 })
6476 });
6477
6478 // Public alias.
6479 Doc.prototype.eachLine = Doc.prototype.iter;
6480
6481 // Kludge to work around strange IE behavior where it'll sometimes
6482 // re-fire a series of drag-related events right after the drop (#1551)
6483 var lastDrop = 0;
6484
6485 function onDrop(e) {
6486 var cm = this;
6487 clearDragCursor(cm);
6488 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))
6489 { return }
6490 e_preventDefault(e);
6491 if (ie) { lastDrop = +new Date; }
6492 var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
6493 if (!pos || cm.isReadOnly()) { return }
6494 // Might be a file drop, in which case we simply extract the text
6495 // and insert it.
6496 if (files && files.length && window.FileReader && window.File) {
6497 var n = files.length, text = Array(n), read = 0;
6498 var loadFile = function (file, i) {
6499 if (cm.options.allowDropFileTypes &&
6500 indexOf(cm.options.allowDropFileTypes, file.type) == -1)
6501 { return }
6502
6503 var reader = new FileReader;
6504 reader.onload = operation(cm, function () {
6505 var content = reader.result;
6506 if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) { content = ""; }
6507 text[i] = content;
6508 if (++read == n) {
6509 pos = clipPos(cm.doc, pos);
6510 var change = {from: pos, to: pos,
6511 text: cm.doc.splitLines(text.join(cm.doc.lineSeparator())),
6512 origin: "paste"};
6513 makeChange(cm.doc, change);
6514 setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change)));
6515 }
6516 });
6517 reader.readAsText(file);
6518 };
6519 for (var i = 0; i < n; ++i) { loadFile(files[i], i); }
6520 } else { // Normal drop
6521 // Don't do a replace if the drop happened inside of the selected text.
6522 if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {
6523 cm.state.draggingText(e);
6524 // Ensure the editor is re-focused
6525 setTimeout(function () { return cm.display.input.focus(); }, 20);
6526 return
6527 }
6528 try {
6529 var text$1 = e.dataTransfer.getData("Text");
6530 if (text$1) {
6531 var selected;
6532 if (cm.state.draggingText && !cm.state.draggingText.copy)
6533 { selected = cm.listSelections(); }
6534 setSelectionNoUndo(cm.doc, simpleSelection(pos, pos));
6535 if (selected) { for (var i$1 = 0; i$1 < selected.length; ++i$1)
6536 { replaceRange(cm.doc, "", selected[i$1].anchor, selected[i$1].head, "drag"); } }
6537 cm.replaceSelection(text$1, "around", "paste");
6538 cm.display.input.focus();
6539 }
6540 }
6541 catch(e){}
6542 }
6543 }
6544
6545 function onDragStart(cm, e) {
6546 if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return }
6547 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return }
6548
6549 e.dataTransfer.setData("Text", cm.getSelection());
6550 e.dataTransfer.effectAllowed = "copyMove";
6551
6552 // Use dummy image instead of default browsers image.
6553 // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
6554 if (e.dataTransfer.setDragImage && !safari) {
6555 var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
6556 img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
6557 if (presto) {
6558 img.width = img.height = 1;
6559 cm.display.wrapper.appendChild(img);
6560 // Force a relayout, or Opera won't use our image for some obscure reason
6561 img._top = img.offsetTop;
6562 }
6563 e.dataTransfer.setDragImage(img, 0, 0);
6564 if (presto) { img.parentNode.removeChild(img); }
6565 }
6566 }
6567
6568 function onDragOver(cm, e) {
6569 var pos = posFromMouse(cm, e);
6570 if (!pos) { return }
6571 var frag = document.createDocumentFragment();
6572 drawSelectionCursor(cm, pos, frag);
6573 if (!cm.display.dragCursor) {
6574 cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors");
6575 cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv);
6576 }
6577 removeChildrenAndAdd(cm.display.dragCursor, frag);
6578 }
6579
6580 function clearDragCursor(cm) {
6581 if (cm.display.dragCursor) {
6582 cm.display.lineSpace.removeChild(cm.display.dragCursor);
6583 cm.display.dragCursor = null;
6584 }
6585 }
6586
6587 // These must be handled carefully, because naively registering a
6588 // handler for each editor will cause the editors to never be
6589 // garbage collected.
6590
6591 function forEachCodeMirror(f) {
6592 if (!document.getElementsByClassName) { return }
6593 var byClass = document.getElementsByClassName("CodeMirror"), editors = [];
6594 for (var i = 0; i < byClass.length; i++) {
6595 var cm = byClass[i].CodeMirror;
6596 if (cm) { editors.push(cm); }
6597 }
6598 if (editors.length) { editors[0].operation(function () {
6599 for (var i = 0; i < editors.length; i++) { f(editors[i]); }
6600 }); }
6601 }
6602
6603 var globalsRegistered = false;
6604 function ensureGlobalHandlers() {
6605 if (globalsRegistered) { return }
6606 registerGlobalHandlers();
6607 globalsRegistered = true;
6608 }
6609 function registerGlobalHandlers() {
6610 // When the window resizes, we need to refresh active editors.
6611 var resizeTimer;
6612 on(window, "resize", function () {
6613 if (resizeTimer == null) { resizeTimer = setTimeout(function () {
6614 resizeTimer = null;
6615 forEachCodeMirror(onResize);
6616 }, 100); }
6617 });
6618 // When the window loses focus, we want to show the editor as blurred
6619 on(window, "blur", function () { return forEachCodeMirror(onBlur); });
6620 }
6621 // Called when the window resizes
6622 function onResize(cm) {
6623 var d = cm.display;
6624 // Might be a text scaling operation, clear size caches.
6625 d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
6626 d.scrollbarsClipped = false;
6627 cm.setSize();
6628 }
6629
6630 var keyNames = {
6631 3: "Pause", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
6632 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
6633 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
6634 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod",
6635 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 127: "Delete", 145: "ScrollLock",
6636 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
6637 221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete",
6638 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert"
6639 };
6640
6641 // Number keys
6642 for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i); }
6643 // Alphabetic keys
6644 for (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1); }
6645 // Function keys
6646 for (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235] = "F" + i$2; }
6647
6648 var keyMap = {};
6649
6650 keyMap.basic = {
6651 "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
6652 "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
6653 "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore",
6654 "Tab": "defaultTab", "Shift-Tab": "indentAuto",
6655 "Enter": "newlineAndIndent", "Insert": "toggleOverwrite",
6656 "Esc": "singleSelection"
6657 };
6658 // Note that the save and find-related commands aren't defined by
6659 // default. User code or addons can define them. Unknown commands
6660 // are simply ignored.
6661 keyMap.pcDefault = {
6662 "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
6663 "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown",
6664 "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
6665 "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
6666 "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
6667 "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
6668 "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection",
6669 "fallthrough": "basic"
6670 };
6671 // Very basic readline/emacs-style bindings, which are standard on Mac.
6672 keyMap.emacsy = {
6673 "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
6674 "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
6675 "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
6676 "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars",
6677 "Ctrl-O": "openLine"
6678 };
6679 keyMap.macDefault = {
6680 "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
6681 "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
6682 "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore",
6683 "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
6684 "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
6685 "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight",
6686 "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd",
6687 "fallthrough": ["basic", "emacsy"]
6688 };
6689 keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
6690
6691 // KEYMAP DISPATCH
6692
6693 function normalizeKeyName(name) {
6694 var parts = name.split(/-(?!$)/);
6695 name = parts[parts.length - 1];
6696 var alt, ctrl, shift, cmd;
6697 for (var i = 0; i < parts.length - 1; i++) {
6698 var mod = parts[i];
6699 if (/^(cmd|meta|m)$/i.test(mod)) { cmd = true; }
6700 else if (/^a(lt)?$/i.test(mod)) { alt = true; }
6701 else if (/^(c|ctrl|control)$/i.test(mod)) { ctrl = true; }
6702 else if (/^s(hift)?$/i.test(mod)) { shift = true; }
6703 else { throw new Error("Unrecognized modifier name: " + mod) }
6704 }
6705 if (alt) { name = "Alt-" + name; }
6706 if (ctrl) { name = "Ctrl-" + name; }
6707 if (cmd) { name = "Cmd-" + name; }
6708 if (shift) { name = "Shift-" + name; }
6709 return name
6710 }
6711
6712 // This is a kludge to keep keymaps mostly working as raw objects
6713 // (backwards compatibility) while at the same time support features
6714 // like normalization and multi-stroke key bindings. It compiles a
6715 // new normalized keymap, and then updates the old object to reflect
6716 // this.
6717 function normalizeKeyMap(keymap) {
6718 var copy = {};
6719 for (var keyname in keymap) { if (keymap.hasOwnProperty(keyname)) {
6720 var value = keymap[keyname];
6721 if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) { continue }
6722 if (value == "...") { delete keymap[keyname]; continue }
6723
6724 var keys = map(keyname.split(" "), normalizeKeyName);
6725 for (var i = 0; i < keys.length; i++) {
6726 var val = (void 0), name = (void 0);
6727 if (i == keys.length - 1) {
6728 name = keys.join(" ");
6729 val = value;
6730 } else {
6731 name = keys.slice(0, i + 1).join(" ");
6732 val = "...";
6733 }
6734 var prev = copy[name];
6735 if (!prev) { copy[name] = val; }
6736 else if (prev != val) { throw new Error("Inconsistent bindings for " + name) }
6737 }
6738 delete keymap[keyname];
6739 } }
6740 for (var prop in copy) { keymap[prop] = copy[prop]; }
6741 return keymap
6742 }
6743
6744 function lookupKey(key, map$$1, handle, context) {
6745 map$$1 = getKeyMap(map$$1);
6746 var found = map$$1.call ? map$$1.call(key, context) : map$$1[key];
6747 if (found === false) { return "nothing" }
6748 if (found === "...") { return "multi" }
6749 if (found != null && handle(found)) { return "handled" }
6750
6751 if (map$$1.fallthrough) {
6752 if (Object.prototype.toString.call(map$$1.fallthrough) != "[object Array]")
6753 { return lookupKey(key, map$$1.fallthrough, handle, context) }
6754 for (var i = 0; i < map$$1.fallthrough.length; i++) {
6755 var result = lookupKey(key, map$$1.fallthrough[i], handle, context);
6756 if (result) { return result }
6757 }
6758 }
6759 }
6760
6761 // Modifier key presses don't count as 'real' key presses for the
6762 // purpose of keymap fallthrough.
6763 function isModifierKey(value) {
6764 var name = typeof value == "string" ? value : keyNames[value.keyCode];
6765 return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod"
6766 }
6767
6768 function addModifierNames(name, event, noShift) {
6769 var base = name;
6770 if (event.altKey && base != "Alt") { name = "Alt-" + name; }
6771 if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") { name = "Ctrl-" + name; }
6772 if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") { name = "Cmd-" + name; }
6773 if (!noShift && event.shiftKey && base != "Shift") { name = "Shift-" + name; }
6774 return name
6775 }
6776
6777 // Look up the name of a key as indicated by an event object.
6778 function keyName(event, noShift) {
6779 if (presto && event.keyCode == 34 && event["char"]) { return false }
6780 var name = keyNames[event.keyCode];
6781 if (name == null || event.altGraphKey) { return false }
6782 // Ctrl-ScrollLock has keyCode 3, same as Ctrl-Pause,
6783 // so we'll use event.code when available (Chrome 48+, FF 38+, Safari 10.1+)
6784 if (event.keyCode == 3 && event.code) { name = event.code; }
6785 return addModifierNames(name, event, noShift)
6786 }
6787
6788 function getKeyMap(val) {
6789 return typeof val == "string" ? keyMap[val] : val
6790 }
6791
6792 // Helper for deleting text near the selection(s), used to implement
6793 // backspace, delete, and similar functionality.
6794 function deleteNearSelection(cm, compute) {
6795 var ranges = cm.doc.sel.ranges, kill = [];
6796 // Build up a set of ranges to kill first, merging overlapping
6797 // ranges.
6798 for (var i = 0; i < ranges.length; i++) {
6799 var toKill = compute(ranges[i]);
6800 while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {
6801 var replaced = kill.pop();
6802 if (cmp(replaced.from, toKill.from) < 0) {
6803 toKill.from = replaced.from;
6804 break
6805 }
6806 }
6807 kill.push(toKill);
6808 }
6809 // Next, remove those actual ranges.
6810 runInOp(cm, function () {
6811 for (var i = kill.length - 1; i >= 0; i--)
6812 { replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete"); }
6813 ensureCursorVisible(cm);
6814 });
6815 }
6816
6817 function moveCharLogically(line, ch, dir) {
6818 var target = skipExtendingChars(line.text, ch + dir, dir);
6819 return target < 0 || target > line.text.length ? null : target
6820 }
6821
6822 function moveLogically(line, start, dir) {
6823 var ch = moveCharLogically(line, start.ch, dir);
6824 return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before")
6825 }
6826
6827 function endOfLine(visually, cm, lineObj, lineNo, dir) {
6828 if (visually) {
6829 var order = getOrder(lineObj, cm.doc.direction);
6830 if (order) {
6831 var part = dir < 0 ? lst(order) : order[0];
6832 var moveInStorageOrder = (dir < 0) == (part.level == 1);
6833 var sticky = moveInStorageOrder ? "after" : "before";
6834 var ch;
6835 // With a wrapped rtl chunk (possibly spanning multiple bidi parts),
6836 // it could be that the last bidi part is not on the last visual line,
6837 // since visual lines contain content order-consecutive chunks.
6838 // Thus, in rtl, we are looking for the first (content-order) character
6839 // in the rtl chunk that is on the last line (that is, the same line
6840 // as the last (content-order) character).
6841 if (part.level > 0 || cm.doc.direction == "rtl") {
6842 var prep = prepareMeasureForLine(cm, lineObj);
6843 ch = dir < 0 ? lineObj.text.length - 1 : 0;
6844 var targetTop = measureCharPrepared(cm, prep, ch).top;
6845 ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch);
6846 if (sticky == "before") { ch = moveCharLogically(lineObj, ch, 1); }
6847 } else { ch = dir < 0 ? part.to : part.from; }
6848 return new Pos(lineNo, ch, sticky)
6849 }
6850 }
6851 return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after")
6852 }
6853
6854 function moveVisually(cm, line, start, dir) {
6855 var bidi = getOrder(line, cm.doc.direction);
6856 if (!bidi) { return moveLogically(line, start, dir) }
6857 if (start.ch >= line.text.length) {
6858 start.ch = line.text.length;
6859 start.sticky = "before";
6860 } else if (start.ch <= 0) {
6861 start.ch = 0;
6862 start.sticky = "after";
6863 }
6864 var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos];
6865 if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) {
6866 // Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines,
6867 // nothing interesting happens.
6868 return moveLogically(line, start, dir)
6869 }
6870
6871 var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); };
6872 var prep;
6873 var getWrappedLineExtent = function (ch) {
6874 if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} }
6875 prep = prep || prepareMeasureForLine(cm, line);
6876 return wrappedLineExtentChar(cm, line, prep, ch)
6877 };
6878 var wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch);
6879
6880 if (cm.doc.direction == "rtl" || part.level == 1) {
6881 var moveInStorageOrder = (part.level == 1) == (dir < 0);
6882 var ch = mv(start, moveInStorageOrder ? 1 : -1);
6883 if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) {
6884 // Case 2: We move within an rtl part or in an rtl editor on the same visual line
6885 var sticky = moveInStorageOrder ? "before" : "after";
6886 return new Pos(start.line, ch, sticky)
6887 }
6888 }
6889
6890 // Case 3: Could not move within this bidi part in this visual line, so leave
6891 // the current bidi part
6892
6893 var searchInVisualLine = function (partPos, dir, wrappedLineExtent) {
6894 var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder
6895 ? new Pos(start.line, mv(ch, 1), "before")
6896 : new Pos(start.line, ch, "after"); };
6897
6898 for (; partPos >= 0 && partPos < bidi.length; partPos += dir) {
6899 var part = bidi[partPos];
6900 var moveInStorageOrder = (dir > 0) == (part.level != 1);
6901 var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1);
6902 if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) }
6903 ch = moveInStorageOrder ? part.from : mv(part.to, -1);
6904 if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) }
6905 }
6906 };
6907
6908 // Case 3a: Look for other bidi parts on the same visual line
6909 var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent);
6910 if (res) { return res }
6911
6912 // Case 3b: Look for other bidi parts on the next visual line
6913 var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1);
6914 if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) {
6915 res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh));
6916 if (res) { return res }
6917 }
6918
6919 // Case 4: Nowhere to move
6920 return null
6921 }
6922
6923 // Commands are parameter-less actions that can be performed on an
6924 // editor, mostly used for keybindings.
6925 var commands = {
6926 selectAll: selectAll,
6927 singleSelection: function (cm) { return cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll); },
6928 killLine: function (cm) { return deleteNearSelection(cm, function (range) {
6929 if (range.empty()) {
6930 var len = getLine(cm.doc, range.head.line).text.length;
6931 if (range.head.ch == len && range.head.line < cm.lastLine())
6932 { return {from: range.head, to: Pos(range.head.line + 1, 0)} }
6933 else
6934 { return {from: range.head, to: Pos(range.head.line, len)} }
6935 } else {
6936 return {from: range.from(), to: range.to()}
6937 }
6938 }); },
6939 deleteLine: function (cm) { return deleteNearSelection(cm, function (range) { return ({
6940 from: Pos(range.from().line, 0),
6941 to: clipPos(cm.doc, Pos(range.to().line + 1, 0))
6942 }); }); },
6943 delLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { return ({
6944 from: Pos(range.from().line, 0), to: range.from()
6945 }); }); },
6946 delWrappedLineLeft: function (cm) { return deleteNearSelection(cm, function (range) {
6947 var top = cm.charCoords(range.head, "div").top + 5;
6948 var leftPos = cm.coordsChar({left: 0, top: top}, "div");
6949 return {from: leftPos, to: range.from()}
6950 }); },
6951 delWrappedLineRight: function (cm) { return deleteNearSelection(cm, function (range) {
6952 var top = cm.charCoords(range.head, "div").top + 5;
6953 var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div");
6954 return {from: range.from(), to: rightPos }
6955 }); },
6956 undo: function (cm) { return cm.undo(); },
6957 redo: function (cm) { return cm.redo(); },
6958 undoSelection: function (cm) { return cm.undoSelection(); },
6959 redoSelection: function (cm) { return cm.redoSelection(); },
6960 goDocStart: function (cm) { return cm.extendSelection(Pos(cm.firstLine(), 0)); },
6961 goDocEnd: function (cm) { return cm.extendSelection(Pos(cm.lastLine())); },
6962 goLineStart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStart(cm, range.head.line); },
6963 {origin: "+move", bias: 1}
6964 ); },
6965 goLineStartSmart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStartSmart(cm, range.head); },
6966 {origin: "+move", bias: 1}
6967 ); },
6968 goLineEnd: function (cm) { return cm.extendSelectionsBy(function (range) { return lineEnd(cm, range.head.line); },
6969 {origin: "+move", bias: -1}
6970 ); },
6971 goLineRight: function (cm) { return cm.extendSelectionsBy(function (range) {
6972 var top = cm.cursorCoords(range.head, "div").top + 5;
6973 return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div")
6974 }, sel_move); },
6975 goLineLeft: function (cm) { return cm.extendSelectionsBy(function (range) {
6976 var top = cm.cursorCoords(range.head, "div").top + 5;
6977 return cm.coordsChar({left: 0, top: top}, "div")
6978 }, sel_move); },
6979 goLineLeftSmart: function (cm) { return cm.extendSelectionsBy(function (range) {
6980 var top = cm.cursorCoords(range.head, "div").top + 5;
6981 var pos = cm.coordsChar({left: 0, top: top}, "div");
6982 if (pos.ch < cm.getLine(pos.line).search(/\S/)) { return lineStartSmart(cm, range.head) }
6983 return pos
6984 }, sel_move); },
6985 goLineUp: function (cm) { return cm.moveV(-1, "line"); },
6986 goLineDown: function (cm) { return cm.moveV(1, "line"); },
6987 goPageUp: function (cm) { return cm.moveV(-1, "page"); },
6988 goPageDown: function (cm) { return cm.moveV(1, "page"); },
6989 goCharLeft: function (cm) { return cm.moveH(-1, "char"); },
6990 goCharRight: function (cm) { return cm.moveH(1, "char"); },
6991 goColumnLeft: function (cm) { return cm.moveH(-1, "column"); },
6992 goColumnRight: function (cm) { return cm.moveH(1, "column"); },
6993 goWordLeft: function (cm) { return cm.moveH(-1, "word"); },
6994 goGroupRight: function (cm) { return cm.moveH(1, "group"); },
6995 goGroupLeft: function (cm) { return cm.moveH(-1, "group"); },
6996 goWordRight: function (cm) { return cm.moveH(1, "word"); },
6997 delCharBefore: function (cm) { return cm.deleteH(-1, "char"); },
6998 delCharAfter: function (cm) { return cm.deleteH(1, "char"); },
6999 delWordBefore: function (cm) { return cm.deleteH(-1, "word"); },
7000 delWordAfter: function (cm) { return cm.deleteH(1, "word"); },
7001 delGroupBefore: function (cm) { return cm.deleteH(-1, "group"); },
7002 delGroupAfter: function (cm) { return cm.deleteH(1, "group"); },
7003 indentAuto: function (cm) { return cm.indentSelection("smart"); },
7004 indentMore: function (cm) { return cm.indentSelection("add"); },
7005 indentLess: function (cm) { return cm.indentSelection("subtract"); },
7006 insertTab: function (cm) { return cm.replaceSelection("\t"); },
7007 insertSoftTab: function (cm) {
7008 var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize;
7009 for (var i = 0; i < ranges.length; i++) {
7010 var pos = ranges[i].from();
7011 var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize);
7012 spaces.push(spaceStr(tabSize - col % tabSize));
7013 }
7014 cm.replaceSelections(spaces);
7015 },
7016 defaultTab: function (cm) {
7017 if (cm.somethingSelected()) { cm.indentSelection("add"); }
7018 else { cm.execCommand("insertTab"); }
7019 },
7020 // Swap the two chars left and right of each selection's head.
7021 // Move cursor behind the two swapped characters afterwards.
7022 //
7023 // Doesn't consider line feeds a character.
7024 // Doesn't scan more than one line above to find a character.
7025 // Doesn't do anything on an empty line.
7026 // Doesn't do anything with non-empty selections.
7027 transposeChars: function (cm) { return runInOp(cm, function () {
7028 var ranges = cm.listSelections(), newSel = [];
7029 for (var i = 0; i < ranges.length; i++) {
7030 if (!ranges[i].empty()) { continue }
7031 var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text;
7032 if (line) {
7033 if (cur.ch == line.length) { cur = new Pos(cur.line, cur.ch - 1); }
7034 if (cur.ch > 0) {
7035 cur = new Pos(cur.line, cur.ch + 1);
7036 cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),
7037 Pos(cur.line, cur.ch - 2), cur, "+transpose");
7038 } else if (cur.line > cm.doc.first) {
7039 var prev = getLine(cm.doc, cur.line - 1).text;
7040 if (prev) {
7041 cur = new Pos(cur.line, 1);
7042 cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() +
7043 prev.charAt(prev.length - 1),
7044 Pos(cur.line - 1, prev.length - 1), cur, "+transpose");
7045 }
7046 }
7047 }
7048 newSel.push(new Range(cur, cur));
7049 }
7050 cm.setSelections(newSel);
7051 }); },
7052 newlineAndIndent: function (cm) { return runInOp(cm, function () {
7053 var sels = cm.listSelections();
7054 for (var i = sels.length - 1; i >= 0; i--)
7055 { cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input"); }
7056 sels = cm.listSelections();
7057 for (var i$1 = 0; i$1 < sels.length; i$1++)
7058 { cm.indentLine(sels[i$1].from().line, null, true); }
7059 ensureCursorVisible(cm);
7060 }); },
7061 openLine: function (cm) { return cm.replaceSelection("\n", "start"); },
7062 toggleOverwrite: function (cm) { return cm.toggleOverwrite(); }
7063 };
7064
7065
7066 function lineStart(cm, lineN) {
7067 var line = getLine(cm.doc, lineN);
7068 var visual = visualLine(line);
7069 if (visual != line) { lineN = lineNo(visual); }
7070 return endOfLine(true, cm, visual, lineN, 1)
7071 }
7072 function lineEnd(cm, lineN) {
7073 var line = getLine(cm.doc, lineN);
7074 var visual = visualLineEnd(line);
7075 if (visual != line) { lineN = lineNo(visual); }
7076 return endOfLine(true, cm, line, lineN, -1)
7077 }
7078 function lineStartSmart(cm, pos) {
7079 var start = lineStart(cm, pos.line);
7080 var line = getLine(cm.doc, start.line);
7081 var order = getOrder(line, cm.doc.direction);
7082 if (!order || order[0].level == 0) {
7083 var firstNonWS = Math.max(0, line.text.search(/\S/));
7084 var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch;
7085 return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky)
7086 }
7087 return start
7088 }
7089
7090 // Run a handler that was bound to a key.
7091 function doHandleBinding(cm, bound, dropShift) {
7092 if (typeof bound == "string") {
7093 bound = commands[bound];
7094 if (!bound) { return false }
7095 }
7096 // Ensure previous input has been read, so that the handler sees a
7097 // consistent view of the document
7098 cm.display.input.ensurePolled();
7099 var prevShift = cm.display.shift, done = false;
7100 try {
7101 if (cm.isReadOnly()) { cm.state.suppressEdits = true; }
7102 if (dropShift) { cm.display.shift = false; }
7103 done = bound(cm) != Pass;
7104 } finally {
7105 cm.display.shift = prevShift;
7106 cm.state.suppressEdits = false;
7107 }
7108 return done
7109 }
7110
7111 function lookupKeyForEditor(cm, name, handle) {
7112 for (var i = 0; i < cm.state.keyMaps.length; i++) {
7113 var result = lookupKey(name, cm.state.keyMaps[i], handle, cm);
7114 if (result) { return result }
7115 }
7116 return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm))
7117 || lookupKey(name, cm.options.keyMap, handle, cm)
7118 }
7119
7120 // Note that, despite the name, this function is also used to check
7121 // for bound mouse clicks.
7122
7123 var stopSeq = new Delayed;
7124
7125 function dispatchKey(cm, name, e, handle) {
7126 var seq = cm.state.keySeq;
7127 if (seq) {
7128 if (isModifierKey(name)) { return "handled" }
7129 if (/\'$/.test(name))
7130 { cm.state.keySeq = null; }
7131 else
7132 { stopSeq.set(50, function () {
7133 if (cm.state.keySeq == seq) {
7134 cm.state.keySeq = null;
7135 cm.display.input.reset();
7136 }
7137 }); }
7138 if (dispatchKeyInner(cm, seq + " " + name, e, handle)) { return true }
7139 }
7140 return dispatchKeyInner(cm, name, e, handle)
7141 }
7142
7143 function dispatchKeyInner(cm, name, e, handle) {
7144 var result = lookupKeyForEditor(cm, name, handle);
7145
7146 if (result == "multi")
7147 { cm.state.keySeq = name; }
7148 if (result == "handled")
7149 { signalLater(cm, "keyHandled", cm, name, e); }
7150
7151 if (result == "handled" || result == "multi") {
7152 e_preventDefault(e);
7153 restartBlink(cm);
7154 }
7155
7156 return !!result
7157 }
7158
7159 // Handle a key from the keydown event.
7160 function handleKeyBinding(cm, e) {
7161 var name = keyName(e, true);
7162 if (!name) { return false }
7163
7164 if (e.shiftKey && !cm.state.keySeq) {
7165 // First try to resolve full name (including 'Shift-'). Failing
7166 // that, see if there is a cursor-motion command (starting with
7167 // 'go') bound to the keyname without 'Shift-'.
7168 return dispatchKey(cm, "Shift-" + name, e, function (b) { return doHandleBinding(cm, b, true); })
7169 || dispatchKey(cm, name, e, function (b) {
7170 if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
7171 { return doHandleBinding(cm, b) }
7172 })
7173 } else {
7174 return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b); })
7175 }
7176 }
7177
7178 // Handle a key from the keypress event
7179 function handleCharBinding(cm, e, ch) {
7180 return dispatchKey(cm, "'" + ch + "'", e, function (b) { return doHandleBinding(cm, b, true); })
7181 }
7182
7183 var lastStoppedKey = null;
7184 function onKeyDown(e) {
7185 var cm = this;
7186 cm.curOp.focus = activeElt();
7187 if (signalDOMEvent(cm, e)) { return }
7188 // IE does strange things with escape.
7189 if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false; }
7190 var code = e.keyCode;
7191 cm.display.shift = code == 16 || e.shiftKey;
7192 var handled = handleKeyBinding(cm, e);
7193 if (presto) {
7194 lastStoppedKey = handled ? code : null;
7195 // Opera has no cut event... we try to at least catch the key combo
7196 if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
7197 { cm.replaceSelection("", null, "cut"); }
7198 }
7199
7200 // Turn mouse into crosshair when Alt is held on Mac.
7201 if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className))
7202 { showCrossHair(cm); }
7203 }
7204
7205 function showCrossHair(cm) {
7206 var lineDiv = cm.display.lineDiv;
7207 addClass(lineDiv, "CodeMirror-crosshair");
7208
7209 function up(e) {
7210 if (e.keyCode == 18 || !e.altKey) {
7211 rmClass(lineDiv, "CodeMirror-crosshair");
7212 off(document, "keyup", up);
7213 off(document, "mouseover", up);
7214 }
7215 }
7216 on(document, "keyup", up);
7217 on(document, "mouseover", up);
7218 }
7219
7220 function onKeyUp(e) {
7221 if (e.keyCode == 16) { this.doc.sel.shift = false; }
7222 signalDOMEvent(this, e);
7223 }
7224
7225 function onKeyPress(e) {
7226 var cm = this;
7227 if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return }
7228 var keyCode = e.keyCode, charCode = e.charCode;
7229 if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return}
7230 if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) { return }
7231 var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
7232 // Some browsers fire keypress events for backspace
7233 if (ch == "\x08") { return }
7234 if (handleCharBinding(cm, e, ch)) { return }
7235 cm.display.input.onKeyPress(e);
7236 }
7237
7238 var DOUBLECLICK_DELAY = 400;
7239
7240 var PastClick = function(time, pos, button) {
7241 this.time = time;
7242 this.pos = pos;
7243 this.button = button;
7244 };
7245
7246 PastClick.prototype.compare = function (time, pos, button) {
7247 return this.time + DOUBLECLICK_DELAY > time &&
7248 cmp(pos, this.pos) == 0 && button == this.button
7249 };
7250
7251 var lastClick, lastDoubleClick;
7252 function clickRepeat(pos, button) {
7253 var now = +new Date;
7254 if (lastDoubleClick && lastDoubleClick.compare(now, pos, button)) {
7255 lastClick = lastDoubleClick = null;
7256 return "triple"
7257 } else if (lastClick && lastClick.compare(now, pos, button)) {
7258 lastDoubleClick = new PastClick(now, pos, button);
7259 lastClick = null;
7260 return "double"
7261 } else {
7262 lastClick = new PastClick(now, pos, button);
7263 lastDoubleClick = null;
7264 return "single"
7265 }
7266 }
7267
7268 // A mouse down can be a single click, double click, triple click,
7269 // start of selection drag, start of text drag, new cursor
7270 // (ctrl-click), rectangle drag (alt-drag), or xwin
7271 // middle-click-paste. Or it might be a click on something we should
7272 // not interfere with, such as a scrollbar or widget.
7273 function onMouseDown(e) {
7274 var cm = this, display = cm.display;
7275 if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) { return }
7276 display.input.ensurePolled();
7277 display.shift = e.shiftKey;
7278
7279 if (eventInWidget(display, e)) {
7280 if (!webkit) {
7281 // Briefly turn off draggability, to allow widgets to do
7282 // normal dragging things.
7283 display.scroller.draggable = false;
7284 setTimeout(function () { return display.scroller.draggable = true; }, 100);
7285 }
7286 return
7287 }
7288 if (clickInGutter(cm, e)) { return }
7289 var pos = posFromMouse(cm, e), button = e_button(e), repeat = pos ? clickRepeat(pos, button) : "single";
7290 window.focus();
7291
7292 // #3261: make sure, that we're not starting a second selection
7293 if (button == 1 && cm.state.selectingText)
7294 { cm.state.selectingText(e); }
7295
7296 if (pos && handleMappedButton(cm, button, pos, repeat, e)) { return }
7297
7298 if (button == 1) {
7299 if (pos) { leftButtonDown(cm, pos, repeat, e); }
7300 else if (e_target(e) == display.scroller) { e_preventDefault(e); }
7301 } else if (button == 2) {
7302 if (pos) { extendSelection(cm.doc, pos); }
7303 setTimeout(function () { return display.input.focus(); }, 20);
7304 } else if (button == 3) {
7305 if (captureRightClick) { cm.display.input.onContextMenu(e); }
7306 else { delayBlurEvent(cm); }
7307 }
7308 }
7309
7310 function handleMappedButton(cm, button, pos, repeat, event) {
7311 var name = "Click";
7312 if (repeat == "double") { name = "Double" + name; }
7313 else if (repeat == "triple") { name = "Triple" + name; }
7314 name = (button == 1 ? "Left" : button == 2 ? "Middle" : "Right") + name;
7315
7316 return dispatchKey(cm, addModifierNames(name, event), event, function (bound) {
7317 if (typeof bound == "string") { bound = commands[bound]; }
7318 if (!bound) { return false }
7319 var done = false;
7320 try {
7321 if (cm.isReadOnly()) { cm.state.suppressEdits = true; }
7322 done = bound(cm, pos) != Pass;
7323 } finally {
7324 cm.state.suppressEdits = false;
7325 }
7326 return done
7327 })
7328 }
7329
7330 function configureMouse(cm, repeat, event) {
7331 var option = cm.getOption("configureMouse");
7332 var value = option ? option(cm, repeat, event) : {};
7333 if (value.unit == null) {
7334 var rect = chromeOS ? event.shiftKey && event.metaKey : event.altKey;
7335 value.unit = rect ? "rectangle" : repeat == "single" ? "char" : repeat == "double" ? "word" : "line";
7336 }
7337 if (value.extend == null || cm.doc.extend) { value.extend = cm.doc.extend || event.shiftKey; }
7338 if (value.addNew == null) { value.addNew = mac ? event.metaKey : event.ctrlKey; }
7339 if (value.moveOnDrag == null) { value.moveOnDrag = !(mac ? event.altKey : event.ctrlKey); }
7340 return value
7341 }
7342
7343 function leftButtonDown(cm, pos, repeat, event) {
7344 if (ie) { setTimeout(bind(ensureFocus, cm), 0); }
7345 else { cm.curOp.focus = activeElt(); }
7346
7347 var behavior = configureMouse(cm, repeat, event);
7348
7349 var sel = cm.doc.sel, contained;
7350 if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() &&
7351 repeat == "single" && (contained = sel.contains(pos)) > -1 &&
7352 (cmp((contained = sel.ranges[contained]).from(), pos) < 0 || pos.xRel > 0) &&
7353 (cmp(contained.to(), pos) > 0 || pos.xRel < 0))
7354 { leftButtonStartDrag(cm, event, pos, behavior); }
7355 else
7356 { leftButtonSelect(cm, event, pos, behavior); }
7357 }
7358
7359 // Start a text drag. When it ends, see if any dragging actually
7360 // happen, and treat as a click if it didn't.
7361 function leftButtonStartDrag(cm, event, pos, behavior) {
7362 var display = cm.display, moved = false;
7363 var dragEnd = operation(cm, function (e) {
7364 if (webkit) { display.scroller.draggable = false; }
7365 cm.state.draggingText = false;
7366 off(display.wrapper.ownerDocument, "mouseup", dragEnd);
7367 off(display.wrapper.ownerDocument, "mousemove", mouseMove);
7368 off(display.scroller, "dragstart", dragStart);
7369 off(display.scroller, "drop", dragEnd);
7370 if (!moved) {
7371 e_preventDefault(e);
7372 if (!behavior.addNew)
7373 { extendSelection(cm.doc, pos, null, null, behavior.extend); }
7374 // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)
7375 if (webkit || ie && ie_version == 9)
7376 { setTimeout(function () {display.wrapper.ownerDocument.body.focus(); display.input.focus();}, 20); }
7377 else
7378 { display.input.focus(); }
7379 }
7380 });
7381 var mouseMove = function(e2) {
7382 moved = moved || Math.abs(event.clientX - e2.clientX) + Math.abs(event.clientY - e2.clientY) >= 10;
7383 };
7384 var dragStart = function () { return moved = true; };
7385 // Let the drag handler handle this.
7386 if (webkit) { display.scroller.draggable = true; }
7387 cm.state.draggingText = dragEnd;
7388 dragEnd.copy = !behavior.moveOnDrag;
7389 // IE's approach to draggable
7390 if (display.scroller.dragDrop) { display.scroller.dragDrop(); }
7391 on(display.wrapper.ownerDocument, "mouseup", dragEnd);
7392 on(display.wrapper.ownerDocument, "mousemove", mouseMove);
7393 on(display.scroller, "dragstart", dragStart);
7394 on(display.scroller, "drop", dragEnd);
7395
7396 delayBlurEvent(cm);
7397 setTimeout(function () { return display.input.focus(); }, 20);
7398 }
7399
7400 function rangeForUnit(cm, pos, unit) {
7401 if (unit == "char") { return new Range(pos, pos) }
7402 if (unit == "word") { return cm.findWordAt(pos) }
7403 if (unit == "line") { return new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) }
7404 var result = unit(cm, pos);
7405 return new Range(result.from, result.to)
7406 }
7407
7408 // Normal selection, as opposed to text dragging.
7409 function leftButtonSelect(cm, event, start, behavior) {
7410 var display = cm.display, doc = cm.doc;
7411 e_preventDefault(event);
7412
7413 var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges;
7414 if (behavior.addNew && !behavior.extend) {
7415 ourIndex = doc.sel.contains(start);
7416 if (ourIndex > -1)
7417 { ourRange = ranges[ourIndex]; }
7418 else
7419 { ourRange = new Range(start, start); }
7420 } else {
7421 ourRange = doc.sel.primary();
7422 ourIndex = doc.sel.primIndex;
7423 }
7424
7425 if (behavior.unit == "rectangle") {
7426 if (!behavior.addNew) { ourRange = new Range(start, start); }
7427 start = posFromMouse(cm, event, true, true);
7428 ourIndex = -1;
7429 } else {
7430 var range$$1 = rangeForUnit(cm, start, behavior.unit);
7431 if (behavior.extend)
7432 { ourRange = extendRange(ourRange, range$$1.anchor, range$$1.head, behavior.extend); }
7433 else
7434 { ourRange = range$$1; }
7435 }
7436
7437 if (!behavior.addNew) {
7438 ourIndex = 0;
7439 setSelection(doc, new Selection([ourRange], 0), sel_mouse);
7440 startSel = doc.sel;
7441 } else if (ourIndex == -1) {
7442 ourIndex = ranges.length;
7443 setSelection(doc, normalizeSelection(cm, ranges.concat([ourRange]), ourIndex),
7444 {scroll: false, origin: "*mouse"});
7445 } else if (ranges.length > 1 && ranges[ourIndex].empty() && behavior.unit == "char" && !behavior.extend) {
7446 setSelection(doc, normalizeSelection(cm, ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0),
7447 {scroll: false, origin: "*mouse"});
7448 startSel = doc.sel;
7449 } else {
7450 replaceOneSelection(doc, ourIndex, ourRange, sel_mouse);
7451 }
7452
7453 var lastPos = start;
7454 function extendTo(pos) {
7455 if (cmp(lastPos, pos) == 0) { return }
7456 lastPos = pos;
7457
7458 if (behavior.unit == "rectangle") {
7459 var ranges = [], tabSize = cm.options.tabSize;
7460 var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize);
7461 var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize);
7462 var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol);
7463 for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line));
7464 line <= end; line++) {
7465 var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize);
7466 if (left == right)
7467 { ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))); }
7468 else if (text.length > leftPos)
7469 { ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))); }
7470 }
7471 if (!ranges.length) { ranges.push(new Range(start, start)); }
7472 setSelection(doc, normalizeSelection(cm, startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex),
7473 {origin: "*mouse", scroll: false});
7474 cm.scrollIntoView(pos);
7475 } else {
7476 var oldRange = ourRange;
7477 var range$$1 = rangeForUnit(cm, pos, behavior.unit);
7478 var anchor = oldRange.anchor, head;
7479 if (cmp(range$$1.anchor, anchor) > 0) {
7480 head = range$$1.head;
7481 anchor = minPos(oldRange.from(), range$$1.anchor);
7482 } else {
7483 head = range$$1.anchor;
7484 anchor = maxPos(oldRange.to(), range$$1.head);
7485 }
7486 var ranges$1 = startSel.ranges.slice(0);
7487 ranges$1[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc, anchor), head));
7488 setSelection(doc, normalizeSelection(cm, ranges$1, ourIndex), sel_mouse);
7489 }
7490 }
7491
7492 var editorSize = display.wrapper.getBoundingClientRect();
7493 // Used to ensure timeout re-tries don't fire when another extend
7494 // happened in the meantime (clearTimeout isn't reliable -- at
7495 // least on Chrome, the timeouts still happen even when cleared,
7496 // if the clear happens after their scheduled firing time).
7497 var counter = 0;
7498
7499 function extend(e) {
7500 var curCount = ++counter;
7501 var cur = posFromMouse(cm, e, true, behavior.unit == "rectangle");
7502 if (!cur) { return }
7503 if (cmp(cur, lastPos) != 0) {
7504 cm.curOp.focus = activeElt();
7505 extendTo(cur);
7506 var visible = visibleLines(display, doc);
7507 if (cur.line >= visible.to || cur.line < visible.from)
7508 { setTimeout(operation(cm, function () {if (counter == curCount) { extend(e); }}), 150); }
7509 } else {
7510 var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;
7511 if (outside) { setTimeout(operation(cm, function () {
7512 if (counter != curCount) { return }
7513 display.scroller.scrollTop += outside;
7514 extend(e);
7515 }), 50); }
7516 }
7517 }
7518
7519 function done(e) {
7520 cm.state.selectingText = false;
7521 counter = Infinity;
7522 e_preventDefault(e);
7523 display.input.focus();
7524 off(display.wrapper.ownerDocument, "mousemove", move);
7525 off(display.wrapper.ownerDocument, "mouseup", up);
7526 doc.history.lastSelOrigin = null;
7527 }
7528
7529 var move = operation(cm, function (e) {
7530 if (e.buttons === 0 || !e_button(e)) { done(e); }
7531 else { extend(e); }
7532 });
7533 var up = operation(cm, done);
7534 cm.state.selectingText = up;
7535 on(display.wrapper.ownerDocument, "mousemove", move);
7536 on(display.wrapper.ownerDocument, "mouseup", up);
7537 }
7538
7539 // Used when mouse-selecting to adjust the anchor to the proper side
7540 // of a bidi jump depending on the visual position of the head.
7541 function bidiSimplify(cm, range$$1) {
7542 var anchor = range$$1.anchor;
7543 var head = range$$1.head;
7544 var anchorLine = getLine(cm.doc, anchor.line);
7545 if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) { return range$$1 }
7546 var order = getOrder(anchorLine);
7547 if (!order) { return range$$1 }
7548 var index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index];
7549 if (part.from != anchor.ch && part.to != anchor.ch) { return range$$1 }
7550 var boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1);
7551 if (boundary == 0 || boundary == order.length) { return range$$1 }
7552
7553 // Compute the relative visual position of the head compared to the
7554 // anchor (<0 is to the left, >0 to the right)
7555 var leftSide;
7556 if (head.line != anchor.line) {
7557 leftSide = (head.line - anchor.line) * (cm.doc.direction == "ltr" ? 1 : -1) > 0;
7558 } else {
7559 var headIndex = getBidiPartAt(order, head.ch, head.sticky);
7560 var dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1);
7561 if (headIndex == boundary - 1 || headIndex == boundary)
7562 { leftSide = dir < 0; }
7563 else
7564 { leftSide = dir > 0; }
7565 }
7566
7567 var usePart = order[boundary + (leftSide ? -1 : 0)];
7568 var from = leftSide == (usePart.level == 1);
7569 var ch = from ? usePart.from : usePart.to, sticky = from ? "after" : "before";
7570 return anchor.ch == ch && anchor.sticky == sticky ? range$$1 : new Range(new Pos(anchor.line, ch, sticky), head)
7571 }
7572
7573
7574 // Determines whether an event happened in the gutter, and fires the
7575 // handlers for the corresponding event.
7576 function gutterEvent(cm, e, type, prevent) {
7577 var mX, mY;
7578 if (e.touches) {
7579 mX = e.touches[0].clientX;
7580 mY = e.touches[0].clientY;
7581 } else {
7582 try { mX = e.clientX; mY = e.clientY; }
7583 catch(e) { return false }
7584 }
7585 if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false }
7586 if (prevent) { e_preventDefault(e); }
7587
7588 var display = cm.display;
7589 var lineBox = display.lineDiv.getBoundingClientRect();
7590
7591 if (mY > lineBox.bottom || !hasHandler(cm, type)) { return e_defaultPrevented(e) }
7592 mY -= lineBox.top - display.viewOffset;
7593
7594 for (var i = 0; i < cm.options.gutters.length; ++i) {
7595 var g = display.gutters.childNodes[i];
7596 if (g && g.getBoundingClientRect().right >= mX) {
7597 var line = lineAtHeight(cm.doc, mY);
7598 var gutter = cm.options.gutters[i];
7599 signal(cm, type, cm, line, gutter, e);
7600 return e_defaultPrevented(e)
7601 }
7602 }
7603 }
7604
7605 function clickInGutter(cm, e) {
7606 return gutterEvent(cm, e, "gutterClick", true)
7607 }
7608
7609 // CONTEXT MENU HANDLING
7610
7611 // To make the context menu work, we need to briefly unhide the
7612 // textarea (making it as unobtrusive as possible) to let the
7613 // right-click take effect on it.
7614 function onContextMenu(cm, e) {
7615 if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) { return }
7616 if (signalDOMEvent(cm, e, "contextmenu")) { return }
7617 if (!captureRightClick) { cm.display.input.onContextMenu(e); }
7618 }
7619
7620 function contextMenuInGutter(cm, e) {
7621 if (!hasHandler(cm, "gutterContextMenu")) { return false }
7622 return gutterEvent(cm, e, "gutterContextMenu", false)
7623 }
7624
7625 function themeChanged(cm) {
7626 cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
7627 cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
7628 clearCaches(cm);
7629 }
7630
7631 var Init = {toString: function(){return "CodeMirror.Init"}};
7632
7633 var defaults = {};
7634 var optionHandlers = {};
7635
7636 function defineOptions(CodeMirror) {
7637 var optionHandlers = CodeMirror.optionHandlers;
7638
7639 function option(name, deflt, handle, notOnInit) {
7640 CodeMirror.defaults[name] = deflt;
7641 if (handle) { optionHandlers[name] =
7642 notOnInit ? function (cm, val, old) {if (old != Init) { handle(cm, val, old); }} : handle; }
7643 }
7644
7645 CodeMirror.defineOption = option;
7646
7647 // Passed to option handlers when there is no old value.
7648 CodeMirror.Init = Init;
7649
7650 // These two are, on init, called from the constructor because they
7651 // have to be initialized before the editor can start at all.
7652 option("value", "", function (cm, val) { return cm.setValue(val); }, true);
7653 option("mode", null, function (cm, val) {
7654 cm.doc.modeOption = val;
7655 loadMode(cm);
7656 }, true);
7657
7658 option("indentUnit", 2, loadMode, true);
7659 option("indentWithTabs", false);
7660 option("smartIndent", true);
7661 option("tabSize", 4, function (cm) {
7662 resetModeState(cm);
7663 clearCaches(cm);
7664 regChange(cm);
7665 }, true);
7666
7667 option("lineSeparator", null, function (cm, val) {
7668 cm.doc.lineSep = val;
7669 if (!val) { return }
7670 var newBreaks = [], lineNo = cm.doc.first;
7671 cm.doc.iter(function (line) {
7672 for (var pos = 0;;) {
7673 var found = line.text.indexOf(val, pos);
7674 if (found == -1) { break }
7675 pos = found + val.length;
7676 newBreaks.push(Pos(lineNo, found));
7677 }
7678 lineNo++;
7679 });
7680 for (var i = newBreaks.length - 1; i >= 0; i--)
7681 { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)); }
7682 });
7683 option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff]/g, function (cm, val, old) {
7684 cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g");
7685 if (old != Init) { cm.refresh(); }
7686 });
7687 option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function (cm) { return cm.refresh(); }, true);
7688 option("electricChars", true);
7689 option("inputStyle", mobile ? "contenteditable" : "textarea", function () {
7690 throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME
7691 }, true);
7692 option("spellcheck", false, function (cm, val) { return cm.getInputField().spellcheck = val; }, true);
7693 option("rtlMoveVisually", !windows);
7694 option("wholeLineUpdateBefore", true);
7695
7696 option("theme", "default", function (cm) {
7697 themeChanged(cm);
7698 guttersChanged(cm);
7699 }, true);
7700 option("keyMap", "default", function (cm, val, old) {
7701 var next = getKeyMap(val);
7702 var prev = old != Init && getKeyMap(old);
7703 if (prev && prev.detach) { prev.detach(cm, next); }
7704 if (next.attach) { next.attach(cm, prev || null); }
7705 });
7706 option("extraKeys", null);
7707 option("configureMouse", null);
7708
7709 option("lineWrapping", false, wrappingChanged, true);
7710 option("gutters", [], function (cm) {
7711 setGuttersForLineNumbers(cm.options);
7712 guttersChanged(cm);
7713 }, true);
7714 option("fixedGutter", true, function (cm, val) {
7715 cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
7716 cm.refresh();
7717 }, true);
7718 option("coverGutterNextToScrollbar", false, function (cm) { return updateScrollbars(cm); }, true);
7719 option("scrollbarStyle", "native", function (cm) {
7720 initScrollbars(cm);
7721 updateScrollbars(cm);
7722 cm.display.scrollbars.setScrollTop(cm.doc.scrollTop);
7723 cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft);
7724 }, true);
7725 option("lineNumbers", false, function (cm) {
7726 setGuttersForLineNumbers(cm.options);
7727 guttersChanged(cm);
7728 }, true);
7729 option("firstLineNumber", 1, guttersChanged, true);
7730 option("lineNumberFormatter", function (integer) { return integer; }, guttersChanged, true);
7731 option("showCursorWhenSelecting", false, updateSelection, true);
7732
7733 option("resetSelectionOnContextMenu", true);
7734 option("lineWiseCopyCut", true);
7735 option("pasteLinesPerSelection", true);
7736 option("selectionsMayTouch", false);
7737
7738 option("readOnly", false, function (cm, val) {
7739 if (val == "nocursor") {
7740 onBlur(cm);
7741 cm.display.input.blur();
7742 }
7743 cm.display.input.readOnlyChanged(val);
7744 });
7745 option("disableInput", false, function (cm, val) {if (!val) { cm.display.input.reset(); }}, true);
7746 option("dragDrop", true, dragDropChanged);
7747 option("allowDropFileTypes", null);
7748
7749 option("cursorBlinkRate", 530);
7750 option("cursorScrollMargin", 0);
7751 option("cursorHeight", 1, updateSelection, true);
7752 option("singleCursorHeightPerLine", true, updateSelection, true);
7753 option("workTime", 100);
7754 option("workDelay", 100);
7755 option("flattenSpans", true, resetModeState, true);
7756 option("addModeClass", false, resetModeState, true);
7757 option("pollInterval", 100);
7758 option("undoDepth", 200, function (cm, val) { return cm.doc.history.undoDepth = val; });
7759 option("historyEventDelay", 1250);
7760 option("viewportMargin", 10, function (cm) { return cm.refresh(); }, true);
7761 option("maxHighlightLength", 10000, resetModeState, true);
7762 option("moveInputWithCursor", true, function (cm, val) {
7763 if (!val) { cm.display.input.resetPosition(); }
7764 });
7765
7766 option("tabindex", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || ""; });
7767 option("autofocus", null);
7768 option("direction", "ltr", function (cm, val) { return cm.doc.setDirection(val); }, true);
7769 option("phrases", null);
7770 }
7771
7772 function guttersChanged(cm) {
7773 updateGutters(cm);
7774 regChange(cm);
7775 alignHorizontally(cm);
7776 }
7777
7778 function dragDropChanged(cm, value, old) {
7779 var wasOn = old && old != Init;
7780 if (!value != !wasOn) {
7781 var funcs = cm.display.dragFunctions;
7782 var toggle = value ? on : off;
7783 toggle(cm.display.scroller, "dragstart", funcs.start);
7784 toggle(cm.display.scroller, "dragenter", funcs.enter);
7785 toggle(cm.display.scroller, "dragover", funcs.over);
7786 toggle(cm.display.scroller, "dragleave", funcs.leave);
7787 toggle(cm.display.scroller, "drop", funcs.drop);
7788 }
7789 }
7790
7791 function wrappingChanged(cm) {
7792 if (cm.options.lineWrapping) {
7793 addClass(cm.display.wrapper, "CodeMirror-wrap");
7794 cm.display.sizer.style.minWidth = "";
7795 cm.display.sizerWidth = null;
7796 } else {
7797 rmClass(cm.display.wrapper, "CodeMirror-wrap");
7798 findMaxLine(cm);
7799 }
7800 estimateLineHeights(cm);
7801 regChange(cm);
7802 clearCaches(cm);
7803 setTimeout(function () { return updateScrollbars(cm); }, 100);
7804 }
7805
7806 // A CodeMirror instance represents an editor. This is the object
7807 // that user code is usually dealing with.
7808
7809 function CodeMirror(place, options) {
7810 var this$1 = this;
7811
7812 if (!(this instanceof CodeMirror)) { return new CodeMirror(place, options) }
7813
7814 this.options = options = options ? copyObj(options) : {};
7815 // Determine effective options based on given values and defaults.
7816 copyObj(defaults, options, false);
7817 setGuttersForLineNumbers(options);
7818
7819 var doc = options.value;
7820 if (typeof doc == "string") { doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction); }
7821 else if (options.mode) { doc.modeOption = options.mode; }
7822 this.doc = doc;
7823
7824 var input = new CodeMirror.inputStyles[options.inputStyle](this);
7825 var display = this.display = new Display(place, doc, input);
7826 display.wrapper.CodeMirror = this;
7827 updateGutters(this);
7828 themeChanged(this);
7829 if (options.lineWrapping)
7830 { this.display.wrapper.className += " CodeMirror-wrap"; }
7831 initScrollbars(this);
7832
7833 this.state = {
7834 keyMaps: [], // stores maps added by addKeyMap
7835 overlays: [], // highlighting overlays, as added by addOverlay
7836 modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info
7837 overwrite: false,
7838 delayingBlurEvent: false,
7839 focused: false,
7840 suppressEdits: false, // used to disable editing during key handlers when in readOnly mode
7841 pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edits in input.poll
7842 selectingText: false,
7843 draggingText: false,
7844 highlight: new Delayed(), // stores highlight worker timeout
7845 keySeq: null, // Unfinished key sequence
7846 specialChars: null
7847 };
7848
7849 if (options.autofocus && !mobile) { display.input.focus(); }
7850
7851 // Override magic textarea content restore that IE sometimes does
7852 // on our hidden textarea on reload
7853 if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20); }
7854
7855 registerEventHandlers(this);
7856 ensureGlobalHandlers();
7857
7858 startOperation(this);
7859 this.curOp.forceUpdate = true;
7860 attachDoc(this, doc);
7861
7862 if ((options.autofocus && !mobile) || this.hasFocus())
7863 { setTimeout(bind(onFocus, this), 20); }
7864 else
7865 { onBlur(this); }
7866
7867 for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt))
7868 { optionHandlers[opt](this$1, options[opt], Init); } }
7869 maybeUpdateLineNumberWidth(this);
7870 if (options.finishInit) { options.finishInit(this); }
7871 for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this$1); }
7872 endOperation(this);
7873 // Suppress optimizelegibility in Webkit, since it breaks text
7874 // measuring on line wrapping boundaries.
7875 if (webkit && options.lineWrapping &&
7876 getComputedStyle(display.lineDiv).textRendering == "optimizelegibility")
7877 { display.lineDiv.style.textRendering = "auto"; }
7878 }
7879
7880 // The default configuration options.
7881 CodeMirror.defaults = defaults;
7882 // Functions to run when options are changed.
7883 CodeMirror.optionHandlers = optionHandlers;
7884
7885 // Attach the necessary event handlers when initializing the editor
7886 function registerEventHandlers(cm) {
7887 var d = cm.display;
7888 on(d.scroller, "mousedown", operation(cm, onMouseDown));
7889 // Older IE's will not fire a second mousedown for a double click
7890 if (ie && ie_version < 11)
7891 { on(d.scroller, "dblclick", operation(cm, function (e) {
7892 if (signalDOMEvent(cm, e)) { return }
7893 var pos = posFromMouse(cm, e);
7894 if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) { return }
7895 e_preventDefault(e);
7896 var word = cm.findWordAt(pos);
7897 extendSelection(cm.doc, word.anchor, word.head);
7898 })); }
7899 else
7900 { on(d.scroller, "dblclick", function (e) { return signalDOMEvent(cm, e) || e_preventDefault(e); }); }
7901 // Some browsers fire contextmenu *after* opening the menu, at
7902 // which point we can't mess with it anymore. Context menu is
7903 // handled in onMouseDown for these browsers.
7904 on(d.scroller, "contextmenu", function (e) { return onContextMenu(cm, e); });
7905
7906 // Used to suppress mouse event handling when a touch happens
7907 var touchFinished, prevTouch = {end: 0};
7908 function finishTouch() {
7909 if (d.activeTouch) {
7910 touchFinished = setTimeout(function () { return d.activeTouch = null; }, 1000);
7911 prevTouch = d.activeTouch;
7912 prevTouch.end = +new Date;
7913 }
7914 }
7915 function isMouseLikeTouchEvent(e) {
7916 if (e.touches.length != 1) { return false }
7917 var touch = e.touches[0];
7918 return touch.radiusX <= 1 && touch.radiusY <= 1
7919 }
7920 function farAway(touch, other) {
7921 if (other.left == null) { return true }
7922 var dx = other.left - touch.left, dy = other.top - touch.top;
7923 return dx * dx + dy * dy > 20 * 20
7924 }
7925 on(d.scroller, "touchstart", function (e) {
7926 if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e) && !clickInGutter(cm, e)) {
7927 d.input.ensurePolled();
7928 clearTimeout(touchFinished);
7929 var now = +new Date;
7930 d.activeTouch = {start: now, moved: false,
7931 prev: now - prevTouch.end <= 300 ? prevTouch : null};
7932 if (e.touches.length == 1) {
7933 d.activeTouch.left = e.touches[0].pageX;
7934 d.activeTouch.top = e.touches[0].pageY;
7935 }
7936 }
7937 });
7938 on(d.scroller, "touchmove", function () {
7939 if (d.activeTouch) { d.activeTouch.moved = true; }
7940 });
7941 on(d.scroller, "touchend", function (e) {
7942 var touch = d.activeTouch;
7943 if (touch && !eventInWidget(d, e) && touch.left != null &&
7944 !touch.moved && new Date - touch.start < 300) {
7945 var pos = cm.coordsChar(d.activeTouch, "page"), range;
7946 if (!touch.prev || farAway(touch, touch.prev)) // Single tap
7947 { range = new Range(pos, pos); }
7948 else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap
7949 { range = cm.findWordAt(pos); }
7950 else // Triple tap
7951 { range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))); }
7952 cm.setSelection(range.anchor, range.head);
7953 cm.focus();
7954 e_preventDefault(e);
7955 }
7956 finishTouch();
7957 });
7958 on(d.scroller, "touchcancel", finishTouch);
7959
7960 // Sync scrolling between fake scrollbars and real scrollable
7961 // area, ensure viewport is updated when scrolling.
7962 on(d.scroller, "scroll", function () {
7963 if (d.scroller.clientHeight) {
7964 updateScrollTop(cm, d.scroller.scrollTop);
7965 setScrollLeft(cm, d.scroller.scrollLeft, true);
7966 signal(cm, "scroll", cm);
7967 }
7968 });
7969
7970 // Listen to wheel events in order to try and update the viewport on time.
7971 on(d.scroller, "mousewheel", function (e) { return onScrollWheel(cm, e); });
7972 on(d.scroller, "DOMMouseScroll", function (e) { return onScrollWheel(cm, e); });
7973
7974 // Prevent wrapper from ever scrolling
7975 on(d.wrapper, "scroll", function () { return d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
7976
7977 d.dragFunctions = {
7978 enter: function (e) {if (!signalDOMEvent(cm, e)) { e_stop(e); }},
7979 over: function (e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e); }},
7980 start: function (e) { return onDragStart(cm, e); },
7981 drop: operation(cm, onDrop),
7982 leave: function (e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm); }}
7983 };
7984
7985 var inp = d.input.getField();
7986 on(inp, "keyup", function (e) { return onKeyUp.call(cm, e); });
7987 on(inp, "keydown", operation(cm, onKeyDown));
7988 on(inp, "keypress", operation(cm, onKeyPress));
7989 on(inp, "focus", function (e) { return onFocus(cm, e); });
7990 on(inp, "blur", function (e) { return onBlur(cm, e); });
7991 }
7992
7993 var initHooks = [];
7994 CodeMirror.defineInitHook = function (f) { return initHooks.push(f); };
7995
7996 // Indent the given line. The how parameter can be "smart",
7997 // "add"/null, "subtract", or "prev". When aggressive is false
7998 // (typically set to true for forced single-line indents), empty
7999 // lines are not indented, and places where the mode returns Pass
8000 // are left alone.
8001 function indentLine(cm, n, how, aggressive) {
8002 var doc = cm.doc, state;
8003 if (how == null) { how = "add"; }
8004 if (how == "smart") {
8005 // Fall back to "prev" when the mode doesn't have an indentation
8006 // method.
8007 if (!doc.mode.indent) { how = "prev"; }
8008 else { state = getContextBefore(cm, n).state; }
8009 }
8010
8011 var tabSize = cm.options.tabSize;
8012 var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
8013 if (line.stateAfter) { line.stateAfter = null; }
8014 var curSpaceString = line.text.match(/^\s*/)[0], indentation;
8015 if (!aggressive && !/\S/.test(line.text)) {
8016 indentation = 0;
8017 how = "not";
8018 } else if (how == "smart") {
8019 indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
8020 if (indentation == Pass || indentation > 150) {
8021 if (!aggressive) { return }
8022 how = "prev";
8023 }
8024 }
8025 if (how == "prev") {
8026 if (n > doc.first) { indentation = countColumn(getLine(doc, n-1).text, null, tabSize); }
8027 else { indentation = 0; }
8028 } else if (how == "add") {
8029 indentation = curSpace + cm.options.indentUnit;
8030 } else if (how == "subtract") {
8031 indentation = curSpace - cm.options.indentUnit;
8032 } else if (typeof how == "number") {
8033 indentation = curSpace + how;
8034 }
8035 indentation = Math.max(0, indentation);
8036
8037 var indentString = "", pos = 0;
8038 if (cm.options.indentWithTabs)
8039 { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";} }
8040 if (pos < indentation) { indentString += spaceStr(indentation - pos); }
8041
8042 if (indentString != curSpaceString) {
8043 replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
8044 line.stateAfter = null;
8045 return true
8046 } else {
8047 // Ensure that, if the cursor was in the whitespace at the start
8048 // of the line, it is moved to the end of that space.
8049 for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) {
8050 var range = doc.sel.ranges[i$1];
8051 if (range.head.line == n && range.head.ch < curSpaceString.length) {
8052 var pos$1 = Pos(n, curSpaceString.length);
8053 replaceOneSelection(doc, i$1, new Range(pos$1, pos$1));
8054 break
8055 }
8056 }
8057 }
8058 }
8059
8060 // This will be set to a {lineWise: bool, text: [string]} object, so
8061 // that, when pasting, we know what kind of selections the copied
8062 // text was made out of.
8063 var lastCopied = null;
8064
8065 function setLastCopied(newLastCopied) {
8066 lastCopied = newLastCopied;
8067 }
8068
8069 function applyTextInput(cm, inserted, deleted, sel, origin) {
8070 var doc = cm.doc;
8071 cm.display.shift = false;
8072 if (!sel) { sel = doc.sel; }
8073
8074 var paste = cm.state.pasteIncoming || origin == "paste";
8075 var textLines = splitLinesAuto(inserted), multiPaste = null;
8076 // When pasting N lines into N selections, insert one line per selection
8077 if (paste && sel.ranges.length > 1) {
8078 if (lastCopied && lastCopied.text.join("\n") == inserted) {
8079 if (sel.ranges.length % lastCopied.text.length == 0) {
8080 multiPaste = [];
8081 for (var i = 0; i < lastCopied.text.length; i++)
8082 { multiPaste.push(doc.splitLines(lastCopied.text[i])); }
8083 }
8084 } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) {
8085 multiPaste = map(textLines, function (l) { return [l]; });
8086 }
8087 }
8088
8089 var updateInput = cm.curOp.updateInput;
8090 // Normal behavior is to insert the new text into every selection
8091 for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) {
8092 var range$$1 = sel.ranges[i$1];
8093 var from = range$$1.from(), to = range$$1.to();
8094 if (range$$1.empty()) {
8095 if (deleted && deleted > 0) // Handle deletion
8096 { from = Pos(from.line, from.ch - deleted); }
8097 else if (cm.state.overwrite && !paste) // Handle overwrite
8098 { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); }
8099 else if (paste && lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == inserted)
8100 { from = to = Pos(from.line, 0); }
8101 }
8102 var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines,
8103 origin: origin || (paste ? "paste" : cm.state.cutIncoming ? "cut" : "+input")};
8104 makeChange(cm.doc, changeEvent);
8105 signalLater(cm, "inputRead", cm, changeEvent);
8106 }
8107 if (inserted && !paste)
8108 { triggerElectric(cm, inserted); }
8109
8110 ensureCursorVisible(cm);
8111 if (cm.curOp.updateInput < 2) { cm.curOp.updateInput = updateInput; }
8112 cm.curOp.typing = true;
8113 cm.state.pasteIncoming = cm.state.cutIncoming = false;
8114 }
8115
8116 function handlePaste(e, cm) {
8117 var pasted = e.clipboardData && e.clipboardData.getData("Text");
8118 if (pasted) {
8119 e.preventDefault();
8120 if (!cm.isReadOnly() && !cm.options.disableInput)
8121 { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, "paste"); }); }
8122 return true
8123 }
8124 }
8125
8126 function triggerElectric(cm, inserted) {
8127 // When an 'electric' character is inserted, immediately trigger a reindent
8128 if (!cm.options.electricChars || !cm.options.smartIndent) { return }
8129 var sel = cm.doc.sel;
8130
8131 for (var i = sel.ranges.length - 1; i >= 0; i--) {
8132 var range$$1 = sel.ranges[i];
8133 if (range$$1.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range$$1.head.line)) { continue }
8134 var mode = cm.getModeAt(range$$1.head);
8135 var indented = false;
8136 if (mode.electricChars) {
8137 for (var j = 0; j < mode.electricChars.length; j++)
8138 { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
8139 indented = indentLine(cm, range$$1.head.line, "smart");
8140 break
8141 } }
8142 } else if (mode.electricInput) {
8143 if (mode.electricInput.test(getLine(cm.doc, range$$1.head.line).text.slice(0, range$$1.head.ch)))
8144 { indented = indentLine(cm, range$$1.head.line, "smart"); }
8145 }
8146 if (indented) { signalLater(cm, "electricInput", cm, range$$1.head.line); }
8147 }
8148 }
8149
8150 function copyableRanges(cm) {
8151 var text = [], ranges = [];
8152 for (var i = 0; i < cm.doc.sel.ranges.length; i++) {
8153 var line = cm.doc.sel.ranges[i].head.line;
8154 var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)};
8155 ranges.push(lineRange);
8156 text.push(cm.getRange(lineRange.anchor, lineRange.head));
8157 }
8158 return {text: text, ranges: ranges}
8159 }
8160
8161 function disableBrowserMagic(field, spellcheck) {
8162 field.setAttribute("autocorrect", "off");
8163 field.setAttribute("autocapitalize", "off");
8164 field.setAttribute("spellcheck", !!spellcheck);
8165 }
8166
8167 function hiddenTextarea() {
8168 var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none");
8169 var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
8170 // The textarea is kept positioned near the cursor to prevent the
8171 // fact that it'll be scrolled into view on input from scrolling
8172 // our fake cursor out of view. On webkit, when wrap=off, paste is
8173 // very slow. So make the area wide instead.
8174 if (webkit) { te.style.width = "1000px"; }
8175 else { te.setAttribute("wrap", "off"); }
8176 // If border: 0; -- iOS fails to open keyboard (issue #1287)
8177 if (ios) { te.style.border = "1px solid black"; }
8178 disableBrowserMagic(te);
8179 return div
8180 }
8181
8182 // The publicly visible API. Note that methodOp(f) means
8183 // 'wrap f in an operation, performed on its `this` parameter'.
8184
8185 // This is not the complete set of editor methods. Most of the
8186 // methods defined on the Doc type are also injected into
8187 // CodeMirror.prototype, for backwards compatibility and
8188 // convenience.
8189
8190 function addEditorMethods(CodeMirror) {
8191 var optionHandlers = CodeMirror.optionHandlers;
8192
8193 var helpers = CodeMirror.helpers = {};
8194
8195 CodeMirror.prototype = {
8196 constructor: CodeMirror,
8197 focus: function(){window.focus(); this.display.input.focus();},
8198
8199 setOption: function(option, value) {
8200 var options = this.options, old = options[option];
8201 if (options[option] == value && option != "mode") { return }
8202 options[option] = value;
8203 if (optionHandlers.hasOwnProperty(option))
8204 { operation(this, optionHandlers[option])(this, value, old); }
8205 signal(this, "optionChange", this, option);
8206 },
8207
8208 getOption: function(option) {return this.options[option]},
8209 getDoc: function() {return this.doc},
8210
8211 addKeyMap: function(map$$1, bottom) {
8212 this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map$$1));
8213 },
8214 removeKeyMap: function(map$$1) {
8215 var maps = this.state.keyMaps;
8216 for (var i = 0; i < maps.length; ++i)
8217 { if (maps[i] == map$$1 || maps[i].name == map$$1) {
8218 maps.splice(i, 1);
8219 return true
8220 } }
8221 },
8222
8223 addOverlay: methodOp(function(spec, options) {
8224 var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
8225 if (mode.startState) { throw new Error("Overlays may not be stateful.") }
8226 insertSorted(this.state.overlays,
8227 {mode: mode, modeSpec: spec, opaque: options && options.opaque,
8228 priority: (options && options.priority) || 0},
8229 function (overlay) { return overlay.priority; });
8230 this.state.modeGen++;
8231 regChange(this);
8232 }),
8233 removeOverlay: methodOp(function(spec) {
8234 var this$1 = this;
8235
8236 var overlays = this.state.overlays;
8237 for (var i = 0; i < overlays.length; ++i) {
8238 var cur = overlays[i].modeSpec;
8239 if (cur == spec || typeof spec == "string" && cur.name == spec) {
8240 overlays.splice(i, 1);
8241 this$1.state.modeGen++;
8242 regChange(this$1);
8243 return
8244 }
8245 }
8246 }),
8247
8248 indentLine: methodOp(function(n, dir, aggressive) {
8249 if (typeof dir != "string" && typeof dir != "number") {
8250 if (dir == null) { dir = this.options.smartIndent ? "smart" : "prev"; }
8251 else { dir = dir ? "add" : "subtract"; }
8252 }
8253 if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive); }
8254 }),
8255 indentSelection: methodOp(function(how) {
8256 var this$1 = this;
8257
8258 var ranges = this.doc.sel.ranges, end = -1;
8259 for (var i = 0; i < ranges.length; i++) {
8260 var range$$1 = ranges[i];
8261 if (!range$$1.empty()) {
8262 var from = range$$1.from(), to = range$$1.to();
8263 var start = Math.max(end, from.line);
8264 end = Math.min(this$1.lastLine(), to.line - (to.ch ? 0 : 1)) + 1;
8265 for (var j = start; j < end; ++j)
8266 { indentLine(this$1, j, how); }
8267 var newRanges = this$1.doc.sel.ranges;
8268 if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0)
8269 { replaceOneSelection(this$1.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll); }
8270 } else if (range$$1.head.line > end) {
8271 indentLine(this$1, range$$1.head.line, how, true);
8272 end = range$$1.head.line;
8273 if (i == this$1.doc.sel.primIndex) { ensureCursorVisible(this$1); }
8274 }
8275 }
8276 }),
8277
8278 // Fetch the parser token for a given character. Useful for hacks
8279 // that want to inspect the mode state (say, for completion).
8280 getTokenAt: function(pos, precise) {
8281 return takeToken(this, pos, precise)
8282 },
8283
8284 getLineTokens: function(line, precise) {
8285 return takeToken(this, Pos(line), precise, true)
8286 },
8287
8288 getTokenTypeAt: function(pos) {
8289 pos = clipPos(this.doc, pos);
8290 var styles = getLineStyles(this, getLine(this.doc, pos.line));
8291 var before = 0, after = (styles.length - 1) / 2, ch = pos.ch;
8292 var type;
8293 if (ch == 0) { type = styles[2]; }
8294 else { for (;;) {
8295 var mid = (before + after) >> 1;
8296 if ((mid ? styles[mid * 2 - 1] : 0) >= ch) { after = mid; }
8297 else if (styles[mid * 2 + 1] < ch) { before = mid + 1; }
8298 else { type = styles[mid * 2 + 2]; break }
8299 } }
8300 var cut = type ? type.indexOf("overlay ") : -1;
8301 return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1)
8302 },
8303
8304 getModeAt: function(pos) {
8305 var mode = this.doc.mode;
8306 if (!mode.innerMode) { return mode }
8307 return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode
8308 },
8309
8310 getHelper: function(pos, type) {
8311 return this.getHelpers(pos, type)[0]
8312 },
8313
8314 getHelpers: function(pos, type) {
8315 var this$1 = this;
8316
8317 var found = [];
8318 if (!helpers.hasOwnProperty(type)) { return found }
8319 var help = helpers[type], mode = this.getModeAt(pos);
8320 if (typeof mode[type] == "string") {
8321 if (help[mode[type]]) { found.push(help[mode[type]]); }
8322 } else if (mode[type]) {
8323 for (var i = 0; i < mode[type].length; i++) {
8324 var val = help[mode[type][i]];
8325 if (val) { found.push(val); }
8326 }
8327 } else if (mode.helperType && help[mode.helperType]) {
8328 found.push(help[mode.helperType]);
8329 } else if (help[mode.name]) {
8330 found.push(help[mode.name]);
8331 }
8332 for (var i$1 = 0; i$1 < help._global.length; i$1++) {
8333 var cur = help._global[i$1];
8334 if (cur.pred(mode, this$1) && indexOf(found, cur.val) == -1)
8335 { found.push(cur.val); }
8336 }
8337 return found
8338 },
8339
8340 getStateAfter: function(line, precise) {
8341 var doc = this.doc;
8342 line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);
8343 return getContextBefore(this, line + 1, precise).state
8344 },
8345
8346 cursorCoords: function(start, mode) {
8347 var pos, range$$1 = this.doc.sel.primary();
8348 if (start == null) { pos = range$$1.head; }
8349 else if (typeof start == "object") { pos = clipPos(this.doc, start); }
8350 else { pos = start ? range$$1.from() : range$$1.to(); }
8351 return cursorCoords(this, pos, mode || "page")
8352 },
8353
8354 charCoords: function(pos, mode) {
8355 return charCoords(this, clipPos(this.doc, pos), mode || "page")
8356 },
8357
8358 coordsChar: function(coords, mode) {
8359 coords = fromCoordSystem(this, coords, mode || "page");
8360 return coordsChar(this, coords.left, coords.top)
8361 },
8362
8363 lineAtHeight: function(height, mode) {
8364 height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top;
8365 return lineAtHeight(this.doc, height + this.display.viewOffset)
8366 },
8367 heightAtLine: function(line, mode, includeWidgets) {
8368 var end = false, lineObj;
8369 if (typeof line == "number") {
8370 var last = this.doc.first + this.doc.size - 1;
8371 if (line < this.doc.first) { line = this.doc.first; }
8372 else if (line > last) { line = last; end = true; }
8373 lineObj = getLine(this.doc, line);
8374 } else {
8375 lineObj = line;
8376 }
8377 return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page", includeWidgets || end).top +
8378 (end ? this.doc.height - heightAtLine(lineObj) : 0)
8379 },
8380
8381 defaultTextHeight: function() { return textHeight(this.display) },
8382 defaultCharWidth: function() { return charWidth(this.display) },
8383
8384 getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}},
8385
8386 addWidget: function(pos, node, scroll, vert, horiz) {
8387 var display = this.display;
8388 pos = cursorCoords(this, clipPos(this.doc, pos));
8389 var top = pos.bottom, left = pos.left;
8390 node.style.position = "absolute";
8391 node.setAttribute("cm-ignore-events", "true");
8392 this.display.input.setUneditable(node);
8393 display.sizer.appendChild(node);
8394 if (vert == "over") {
8395 top = pos.top;
8396 } else if (vert == "above" || vert == "near") {
8397 var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
8398 hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
8399 // Default to positioning above (if specified and possible); otherwise default to positioning below
8400 if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
8401 { top = pos.top - node.offsetHeight; }
8402 else if (pos.bottom + node.offsetHeight <= vspace)
8403 { top = pos.bottom; }
8404 if (left + node.offsetWidth > hspace)
8405 { left = hspace - node.offsetWidth; }
8406 }
8407 node.style.top = top + "px";
8408 node.style.left = node.style.right = "";
8409 if (horiz == "right") {
8410 left = display.sizer.clientWidth - node.offsetWidth;
8411 node.style.right = "0px";
8412 } else {
8413 if (horiz == "left") { left = 0; }
8414 else if (horiz == "middle") { left = (display.sizer.clientWidth - node.offsetWidth) / 2; }
8415 node.style.left = left + "px";
8416 }
8417 if (scroll)
8418 { scrollIntoView(this, {left: left, top: top, right: left + node.offsetWidth, bottom: top + node.offsetHeight}); }
8419 },
8420
8421 triggerOnKeyDown: methodOp(onKeyDown),
8422 triggerOnKeyPress: methodOp(onKeyPress),
8423 triggerOnKeyUp: onKeyUp,
8424 triggerOnMouseDown: methodOp(onMouseDown),
8425
8426 execCommand: function(cmd) {
8427 if (commands.hasOwnProperty(cmd))
8428 { return commands[cmd].call(null, this) }
8429 },
8430
8431 triggerElectric: methodOp(function(text) { triggerElectric(this, text); }),
8432
8433 findPosH: function(from, amount, unit, visually) {
8434 var this$1 = this;
8435
8436 var dir = 1;
8437 if (amount < 0) { dir = -1; amount = -amount; }
8438 var cur = clipPos(this.doc, from);
8439 for (var i = 0; i < amount; ++i) {
8440 cur = findPosH(this$1.doc, cur, dir, unit, visually);
8441 if (cur.hitSide) { break }
8442 }
8443 return cur
8444 },
8445
8446 moveH: methodOp(function(dir, unit) {
8447 var this$1 = this;
8448
8449 this.extendSelectionsBy(function (range$$1) {
8450 if (this$1.display.shift || this$1.doc.extend || range$$1.empty())
8451 { return findPosH(this$1.doc, range$$1.head, dir, unit, this$1.options.rtlMoveVisually) }
8452 else
8453 { return dir < 0 ? range$$1.from() : range$$1.to() }
8454 }, sel_move);
8455 }),
8456
8457 deleteH: methodOp(function(dir, unit) {
8458 var sel = this.doc.sel, doc = this.doc;
8459 if (sel.somethingSelected())
8460 { doc.replaceSelection("", null, "+delete"); }
8461 else
8462 { deleteNearSelection(this, function (range$$1) {
8463 var other = findPosH(doc, range$$1.head, dir, unit, false);
8464 return dir < 0 ? {from: other, to: range$$1.head} : {from: range$$1.head, to: other}
8465 }); }
8466 }),
8467
8468 findPosV: function(from, amount, unit, goalColumn) {
8469 var this$1 = this;
8470
8471 var dir = 1, x = goalColumn;
8472 if (amount < 0) { dir = -1; amount = -amount; }
8473 var cur = clipPos(this.doc, from);
8474 for (var i = 0; i < amount; ++i) {
8475 var coords = cursorCoords(this$1, cur, "div");
8476 if (x == null) { x = coords.left; }
8477 else { coords.left = x; }
8478 cur = findPosV(this$1, coords, dir, unit);
8479 if (cur.hitSide) { break }
8480 }
8481 return cur
8482 },
8483
8484 moveV: methodOp(function(dir, unit) {
8485 var this$1 = this;
8486
8487 var doc = this.doc, goals = [];
8488 var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected();
8489 doc.extendSelectionsBy(function (range$$1) {
8490 if (collapse)
8491 { return dir < 0 ? range$$1.from() : range$$1.to() }
8492 var headPos = cursorCoords(this$1, range$$1.head, "div");
8493 if (range$$1.goalColumn != null) { headPos.left = range$$1.goalColumn; }
8494 goals.push(headPos.left);
8495 var pos = findPosV(this$1, headPos, dir, unit);
8496 if (unit == "page" && range$$1 == doc.sel.primary())
8497 { addToScrollTop(this$1, charCoords(this$1, pos, "div").top - headPos.top); }
8498 return pos
8499 }, sel_move);
8500 if (goals.length) { for (var i = 0; i < doc.sel.ranges.length; i++)
8501 { doc.sel.ranges[i].goalColumn = goals[i]; } }
8502 }),
8503
8504 // Find the word at the given position (as returned by coordsChar).
8505 findWordAt: function(pos) {
8506 var doc = this.doc, line = getLine(doc, pos.line).text;
8507 var start = pos.ch, end = pos.ch;
8508 if (line) {
8509 var helper = this.getHelper(pos, "wordChars");
8510 if ((pos.sticky == "before" || end == line.length) && start) { --start; } else { ++end; }
8511 var startChar = line.charAt(start);
8512 var check = isWordChar(startChar, helper)
8513 ? function (ch) { return isWordChar(ch, helper); }
8514 : /\s/.test(startChar) ? function (ch) { return /\s/.test(ch); }
8515 : function (ch) { return (!/\s/.test(ch) && !isWordChar(ch)); };
8516 while (start > 0 && check(line.charAt(start - 1))) { --start; }
8517 while (end < line.length && check(line.charAt(end))) { ++end; }
8518 }
8519 return new Range(Pos(pos.line, start), Pos(pos.line, end))
8520 },
8521
8522 toggleOverwrite: function(value) {
8523 if (value != null && value == this.state.overwrite) { return }
8524 if (this.state.overwrite = !this.state.overwrite)
8525 { addClass(this.display.cursorDiv, "CodeMirror-overwrite"); }
8526 else
8527 { rmClass(this.display.cursorDiv, "CodeMirror-overwrite"); }
8528
8529 signal(this, "overwriteToggle", this, this.state.overwrite);
8530 },
8531 hasFocus: function() { return this.display.input.getField() == activeElt() },
8532 isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) },
8533
8534 scrollTo: methodOp(function (x, y) { scrollToCoords(this, x, y); }),
8535 getScrollInfo: function() {
8536 var scroller = this.display.scroller;
8537 return {left: scroller.scrollLeft, top: scroller.scrollTop,
8538 height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight,
8539 width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth,
8540 clientHeight: displayHeight(this), clientWidth: displayWidth(this)}
8541 },
8542
8543 scrollIntoView: methodOp(function(range$$1, margin) {
8544 if (range$$1 == null) {
8545 range$$1 = {from: this.doc.sel.primary().head, to: null};
8546 if (margin == null) { margin = this.options.cursorScrollMargin; }
8547 } else if (typeof range$$1 == "number") {
8548 range$$1 = {from: Pos(range$$1, 0), to: null};
8549 } else if (range$$1.from == null) {
8550 range$$1 = {from: range$$1, to: null};
8551 }
8552 if (!range$$1.to) { range$$1.to = range$$1.from; }
8553 range$$1.margin = margin || 0;
8554
8555 if (range$$1.from.line != null) {
8556 scrollToRange(this, range$$1);
8557 } else {
8558 scrollToCoordsRange(this, range$$1.from, range$$1.to, range$$1.margin);
8559 }
8560 }),
8561
8562 setSize: methodOp(function(width, height) {
8563 var this$1 = this;
8564
8565 var interpret = function (val) { return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; };
8566 if (width != null) { this.display.wrapper.style.width = interpret(width); }
8567 if (height != null) { this.display.wrapper.style.height = interpret(height); }
8568 if (this.options.lineWrapping) { clearLineMeasurementCache(this); }
8569 var lineNo$$1 = this.display.viewFrom;
8570 this.doc.iter(lineNo$$1, this.display.viewTo, function (line) {
8571 if (line.widgets) { for (var i = 0; i < line.widgets.length; i++)
8572 { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo$$1, "widget"); break } } }
8573 ++lineNo$$1;
8574 });
8575 this.curOp.forceUpdate = true;
8576 signal(this, "refresh", this);
8577 }),
8578
8579 operation: function(f){return runInOp(this, f)},
8580 startOperation: function(){return startOperation(this)},
8581 endOperation: function(){return endOperation(this)},
8582
8583 refresh: methodOp(function() {
8584 var oldHeight = this.display.cachedTextHeight;
8585 regChange(this);
8586 this.curOp.forceUpdate = true;
8587 clearCaches(this);
8588 scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop);
8589 updateGutterSpace(this);
8590 if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5)
8591 { estimateLineHeights(this); }
8592 signal(this, "refresh", this);
8593 }),
8594
8595 swapDoc: methodOp(function(doc) {
8596 var old = this.doc;
8597 old.cm = null;
8598 attachDoc(this, doc);
8599 clearCaches(this);
8600 this.display.input.reset();
8601 scrollToCoords(this, doc.scrollLeft, doc.scrollTop);
8602 this.curOp.forceScroll = true;
8603 signalLater(this, "swapDoc", this, old);
8604 return old
8605 }),
8606
8607 phrase: function(phraseText) {
8608 var phrases = this.options.phrases;
8609 return phrases && Object.prototype.hasOwnProperty.call(phrases, phraseText) ? phrases[phraseText] : phraseText
8610 },
8611
8612 getInputField: function(){return this.display.input.getField()},
8613 getWrapperElement: function(){return this.display.wrapper},
8614 getScrollerElement: function(){return this.display.scroller},
8615 getGutterElement: function(){return this.display.gutters}
8616 };
8617 eventMixin(CodeMirror);
8618
8619 CodeMirror.registerHelper = function(type, name, value) {
8620 if (!helpers.hasOwnProperty(type)) { helpers[type] = CodeMirror[type] = {_global: []}; }
8621 helpers[type][name] = value;
8622 };
8623 CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {
8624 CodeMirror.registerHelper(type, name, value);
8625 helpers[type]._global.push({pred: predicate, val: value});
8626 };
8627 }
8628
8629 // Used for horizontal relative motion. Dir is -1 or 1 (left or
8630 // right), unit can be "char", "column" (like char, but doesn't
8631 // cross line boundaries), "word" (across next word), or "group" (to
8632 // the start of next group of word or non-word-non-whitespace
8633 // chars). The visually param controls whether, in right-to-left
8634 // text, direction 1 means to move towards the next index in the
8635 // string, or towards the character to the right of the current
8636 // position. The resulting position will have a hitSide=true
8637 // property if it reached the end of the document.
8638 function findPosH(doc, pos, dir, unit, visually) {
8639 var oldPos = pos;
8640 var origDir = dir;
8641 var lineObj = getLine(doc, pos.line);
8642 function findNextLine() {
8643 var l = pos.line + dir;
8644 if (l < doc.first || l >= doc.first + doc.size) { return false }
8645 pos = new Pos(l, pos.ch, pos.sticky);
8646 return lineObj = getLine(doc, l)
8647 }
8648 function moveOnce(boundToLine) {
8649 var next;
8650 if (visually) {
8651 next = moveVisually(doc.cm, lineObj, pos, dir);
8652 } else {
8653 next = moveLogically(lineObj, pos, dir);
8654 }
8655 if (next == null) {
8656 if (!boundToLine && findNextLine())
8657 { pos = endOfLine(visually, doc.cm, lineObj, pos.line, dir); }
8658 else
8659 { return false }
8660 } else {
8661 pos = next;
8662 }
8663 return true
8664 }
8665
8666 if (unit == "char") {
8667 moveOnce();
8668 } else if (unit == "column") {
8669 moveOnce(true);
8670 } else if (unit == "word" || unit == "group") {
8671 var sawType = null, group = unit == "group";
8672 var helper = doc.cm && doc.cm.getHelper(pos, "wordChars");
8673 for (var first = true;; first = false) {
8674 if (dir < 0 && !moveOnce(!first)) { break }
8675 var cur = lineObj.text.charAt(pos.ch) || "\n";
8676 var type = isWordChar(cur, helper) ? "w"
8677 : group && cur == "\n" ? "n"
8678 : !group || /\s/.test(cur) ? null
8679 : "p";
8680 if (group && !first && !type) { type = "s"; }
8681 if (sawType && sawType != type) {
8682 if (dir < 0) {dir = 1; moveOnce(); pos.sticky = "after";}
8683 break
8684 }
8685
8686 if (type) { sawType = type; }
8687 if (dir > 0 && !moveOnce(!first)) { break }
8688 }
8689 }
8690 var result = skipAtomic(doc, pos, oldPos, origDir, true);
8691 if (equalCursorPos(oldPos, result)) { result.hitSide = true; }
8692 return result
8693 }
8694
8695 // For relative vertical movement. Dir may be -1 or 1. Unit can be
8696 // "page" or "line". The resulting position will have a hitSide=true
8697 // property if it reached the end of the document.
8698 function findPosV(cm, pos, dir, unit) {
8699 var doc = cm.doc, x = pos.left, y;
8700 if (unit == "page") {
8701 var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
8702 var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3);
8703 y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount;
8704
8705 } else if (unit == "line") {
8706 y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
8707 }
8708 var target;
8709 for (;;) {
8710 target = coordsChar(cm, x, y);
8711 if (!target.outside) { break }
8712 if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break }
8713 y += dir * 5;
8714 }
8715 return target
8716 }
8717
8718 // CONTENTEDITABLE INPUT STYLE
8719
8720 var ContentEditableInput = function(cm) {
8721 this.cm = cm;
8722 this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null;
8723 this.polling = new Delayed();
8724 this.composing = null;
8725 this.gracePeriod = false;
8726 this.readDOMTimeout = null;
8727 };
8728
8729 ContentEditableInput.prototype.init = function (display) {
8730 var this$1 = this;
8731
8732 var input = this, cm = input.cm;
8733 var div = input.div = display.lineDiv;
8734 disableBrowserMagic(div, cm.options.spellcheck);
8735
8736 on(div, "paste", function (e) {
8737 if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
8738 // IE doesn't fire input events, so we schedule a read for the pasted content in this way
8739 if (ie_version <= 11) { setTimeout(operation(cm, function () { return this$1.updateFromDOM(); }), 20); }
8740 });
8741
8742 on(div, "compositionstart", function (e) {
8743 this$1.composing = {data: e.data, done: false};
8744 });
8745 on(div, "compositionupdate", function (e) {
8746 if (!this$1.composing) { this$1.composing = {data: e.data, done: false}; }
8747 });
8748 on(div, "compositionend", function (e) {
8749 if (this$1.composing) {
8750 if (e.data != this$1.composing.data) { this$1.readFromDOMSoon(); }
8751 this$1.composing.done = true;
8752 }
8753 });
8754
8755 on(div, "touchstart", function () { return input.forceCompositionEnd(); });
8756
8757 on(div, "input", function () {
8758 if (!this$1.composing) { this$1.readFromDOMSoon(); }
8759 });
8760
8761 function onCopyCut(e) {
8762 if (signalDOMEvent(cm, e)) { return }
8763 if (cm.somethingSelected()) {
8764 setLastCopied({lineWise: false, text: cm.getSelections()});
8765 if (e.type == "cut") { cm.replaceSelection("", null, "cut"); }
8766 } else if (!cm.options.lineWiseCopyCut) {
8767 return
8768 } else {
8769 var ranges = copyableRanges(cm);
8770 setLastCopied({lineWise: true, text: ranges.text});
8771 if (e.type == "cut") {
8772 cm.operation(function () {
8773 cm.setSelections(ranges.ranges, 0, sel_dontScroll);
8774 cm.replaceSelection("", null, "cut");
8775 });
8776 }
8777 }
8778 if (e.clipboardData) {
8779 e.clipboardData.clearData();
8780 var content = lastCopied.text.join("\n");
8781 // iOS exposes the clipboard API, but seems to discard content inserted into it
8782 e.clipboardData.setData("Text", content);
8783 if (e.clipboardData.getData("Text") == content) {
8784 e.preventDefault();
8785 return
8786 }
8787 }
8788 // Old-fashioned briefly-focus-a-textarea hack
8789 var kludge = hiddenTextarea(), te = kludge.firstChild;
8790 cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild);
8791 te.value = lastCopied.text.join("\n");
8792 var hadFocus = document.activeElement;
8793 selectInput(te);
8794 setTimeout(function () {
8795 cm.display.lineSpace.removeChild(kludge);
8796 hadFocus.focus();
8797 if (hadFocus == div) { input.showPrimarySelection(); }
8798 }, 50);
8799 }
8800 on(div, "copy", onCopyCut);
8801 on(div, "cut", onCopyCut);
8802 };
8803
8804 ContentEditableInput.prototype.prepareSelection = function () {
8805 var result = prepareSelection(this.cm, false);
8806 result.focus = this.cm.state.focused;
8807 return result
8808 };
8809
8810 ContentEditableInput.prototype.showSelection = function (info, takeFocus) {
8811 if (!info || !this.cm.display.view.length) { return }
8812 if (info.focus || takeFocus) { this.showPrimarySelection(); }
8813 this.showMultipleSelections(info);
8814 };
8815
8816 ContentEditableInput.prototype.getSelection = function () {
8817 return this.cm.display.wrapper.ownerDocument.getSelection()
8818 };
8819
8820 ContentEditableInput.prototype.showPrimarySelection = function () {
8821 var sel = this.getSelection(), cm = this.cm, prim = cm.doc.sel.primary();
8822 var from = prim.from(), to = prim.to();
8823
8824 if (cm.display.viewTo == cm.display.viewFrom || from.line >= cm.display.viewTo || to.line < cm.display.viewFrom) {
8825 sel.removeAllRanges();
8826 return
8827 }
8828
8829 var curAnchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
8830 var curFocus = domToPos(cm, sel.focusNode, sel.focusOffset);
8831 if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad &&
8832 cmp(minPos(curAnchor, curFocus), from) == 0 &&
8833 cmp(maxPos(curAnchor, curFocus), to) == 0)
8834 { return }
8835
8836 var view = cm.display.view;
8837 var start = (from.line >= cm.display.viewFrom && posToDOM(cm, from)) ||
8838 {node: view[0].measure.map[2], offset: 0};
8839 var end = to.line < cm.display.viewTo && posToDOM(cm, to);
8840 if (!end) {
8841 var measure = view[view.length - 1].measure;
8842 var map$$1 = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map;
8843 end = {node: map$$1[map$$1.length - 1], offset: map$$1[map$$1.length - 2] - map$$1[map$$1.length - 3]};
8844 }
8845
8846 if (!start || !end) {
8847 sel.removeAllRanges();
8848 return
8849 }
8850
8851 var old = sel.rangeCount && sel.getRangeAt(0), rng;
8852 try { rng = range(start.node, start.offset, end.offset, end.node); }
8853 catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible
8854 if (rng) {
8855 if (!gecko && cm.state.focused) {
8856 sel.collapse(start.node, start.offset);
8857 if (!rng.collapsed) {
8858 sel.removeAllRanges();
8859 sel.addRange(rng);
8860 }
8861 } else {
8862 sel.removeAllRanges();
8863 sel.addRange(rng);
8864 }
8865 if (old && sel.anchorNode == null) { sel.addRange(old); }
8866 else if (gecko) { this.startGracePeriod(); }
8867 }
8868 this.rememberSelection();
8869 };
8870
8871 ContentEditableInput.prototype.startGracePeriod = function () {
8872 var this$1 = this;
8873
8874 clearTimeout(this.gracePeriod);
8875 this.gracePeriod = setTimeout(function () {
8876 this$1.gracePeriod = false;
8877 if (this$1.selectionChanged())
8878 { this$1.cm.operation(function () { return this$1.cm.curOp.selectionChanged = true; }); }
8879 }, 20);
8880 };
8881
8882 ContentEditableInput.prototype.showMultipleSelections = function (info) {
8883 removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors);
8884 removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection);
8885 };
8886
8887 ContentEditableInput.prototype.rememberSelection = function () {
8888 var sel = this.getSelection();
8889 this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset;
8890 this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset;
8891 };
8892
8893 ContentEditableInput.prototype.selectionInEditor = function () {
8894 var sel = this.getSelection();
8895 if (!sel.rangeCount) { return false }
8896 var node = sel.getRangeAt(0).commonAncestorContainer;
8897 return contains(this.div, node)
8898 };
8899
8900 ContentEditableInput.prototype.focus = function () {
8901 if (this.cm.options.readOnly != "nocursor") {
8902 if (!this.selectionInEditor())
8903 { this.showSelection(this.prepareSelection(), true); }
8904 this.div.focus();
8905 }
8906 };
8907 ContentEditableInput.prototype.blur = function () { this.div.blur(); };
8908 ContentEditableInput.prototype.getField = function () { return this.div };
8909
8910 ContentEditableInput.prototype.supportsTouch = function () { return true };
8911
8912 ContentEditableInput.prototype.receivedFocus = function () {
8913 var input = this;
8914 if (this.selectionInEditor())
8915 { this.pollSelection(); }
8916 else
8917 { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }); }
8918
8919 function poll() {
8920 if (input.cm.state.focused) {
8921 input.pollSelection();
8922 input.polling.set(input.cm.options.pollInterval, poll);
8923 }
8924 }
8925 this.polling.set(this.cm.options.pollInterval, poll);
8926 };
8927
8928 ContentEditableInput.prototype.selectionChanged = function () {
8929 var sel = this.getSelection();
8930 return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||
8931 sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset
8932 };
8933
8934 ContentEditableInput.prototype.pollSelection = function () {
8935 if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) { return }
8936 var sel = this.getSelection(), cm = this.cm;
8937 // On Android Chrome (version 56, at least), backspacing into an
8938 // uneditable block element will put the cursor in that element,
8939 // and then, because it's not editable, hide the virtual keyboard.
8940 // Because Android doesn't allow us to actually detect backspace
8941 // presses in a sane way, this code checks for when that happens
8942 // and simulates a backspace press in this case.
8943 if (android && chrome && this.cm.options.gutters.length && isInGutter(sel.anchorNode)) {
8944 this.cm.triggerOnKeyDown({type: "keydown", keyCode: 8, preventDefault: Math.abs});
8945 this.blur();
8946 this.focus();
8947 return
8948 }
8949 if (this.composing) { return }
8950 this.rememberSelection();
8951 var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
8952 var head = domToPos(cm, sel.focusNode, sel.focusOffset);
8953 if (anchor && head) { runInOp(cm, function () {
8954 setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll);
8955 if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true; }
8956 }); }
8957 };
8958
8959 ContentEditableInput.prototype.pollContent = function () {
8960 if (this.readDOMTimeout != null) {
8961 clearTimeout(this.readDOMTimeout);
8962 this.readDOMTimeout = null;
8963 }
8964
8965 var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary();
8966 var from = sel.from(), to = sel.to();
8967 if (from.ch == 0 && from.line > cm.firstLine())
8968 { from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length); }
8969 if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine())
8970 { to = Pos(to.line + 1, 0); }
8971 if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return false }
8972
8973 var fromIndex, fromLine, fromNode;
8974 if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) {
8975 fromLine = lineNo(display.view[0].line);
8976 fromNode = display.view[0].node;
8977 } else {
8978 fromLine = lineNo(display.view[fromIndex].line);
8979 fromNode = display.view[fromIndex - 1].node.nextSibling;
8980 }
8981 var toIndex = findViewIndex(cm, to.line);
8982 var toLine, toNode;
8983 if (toIndex == display.view.length - 1) {
8984 toLine = display.viewTo - 1;
8985 toNode = display.lineDiv.lastChild;
8986 } else {
8987 toLine = lineNo(display.view[toIndex + 1].line) - 1;
8988 toNode = display.view[toIndex + 1].node.previousSibling;
8989 }
8990
8991 if (!fromNode) { return false }
8992 var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine));
8993 var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length));
8994 while (newText.length > 1 && oldText.length > 1) {
8995 if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; }
8996 else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; }
8997 else { break }
8998 }
8999
9000 var cutFront = 0, cutEnd = 0;
9001 var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length);
9002 while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront))
9003 { ++cutFront; }
9004 var newBot = lst(newText), oldBot = lst(oldText);
9005 var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0),
9006 oldBot.length - (oldText.length == 1 ? cutFront : 0));
9007 while (cutEnd < maxCutEnd &&
9008 newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1))
9009 { ++cutEnd; }
9010 // Try to move start of change to start of selection if ambiguous
9011 if (newText.length == 1 && oldText.length == 1 && fromLine == from.line) {
9012 while (cutFront && cutFront > from.ch &&
9013 newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) {
9014 cutFront--;
9015 cutEnd++;
9016 }
9017 }
9018
9019 newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, "");
9020 newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, "");
9021
9022 var chFrom = Pos(fromLine, cutFront);
9023 var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0);
9024 if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {
9025 replaceRange(cm.doc, newText, chFrom, chTo, "+input");
9026 return true
9027 }
9028 };
9029
9030 ContentEditableInput.prototype.ensurePolled = function () {
9031 this.forceCompositionEnd();
9032 };
9033 ContentEditableInput.prototype.reset = function () {
9034 this.forceCompositionEnd();
9035 };
9036 ContentEditableInput.prototype.forceCompositionEnd = function () {
9037 if (!this.composing) { return }
9038 clearTimeout(this.readDOMTimeout);
9039 this.composing = null;
9040 this.updateFromDOM();
9041 this.div.blur();
9042 this.div.focus();
9043 };
9044 ContentEditableInput.prototype.readFromDOMSoon = function () {
9045 var this$1 = this;
9046
9047 if (this.readDOMTimeout != null) { return }
9048 this.readDOMTimeout = setTimeout(function () {
9049 this$1.readDOMTimeout = null;
9050 if (this$1.composing) {
9051 if (this$1.composing.done) { this$1.composing = null; }
9052 else { return }
9053 }
9054 this$1.updateFromDOM();
9055 }, 80);
9056 };
9057
9058 ContentEditableInput.prototype.updateFromDOM = function () {
9059 var this$1 = this;
9060
9061 if (this.cm.isReadOnly() || !this.pollContent())
9062 { runInOp(this.cm, function () { return regChange(this$1.cm); }); }
9063 };
9064
9065 ContentEditableInput.prototype.setUneditable = function (node) {
9066 node.contentEditable = "false";
9067 };
9068
9069 ContentEditableInput.prototype.onKeyPress = function (e) {
9070 if (e.charCode == 0 || this.composing) { return }
9071 e.preventDefault();
9072 if (!this.cm.isReadOnly())
9073 { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0); }
9074 };
9075
9076 ContentEditableInput.prototype.readOnlyChanged = function (val) {
9077 this.div.contentEditable = String(val != "nocursor");
9078 };
9079
9080 ContentEditableInput.prototype.onContextMenu = function () {};
9081 ContentEditableInput.prototype.resetPosition = function () {};
9082
9083 ContentEditableInput.prototype.needsContentAttribute = true;
9084
9085 function posToDOM(cm, pos) {
9086 var view = findViewForLine(cm, pos.line);
9087 if (!view || view.hidden) { return null }
9088 var line = getLine(cm.doc, pos.line);
9089 var info = mapFromLineView(view, line, pos.line);
9090
9091 var order = getOrder(line, cm.doc.direction), side = "left";
9092 if (order) {
9093 var partPos = getBidiPartAt(order, pos.ch);
9094 side = partPos % 2 ? "right" : "left";
9095 }
9096 var result = nodeAndOffsetInLineMap(info.map, pos.ch, side);
9097 result.offset = result.collapse == "right" ? result.end : result.start;
9098 return result
9099 }
9100
9101 function isInGutter(node) {
9102 for (var scan = node; scan; scan = scan.parentNode)
9103 { if (/CodeMirror-gutter-wrapper/.test(scan.className)) { return true } }
9104 return false
9105 }
9106
9107 function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos }
9108
9109 function domTextBetween(cm, from, to, fromLine, toLine) {
9110 var text = "", closing = false, lineSep = cm.doc.lineSeparator(), extraLinebreak = false;
9111 function recognizeMarker(id) { return function (marker) { return marker.id == id; } }
9112 function close() {
9113 if (closing) {
9114 text += lineSep;
9115 if (extraLinebreak) { text += lineSep; }
9116 closing = extraLinebreak = false;
9117 }
9118 }
9119 function addText(str) {
9120 if (str) {
9121 close();
9122 text += str;
9123 }
9124 }
9125 function walk(node) {
9126 if (node.nodeType == 1) {
9127 var cmText = node.getAttribute("cm-text");
9128 if (cmText) {
9129 addText(cmText);
9130 return
9131 }
9132 var markerID = node.getAttribute("cm-marker"), range$$1;
9133 if (markerID) {
9134 var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID));
9135 if (found.length && (range$$1 = found[0].find(0)))
9136 { addText(getBetween(cm.doc, range$$1.from, range$$1.to).join(lineSep)); }
9137 return
9138 }
9139 if (node.getAttribute("contenteditable") == "false") { return }
9140 var isBlock = /^(pre|div|p|li|table|br)$/i.test(node.nodeName);
9141 if (!/^br$/i.test(node.nodeName) && node.textContent.length == 0) { return }
9142
9143 if (isBlock) { close(); }
9144 for (var i = 0; i < node.childNodes.length; i++)
9145 { walk(node.childNodes[i]); }
9146
9147 if (/^(pre|p)$/i.test(node.nodeName)) { extraLinebreak = true; }
9148 if (isBlock) { closing = true; }
9149 } else if (node.nodeType == 3) {
9150 addText(node.nodeValue.replace(/\u200b/g, "").replace(/\u00a0/g, " "));
9151 }
9152 }
9153 for (;;) {
9154 walk(from);
9155 if (from == to) { break }
9156 from = from.nextSibling;
9157 extraLinebreak = false;
9158 }
9159 return text
9160 }
9161
9162 function domToPos(cm, node, offset) {
9163 var lineNode;
9164 if (node == cm.display.lineDiv) {
9165 lineNode = cm.display.lineDiv.childNodes[offset];
9166 if (!lineNode) { return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) }
9167 node = null; offset = 0;
9168 } else {
9169 for (lineNode = node;; lineNode = lineNode.parentNode) {
9170 if (!lineNode || lineNode == cm.display.lineDiv) { return null }
9171 if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) { break }
9172 }
9173 }
9174 for (var i = 0; i < cm.display.view.length; i++) {
9175 var lineView = cm.display.view[i];
9176 if (lineView.node == lineNode)
9177 { return locateNodeInLineView(lineView, node, offset) }
9178 }
9179 }
9180
9181 function locateNodeInLineView(lineView, node, offset) {
9182 var wrapper = lineView.text.firstChild, bad = false;
9183 if (!node || !contains(wrapper, node)) { return badPos(Pos(lineNo(lineView.line), 0), true) }
9184 if (node == wrapper) {
9185 bad = true;
9186 node = wrapper.childNodes[offset];
9187 offset = 0;
9188 if (!node) {
9189 var line = lineView.rest ? lst(lineView.rest) : lineView.line;
9190 return badPos(Pos(lineNo(line), line.text.length), bad)
9191 }
9192 }
9193
9194 var textNode = node.nodeType == 3 ? node : null, topNode = node;
9195 if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {
9196 textNode = node.firstChild;
9197 if (offset) { offset = textNode.nodeValue.length; }
9198 }
9199 while (topNode.parentNode != wrapper) { topNode = topNode.parentNode; }
9200 var measure = lineView.measure, maps = measure.maps;
9201
9202 function find(textNode, topNode, offset) {
9203 for (var i = -1; i < (maps ? maps.length : 0); i++) {
9204 var map$$1 = i < 0 ? measure.map : maps[i];
9205 for (var j = 0; j < map$$1.length; j += 3) {
9206 var curNode = map$$1[j + 2];
9207 if (curNode == textNode || curNode == topNode) {
9208 var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]);
9209 var ch = map$$1[j] + offset;
9210 if (offset < 0 || curNode != textNode) { ch = map$$1[j + (offset ? 1 : 0)]; }
9211 return Pos(line, ch)
9212 }
9213 }
9214 }
9215 }
9216 var found = find(textNode, topNode, offset);
9217 if (found) { return badPos(found, bad) }
9218
9219 // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems
9220 for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) {
9221 found = find(after, after.firstChild, 0);
9222 if (found)
9223 { return badPos(Pos(found.line, found.ch - dist), bad) }
9224 else
9225 { dist += after.textContent.length; }
9226 }
9227 for (var before = topNode.previousSibling, dist$1 = offset; before; before = before.previousSibling) {
9228 found = find(before, before.firstChild, -1);
9229 if (found)
9230 { return badPos(Pos(found.line, found.ch + dist$1), bad) }
9231 else
9232 { dist$1 += before.textContent.length; }
9233 }
9234 }
9235
9236 // TEXTAREA INPUT STYLE
9237
9238 var TextareaInput = function(cm) {
9239 this.cm = cm;
9240 // See input.poll and input.reset
9241 this.prevInput = "";
9242
9243 // Flag that indicates whether we expect input to appear real soon
9244 // now (after some event like 'keypress' or 'input') and are
9245 // polling intensively.
9246 this.pollingFast = false;
9247 // Self-resetting timeout for the poller
9248 this.polling = new Delayed();
9249 // Used to work around IE issue with selection being forgotten when focus moves away from textarea
9250 this.hasSelection = false;
9251 this.composing = null;
9252 };
9253
9254 TextareaInput.prototype.init = function (display) {
9255 var this$1 = this;
9256
9257 var input = this, cm = this.cm;
9258 this.createField(display);
9259 var te = this.textarea;
9260
9261 display.wrapper.insertBefore(this.wrapper, display.wrapper.firstChild);
9262
9263 // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)
9264 if (ios) { te.style.width = "0px"; }
9265
9266 on(te, "input", function () {
9267 if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = null; }
9268 input.poll();
9269 });
9270
9271 on(te, "paste", function (e) {
9272 if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
9273
9274 cm.state.pasteIncoming = true;
9275 input.fastPoll();
9276 });
9277
9278 function prepareCopyCut(e) {
9279 if (signalDOMEvent(cm, e)) { return }
9280 if (cm.somethingSelected()) {
9281 setLastCopied({lineWise: false, text: cm.getSelections()});
9282 } else if (!cm.options.lineWiseCopyCut) {
9283 return
9284 } else {
9285 var ranges = copyableRanges(cm);
9286 setLastCopied({lineWise: true, text: ranges.text});
9287 if (e.type == "cut") {
9288 cm.setSelections(ranges.ranges, null, sel_dontScroll);
9289 } else {
9290 input.prevInput = "";
9291 te.value = ranges.text.join("\n");
9292 selectInput(te);
9293 }
9294 }
9295 if (e.type == "cut") { cm.state.cutIncoming = true; }
9296 }
9297 on(te, "cut", prepareCopyCut);
9298 on(te, "copy", prepareCopyCut);
9299
9300 on(display.scroller, "paste", function (e) {
9301 if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return }
9302 cm.state.pasteIncoming = true;
9303 input.focus();
9304 });
9305
9306 // Prevent normal selection in the editor (we handle our own)
9307 on(display.lineSpace, "selectstart", function (e) {
9308 if (!eventInWidget(display, e)) { e_preventDefault(e); }
9309 });
9310
9311 on(te, "compositionstart", function () {
9312 var start = cm.getCursor("from");
9313 if (input.composing) { input.composing.range.clear(); }
9314 input.composing = {
9315 start: start,
9316 range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"})
9317 };
9318 });
9319 on(te, "compositionend", function () {
9320 if (input.composing) {
9321 input.poll();
9322 input.composing.range.clear();
9323 input.composing = null;
9324 }
9325 });
9326 };
9327
9328 TextareaInput.prototype.createField = function (_display) {
9329 // Wraps and hides input textarea
9330 this.wrapper = hiddenTextarea();
9331 // The semihidden textarea that is focused when the editor is
9332 // focused, and receives input.
9333 this.textarea = this.wrapper.firstChild;
9334 };
9335
9336 TextareaInput.prototype.prepareSelection = function () {
9337 // Redraw the selection and/or cursor
9338 var cm = this.cm, display = cm.display, doc = cm.doc;
9339 var result = prepareSelection(cm);
9340
9341 // Move the hidden textarea near the cursor to prevent scrolling artifacts
9342 if (cm.options.moveInputWithCursor) {
9343 var headPos = cursorCoords(cm, doc.sel.primary().head, "div");
9344 var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();
9345 result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
9346 headPos.top + lineOff.top - wrapOff.top));
9347 result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
9348 headPos.left + lineOff.left - wrapOff.left));
9349 }
9350
9351 return result
9352 };
9353
9354 TextareaInput.prototype.showSelection = function (drawn) {
9355 var cm = this.cm, display = cm.display;
9356 removeChildrenAndAdd(display.cursorDiv, drawn.cursors);
9357 removeChildrenAndAdd(display.selectionDiv, drawn.selection);
9358 if (drawn.teTop != null) {
9359 this.wrapper.style.top = drawn.teTop + "px";
9360 this.wrapper.style.left = drawn.teLeft + "px";
9361 }
9362 };
9363
9364 // Reset the input to correspond to the selection (or to be empty,
9365 // when not typing and nothing is selected)
9366 TextareaInput.prototype.reset = function (typing) {
9367 if (this.contextMenuPending || this.composing) { return }
9368 var cm = this.cm;
9369 if (cm.somethingSelected()) {
9370 this.prevInput = "";
9371 var content = cm.getSelection();
9372 this.textarea.value = content;
9373 if (cm.state.focused) { selectInput(this.textarea); }
9374 if (ie && ie_version >= 9) { this.hasSelection = content; }
9375 } else if (!typing) {
9376 this.prevInput = this.textarea.value = "";
9377 if (ie && ie_version >= 9) { this.hasSelection = null; }
9378 }
9379 };
9380
9381 TextareaInput.prototype.getField = function () { return this.textarea };
9382
9383 TextareaInput.prototype.supportsTouch = function () { return false };
9384
9385 TextareaInput.prototype.focus = function () {
9386 if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) {
9387 try { this.textarea.focus(); }
9388 catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM
9389 }
9390 };
9391
9392 TextareaInput.prototype.blur = function () { this.textarea.blur(); };
9393
9394 TextareaInput.prototype.resetPosition = function () {
9395 this.wrapper.style.top = this.wrapper.style.left = 0;
9396 };
9397
9398 TextareaInput.prototype.receivedFocus = function () { this.slowPoll(); };
9399
9400 // Poll for input changes, using the normal rate of polling. This
9401 // runs as long as the editor is focused.
9402 TextareaInput.prototype.slowPoll = function () {
9403 var this$1 = this;
9404
9405 if (this.pollingFast) { return }
9406 this.polling.set(this.cm.options.pollInterval, function () {
9407 this$1.poll();
9408 if (this$1.cm.state.focused) { this$1.slowPoll(); }
9409 });
9410 };
9411
9412 // When an event has just come in that is likely to add or change
9413 // something in the input textarea, we poll faster, to ensure that
9414 // the change appears on the screen quickly.
9415 TextareaInput.prototype.fastPoll = function () {
9416 var missed = false, input = this;
9417 input.pollingFast = true;
9418 function p() {
9419 var changed = input.poll();
9420 if (!changed && !missed) {missed = true; input.polling.set(60, p);}
9421 else {input.pollingFast = false; input.slowPoll();}
9422 }
9423 input.polling.set(20, p);
9424 };
9425
9426 // Read input from the textarea, and update the document to match.
9427 // When something is selected, it is present in the textarea, and
9428 // selected (unless it is huge, in which case a placeholder is
9429 // used). When nothing is selected, the cursor sits after previously
9430 // seen text (can be empty), which is stored in prevInput (we must
9431 // not reset the textarea when typing, because that breaks IME).
9432 TextareaInput.prototype.poll = function () {
9433 var this$1 = this;
9434
9435 var cm = this.cm, input = this.textarea, prevInput = this.prevInput;
9436 // Since this is called a *lot*, try to bail out as cheaply as
9437 // possible when it is clear that nothing happened. hasSelection
9438 // will be the case when there is a lot of text in the textarea,
9439 // in which case reading its value would be expensive.
9440 if (this.contextMenuPending || !cm.state.focused ||
9441 (hasSelection(input) && !prevInput && !this.composing) ||
9442 cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq)
9443 { return false }
9444
9445 var text = input.value;
9446 // If nothing changed, bail.
9447 if (text == prevInput && !cm.somethingSelected()) { return false }
9448 // Work around nonsensical selection resetting in IE9/10, and
9449 // inexplicable appearance of private area unicode characters on
9450 // some key combos in Mac (#2689).
9451 if (ie && ie_version >= 9 && this.hasSelection === text ||
9452 mac && /[\uf700-\uf7ff]/.test(text)) {
9453 cm.display.input.reset();
9454 return false
9455 }
9456
9457 if (cm.doc.sel == cm.display.selForContextMenu) {
9458 var first = text.charCodeAt(0);
9459 if (first == 0x200b && !prevInput) { prevInput = "\u200b"; }
9460 if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") }
9461 }
9462 // Find the part of the input that is actually new
9463 var same = 0, l = Math.min(prevInput.length, text.length);
9464 while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same; }
9465
9466 runInOp(cm, function () {
9467 applyTextInput(cm, text.slice(same), prevInput.length - same,
9468 null, this$1.composing ? "*compose" : null);
9469
9470 // Don't leave long text in the textarea, since it makes further polling slow
9471 if (text.length > 1000 || text.indexOf("\n") > -1) { input.value = this$1.prevInput = ""; }
9472 else { this$1.prevInput = text; }
9473
9474 if (this$1.composing) {
9475 this$1.composing.range.clear();
9476 this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor("to"),
9477 {className: "CodeMirror-composing"});
9478 }
9479 });
9480 return true
9481 };
9482
9483 TextareaInput.prototype.ensurePolled = function () {
9484 if (this.pollingFast && this.poll()) { this.pollingFast = false; }
9485 };
9486
9487 TextareaInput.prototype.onKeyPress = function () {
9488 if (ie && ie_version >= 9) { this.hasSelection = null; }
9489 this.fastPoll();
9490 };
9491
9492 TextareaInput.prototype.onContextMenu = function (e) {
9493 var input = this, cm = input.cm, display = cm.display, te = input.textarea;
9494 if (input.contextMenuPending) { input.contextMenuPending(); }
9495 var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
9496 if (!pos || presto) { return } // Opera is difficult.
9497
9498 // Reset the current text selection only if the click is done outside of the selection
9499 // and 'resetSelectionOnContextMenu' option is true.
9500 var reset = cm.options.resetSelectionOnContextMenu;
9501 if (reset && cm.doc.sel.contains(pos) == -1)
9502 { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll); }
9503
9504 var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText;
9505 var wrapperBox = input.wrapper.offsetParent.getBoundingClientRect();
9506 input.wrapper.style.cssText = "position: static";
9507 te.style.cssText = "position: absolute; width: 30px; height: 30px;\n top: " + (e.clientY - wrapperBox.top - 5) + "px; left: " + (e.clientX - wrapperBox.left - 5) + "px;\n z-index: 1000; background: " + (ie ? "rgba(255, 255, 255, .05)" : "transparent") + ";\n outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
9508 var oldScrollY;
9509 if (webkit) { oldScrollY = window.scrollY; } // Work around Chrome issue (#2712)
9510 display.input.focus();
9511 if (webkit) { window.scrollTo(null, oldScrollY); }
9512 display.input.reset();
9513 // Adds "Select all" to context menu in FF
9514 if (!cm.somethingSelected()) { te.value = input.prevInput = " "; }
9515 input.contextMenuPending = rehide;
9516 display.selForContextMenu = cm.doc.sel;
9517 clearTimeout(display.detectingSelectAll);
9518
9519 // Select-all will be greyed out if there's nothing to select, so
9520 // this adds a zero-width space so that we can later check whether
9521 // it got selected.
9522 function prepareSelectAllHack() {
9523 if (te.selectionStart != null) {
9524 var selected = cm.somethingSelected();
9525 var extval = "\u200b" + (selected ? te.value : "");
9526 te.value = "\u21da"; // Used to catch context-menu undo
9527 te.value = extval;
9528 input.prevInput = selected ? "" : "\u200b";
9529 te.selectionStart = 1; te.selectionEnd = extval.length;
9530 // Re-set this, in case some other handler touched the
9531 // selection in the meantime.
9532 display.selForContextMenu = cm.doc.sel;
9533 }
9534 }
9535 function rehide() {
9536 if (input.contextMenuPending != rehide) { return }
9537 input.contextMenuPending = false;
9538 input.wrapper.style.cssText = oldWrapperCSS;
9539 te.style.cssText = oldCSS;
9540 if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos); }
9541
9542 // Try to detect the user choosing select-all
9543 if (te.selectionStart != null) {
9544 if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack(); }
9545 var i = 0, poll = function () {
9546 if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 &&
9547 te.selectionEnd > 0 && input.prevInput == "\u200b") {
9548 operation(cm, selectAll)(cm);
9549 } else if (i++ < 10) {
9550 display.detectingSelectAll = setTimeout(poll, 500);
9551 } else {
9552 display.selForContextMenu = null;
9553 display.input.reset();
9554 }
9555 };
9556 display.detectingSelectAll = setTimeout(poll, 200);
9557 }
9558 }
9559
9560 if (ie && ie_version >= 9) { prepareSelectAllHack(); }
9561 if (captureRightClick) {
9562 e_stop(e);
9563 var mouseup = function () {
9564 off(window, "mouseup", mouseup);
9565 setTimeout(rehide, 20);
9566 };
9567 on(window, "mouseup", mouseup);
9568 } else {
9569 setTimeout(rehide, 50);
9570 }
9571 };
9572
9573 TextareaInput.prototype.readOnlyChanged = function (val) {
9574 if (!val) { this.reset(); }
9575 this.textarea.disabled = val == "nocursor";
9576 };
9577
9578 TextareaInput.prototype.setUneditable = function () {};
9579
9580 TextareaInput.prototype.needsContentAttribute = false;
9581
9582 function fromTextArea(textarea, options) {
9583 options = options ? copyObj(options) : {};
9584 options.value = textarea.value;
9585 if (!options.tabindex && textarea.tabIndex)
9586 { options.tabindex = textarea.tabIndex; }
9587 if (!options.placeholder && textarea.placeholder)
9588 { options.placeholder = textarea.placeholder; }
9589 // Set autofocus to true if this textarea is focused, or if it has
9590 // autofocus and no other element is focused.
9591 if (options.autofocus == null) {
9592 var hasFocus = activeElt();
9593 options.autofocus = hasFocus == textarea ||
9594 textarea.getAttribute("autofocus") != null && hasFocus == document.body;
9595 }
9596
9597 function save() {textarea.value = cm.getValue();}
9598
9599 var realSubmit;
9600 if (textarea.form) {
9601 on(textarea.form, "submit", save);
9602 // Deplorable hack to make the submit method do the right thing.
9603 if (!options.leaveSubmitMethodAlone) {
9604 var form = textarea.form;
9605 realSubmit = form.submit;
9606 try {
9607 var wrappedSubmit = form.submit = function () {
9608 save();
9609 form.submit = realSubmit;
9610 form.submit();
9611 form.submit = wrappedSubmit;
9612 };
9613 } catch(e) {}
9614 }
9615 }
9616
9617 options.finishInit = function (cm) {
9618 cm.save = save;
9619 cm.getTextArea = function () { return textarea; };
9620 cm.toTextArea = function () {
9621 cm.toTextArea = isNaN; // Prevent this from being ran twice
9622 save();
9623 textarea.parentNode.removeChild(cm.getWrapperElement());
9624 textarea.style.display = "";
9625 if (textarea.form) {
9626 off(textarea.form, "submit", save);
9627 if (typeof textarea.form.submit == "function")
9628 { textarea.form.submit = realSubmit; }
9629 }
9630 };
9631 };
9632
9633 textarea.style.display = "none";
9634 var cm = CodeMirror(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); },
9635 options);
9636 return cm
9637 }
9638
9639 function addLegacyProps(CodeMirror) {
9640 CodeMirror.off = off;
9641 CodeMirror.on = on;
9642 CodeMirror.wheelEventPixels = wheelEventPixels;
9643 CodeMirror.Doc = Doc;
9644 CodeMirror.splitLines = splitLinesAuto;
9645 CodeMirror.countColumn = countColumn;
9646 CodeMirror.findColumn = findColumn;
9647 CodeMirror.isWordChar = isWordCharBasic;
9648 CodeMirror.Pass = Pass;
9649 CodeMirror.signal = signal;
9650 CodeMirror.Line = Line;
9651 CodeMirror.changeEnd = changeEnd;
9652 CodeMirror.scrollbarModel = scrollbarModel;
9653 CodeMirror.Pos = Pos;
9654 CodeMirror.cmpPos = cmp;
9655 CodeMirror.modes = modes;
9656 CodeMirror.mimeModes = mimeModes;
9657 CodeMirror.resolveMode = resolveMode;
9658 CodeMirror.getMode = getMode;
9659 CodeMirror.modeExtensions = modeExtensions;
9660 CodeMirror.extendMode = extendMode;
9661 CodeMirror.copyState = copyState;
9662 CodeMirror.startState = startState;
9663 CodeMirror.innerMode = innerMode;
9664 CodeMirror.commands = commands;
9665 CodeMirror.keyMap = keyMap;
9666 CodeMirror.keyName = keyName;
9667 CodeMirror.isModifierKey = isModifierKey;
9668 CodeMirror.lookupKey = lookupKey;
9669 CodeMirror.normalizeKeyMap = normalizeKeyMap;
9670 CodeMirror.StringStream = StringStream;
9671 CodeMirror.SharedTextMarker = SharedTextMarker;
9672 CodeMirror.TextMarker = TextMarker;
9673 CodeMirror.LineWidget = LineWidget;
9674 CodeMirror.e_preventDefault = e_preventDefault;
9675 CodeMirror.e_stopPropagation = e_stopPropagation;
9676 CodeMirror.e_stop = e_stop;
9677 CodeMirror.addClass = addClass;
9678 CodeMirror.contains = contains;
9679 CodeMirror.rmClass = rmClass;
9680 CodeMirror.keyNames = keyNames;
9681 }
9682
9683 // EDITOR CONSTRUCTOR
9684
9685 defineOptions(CodeMirror);
9686
9687 addEditorMethods(CodeMirror);
9688
9689 // Set up methods on CodeMirror's prototype to redirect to the editor's document.
9690 var dontDelegate = "iter insert remove copy getEditor constructor".split(" ");
9691 for (var prop in Doc.prototype) { if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
9692 { CodeMirror.prototype[prop] = (function(method) {
9693 return function() {return method.apply(this.doc, arguments)}
9694 })(Doc.prototype[prop]); } }
9695
9696 eventMixin(Doc);
9697 CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput};
9698
9699 // Extra arguments are stored as the mode's dependencies, which is
9700 // used by (legacy) mechanisms like loadmode.js to automatically
9701 // load a mode. (Preferred mechanism is the require/define calls.)
9702 CodeMirror.defineMode = function(name/*, mode, …*/) {
9703 if (!CodeMirror.defaults.mode && name != "null") { CodeMirror.defaults.mode = name; }
9704 defineMode.apply(this, arguments);
9705 };
9706
9707 CodeMirror.defineMIME = defineMIME;
9708
9709 // Minimal default mode.
9710 CodeMirror.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); });
9711 CodeMirror.defineMIME("text/plain", "null");
9712
9713 // EXTENSIONS
9714
9715 CodeMirror.defineExtension = function (name, func) {
9716 CodeMirror.prototype[name] = func;
9717 };
9718 CodeMirror.defineDocExtension = function (name, func) {
9719 Doc.prototype[name] = func;
9720 };
9721
9722 CodeMirror.fromTextArea = fromTextArea;
9723
9724 addLegacyProps(CodeMirror);
9725
9726 CodeMirror.version = "5.42.2";
9727
9728 return CodeMirror;
9729
9730 })));
9731