comparison plugins/managesieve/codemirror/lib/codemirror.js @ 0:1e000243b222

vanilla 1.3.3 distro, I hope
author Charlie Root
date Thu, 04 Jan 2018 15:50:29 -0500
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:1e000243b222
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
4 // This is CodeMirror (http://codemirror.net), a code editor
5 // implemented in JavaScript on top of the browser's DOM.
6 //
7 // You can find some technical background for some of the code below
8 // at http://marijnhaverbeke.nl/blog/#cm-internals .
9
10 (function (global, factory) {
11 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
12 typeof define === 'function' && define.amd ? define(factory) :
13 (global.CodeMirror = factory());
14 }(this, (function () { 'use strict';
15
16 // Kludges for bugs and behavior differences that can't be feature
17 // detected are enabled based on userAgent etc sniffing.
18 var userAgent = navigator.userAgent
19 var platform = navigator.platform
20
21 var gecko = /gecko\/\d/i.test(userAgent)
22 var ie_upto10 = /MSIE \d/.test(userAgent)
23 var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent)
24 var ie = ie_upto10 || ie_11up
25 var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : ie_11up[1])
26 var webkit = /WebKit\//.test(userAgent)
27 var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent)
28 var chrome = /Chrome\//.test(userAgent)
29 var presto = /Opera\//.test(userAgent)
30 var safari = /Apple Computer/.test(navigator.vendor)
31 var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent)
32 var phantom = /PhantomJS/.test(userAgent)
33
34 var ios = /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent)
35 // This is woefully incomplete. Suggestions for alternative methods welcome.
36 var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent)
37 var mac = ios || /Mac/.test(platform)
38 var chromeOS = /\bCrOS\b/.test(userAgent)
39 var windows = /win/i.test(platform)
40
41 var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/)
42 if (presto_version) { presto_version = Number(presto_version[1]) }
43 if (presto_version && presto_version >= 15) { presto = false; webkit = true }
44 // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
45 var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11))
46 var captureRightClick = gecko || (ie && ie_version >= 9)
47
48 function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") }
49
50 var rmClass = function(node, cls) {
51 var current = node.className
52 var match = classTest(cls).exec(current)
53 if (match) {
54 var after = current.slice(match.index + match[0].length)
55 node.className = current.slice(0, match.index) + (after ? match[1] + after : "")
56 }
57 }
58
59 function removeChildren(e) {
60 for (var count = e.childNodes.length; count > 0; --count)
61 { e.removeChild(e.firstChild) }
62 return e
63 }
64
65 function removeChildrenAndAdd(parent, e) {
66 return removeChildren(parent).appendChild(e)
67 }
68
69 function elt(tag, content, className, style) {
70 var e = document.createElement(tag)
71 if (className) { e.className = className }
72 if (style) { e.style.cssText = style }
73 if (typeof content == "string") { e.appendChild(document.createTextNode(content)) }
74 else if (content) { for (var i = 0; i < content.length; ++i) { e.appendChild(content[i]) } }
75 return e
76 }
77
78 var range
79 if (document.createRange) { range = function(node, start, end, endNode) {
80 var r = document.createRange()
81 r.setEnd(endNode || node, end)
82 r.setStart(node, start)
83 return r
84 } }
85 else { range = function(node, start, end) {
86 var r = document.body.createTextRange()
87 try { r.moveToElementText(node.parentNode) }
88 catch(e) { return r }
89 r.collapse(true)
90 r.moveEnd("character", end)
91 r.moveStart("character", start)
92 return r
93 } }
94
95 function contains(parent, child) {
96 if (child.nodeType == 3) // Android browser always returns false when child is a textnode
97 { child = child.parentNode }
98 if (parent.contains)
99 { return parent.contains(child) }
100 do {
101 if (child.nodeType == 11) { child = child.host }
102 if (child == parent) { return true }
103 } while (child = child.parentNode)
104 }
105
106 function activeElt() {
107 // IE and Edge may throw an "Unspecified Error" when accessing document.activeElement.
108 // IE < 10 will throw when accessed while the page is loading or in an iframe.
109 // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable.
110 var activeElement
111 try {
112 activeElement = document.activeElement
113 } catch(e) {
114 activeElement = document.body || null
115 }
116 while (activeElement && activeElement.root && activeElement.root.activeElement)
117 { activeElement = activeElement.root.activeElement }
118 return activeElement
119 }
120
121 function addClass(node, cls) {
122 var current = node.className
123 if (!classTest(cls).test(current)) { node.className += (current ? " " : "") + cls }
124 }
125 function joinClasses(a, b) {
126 var as = a.split(" ")
127 for (var i = 0; i < as.length; i++)
128 { if (as[i] && !classTest(as[i]).test(b)) { b += " " + as[i] } }
129 return b
130 }
131
132 var selectInput = function(node) { node.select() }
133 if (ios) // Mobile Safari apparently has a bug where select() is broken.
134 { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length } }
135 else if (ie) // Suppress mysterious IE10 errors
136 { selectInput = function(node) { try { node.select() } catch(_e) {} } }
137
138 function bind(f) {
139 var args = Array.prototype.slice.call(arguments, 1)
140 return function(){return f.apply(null, args)}
141 }
142
143 function copyObj(obj, target, overwrite) {
144 if (!target) { target = {} }
145 for (var prop in obj)
146 { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
147 { target[prop] = obj[prop] } }
148 return target
149 }
150
151 // Counts the column offset in a string, taking tabs into account.
152 // Used mostly to find indentation.
153 function countColumn(string, end, tabSize, startIndex, startValue) {
154 if (end == null) {
155 end = string.search(/[^\s\u00a0]/)
156 if (end == -1) { end = string.length }
157 }
158 for (var i = startIndex || 0, n = startValue || 0;;) {
159 var nextTab = string.indexOf("\t", i)
160 if (nextTab < 0 || nextTab >= end)
161 { return n + (end - i) }
162 n += nextTab - i
163 n += tabSize - (n % tabSize)
164 i = nextTab + 1
165 }
166 }
167
168 function Delayed() {this.id = null}
169 Delayed.prototype.set = function(ms, f) {
170 clearTimeout(this.id)
171 this.id = setTimeout(f, ms)
172 }
173
174 function indexOf(array, elt) {
175 for (var i = 0; i < array.length; ++i)
176 { if (array[i] == elt) { return i } }
177 return -1
178 }
179
180 // Number of pixels added to scroller and sizer to hide scrollbar
181 var scrollerGap = 30
182
183 // Returned or thrown by various protocols to signal 'I'm not
184 // handling this'.
185 var Pass = {toString: function(){return "CodeMirror.Pass"}}
186
187 // Reused option objects for setSelection & friends
188 var sel_dontScroll = {scroll: false};
189 var sel_mouse = {origin: "*mouse"};
190 var sel_move = {origin: "+move"}
191
192 // The inverse of countColumn -- find the offset that corresponds to
193 // a particular column.
194 function findColumn(string, goal, tabSize) {
195 for (var pos = 0, col = 0;;) {
196 var nextTab = string.indexOf("\t", pos)
197 if (nextTab == -1) { nextTab = string.length }
198 var skipped = nextTab - pos
199 if (nextTab == string.length || col + skipped >= goal)
200 { return pos + Math.min(skipped, goal - col) }
201 col += nextTab - pos
202 col += tabSize - (col % tabSize)
203 pos = nextTab + 1
204 if (col >= goal) { return pos }
205 }
206 }
207
208 var spaceStrs = [""]
209 function spaceStr(n) {
210 while (spaceStrs.length <= n)
211 { spaceStrs.push(lst(spaceStrs) + " ") }
212 return spaceStrs[n]
213 }
214
215 function lst(arr) { return arr[arr.length-1] }
216
217 function map(array, f) {
218 var out = []
219 for (var i = 0; i < array.length; i++) { out[i] = f(array[i], i) }
220 return out
221 }
222
223 function insertSorted(array, value, score) {
224 var pos = 0, priority = score(value)
225 while (pos < array.length && score(array[pos]) <= priority) { pos++ }
226 array.splice(pos, 0, value)
227 }
228
229 function nothing() {}
230
231 function createObj(base, props) {
232 var inst
233 if (Object.create) {
234 inst = Object.create(base)
235 } else {
236 nothing.prototype = base
237 inst = new nothing()
238 }
239 if (props) { copyObj(props, inst) }
240 return inst
241 }
242
243 var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/
244 function isWordCharBasic(ch) {
245 return /\w/.test(ch) || ch > "\x80" &&
246 (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch))
247 }
248 function isWordChar(ch, helper) {
249 if (!helper) { return isWordCharBasic(ch) }
250 if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) { return true }
251 return helper.test(ch)
252 }
253
254 function isEmpty(obj) {
255 for (var n in obj) { if (obj.hasOwnProperty(n) && obj[n]) { return false } }
256 return true
257 }
258
259 // Extending unicode characters. A series of a non-extending char +
260 // any number of extending chars is treated as a single unit as far
261 // as editing and measuring is concerned. This is not fully correct,
262 // since some scripts/fonts/browsers also treat other configurations
263 // of code points as a group.
264 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]/
265 function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) }
266
267 // The display handles the DOM integration, both for input reading
268 // and content drawing. It holds references to DOM nodes and
269 // display-related state.
270
271 function Display(place, doc, input) {
272 var d = this
273 this.input = input
274
275 // Covers bottom-right square when both scrollbars are present.
276 d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler")
277 d.scrollbarFiller.setAttribute("cm-not-content", "true")
278 // Covers bottom of gutter when coverGutterNextToScrollbar is on
279 // and h scrollbar is present.
280 d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler")
281 d.gutterFiller.setAttribute("cm-not-content", "true")
282 // Will contain the actual code, positioned to cover the viewport.
283 d.lineDiv = elt("div", null, "CodeMirror-code")
284 // Elements are added to these to represent selection and cursors.
285 d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1")
286 d.cursorDiv = elt("div", null, "CodeMirror-cursors")
287 // A visibility: hidden element used to find the size of things.
288 d.measure = elt("div", null, "CodeMirror-measure")
289 // When lines outside of the viewport are measured, they are drawn in this.
290 d.lineMeasure = elt("div", null, "CodeMirror-measure")
291 // Wraps everything that needs to exist inside the vertically-padded coordinate system
292 d.lineSpace = elt("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],
293 null, "position: relative; outline: none")
294 // Moved around its parent to cover visible view.
295 d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null, "position: relative")
296 // Set to the height of the document, allowing scrolling.
297 d.sizer = elt("div", [d.mover], "CodeMirror-sizer")
298 d.sizerWidth = null
299 // Behavior of elts with overflow: auto and padding is
300 // inconsistent across browsers. This is used to ensure the
301 // scrollable area is big enough.
302 d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;")
303 // Will contain the gutters, if any.
304 d.gutters = elt("div", null, "CodeMirror-gutters")
305 d.lineGutter = null
306 // Actual scrollable element.
307 d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll")
308 d.scroller.setAttribute("tabIndex", "-1")
309 // The element in which the editor lives.
310 d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror")
311
312 // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)
313 if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0 }
314 if (!webkit && !(gecko && mobile)) { d.scroller.draggable = true }
315
316 if (place) {
317 if (place.appendChild) { place.appendChild(d.wrapper) }
318 else { place(d.wrapper) }
319 }
320
321 // Current rendered range (may be bigger than the view window).
322 d.viewFrom = d.viewTo = doc.first
323 d.reportedViewFrom = d.reportedViewTo = doc.first
324 // Information about the rendered lines.
325 d.view = []
326 d.renderedView = null
327 // Holds info about a single rendered line when it was rendered
328 // for measurement, while not in view.
329 d.externalMeasured = null
330 // Empty space (in pixels) above the view
331 d.viewOffset = 0
332 d.lastWrapHeight = d.lastWrapWidth = 0
333 d.updateLineNumbers = null
334
335 d.nativeBarWidth = d.barHeight = d.barWidth = 0
336 d.scrollbarsClipped = false
337
338 // Used to only resize the line number gutter when necessary (when
339 // the amount of lines crosses a boundary that makes its width change)
340 d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null
341 // Set to true when a non-horizontal-scrolling line widget is
342 // added. As an optimization, line widget aligning is skipped when
343 // this is false.
344 d.alignWidgets = false
345
346 d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null
347
348 // Tracks the maximum line length so that the horizontal scrollbar
349 // can be kept static when scrolling.
350 d.maxLine = null
351 d.maxLineLength = 0
352 d.maxLineChanged = false
353
354 // Used for measuring wheel scrolling granularity
355 d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null
356
357 // True when shift is held down.
358 d.shift = false
359
360 // Used to track whether anything happened since the context menu
361 // was opened.
362 d.selForContextMenu = null
363
364 d.activeTouch = null
365
366 input.init(d)
367 }
368
369 // Find the line object corresponding to the given line number.
370 function getLine(doc, n) {
371 n -= doc.first
372 if (n < 0 || n >= doc.size) { throw new Error("There is no line " + (n + doc.first) + " in the document.") }
373 var chunk = doc
374 while (!chunk.lines) {
375 for (var i = 0;; ++i) {
376 var child = chunk.children[i], sz = child.chunkSize()
377 if (n < sz) { chunk = child; break }
378 n -= sz
379 }
380 }
381 return chunk.lines[n]
382 }
383
384 // Get the part of a document between two positions, as an array of
385 // strings.
386 function getBetween(doc, start, end) {
387 var out = [], n = start.line
388 doc.iter(start.line, end.line + 1, function (line) {
389 var text = line.text
390 if (n == end.line) { text = text.slice(0, end.ch) }
391 if (n == start.line) { text = text.slice(start.ch) }
392 out.push(text)
393 ++n
394 })
395 return out
396 }
397 // Get the lines between from and to, as array of strings.
398 function getLines(doc, from, to) {
399 var out = []
400 doc.iter(from, to, function (line) { out.push(line.text) }) // iter aborts when callback returns truthy value
401 return out
402 }
403
404 // Update the height of a line, propagating the height change
405 // upwards to parent nodes.
406 function updateLineHeight(line, height) {
407 var diff = height - line.height
408 if (diff) { for (var n = line; n; n = n.parent) { n.height += diff } }
409 }
410
411 // Given a line object, find its line number by walking up through
412 // its parent links.
413 function lineNo(line) {
414 if (line.parent == null) { return null }
415 var cur = line.parent, no = indexOf(cur.lines, line)
416 for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
417 for (var i = 0;; ++i) {
418 if (chunk.children[i] == cur) { break }
419 no += chunk.children[i].chunkSize()
420 }
421 }
422 return no + cur.first
423 }
424
425 // Find the line at the given vertical position, using the height
426 // information in the document tree.
427 function lineAtHeight(chunk, h) {
428 var n = chunk.first
429 outer: do {
430 for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) {
431 var child = chunk.children[i$1], ch = child.height
432 if (h < ch) { chunk = child; continue outer }
433 h -= ch
434 n += child.chunkSize()
435 }
436 return n
437 } while (!chunk.lines)
438 var i = 0
439 for (; i < chunk.lines.length; ++i) {
440 var line = chunk.lines[i], lh = line.height
441 if (h < lh) { break }
442 h -= lh
443 }
444 return n + i
445 }
446
447 function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size}
448
449 function lineNumberFor(options, i) {
450 return String(options.lineNumberFormatter(i + options.firstLineNumber))
451 }
452
453 // A Pos instance represents a position within the text.
454 function Pos (line, ch) {
455 if (!(this instanceof Pos)) { return new Pos(line, ch) }
456 this.line = line; this.ch = ch
457 }
458
459 // Compare two positions, return 0 if they are the same, a negative
460 // number when a is less, and a positive number otherwise.
461 function cmp(a, b) { return a.line - b.line || a.ch - b.ch }
462
463 function copyPos(x) {return Pos(x.line, x.ch)}
464 function maxPos(a, b) { return cmp(a, b) < 0 ? b : a }
465 function minPos(a, b) { return cmp(a, b) < 0 ? a : b }
466
467 // Most of the external API clips given positions to make sure they
468 // actually exist within the document.
469 function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))}
470 function clipPos(doc, pos) {
471 if (pos.line < doc.first) { return Pos(doc.first, 0) }
472 var last = doc.first + doc.size - 1
473 if (pos.line > last) { return Pos(last, getLine(doc, last).text.length) }
474 return clipToLen(pos, getLine(doc, pos.line).text.length)
475 }
476 function clipToLen(pos, linelen) {
477 var ch = pos.ch
478 if (ch == null || ch > linelen) { return Pos(pos.line, linelen) }
479 else if (ch < 0) { return Pos(pos.line, 0) }
480 else { return pos }
481 }
482 function clipPosArray(doc, array) {
483 var out = []
484 for (var i = 0; i < array.length; i++) { out[i] = clipPos(doc, array[i]) }
485 return out
486 }
487
488 // Optimize some code when these features are not used.
489 var sawReadOnlySpans = false;
490 var sawCollapsedSpans = false
491
492 function seeReadOnlySpans() {
493 sawReadOnlySpans = true
494 }
495
496 function seeCollapsedSpans() {
497 sawCollapsedSpans = true
498 }
499
500 // TEXTMARKER SPANS
501
502 function MarkedSpan(marker, from, to) {
503 this.marker = marker
504 this.from = from; this.to = to
505 }
506
507 // Search an array of spans for a span matching the given marker.
508 function getMarkedSpanFor(spans, marker) {
509 if (spans) { for (var i = 0; i < spans.length; ++i) {
510 var span = spans[i]
511 if (span.marker == marker) { return span }
512 } }
513 }
514 // Remove a span from an array, returning undefined if no spans are
515 // left (we don't store arrays for lines without spans).
516 function removeMarkedSpan(spans, span) {
517 var r
518 for (var i = 0; i < spans.length; ++i)
519 { if (spans[i] != span) { (r || (r = [])).push(spans[i]) } }
520 return r
521 }
522 // Add a span to a line.
523 function addMarkedSpan(line, span) {
524 line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span]
525 span.marker.attachLine(line)
526 }
527
528 // Used for the algorithm that adjusts markers for a change in the
529 // document. These functions cut an array of spans at a given
530 // character position, returning an array of remaining chunks (or
531 // undefined if nothing remains).
532 function markedSpansBefore(old, startCh, isInsert) {
533 var nw
534 if (old) { for (var i = 0; i < old.length; ++i) {
535 var span = old[i], marker = span.marker
536 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh)
537 if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) {
538 var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh);(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to))
539 }
540 } }
541 return nw
542 }
543 function markedSpansAfter(old, endCh, isInsert) {
544 var nw
545 if (old) { for (var i = 0; i < old.length; ++i) {
546 var span = old[i], marker = span.marker
547 var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh)
548 if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) {
549 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh);(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh,
550 span.to == null ? null : span.to - endCh))
551 }
552 } }
553 return nw
554 }
555
556 // Given a change object, compute the new set of marker spans that
557 // cover the line in which the change took place. Removes spans
558 // entirely within the change, reconnects spans belonging to the
559 // same marker that appear on both sides of the change, and cuts off
560 // spans partially within the change. Returns an array of span
561 // arrays with one element for each line in (after) the change.
562 function stretchSpansOverChange(doc, change) {
563 if (change.full) { return null }
564 var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans
565 var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans
566 if (!oldFirst && !oldLast) { return null }
567
568 var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0
569 // Get the spans that 'stick out' on both sides
570 var first = markedSpansBefore(oldFirst, startCh, isInsert)
571 var last = markedSpansAfter(oldLast, endCh, isInsert)
572
573 // Next, merge those two ends
574 var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0)
575 if (first) {
576 // Fix up .to properties of first
577 for (var i = 0; i < first.length; ++i) {
578 var span = first[i]
579 if (span.to == null) {
580 var found = getMarkedSpanFor(last, span.marker)
581 if (!found) { span.to = startCh }
582 else if (sameLine) { span.to = found.to == null ? null : found.to + offset }
583 }
584 }
585 }
586 if (last) {
587 // Fix up .from in last (or move them into first in case of sameLine)
588 for (var i$1 = 0; i$1 < last.length; ++i$1) {
589 var span$1 = last[i$1]
590 if (span$1.to != null) { span$1.to += offset }
591 if (span$1.from == null) {
592 var found$1 = getMarkedSpanFor(first, span$1.marker)
593 if (!found$1) {
594 span$1.from = offset
595 if (sameLine) { (first || (first = [])).push(span$1) }
596 }
597 } else {
598 span$1.from += offset
599 if (sameLine) { (first || (first = [])).push(span$1) }
600 }
601 }
602 }
603 // Make sure we didn't create any zero-length spans
604 if (first) { first = clearEmptySpans(first) }
605 if (last && last != first) { last = clearEmptySpans(last) }
606
607 var newMarkers = [first]
608 if (!sameLine) {
609 // Fill gap with whole-line-spans
610 var gap = change.text.length - 2, gapMarkers
611 if (gap > 0 && first)
612 { for (var i$2 = 0; i$2 < first.length; ++i$2)
613 { if (first[i$2].to == null)
614 { (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i$2].marker, null, null)) } } }
615 for (var i$3 = 0; i$3 < gap; ++i$3)
616 { newMarkers.push(gapMarkers) }
617 newMarkers.push(last)
618 }
619 return newMarkers
620 }
621
622 // Remove spans that are empty and don't have a clearWhenEmpty
623 // option of false.
624 function clearEmptySpans(spans) {
625 for (var i = 0; i < spans.length; ++i) {
626 var span = spans[i]
627 if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)
628 { spans.splice(i--, 1) }
629 }
630 if (!spans.length) { return null }
631 return spans
632 }
633
634 // Used to 'clip' out readOnly ranges when making a change.
635 function removeReadOnlyRanges(doc, from, to) {
636 var markers = null
637 doc.iter(from.line, to.line + 1, function (line) {
638 if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
639 var mark = line.markedSpans[i].marker
640 if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
641 { (markers || (markers = [])).push(mark) }
642 } }
643 })
644 if (!markers) { return null }
645 var parts = [{from: from, to: to}]
646 for (var i = 0; i < markers.length; ++i) {
647 var mk = markers[i], m = mk.find(0)
648 for (var j = 0; j < parts.length; ++j) {
649 var p = parts[j]
650 if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) { continue }
651 var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to)
652 if (dfrom < 0 || !mk.inclusiveLeft && !dfrom)
653 { newParts.push({from: p.from, to: m.from}) }
654 if (dto > 0 || !mk.inclusiveRight && !dto)
655 { newParts.push({from: m.to, to: p.to}) }
656 parts.splice.apply(parts, newParts)
657 j += newParts.length - 1
658 }
659 }
660 return parts
661 }
662
663 // Connect or disconnect spans from a line.
664 function detachMarkedSpans(line) {
665 var spans = line.markedSpans
666 if (!spans) { return }
667 for (var i = 0; i < spans.length; ++i)
668 { spans[i].marker.detachLine(line) }
669 line.markedSpans = null
670 }
671 function attachMarkedSpans(line, spans) {
672 if (!spans) { return }
673 for (var i = 0; i < spans.length; ++i)
674 { spans[i].marker.attachLine(line) }
675 line.markedSpans = spans
676 }
677
678 // Helpers used when computing which overlapping collapsed span
679 // counts as the larger one.
680 function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 }
681 function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 }
682
683 // Returns a number indicating which of two overlapping collapsed
684 // spans is larger (and thus includes the other). Falls back to
685 // comparing ids when the spans cover exactly the same range.
686 function compareCollapsedMarkers(a, b) {
687 var lenDiff = a.lines.length - b.lines.length
688 if (lenDiff != 0) { return lenDiff }
689 var aPos = a.find(), bPos = b.find()
690 var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b)
691 if (fromCmp) { return -fromCmp }
692 var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b)
693 if (toCmp) { return toCmp }
694 return b.id - a.id
695 }
696
697 // Find out whether a line ends or starts in a collapsed span. If
698 // so, return the marker for that span.
699 function collapsedSpanAtSide(line, start) {
700 var sps = sawCollapsedSpans && line.markedSpans, found
701 if (sps) { for (var sp = void 0, i = 0; i < sps.length; ++i) {
702 sp = sps[i]
703 if (sp.marker.collapsed && (start ? sp.from : sp.to) == null &&
704 (!found || compareCollapsedMarkers(found, sp.marker) < 0))
705 { found = sp.marker }
706 } }
707 return found
708 }
709 function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) }
710 function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) }
711
712 // Test whether there exists a collapsed span that partially
713 // overlaps (covers the start or end, but not both) of a new span.
714 // Such overlap is not allowed.
715 function conflictingCollapsedRange(doc, lineNo$$1, from, to, marker) {
716 var line = getLine(doc, lineNo$$1)
717 var sps = sawCollapsedSpans && line.markedSpans
718 if (sps) { for (var i = 0; i < sps.length; ++i) {
719 var sp = sps[i]
720 if (!sp.marker.collapsed) { continue }
721 var found = sp.marker.find(0)
722 var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker)
723 var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker)
724 if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue }
725 if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) ||
726 fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0))
727 { return true }
728 } }
729 }
730
731 // A visual line is a line as drawn on the screen. Folding, for
732 // example, can cause multiple logical lines to appear on the same
733 // visual line. This finds the start of the visual line that the
734 // given line is part of (usually that is the line itself).
735 function visualLine(line) {
736 var merged
737 while (merged = collapsedSpanAtStart(line))
738 { line = merged.find(-1, true).line }
739 return line
740 }
741
742 // Returns an array of logical lines that continue the visual line
743 // started by the argument, or undefined if there are no such lines.
744 function visualLineContinued(line) {
745 var merged, lines
746 while (merged = collapsedSpanAtEnd(line)) {
747 line = merged.find(1, true).line
748 ;(lines || (lines = [])).push(line)
749 }
750 return lines
751 }
752
753 // Get the line number of the start of the visual line that the
754 // given line number is part of.
755 function visualLineNo(doc, lineN) {
756 var line = getLine(doc, lineN), vis = visualLine(line)
757 if (line == vis) { return lineN }
758 return lineNo(vis)
759 }
760
761 // Get the line number of the start of the next visual line after
762 // the given line.
763 function visualLineEndNo(doc, lineN) {
764 if (lineN > doc.lastLine()) { return lineN }
765 var line = getLine(doc, lineN), merged
766 if (!lineIsHidden(doc, line)) { return lineN }
767 while (merged = collapsedSpanAtEnd(line))
768 { line = merged.find(1, true).line }
769 return lineNo(line) + 1
770 }
771
772 // Compute whether a line is hidden. Lines count as hidden when they
773 // are part of a visual line that starts with another line, or when
774 // they are entirely covered by collapsed, non-widget span.
775 function lineIsHidden(doc, line) {
776 var sps = sawCollapsedSpans && line.markedSpans
777 if (sps) { for (var sp = void 0, i = 0; i < sps.length; ++i) {
778 sp = sps[i]
779 if (!sp.marker.collapsed) { continue }
780 if (sp.from == null) { return true }
781 if (sp.marker.widgetNode) { continue }
782 if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
783 { return true }
784 } }
785 }
786 function lineIsHiddenInner(doc, line, span) {
787 if (span.to == null) {
788 var end = span.marker.find(1, true)
789 return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker))
790 }
791 if (span.marker.inclusiveRight && span.to == line.text.length)
792 { return true }
793 for (var sp = void 0, i = 0; i < line.markedSpans.length; ++i) {
794 sp = line.markedSpans[i]
795 if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to &&
796 (sp.to == null || sp.to != span.from) &&
797 (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
798 lineIsHiddenInner(doc, line, sp)) { return true }
799 }
800 }
801
802 // Find the height above the given line.
803 function heightAtLine(lineObj) {
804 lineObj = visualLine(lineObj)
805
806 var h = 0, chunk = lineObj.parent
807 for (var i = 0; i < chunk.lines.length; ++i) {
808 var line = chunk.lines[i]
809 if (line == lineObj) { break }
810 else { h += line.height }
811 }
812 for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
813 for (var i$1 = 0; i$1 < p.children.length; ++i$1) {
814 var cur = p.children[i$1]
815 if (cur == chunk) { break }
816 else { h += cur.height }
817 }
818 }
819 return h
820 }
821
822 // Compute the character length of a line, taking into account
823 // collapsed ranges (see markText) that might hide parts, and join
824 // other lines onto it.
825 function lineLength(line) {
826 if (line.height == 0) { return 0 }
827 var len = line.text.length, merged, cur = line
828 while (merged = collapsedSpanAtStart(cur)) {
829 var found = merged.find(0, true)
830 cur = found.from.line
831 len += found.from.ch - found.to.ch
832 }
833 cur = line
834 while (merged = collapsedSpanAtEnd(cur)) {
835 var found$1 = merged.find(0, true)
836 len -= cur.text.length - found$1.from.ch
837 cur = found$1.to.line
838 len += cur.text.length - found$1.to.ch
839 }
840 return len
841 }
842
843 // Find the longest line in the document.
844 function findMaxLine(cm) {
845 var d = cm.display, doc = cm.doc
846 d.maxLine = getLine(doc, doc.first)
847 d.maxLineLength = lineLength(d.maxLine)
848 d.maxLineChanged = true
849 doc.iter(function (line) {
850 var len = lineLength(line)
851 if (len > d.maxLineLength) {
852 d.maxLineLength = len
853 d.maxLine = line
854 }
855 })
856 }
857
858 // BIDI HELPERS
859
860 function iterateBidiSections(order, from, to, f) {
861 if (!order) { return f(from, to, "ltr") }
862 var found = false
863 for (var i = 0; i < order.length; ++i) {
864 var part = order[i]
865 if (part.from < to && part.to > from || from == to && part.to == from) {
866 f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr")
867 found = true
868 }
869 }
870 if (!found) { f(from, to, "ltr") }
871 }
872
873 function bidiLeft(part) { return part.level % 2 ? part.to : part.from }
874 function bidiRight(part) { return part.level % 2 ? part.from : part.to }
875
876 function lineLeft(line) { var order = getOrder(line); return order ? bidiLeft(order[0]) : 0 }
877 function lineRight(line) {
878 var order = getOrder(line)
879 if (!order) { return line.text.length }
880 return bidiRight(lst(order))
881 }
882
883 function compareBidiLevel(order, a, b) {
884 var linedir = order[0].level
885 if (a == linedir) { return true }
886 if (b == linedir) { return false }
887 return a < b
888 }
889
890 var bidiOther = null
891 function getBidiPartAt(order, pos) {
892 var found
893 bidiOther = null
894 for (var i = 0; i < order.length; ++i) {
895 var cur = order[i]
896 if (cur.from < pos && cur.to > pos) { return i }
897 if ((cur.from == pos || cur.to == pos)) {
898 if (found == null) {
899 found = i
900 } else if (compareBidiLevel(order, cur.level, order[found].level)) {
901 if (cur.from != cur.to) { bidiOther = found }
902 return i
903 } else {
904 if (cur.from != cur.to) { bidiOther = i }
905 return found
906 }
907 }
908 }
909 return found
910 }
911
912 function moveInLine(line, pos, dir, byUnit) {
913 if (!byUnit) { return pos + dir }
914 do { pos += dir }
915 while (pos > 0 && isExtendingChar(line.text.charAt(pos)))
916 return pos
917 }
918
919 // This is needed in order to move 'visually' through bi-directional
920 // text -- i.e., pressing left should make the cursor go left, even
921 // when in RTL text. The tricky part is the 'jumps', where RTL and
922 // LTR text touch each other. This often requires the cursor offset
923 // to move more than one unit, in order to visually move one unit.
924 function moveVisually(line, start, dir, byUnit) {
925 var bidi = getOrder(line)
926 if (!bidi) { return moveLogically(line, start, dir, byUnit) }
927 var pos = getBidiPartAt(bidi, start), part = bidi[pos]
928 var target = moveInLine(line, start, part.level % 2 ? -dir : dir, byUnit)
929
930 for (;;) {
931 if (target > part.from && target < part.to) { return target }
932 if (target == part.from || target == part.to) {
933 if (getBidiPartAt(bidi, target) == pos) { return target }
934 part = bidi[pos += dir]
935 return (dir > 0) == part.level % 2 ? part.to : part.from
936 } else {
937 part = bidi[pos += dir]
938 if (!part) { return null }
939 if ((dir > 0) == part.level % 2)
940 { target = moveInLine(line, part.to, -1, byUnit) }
941 else
942 { target = moveInLine(line, part.from, 1, byUnit) }
943 }
944 }
945 }
946
947 function moveLogically(line, start, dir, byUnit) {
948 var target = start + dir
949 if (byUnit) { while (target > 0 && isExtendingChar(line.text.charAt(target))) { target += dir } }
950 return target < 0 || target > line.text.length ? null : target
951 }
952
953 // Bidirectional ordering algorithm
954 // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
955 // that this (partially) implements.
956
957 // One-char codes used for character types:
958 // L (L): Left-to-Right
959 // R (R): Right-to-Left
960 // r (AL): Right-to-Left Arabic
961 // 1 (EN): European Number
962 // + (ES): European Number Separator
963 // % (ET): European Number Terminator
964 // n (AN): Arabic Number
965 // , (CS): Common Number Separator
966 // m (NSM): Non-Spacing Mark
967 // b (BN): Boundary Neutral
968 // s (B): Paragraph Separator
969 // t (S): Segment Separator
970 // w (WS): Whitespace
971 // N (ON): Other Neutrals
972
973 // Returns null if characters are ordered as they appear
974 // (left-to-right), or an array of sections ({from, to, level}
975 // objects) in the order in which they occur visually.
976 var bidiOrdering = (function() {
977 // Character types for codepoints 0 to 0xff
978 var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN"
979 // Character types for codepoints 0x600 to 0x6ff
980 var arabicTypes = "rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmm"
981 function charType(code) {
982 if (code <= 0xf7) { return lowTypes.charAt(code) }
983 else if (0x590 <= code && code <= 0x5f4) { return "R" }
984 else if (0x600 <= code && code <= 0x6ed) { return arabicTypes.charAt(code - 0x600) }
985 else if (0x6ee <= code && code <= 0x8ac) { return "r" }
986 else if (0x2000 <= code && code <= 0x200b) { return "w" }
987 else if (code == 0x200c) { return "b" }
988 else { return "L" }
989 }
990
991 var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/
992 var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/
993 // Browsers seem to always treat the boundaries of block elements as being L.
994 var outerType = "L"
995
996 function BidiSpan(level, from, to) {
997 this.level = level
998 this.from = from; this.to = to
999 }
1000
1001 return function(str) {
1002 if (!bidiRE.test(str)) { return false }
1003 var len = str.length, types = []
1004 for (var i = 0; i < len; ++i)
1005 { types.push(charType(str.charCodeAt(i))) }
1006
1007 // W1. Examine each non-spacing mark (NSM) in the level run, and
1008 // change the type of the NSM to the type of the previous
1009 // character. If the NSM is at the start of the level run, it will
1010 // get the type of sor.
1011 for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) {
1012 var type = types[i$1]
1013 if (type == "m") { types[i$1] = prev }
1014 else { prev = type }
1015 }
1016
1017 // W2. Search backwards from each instance of a European number
1018 // until the first strong type (R, L, AL, or sor) is found. If an
1019 // AL is found, change the type of the European number to Arabic
1020 // number.
1021 // W3. Change all ALs to R.
1022 for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) {
1023 var type$1 = types[i$2]
1024 if (type$1 == "1" && cur == "r") { types[i$2] = "n" }
1025 else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == "r") { types[i$2] = "R" } }
1026 }
1027
1028 // W4. A single European separator between two European numbers
1029 // changes to a European number. A single common separator between
1030 // two numbers of the same type changes to that type.
1031 for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) {
1032 var type$2 = types[i$3]
1033 if (type$2 == "+" && prev$1 == "1" && types[i$3+1] == "1") { types[i$3] = "1" }
1034 else if (type$2 == "," && prev$1 == types[i$3+1] &&
1035 (prev$1 == "1" || prev$1 == "n")) { types[i$3] = prev$1 }
1036 prev$1 = type$2
1037 }
1038
1039 // W5. A sequence of European terminators adjacent to European
1040 // numbers changes to all European numbers.
1041 // W6. Otherwise, separators and terminators change to Other
1042 // Neutral.
1043 for (var i$4 = 0; i$4 < len; ++i$4) {
1044 var type$3 = types[i$4]
1045 if (type$3 == ",") { types[i$4] = "N" }
1046 else if (type$3 == "%") {
1047 var end = void 0
1048 for (end = i$4 + 1; end < len && types[end] == "%"; ++end) {}
1049 var replace = (i$4 && types[i$4-1] == "!") || (end < len && types[end] == "1") ? "1" : "N"
1050 for (var j = i$4; j < end; ++j) { types[j] = replace }
1051 i$4 = end - 1
1052 }
1053 }
1054
1055 // W7. Search backwards from each instance of a European number
1056 // until the first strong type (R, L, or sor) is found. If an L is
1057 // found, then change the type of the European number to L.
1058 for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) {
1059 var type$4 = types[i$5]
1060 if (cur$1 == "L" && type$4 == "1") { types[i$5] = "L" }
1061 else if (isStrong.test(type$4)) { cur$1 = type$4 }
1062 }
1063
1064 // N1. A sequence of neutrals takes the direction of the
1065 // surrounding strong text if the text on both sides has the same
1066 // direction. European and Arabic numbers act as if they were R in
1067 // terms of their influence on neutrals. Start-of-level-run (sor)
1068 // and end-of-level-run (eor) are used at level run boundaries.
1069 // N2. Any remaining neutrals take the embedding direction.
1070 for (var i$6 = 0; i$6 < len; ++i$6) {
1071 if (isNeutral.test(types[i$6])) {
1072 var end$1 = void 0
1073 for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {}
1074 var before = (i$6 ? types[i$6-1] : outerType) == "L"
1075 var after = (end$1 < len ? types[end$1] : outerType) == "L"
1076 var replace$1 = before || after ? "L" : "R"
1077 for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1 }
1078 i$6 = end$1 - 1
1079 }
1080 }
1081
1082 // Here we depart from the documented algorithm, in order to avoid
1083 // building up an actual levels array. Since there are only three
1084 // levels (0, 1, 2) in an implementation that doesn't take
1085 // explicit embedding into account, we can build up the order on
1086 // the fly, without following the level-based algorithm.
1087 var order = [], m
1088 for (var i$7 = 0; i$7 < len;) {
1089 if (countsAsLeft.test(types[i$7])) {
1090 var start = i$7
1091 for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {}
1092 order.push(new BidiSpan(0, start, i$7))
1093 } else {
1094 var pos = i$7, at = order.length
1095 for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {}
1096 for (var j$2 = pos; j$2 < i$7;) {
1097 if (countsAsNum.test(types[j$2])) {
1098 if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)) }
1099 var nstart = j$2
1100 for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {}
1101 order.splice(at, 0, new BidiSpan(2, nstart, j$2))
1102 pos = j$2
1103 } else { ++j$2 }
1104 }
1105 if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)) }
1106 }
1107 }
1108 if (order[0].level == 1 && (m = str.match(/^\s+/))) {
1109 order[0].from = m[0].length
1110 order.unshift(new BidiSpan(0, 0, m[0].length))
1111 }
1112 if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
1113 lst(order).to -= m[0].length
1114 order.push(new BidiSpan(0, len - m[0].length, len))
1115 }
1116 if (order[0].level == 2)
1117 { order.unshift(new BidiSpan(1, order[0].to, order[0].to)) }
1118 if (order[0].level != lst(order).level)
1119 { order.push(new BidiSpan(order[0].level, len, len)) }
1120
1121 return order
1122 }
1123 })()
1124
1125 // Get the bidi ordering for the given line (and cache it). Returns
1126 // false for lines that are fully left-to-right, and an array of
1127 // BidiSpan objects otherwise.
1128 function getOrder(line) {
1129 var order = line.order
1130 if (order == null) { order = line.order = bidiOrdering(line.text) }
1131 return order
1132 }
1133
1134 // EVENT HANDLING
1135
1136 // Lightweight event framework. on/off also work on DOM nodes,
1137 // registering native DOM handlers.
1138
1139 var noHandlers = []
1140
1141 var on = function(emitter, type, f) {
1142 if (emitter.addEventListener) {
1143 emitter.addEventListener(type, f, false)
1144 } else if (emitter.attachEvent) {
1145 emitter.attachEvent("on" + type, f)
1146 } else {
1147 var map$$1 = emitter._handlers || (emitter._handlers = {})
1148 map$$1[type] = (map$$1[type] || noHandlers).concat(f)
1149 }
1150 }
1151
1152 function getHandlers(emitter, type) {
1153 return emitter._handlers && emitter._handlers[type] || noHandlers
1154 }
1155
1156 function off(emitter, type, f) {
1157 if (emitter.removeEventListener) {
1158 emitter.removeEventListener(type, f, false)
1159 } else if (emitter.detachEvent) {
1160 emitter.detachEvent("on" + type, f)
1161 } else {
1162 var map$$1 = emitter._handlers, arr = map$$1 && map$$1[type]
1163 if (arr) {
1164 var index = indexOf(arr, f)
1165 if (index > -1)
1166 { map$$1[type] = arr.slice(0, index).concat(arr.slice(index + 1)) }
1167 }
1168 }
1169 }
1170
1171 function signal(emitter, type /*, values...*/) {
1172 var handlers = getHandlers(emitter, type)
1173 if (!handlers.length) { return }
1174 var args = Array.prototype.slice.call(arguments, 2)
1175 for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args) }
1176 }
1177
1178 // The DOM events that CodeMirror handles can be overridden by
1179 // registering a (non-DOM) handler on the editor for the event name,
1180 // and preventDefault-ing the event in that handler.
1181 function signalDOMEvent(cm, e, override) {
1182 if (typeof e == "string")
1183 { e = {type: e, preventDefault: function() { this.defaultPrevented = true }} }
1184 signal(cm, override || e.type, cm, e)
1185 return e_defaultPrevented(e) || e.codemirrorIgnore
1186 }
1187
1188 function signalCursorActivity(cm) {
1189 var arr = cm._handlers && cm._handlers.cursorActivity
1190 if (!arr) { return }
1191 var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = [])
1192 for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1)
1193 { set.push(arr[i]) } }
1194 }
1195
1196 function hasHandler(emitter, type) {
1197 return getHandlers(emitter, type).length > 0
1198 }
1199
1200 // Add on and off methods to a constructor's prototype, to make
1201 // registering events on such objects more convenient.
1202 function eventMixin(ctor) {
1203 ctor.prototype.on = function(type, f) {on(this, type, f)}
1204 ctor.prototype.off = function(type, f) {off(this, type, f)}
1205 }
1206
1207 // Due to the fact that we still support jurassic IE versions, some
1208 // compatibility wrappers are needed.
1209
1210 function e_preventDefault(e) {
1211 if (e.preventDefault) { e.preventDefault() }
1212 else { e.returnValue = false }
1213 }
1214 function e_stopPropagation(e) {
1215 if (e.stopPropagation) { e.stopPropagation() }
1216 else { e.cancelBubble = true }
1217 }
1218 function e_defaultPrevented(e) {
1219 return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false
1220 }
1221 function e_stop(e) {e_preventDefault(e); e_stopPropagation(e)}
1222
1223 function e_target(e) {return e.target || e.srcElement}
1224 function e_button(e) {
1225 var b = e.which
1226 if (b == null) {
1227 if (e.button & 1) { b = 1 }
1228 else if (e.button & 2) { b = 3 }
1229 else if (e.button & 4) { b = 2 }
1230 }
1231 if (mac && e.ctrlKey && b == 1) { b = 3 }
1232 return b
1233 }
1234
1235 // Detect drag-and-drop
1236 var dragAndDrop = function() {
1237 // There is *some* kind of drag-and-drop support in IE6-8, but I
1238 // couldn't get it to work yet.
1239 if (ie && ie_version < 9) { return false }
1240 var div = elt('div')
1241 return "draggable" in div || "dragDrop" in div
1242 }()
1243
1244 var zwspSupported
1245 function zeroWidthElement(measure) {
1246 if (zwspSupported == null) {
1247 var test = elt("span", "\u200b")
1248 removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]))
1249 if (measure.firstChild.offsetHeight != 0)
1250 { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8) }
1251 }
1252 var node = zwspSupported ? elt("span", "\u200b") :
1253 elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px")
1254 node.setAttribute("cm-text", "")
1255 return node
1256 }
1257
1258 // Feature-detect IE's crummy client rect reporting for bidi text
1259 var badBidiRects
1260 function hasBadBidiRects(measure) {
1261 if (badBidiRects != null) { return badBidiRects }
1262 var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA"))
1263 var r0 = range(txt, 0, 1).getBoundingClientRect()
1264 var r1 = range(txt, 1, 2).getBoundingClientRect()
1265 removeChildren(measure)
1266 if (!r0 || r0.left == r0.right) { return false } // Safari returns null in some cases (#2780)
1267 return badBidiRects = (r1.right - r0.right < 3)
1268 }
1269
1270 // See if "".split is the broken IE version, if so, provide an
1271 // alternative way to split lines.
1272 var splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function (string) {
1273 var pos = 0, result = [], l = string.length
1274 while (pos <= l) {
1275 var nl = string.indexOf("\n", pos)
1276 if (nl == -1) { nl = string.length }
1277 var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl)
1278 var rt = line.indexOf("\r")
1279 if (rt != -1) {
1280 result.push(line.slice(0, rt))
1281 pos += rt + 1
1282 } else {
1283 result.push(line)
1284 pos = nl + 1
1285 }
1286 }
1287 return result
1288 } : function (string) { return string.split(/\r\n?|\n/); }
1289
1290 var hasSelection = window.getSelection ? function (te) {
1291 try { return te.selectionStart != te.selectionEnd }
1292 catch(e) { return false }
1293 } : function (te) {
1294 var range$$1
1295 try {range$$1 = te.ownerDocument.selection.createRange()}
1296 catch(e) {}
1297 if (!range$$1 || range$$1.parentElement() != te) { return false }
1298 return range$$1.compareEndPoints("StartToEnd", range$$1) != 0
1299 }
1300
1301 var hasCopyEvent = (function () {
1302 var e = elt("div")
1303 if ("oncopy" in e) { return true }
1304 e.setAttribute("oncopy", "return;")
1305 return typeof e.oncopy == "function"
1306 })()
1307
1308 var badZoomedRects = null
1309 function hasBadZoomedRects(measure) {
1310 if (badZoomedRects != null) { return badZoomedRects }
1311 var node = removeChildrenAndAdd(measure, elt("span", "x"))
1312 var normal = node.getBoundingClientRect()
1313 var fromRange = range(node, 0, 1).getBoundingClientRect()
1314 return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1
1315 }
1316
1317 // Known modes, by name and by MIME
1318 var modes = {};
1319 var mimeModes = {}
1320
1321 // Extra arguments are stored as the mode's dependencies, which is
1322 // used by (legacy) mechanisms like loadmode.js to automatically
1323 // load a mode. (Preferred mechanism is the require/define calls.)
1324 function defineMode(name, mode) {
1325 if (arguments.length > 2)
1326 { mode.dependencies = Array.prototype.slice.call(arguments, 2) }
1327 modes[name] = mode
1328 }
1329
1330 function defineMIME(mime, spec) {
1331 mimeModes[mime] = spec
1332 }
1333
1334 // Given a MIME type, a {name, ...options} config object, or a name
1335 // string, return a mode config object.
1336 function resolveMode(spec) {
1337 if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
1338 spec = mimeModes[spec]
1339 } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
1340 var found = mimeModes[spec.name]
1341 if (typeof found == "string") { found = {name: found} }
1342 spec = createObj(found, spec)
1343 spec.name = found.name
1344 } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
1345 return resolveMode("application/xml")
1346 } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) {
1347 return resolveMode("application/json")
1348 }
1349 if (typeof spec == "string") { return {name: spec} }
1350 else { return spec || {name: "null"} }
1351 }
1352
1353 // Given a mode spec (anything that resolveMode accepts), find and
1354 // initialize an actual mode object.
1355 function getMode(options, spec) {
1356 spec = resolveMode(spec)
1357 var mfactory = modes[spec.name]
1358 if (!mfactory) { return getMode(options, "text/plain") }
1359 var modeObj = mfactory(options, spec)
1360 if (modeExtensions.hasOwnProperty(spec.name)) {
1361 var exts = modeExtensions[spec.name]
1362 for (var prop in exts) {
1363 if (!exts.hasOwnProperty(prop)) { continue }
1364 if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop] }
1365 modeObj[prop] = exts[prop]
1366 }
1367 }
1368 modeObj.name = spec.name
1369 if (spec.helperType) { modeObj.helperType = spec.helperType }
1370 if (spec.modeProps) { for (var prop$1 in spec.modeProps)
1371 { modeObj[prop$1] = spec.modeProps[prop$1] } }
1372
1373 return modeObj
1374 }
1375
1376 // This can be used to attach properties to mode objects from
1377 // outside the actual mode definition.
1378 var modeExtensions = {}
1379 function extendMode(mode, properties) {
1380 var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {})
1381 copyObj(properties, exts)
1382 }
1383
1384 function copyState(mode, state) {
1385 if (state === true) { return state }
1386 if (mode.copyState) { return mode.copyState(state) }
1387 var nstate = {}
1388 for (var n in state) {
1389 var val = state[n]
1390 if (val instanceof Array) { val = val.concat([]) }
1391 nstate[n] = val
1392 }
1393 return nstate
1394 }
1395
1396 // Given a mode and a state (for that mode), find the inner mode and
1397 // state at the position that the state refers to.
1398 function innerMode(mode, state) {
1399 var info
1400 while (mode.innerMode) {
1401 info = mode.innerMode(state)
1402 if (!info || info.mode == mode) { break }
1403 state = info.state
1404 mode = info.mode
1405 }
1406 return info || {mode: mode, state: state}
1407 }
1408
1409 function startState(mode, a1, a2) {
1410 return mode.startState ? mode.startState(a1, a2) : true
1411 }
1412
1413 // STRING STREAM
1414
1415 // Fed to the mode parsers, provides helper functions to make
1416 // parsers more succinct.
1417
1418 var StringStream = function(string, tabSize) {
1419 this.pos = this.start = 0
1420 this.string = string
1421 this.tabSize = tabSize || 8
1422 this.lastColumnPos = this.lastColumnValue = 0
1423 this.lineStart = 0
1424 }
1425
1426 StringStream.prototype = {
1427 eol: function() {return this.pos >= this.string.length},
1428 sol: function() {return this.pos == this.lineStart},
1429 peek: function() {return this.string.charAt(this.pos) || undefined},
1430 next: function() {
1431 if (this.pos < this.string.length)
1432 { return this.string.charAt(this.pos++) }
1433 },
1434 eat: function(match) {
1435 var ch = this.string.charAt(this.pos)
1436 var ok
1437 if (typeof match == "string") { ok = ch == match }
1438 else { ok = ch && (match.test ? match.test(ch) : match(ch)) }
1439 if (ok) {++this.pos; return ch}
1440 },
1441 eatWhile: function(match) {
1442 var start = this.pos
1443 while (this.eat(match)){}
1444 return this.pos > start
1445 },
1446 eatSpace: function() {
1447 var this$1 = this;
1448
1449 var start = this.pos
1450 while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this$1.pos }
1451 return this.pos > start
1452 },
1453 skipToEnd: function() {this.pos = this.string.length},
1454 skipTo: function(ch) {
1455 var found = this.string.indexOf(ch, this.pos)
1456 if (found > -1) {this.pos = found; return true}
1457 },
1458 backUp: function(n) {this.pos -= n},
1459 column: function() {
1460 if (this.lastColumnPos < this.start) {
1461 this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue)
1462 this.lastColumnPos = this.start
1463 }
1464 return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
1465 },
1466 indentation: function() {
1467 return countColumn(this.string, null, this.tabSize) -
1468 (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
1469 },
1470 match: function(pattern, consume, caseInsensitive) {
1471 if (typeof pattern == "string") {
1472 var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; }
1473 var substr = this.string.substr(this.pos, pattern.length)
1474 if (cased(substr) == cased(pattern)) {
1475 if (consume !== false) { this.pos += pattern.length }
1476 return true
1477 }
1478 } else {
1479 var match = this.string.slice(this.pos).match(pattern)
1480 if (match && match.index > 0) { return null }
1481 if (match && consume !== false) { this.pos += match[0].length }
1482 return match
1483 }
1484 },
1485 current: function(){return this.string.slice(this.start, this.pos)},
1486 hideFirstChars: function(n, inner) {
1487 this.lineStart += n
1488 try { return inner() }
1489 finally { this.lineStart -= n }
1490 }
1491 }
1492
1493 // Compute a style array (an array starting with a mode generation
1494 // -- for invalidation -- followed by pairs of end positions and
1495 // style strings), which is used to highlight the tokens on the
1496 // line.
1497 function highlightLine(cm, line, state, forceToEnd) {
1498 // A styles array always starts with a number identifying the
1499 // mode/overlays that it is based on (for easy invalidation).
1500 var st = [cm.state.modeGen], lineClasses = {}
1501 // Compute the base array of styles
1502 runMode(cm, line.text, cm.doc.mode, state, function (end, style) { return st.push(end, style); },
1503 lineClasses, forceToEnd)
1504
1505 // Run overlays, adjust style array.
1506 var loop = function ( o ) {
1507 var overlay = cm.state.overlays[o], i = 1, at = 0
1508 runMode(cm, line.text, overlay.mode, true, function (end, style) {
1509 var start = i
1510 // Ensure there's a token end at the current position, and that i points at it
1511 while (at < end) {
1512 var i_end = st[i]
1513 if (i_end > end)
1514 { st.splice(i, 1, end, st[i+1], i_end) }
1515 i += 2
1516 at = Math.min(end, i_end)
1517 }
1518 if (!style) { return }
1519 if (overlay.opaque) {
1520 st.splice(start, i - start, end, "overlay " + style)
1521 i = start + 2
1522 } else {
1523 for (; start < i; start += 2) {
1524 var cur = st[start+1]
1525 st[start+1] = (cur ? cur + " " : "") + "overlay " + style
1526 }
1527 }
1528 }, lineClasses)
1529 };
1530
1531 for (var o = 0; o < cm.state.overlays.length; ++o) loop( o );
1532
1533 return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null}
1534 }
1535
1536 function getLineStyles(cm, line, updateFrontier) {
1537 if (!line.styles || line.styles[0] != cm.state.modeGen) {
1538 var state = getStateBefore(cm, lineNo(line))
1539 var result = highlightLine(cm, line, line.text.length > cm.options.maxHighlightLength ? copyState(cm.doc.mode, state) : state)
1540 line.stateAfter = state
1541 line.styles = result.styles
1542 if (result.classes) { line.styleClasses = result.classes }
1543 else if (line.styleClasses) { line.styleClasses = null }
1544 if (updateFrontier === cm.doc.frontier) { cm.doc.frontier++ }
1545 }
1546 return line.styles
1547 }
1548
1549 function getStateBefore(cm, n, precise) {
1550 var doc = cm.doc, display = cm.display
1551 if (!doc.mode.startState) { return true }
1552 var pos = findStartLine(cm, n, precise), state = pos > doc.first && getLine(doc, pos-1).stateAfter
1553 if (!state) { state = startState(doc.mode) }
1554 else { state = copyState(doc.mode, state) }
1555 doc.iter(pos, n, function (line) {
1556 processLine(cm, line.text, state)
1557 var save = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo
1558 line.stateAfter = save ? copyState(doc.mode, state) : null
1559 ++pos
1560 })
1561 if (precise) { doc.frontier = pos }
1562 return state
1563 }
1564
1565 // Lightweight form of highlight -- proceed over this line and
1566 // update state, but don't save a style array. Used for lines that
1567 // aren't currently visible.
1568 function processLine(cm, text, state, startAt) {
1569 var mode = cm.doc.mode
1570 var stream = new StringStream(text, cm.options.tabSize)
1571 stream.start = stream.pos = startAt || 0
1572 if (text == "") { callBlankLine(mode, state) }
1573 while (!stream.eol()) {
1574 readToken(mode, stream, state)
1575 stream.start = stream.pos
1576 }
1577 }
1578
1579 function callBlankLine(mode, state) {
1580 if (mode.blankLine) { return mode.blankLine(state) }
1581 if (!mode.innerMode) { return }
1582 var inner = innerMode(mode, state)
1583 if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) }
1584 }
1585
1586 function readToken(mode, stream, state, inner) {
1587 for (var i = 0; i < 10; i++) {
1588 if (inner) { inner[0] = innerMode(mode, state).mode }
1589 var style = mode.token(stream, state)
1590 if (stream.pos > stream.start) { return style }
1591 }
1592 throw new Error("Mode " + mode.name + " failed to advance stream.")
1593 }
1594
1595 // Utility for getTokenAt and getLineTokens
1596 function takeToken(cm, pos, precise, asArray) {
1597 var getObj = function (copy) { return ({
1598 start: stream.start, end: stream.pos,
1599 string: stream.current(),
1600 type: style || null,
1601 state: copy ? copyState(doc.mode, state) : state
1602 }); }
1603
1604 var doc = cm.doc, mode = doc.mode, style
1605 pos = clipPos(doc, pos)
1606 var line = getLine(doc, pos.line), state = getStateBefore(cm, pos.line, precise)
1607 var stream = new StringStream(line.text, cm.options.tabSize), tokens
1608 if (asArray) { tokens = [] }
1609 while ((asArray || stream.pos < pos.ch) && !stream.eol()) {
1610 stream.start = stream.pos
1611 style = readToken(mode, stream, state)
1612 if (asArray) { tokens.push(getObj(true)) }
1613 }
1614 return asArray ? tokens : getObj()
1615 }
1616
1617 function extractLineClasses(type, output) {
1618 if (type) { for (;;) {
1619 var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/)
1620 if (!lineClass) { break }
1621 type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length)
1622 var prop = lineClass[1] ? "bgClass" : "textClass"
1623 if (output[prop] == null)
1624 { output[prop] = lineClass[2] }
1625 else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop]))
1626 { output[prop] += " " + lineClass[2] }
1627 } }
1628 return type
1629 }
1630
1631 // Run the given mode's parser over a line, calling f for each token.
1632 function runMode(cm, text, mode, state, f, lineClasses, forceToEnd) {
1633 var flattenSpans = mode.flattenSpans
1634 if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans }
1635 var curStart = 0, curStyle = null
1636 var stream = new StringStream(text, cm.options.tabSize), style
1637 var inner = cm.options.addModeClass && [null]
1638 if (text == "") { extractLineClasses(callBlankLine(mode, state), lineClasses) }
1639 while (!stream.eol()) {
1640 if (stream.pos > cm.options.maxHighlightLength) {
1641 flattenSpans = false
1642 if (forceToEnd) { processLine(cm, text, state, stream.pos) }
1643 stream.pos = text.length
1644 style = null
1645 } else {
1646 style = extractLineClasses(readToken(mode, stream, state, inner), lineClasses)
1647 }
1648 if (inner) {
1649 var mName = inner[0].name
1650 if (mName) { style = "m-" + (style ? mName + " " + style : mName) }
1651 }
1652 if (!flattenSpans || curStyle != style) {
1653 while (curStart < stream.start) {
1654 curStart = Math.min(stream.start, curStart + 5000)
1655 f(curStart, curStyle)
1656 }
1657 curStyle = style
1658 }
1659 stream.start = stream.pos
1660 }
1661 while (curStart < stream.pos) {
1662 // Webkit seems to refuse to render text nodes longer than 57444
1663 // characters, and returns inaccurate measurements in nodes
1664 // starting around 5000 chars.
1665 var pos = Math.min(stream.pos, curStart + 5000)
1666 f(pos, curStyle)
1667 curStart = pos
1668 }
1669 }
1670
1671 // Finds the line to start with when starting a parse. Tries to
1672 // find a line with a stateAfter, so that it can start with a
1673 // valid state. If that fails, it returns the line with the
1674 // smallest indentation, which tends to need the least context to
1675 // parse correctly.
1676 function findStartLine(cm, n, precise) {
1677 var minindent, minline, doc = cm.doc
1678 var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100)
1679 for (var search = n; search > lim; --search) {
1680 if (search <= doc.first) { return doc.first }
1681 var line = getLine(doc, search - 1)
1682 if (line.stateAfter && (!precise || search <= doc.frontier)) { return search }
1683 var indented = countColumn(line.text, null, cm.options.tabSize)
1684 if (minline == null || minindent > indented) {
1685 minline = search - 1
1686 minindent = indented
1687 }
1688 }
1689 return minline
1690 }
1691
1692 // LINE DATA STRUCTURE
1693
1694 // Line objects. These hold state related to a line, including
1695 // highlighting info (the styles array).
1696 function Line(text, markedSpans, estimateHeight) {
1697 this.text = text
1698 attachMarkedSpans(this, markedSpans)
1699 this.height = estimateHeight ? estimateHeight(this) : 1
1700 }
1701 eventMixin(Line)
1702 Line.prototype.lineNo = function() { return lineNo(this) }
1703
1704 // Change the content (text, markers) of a line. Automatically
1705 // invalidates cached information and tries to re-estimate the
1706 // line's height.
1707 function updateLine(line, text, markedSpans, estimateHeight) {
1708 line.text = text
1709 if (line.stateAfter) { line.stateAfter = null }
1710 if (line.styles) { line.styles = null }
1711 if (line.order != null) { line.order = null }
1712 detachMarkedSpans(line)
1713 attachMarkedSpans(line, markedSpans)
1714 var estHeight = estimateHeight ? estimateHeight(line) : 1
1715 if (estHeight != line.height) { updateLineHeight(line, estHeight) }
1716 }
1717
1718 // Detach a line from the document tree and its markers.
1719 function cleanUpLine(line) {
1720 line.parent = null
1721 detachMarkedSpans(line)
1722 }
1723
1724 // Convert a style as returned by a mode (either null, or a string
1725 // containing one or more styles) to a CSS style. This is cached,
1726 // and also looks for line-wide styles.
1727 var styleToClassCache = {};
1728 var styleToClassCacheWithMode = {}
1729 function interpretTokenStyle(style, options) {
1730 if (!style || /^\s*$/.test(style)) { return null }
1731 var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache
1732 return cache[style] ||
1733 (cache[style] = style.replace(/\S+/g, "cm-$&"))
1734 }
1735
1736 // Render the DOM representation of the text of a line. Also builds
1737 // up a 'line map', which points at the DOM nodes that represent
1738 // specific stretches of text, and is used by the measuring code.
1739 // The returned object contains the DOM node, this map, and
1740 // information about line-wide styles that were set by the mode.
1741 function buildLineContent(cm, lineView) {
1742 // The padding-right forces the element to have a 'border', which
1743 // is needed on Webkit to be able to get line-level bounding
1744 // rectangles for it (in measureChar).
1745 var content = elt("span", null, null, webkit ? "padding-right: .1px" : null)
1746 var builder = {pre: elt("pre", [content], "CodeMirror-line"), content: content,
1747 col: 0, pos: 0, cm: cm,
1748 trailingSpace: false,
1749 splitSpaces: (ie || webkit) && cm.getOption("lineWrapping")}
1750 lineView.measure = {}
1751
1752 // Iterate over the logical lines that make up this visual line.
1753 for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {
1754 var line = i ? lineView.rest[i - 1] : lineView.line, order = void 0
1755 builder.pos = 0
1756 builder.addToken = buildToken
1757 // Optionally wire in some hacks into the token-rendering
1758 // algorithm, to deal with browser quirks.
1759 if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line)))
1760 { builder.addToken = buildTokenBadBidi(builder.addToken, order) }
1761 builder.map = []
1762 var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line)
1763 insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate))
1764 if (line.styleClasses) {
1765 if (line.styleClasses.bgClass)
1766 { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || "") }
1767 if (line.styleClasses.textClass)
1768 { builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || "") }
1769 }
1770
1771 // Ensure at least a single node is present, for measuring.
1772 if (builder.map.length == 0)
1773 { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))) }
1774
1775 // Store the map and a cache object for the current logical line
1776 if (i == 0) {
1777 lineView.measure.map = builder.map
1778 lineView.measure.cache = {}
1779 } else {
1780 (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map)
1781 ;(lineView.measure.caches || (lineView.measure.caches = [])).push({})
1782 }
1783 }
1784
1785 // See issue #2901
1786 if (webkit) {
1787 var last = builder.content.lastChild
1788 if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab")))
1789 { builder.content.className = "cm-tab-wrap-hack" }
1790 }
1791
1792 signal(cm, "renderLine", cm, lineView.line, builder.pre)
1793 if (builder.pre.className)
1794 { builder.textClass = joinClasses(builder.pre.className, builder.textClass || "") }
1795
1796 return builder
1797 }
1798
1799 function defaultSpecialCharPlaceholder(ch) {
1800 var token = elt("span", "\u2022", "cm-invalidchar")
1801 token.title = "\\u" + ch.charCodeAt(0).toString(16)
1802 token.setAttribute("aria-label", token.title)
1803 return token
1804 }
1805
1806 // Build up the DOM representation for a single token, and add it to
1807 // the line map. Takes care to render special characters separately.
1808 function buildToken(builder, text, style, startStyle, endStyle, title, css) {
1809 if (!text) { return }
1810 var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text
1811 var special = builder.cm.state.specialChars, mustWrap = false
1812 var content
1813 if (!special.test(text)) {
1814 builder.col += text.length
1815 content = document.createTextNode(displayText)
1816 builder.map.push(builder.pos, builder.pos + text.length, content)
1817 if (ie && ie_version < 9) { mustWrap = true }
1818 builder.pos += text.length
1819 } else {
1820 content = document.createDocumentFragment()
1821 var pos = 0
1822 while (true) {
1823 special.lastIndex = pos
1824 var m = special.exec(text)
1825 var skipped = m ? m.index - pos : text.length - pos
1826 if (skipped) {
1827 var txt = document.createTextNode(displayText.slice(pos, pos + skipped))
1828 if (ie && ie_version < 9) { content.appendChild(elt("span", [txt])) }
1829 else { content.appendChild(txt) }
1830 builder.map.push(builder.pos, builder.pos + skipped, txt)
1831 builder.col += skipped
1832 builder.pos += skipped
1833 }
1834 if (!m) { break }
1835 pos += skipped + 1
1836 var txt$1 = void 0
1837 if (m[0] == "\t") {
1838 var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize
1839 txt$1 = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"))
1840 txt$1.setAttribute("role", "presentation")
1841 txt$1.setAttribute("cm-text", "\t")
1842 builder.col += tabWidth
1843 } else if (m[0] == "\r" || m[0] == "\n") {
1844 txt$1 = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar"))
1845 txt$1.setAttribute("cm-text", m[0])
1846 builder.col += 1
1847 } else {
1848 txt$1 = builder.cm.options.specialCharPlaceholder(m[0])
1849 txt$1.setAttribute("cm-text", m[0])
1850 if (ie && ie_version < 9) { content.appendChild(elt("span", [txt$1])) }
1851 else { content.appendChild(txt$1) }
1852 builder.col += 1
1853 }
1854 builder.map.push(builder.pos, builder.pos + 1, txt$1)
1855 builder.pos++
1856 }
1857 }
1858 builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32
1859 if (style || startStyle || endStyle || mustWrap || css) {
1860 var fullStyle = style || ""
1861 if (startStyle) { fullStyle += startStyle }
1862 if (endStyle) { fullStyle += endStyle }
1863 var token = elt("span", [content], fullStyle, css)
1864 if (title) { token.title = title }
1865 return builder.content.appendChild(token)
1866 }
1867 builder.content.appendChild(content)
1868 }
1869
1870 function splitSpaces(text, trailingBefore) {
1871 if (text.length > 1 && !/ /.test(text)) { return text }
1872 var spaceBefore = trailingBefore, result = ""
1873 for (var i = 0; i < text.length; i++) {
1874 var ch = text.charAt(i)
1875 if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32))
1876 { ch = "\u00a0" }
1877 result += ch
1878 spaceBefore = ch == " "
1879 }
1880 return result
1881 }
1882
1883 // Work around nonsense dimensions being reported for stretches of
1884 // right-to-left text.
1885 function buildTokenBadBidi(inner, order) {
1886 return function (builder, text, style, startStyle, endStyle, title, css) {
1887 style = style ? style + " cm-force-border" : "cm-force-border"
1888 var start = builder.pos, end = start + text.length
1889 for (;;) {
1890 // Find the part that overlaps with the start of this text
1891 var part = void 0
1892 for (var i = 0; i < order.length; i++) {
1893 part = order[i]
1894 if (part.to > start && part.from <= start) { break }
1895 }
1896 if (part.to >= end) { return inner(builder, text, style, startStyle, endStyle, title, css) }
1897 inner(builder, text.slice(0, part.to - start), style, startStyle, null, title, css)
1898 startStyle = null
1899 text = text.slice(part.to - start)
1900 start = part.to
1901 }
1902 }
1903 }
1904
1905 function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
1906 var widget = !ignoreWidget && marker.widgetNode
1907 if (widget) { builder.map.push(builder.pos, builder.pos + size, widget) }
1908 if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {
1909 if (!widget)
1910 { widget = builder.content.appendChild(document.createElement("span")) }
1911 widget.setAttribute("cm-marker", marker.id)
1912 }
1913 if (widget) {
1914 builder.cm.display.input.setUneditable(widget)
1915 builder.content.appendChild(widget)
1916 }
1917 builder.pos += size
1918 builder.trailingSpace = false
1919 }
1920
1921 // Outputs a number of spans to make up a line, taking highlighting
1922 // and marked text into account.
1923 function insertLineContent(line, builder, styles) {
1924 var spans = line.markedSpans, allText = line.text, at = 0
1925 if (!spans) {
1926 for (var i$1 = 1; i$1 < styles.length; i$1+=2)
1927 { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpretTokenStyle(styles[i$1+1], builder.cm.options)) }
1928 return
1929 }
1930
1931 var len = allText.length, pos = 0, i = 1, text = "", style, css
1932 var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed
1933 for (;;) {
1934 if (nextChange == pos) { // Update current marker set
1935 spanStyle = spanEndStyle = spanStartStyle = title = css = ""
1936 collapsed = null; nextChange = Infinity
1937 var foundBookmarks = [], endStyles = void 0
1938 for (var j = 0; j < spans.length; ++j) {
1939 var sp = spans[j], m = sp.marker
1940 if (m.type == "bookmark" && sp.from == pos && m.widgetNode) {
1941 foundBookmarks.push(m)
1942 } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) {
1943 if (sp.to != null && sp.to != pos && nextChange > sp.to) {
1944 nextChange = sp.to
1945 spanEndStyle = ""
1946 }
1947 if (m.className) { spanStyle += " " + m.className }
1948 if (m.css) { css = (css ? css + ";" : "") + m.css }
1949 if (m.startStyle && sp.from == pos) { spanStartStyle += " " + m.startStyle }
1950 if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [])).push(m.endStyle, sp.to) }
1951 if (m.title && !title) { title = m.title }
1952 if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))
1953 { collapsed = sp }
1954 } else if (sp.from > pos && nextChange > sp.from) {
1955 nextChange = sp.from
1956 }
1957 }
1958 if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2)
1959 { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += " " + endStyles[j$1] } } }
1960
1961 if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2)
1962 { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]) } }
1963 if (collapsed && (collapsed.from || 0) == pos) {
1964 buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos,
1965 collapsed.marker, collapsed.from == null)
1966 if (collapsed.to == null) { return }
1967 if (collapsed.to == pos) { collapsed = false }
1968 }
1969 }
1970 if (pos >= len) { break }
1971
1972 var upto = Math.min(len, nextChange)
1973 while (true) {
1974 if (text) {
1975 var end = pos + text.length
1976 if (!collapsed) {
1977 var tokenText = end > upto ? text.slice(0, upto - pos) : text
1978 builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
1979 spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", title, css)
1980 }
1981 if (end >= upto) {text = text.slice(upto - pos); pos = upto; break}
1982 pos = end
1983 spanStartStyle = ""
1984 }
1985 text = allText.slice(at, at = styles[i++])
1986 style = interpretTokenStyle(styles[i++], builder.cm.options)
1987 }
1988 }
1989 }
1990
1991
1992 // These objects are used to represent the visible (currently drawn)
1993 // part of the document. A LineView may correspond to multiple
1994 // logical lines, if those are connected by collapsed ranges.
1995 function LineView(doc, line, lineN) {
1996 // The starting line
1997 this.line = line
1998 // Continuing lines, if any
1999 this.rest = visualLineContinued(line)
2000 // Number of logical lines in this visual line
2001 this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1
2002 this.node = this.text = null
2003 this.hidden = lineIsHidden(doc, line)
2004 }
2005
2006 // Create a range of LineView objects for the given lines.
2007 function buildViewArray(cm, from, to) {
2008 var array = [], nextPos
2009 for (var pos = from; pos < to; pos = nextPos) {
2010 var view = new LineView(cm.doc, getLine(cm.doc, pos), pos)
2011 nextPos = pos + view.size
2012 array.push(view)
2013 }
2014 return array
2015 }
2016
2017 var operationGroup = null
2018
2019 function pushOperation(op) {
2020 if (operationGroup) {
2021 operationGroup.ops.push(op)
2022 } else {
2023 op.ownsGroup = operationGroup = {
2024 ops: [op],
2025 delayedCallbacks: []
2026 }
2027 }
2028 }
2029
2030 function fireCallbacksForOps(group) {
2031 // Calls delayed callbacks and cursorActivity handlers until no
2032 // new ones appear
2033 var callbacks = group.delayedCallbacks, i = 0
2034 do {
2035 for (; i < callbacks.length; i++)
2036 { callbacks[i].call(null) }
2037 for (var j = 0; j < group.ops.length; j++) {
2038 var op = group.ops[j]
2039 if (op.cursorActivityHandlers)
2040 { while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
2041 { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm) } }
2042 }
2043 } while (i < callbacks.length)
2044 }
2045
2046 function finishOperation(op, endCb) {
2047 var group = op.ownsGroup
2048 if (!group) { return }
2049
2050 try { fireCallbacksForOps(group) }
2051 finally {
2052 operationGroup = null
2053 endCb(group)
2054 }
2055 }
2056
2057 var orphanDelayedCallbacks = null
2058
2059 // Often, we want to signal events at a point where we are in the
2060 // middle of some work, but don't want the handler to start calling
2061 // other methods on the editor, which might be in an inconsistent
2062 // state or simply not expect any other events to happen.
2063 // signalLater looks whether there are any handlers, and schedules
2064 // them to be executed when the last operation ends, or, if no
2065 // operation is active, when a timeout fires.
2066 function signalLater(emitter, type /*, values...*/) {
2067 var arr = getHandlers(emitter, type)
2068 if (!arr.length) { return }
2069 var args = Array.prototype.slice.call(arguments, 2), list
2070 if (operationGroup) {
2071 list = operationGroup.delayedCallbacks
2072 } else if (orphanDelayedCallbacks) {
2073 list = orphanDelayedCallbacks
2074 } else {
2075 list = orphanDelayedCallbacks = []
2076 setTimeout(fireOrphanDelayed, 0)
2077 }
2078 var loop = function ( i ) {
2079 list.push(function () { return arr[i].apply(null, args); })
2080 };
2081
2082 for (var i = 0; i < arr.length; ++i)
2083 loop( i );
2084 }
2085
2086 function fireOrphanDelayed() {
2087 var delayed = orphanDelayedCallbacks
2088 orphanDelayedCallbacks = null
2089 for (var i = 0; i < delayed.length; ++i) { delayed[i]() }
2090 }
2091
2092 // When an aspect of a line changes, a string is added to
2093 // lineView.changes. This updates the relevant part of the line's
2094 // DOM structure.
2095 function updateLineForChanges(cm, lineView, lineN, dims) {
2096 for (var j = 0; j < lineView.changes.length; j++) {
2097 var type = lineView.changes[j]
2098 if (type == "text") { updateLineText(cm, lineView) }
2099 else if (type == "gutter") { updateLineGutter(cm, lineView, lineN, dims) }
2100 else if (type == "class") { updateLineClasses(lineView) }
2101 else if (type == "widget") { updateLineWidgets(cm, lineView, dims) }
2102 }
2103 lineView.changes = null
2104 }
2105
2106 // Lines with gutter elements, widgets or a background class need to
2107 // be wrapped, and have the extra elements added to the wrapper div
2108 function ensureLineWrapped(lineView) {
2109 if (lineView.node == lineView.text) {
2110 lineView.node = elt("div", null, null, "position: relative")
2111 if (lineView.text.parentNode)
2112 { lineView.text.parentNode.replaceChild(lineView.node, lineView.text) }
2113 lineView.node.appendChild(lineView.text)
2114 if (ie && ie_version < 8) { lineView.node.style.zIndex = 2 }
2115 }
2116 return lineView.node
2117 }
2118
2119 function updateLineBackground(lineView) {
2120 var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass
2121 if (cls) { cls += " CodeMirror-linebackground" }
2122 if (lineView.background) {
2123 if (cls) { lineView.background.className = cls }
2124 else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null }
2125 } else if (cls) {
2126 var wrap = ensureLineWrapped(lineView)
2127 lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild)
2128 }
2129 }
2130
2131 // Wrapper around buildLineContent which will reuse the structure
2132 // in display.externalMeasured when possible.
2133 function getLineContent(cm, lineView) {
2134 var ext = cm.display.externalMeasured
2135 if (ext && ext.line == lineView.line) {
2136 cm.display.externalMeasured = null
2137 lineView.measure = ext.measure
2138 return ext.built
2139 }
2140 return buildLineContent(cm, lineView)
2141 }
2142
2143 // Redraw the line's text. Interacts with the background and text
2144 // classes because the mode may output tokens that influence these
2145 // classes.
2146 function updateLineText(cm, lineView) {
2147 var cls = lineView.text.className
2148 var built = getLineContent(cm, lineView)
2149 if (lineView.text == lineView.node) { lineView.node = built.pre }
2150 lineView.text.parentNode.replaceChild(built.pre, lineView.text)
2151 lineView.text = built.pre
2152 if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {
2153 lineView.bgClass = built.bgClass
2154 lineView.textClass = built.textClass
2155 updateLineClasses(lineView)
2156 } else if (cls) {
2157 lineView.text.className = cls
2158 }
2159 }
2160
2161 function updateLineClasses(lineView) {
2162 updateLineBackground(lineView)
2163 if (lineView.line.wrapClass)
2164 { ensureLineWrapped(lineView).className = lineView.line.wrapClass }
2165 else if (lineView.node != lineView.text)
2166 { lineView.node.className = "" }
2167 var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass
2168 lineView.text.className = textClass || ""
2169 }
2170
2171 function updateLineGutter(cm, lineView, lineN, dims) {
2172 if (lineView.gutter) {
2173 lineView.node.removeChild(lineView.gutter)
2174 lineView.gutter = null
2175 }
2176 if (lineView.gutterBackground) {
2177 lineView.node.removeChild(lineView.gutterBackground)
2178 lineView.gutterBackground = null
2179 }
2180 if (lineView.line.gutterClass) {
2181 var wrap = ensureLineWrapped(lineView)
2182 lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass,
2183 ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px; width: " + (dims.gutterTotalWidth) + "px"))
2184 wrap.insertBefore(lineView.gutterBackground, lineView.text)
2185 }
2186 var markers = lineView.line.gutterMarkers
2187 if (cm.options.lineNumbers || markers) {
2188 var wrap$1 = ensureLineWrapped(lineView)
2189 var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"))
2190 cm.display.input.setUneditable(gutterWrap)
2191 wrap$1.insertBefore(gutterWrap, lineView.text)
2192 if (lineView.line.gutterClass)
2193 { gutterWrap.className += " " + lineView.line.gutterClass }
2194 if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
2195 { lineView.lineNumber = gutterWrap.appendChild(
2196 elt("div", lineNumberFor(cm.options, lineN),
2197 "CodeMirror-linenumber CodeMirror-gutter-elt",
2198 ("left: " + (dims.gutterLeft["CodeMirror-linenumbers"]) + "px; width: " + (cm.display.lineNumInnerWidth) + "px"))) }
2199 if (markers) { for (var k = 0; k < cm.options.gutters.length; ++k) {
2200 var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id]
2201 if (found)
2202 { gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt",
2203 ("left: " + (dims.gutterLeft[id]) + "px; width: " + (dims.gutterWidth[id]) + "px"))) }
2204 } }
2205 }
2206 }
2207
2208 function updateLineWidgets(cm, lineView, dims) {
2209 if (lineView.alignable) { lineView.alignable = null }
2210 for (var node = lineView.node.firstChild, next = void 0; node; node = next) {
2211 next = node.nextSibling
2212 if (node.className == "CodeMirror-linewidget")
2213 { lineView.node.removeChild(node) }
2214 }
2215 insertLineWidgets(cm, lineView, dims)
2216 }
2217
2218 // Build a line's DOM representation from scratch
2219 function buildLineElement(cm, lineView, lineN, dims) {
2220 var built = getLineContent(cm, lineView)
2221 lineView.text = lineView.node = built.pre
2222 if (built.bgClass) { lineView.bgClass = built.bgClass }
2223 if (built.textClass) { lineView.textClass = built.textClass }
2224
2225 updateLineClasses(lineView)
2226 updateLineGutter(cm, lineView, lineN, dims)
2227 insertLineWidgets(cm, lineView, dims)
2228 return lineView.node
2229 }
2230
2231 // A lineView may contain multiple logical lines (when merged by
2232 // collapsed spans). The widgets for all of them need to be drawn.
2233 function insertLineWidgets(cm, lineView, dims) {
2234 insertLineWidgetsFor(cm, lineView.line, lineView, dims, true)
2235 if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
2236 { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false) } }
2237 }
2238
2239 function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {
2240 if (!line.widgets) { return }
2241 var wrap = ensureLineWrapped(lineView)
2242 for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
2243 var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget")
2244 if (!widget.handleMouseEvents) { node.setAttribute("cm-ignore-events", "true") }
2245 positionLineWidget(widget, node, lineView, dims)
2246 cm.display.input.setUneditable(node)
2247 if (allowAbove && widget.above)
2248 { wrap.insertBefore(node, lineView.gutter || lineView.text) }
2249 else
2250 { wrap.appendChild(node) }
2251 signalLater(widget, "redraw")
2252 }
2253 }
2254
2255 function positionLineWidget(widget, node, lineView, dims) {
2256 if (widget.noHScroll) {
2257 (lineView.alignable || (lineView.alignable = [])).push(node)
2258 var width = dims.wrapperWidth
2259 node.style.left = dims.fixedPos + "px"
2260 if (!widget.coverGutter) {
2261 width -= dims.gutterTotalWidth
2262 node.style.paddingLeft = dims.gutterTotalWidth + "px"
2263 }
2264 node.style.width = width + "px"
2265 }
2266 if (widget.coverGutter) {
2267 node.style.zIndex = 5
2268 node.style.position = "relative"
2269 if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + "px" }
2270 }
2271 }
2272
2273 function widgetHeight(widget) {
2274 if (widget.height != null) { return widget.height }
2275 var cm = widget.doc.cm
2276 if (!cm) { return 0 }
2277 if (!contains(document.body, widget.node)) {
2278 var parentStyle = "position: relative;"
2279 if (widget.coverGutter)
2280 { parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;" }
2281 if (widget.noHScroll)
2282 { parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;" }
2283 removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle))
2284 }
2285 return widget.height = widget.node.parentNode.offsetHeight
2286 }
2287
2288 // Return true when the given mouse event happened in a widget
2289 function eventInWidget(display, e) {
2290 for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
2291 if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") ||
2292 (n.parentNode == display.sizer && n != display.mover))
2293 { return true }
2294 }
2295 }
2296
2297 // POSITION MEASUREMENT
2298
2299 function paddingTop(display) {return display.lineSpace.offsetTop}
2300 function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight}
2301 function paddingH(display) {
2302 if (display.cachedPaddingH) { return display.cachedPaddingH }
2303 var e = removeChildrenAndAdd(display.measure, elt("pre", "x"))
2304 var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle
2305 var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)}
2306 if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data }
2307 return data
2308 }
2309
2310 function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth }
2311 function displayWidth(cm) {
2312 return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth
2313 }
2314 function displayHeight(cm) {
2315 return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight
2316 }
2317
2318 // Ensure the lineView.wrapping.heights array is populated. This is
2319 // an array of bottom offsets for the lines that make up a drawn
2320 // line. When lineWrapping is on, there might be more than one
2321 // height.
2322 function ensureLineHeights(cm, lineView, rect) {
2323 var wrapping = cm.options.lineWrapping
2324 var curWidth = wrapping && displayWidth(cm)
2325 if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {
2326 var heights = lineView.measure.heights = []
2327 if (wrapping) {
2328 lineView.measure.width = curWidth
2329 var rects = lineView.text.firstChild.getClientRects()
2330 for (var i = 0; i < rects.length - 1; i++) {
2331 var cur = rects[i], next = rects[i + 1]
2332 if (Math.abs(cur.bottom - next.bottom) > 2)
2333 { heights.push((cur.bottom + next.top) / 2 - rect.top) }
2334 }
2335 }
2336 heights.push(rect.bottom - rect.top)
2337 }
2338 }
2339
2340 // Find a line map (mapping character offsets to text nodes) and a
2341 // measurement cache for the given line number. (A line view might
2342 // contain multiple lines when collapsed ranges are present.)
2343 function mapFromLineView(lineView, line, lineN) {
2344 if (lineView.line == line)
2345 { return {map: lineView.measure.map, cache: lineView.measure.cache} }
2346 for (var i = 0; i < lineView.rest.length; i++)
2347 { if (lineView.rest[i] == line)
2348 { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } }
2349 for (var i$1 = 0; i$1 < lineView.rest.length; i$1++)
2350 { if (lineNo(lineView.rest[i$1]) > lineN)
2351 { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } }
2352 }
2353
2354 // Render a line into the hidden node display.externalMeasured. Used
2355 // when measurement is needed for a line that's not in the viewport.
2356 function updateExternalMeasurement(cm, line) {
2357 line = visualLine(line)
2358 var lineN = lineNo(line)
2359 var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN)
2360 view.lineN = lineN
2361 var built = view.built = buildLineContent(cm, view)
2362 view.text = built.pre
2363 removeChildrenAndAdd(cm.display.lineMeasure, built.pre)
2364 return view
2365 }
2366
2367 // Get a {top, bottom, left, right} box (in line-local coordinates)
2368 // for a given character.
2369 function measureChar(cm, line, ch, bias) {
2370 return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias)
2371 }
2372
2373 // Find a line view that corresponds to the given line number.
2374 function findViewForLine(cm, lineN) {
2375 if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo)
2376 { return cm.display.view[findViewIndex(cm, lineN)] }
2377 var ext = cm.display.externalMeasured
2378 if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size)
2379 { return ext }
2380 }
2381
2382 // Measurement can be split in two steps, the set-up work that
2383 // applies to the whole line, and the measurement of the actual
2384 // character. Functions like coordsChar, that need to do a lot of
2385 // measurements in a row, can thus ensure that the set-up work is
2386 // only done once.
2387 function prepareMeasureForLine(cm, line) {
2388 var lineN = lineNo(line)
2389 var view = findViewForLine(cm, lineN)
2390 if (view && !view.text) {
2391 view = null
2392 } else if (view && view.changes) {
2393 updateLineForChanges(cm, view, lineN, getDimensions(cm))
2394 cm.curOp.forceUpdate = true
2395 }
2396 if (!view)
2397 { view = updateExternalMeasurement(cm, line) }
2398
2399 var info = mapFromLineView(view, line, lineN)
2400 return {
2401 line: line, view: view, rect: null,
2402 map: info.map, cache: info.cache, before: info.before,
2403 hasHeights: false
2404 }
2405 }
2406
2407 // Given a prepared measurement object, measures the position of an
2408 // actual character (or fetches it from the cache).
2409 function measureCharPrepared(cm, prepared, ch, bias, varHeight) {
2410 if (prepared.before) { ch = -1 }
2411 var key = ch + (bias || ""), found
2412 if (prepared.cache.hasOwnProperty(key)) {
2413 found = prepared.cache[key]
2414 } else {
2415 if (!prepared.rect)
2416 { prepared.rect = prepared.view.text.getBoundingClientRect() }
2417 if (!prepared.hasHeights) {
2418 ensureLineHeights(cm, prepared.view, prepared.rect)
2419 prepared.hasHeights = true
2420 }
2421 found = measureCharInner(cm, prepared, ch, bias)
2422 if (!found.bogus) { prepared.cache[key] = found }
2423 }
2424 return {left: found.left, right: found.right,
2425 top: varHeight ? found.rtop : found.top,
2426 bottom: varHeight ? found.rbottom : found.bottom}
2427 }
2428
2429 var nullRect = {left: 0, right: 0, top: 0, bottom: 0}
2430
2431 function nodeAndOffsetInLineMap(map$$1, ch, bias) {
2432 var node, start, end, collapse, mStart, mEnd
2433 // First, search the line map for the text node corresponding to,
2434 // or closest to, the target character.
2435 for (var i = 0; i < map$$1.length; i += 3) {
2436 mStart = map$$1[i]
2437 mEnd = map$$1[i + 1]
2438 if (ch < mStart) {
2439 start = 0; end = 1
2440 collapse = "left"
2441 } else if (ch < mEnd) {
2442 start = ch - mStart
2443 end = start + 1
2444 } else if (i == map$$1.length - 3 || ch == mEnd && map$$1[i + 3] > ch) {
2445 end = mEnd - mStart
2446 start = end - 1
2447 if (ch >= mEnd) { collapse = "right" }
2448 }
2449 if (start != null) {
2450 node = map$$1[i + 2]
2451 if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right"))
2452 { collapse = bias }
2453 if (bias == "left" && start == 0)
2454 { while (i && map$$1[i - 2] == map$$1[i - 3] && map$$1[i - 1].insertLeft) {
2455 node = map$$1[(i -= 3) + 2]
2456 collapse = "left"
2457 } }
2458 if (bias == "right" && start == mEnd - mStart)
2459 { while (i < map$$1.length - 3 && map$$1[i + 3] == map$$1[i + 4] && !map$$1[i + 5].insertLeft) {
2460 node = map$$1[(i += 3) + 2]
2461 collapse = "right"
2462 } }
2463 break
2464 }
2465 }
2466 return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd}
2467 }
2468
2469 function getUsefulRect(rects, bias) {
2470 var rect = nullRect
2471 if (bias == "left") { for (var i = 0; i < rects.length; i++) {
2472 if ((rect = rects[i]).left != rect.right) { break }
2473 } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) {
2474 if ((rect = rects[i$1]).left != rect.right) { break }
2475 } }
2476 return rect
2477 }
2478
2479 function measureCharInner(cm, prepared, ch, bias) {
2480 var place = nodeAndOffsetInLineMap(prepared.map, ch, bias)
2481 var node = place.node, start = place.start, end = place.end, collapse = place.collapse
2482
2483 var rect
2484 if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.
2485 for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonsense rectangles are returned
2486 while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start }
2487 while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) { ++end }
2488 if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart)
2489 { rect = node.parentNode.getBoundingClientRect() }
2490 else
2491 { rect = getUsefulRect(range(node, start, end).getClientRects(), bias) }
2492 if (rect.left || rect.right || start == 0) { break }
2493 end = start
2494 start = start - 1
2495 collapse = "right"
2496 }
2497 if (ie && ie_version < 11) { rect = maybeUpdateRectForZooming(cm.display.measure, rect) }
2498 } else { // If it is a widget, simply get the box for the whole widget.
2499 if (start > 0) { collapse = bias = "right" }
2500 var rects
2501 if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)
2502 { rect = rects[bias == "right" ? rects.length - 1 : 0] }
2503 else
2504 { rect = node.getBoundingClientRect() }
2505 }
2506 if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {
2507 var rSpan = node.parentNode.getClientRects()[0]
2508 if (rSpan)
2509 { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom} }
2510 else
2511 { rect = nullRect }
2512 }
2513
2514 var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top
2515 var mid = (rtop + rbot) / 2
2516 var heights = prepared.view.measure.heights
2517 var i = 0
2518 for (; i < heights.length - 1; i++)
2519 { if (mid < heights[i]) { break } }
2520 var top = i ? heights[i - 1] : 0, bot = heights[i]
2521 var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left,
2522 right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left,
2523 top: top, bottom: bot}
2524 if (!rect.left && !rect.right) { result.bogus = true }
2525 if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot }
2526
2527 return result
2528 }
2529
2530 // Work around problem with bounding client rects on ranges being
2531 // returned incorrectly when zoomed on IE10 and below.
2532 function maybeUpdateRectForZooming(measure, rect) {
2533 if (!window.screen || screen.logicalXDPI == null ||
2534 screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))
2535 { return rect }
2536 var scaleX = screen.logicalXDPI / screen.deviceXDPI
2537 var scaleY = screen.logicalYDPI / screen.deviceYDPI
2538 return {left: rect.left * scaleX, right: rect.right * scaleX,
2539 top: rect.top * scaleY, bottom: rect.bottom * scaleY}
2540 }
2541
2542 function clearLineMeasurementCacheFor(lineView) {
2543 if (lineView.measure) {
2544 lineView.measure.cache = {}
2545 lineView.measure.heights = null
2546 if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
2547 { lineView.measure.caches[i] = {} } }
2548 }
2549 }
2550
2551 function clearLineMeasurementCache(cm) {
2552 cm.display.externalMeasure = null
2553 removeChildren(cm.display.lineMeasure)
2554 for (var i = 0; i < cm.display.view.length; i++)
2555 { clearLineMeasurementCacheFor(cm.display.view[i]) }
2556 }
2557
2558 function clearCaches(cm) {
2559 clearLineMeasurementCache(cm)
2560 cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null
2561 if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true }
2562 cm.display.lineNumChars = null
2563 }
2564
2565 function pageScrollX() { return window.pageXOffset || (document.documentElement || document.body).scrollLeft }
2566 function pageScrollY() { return window.pageYOffset || (document.documentElement || document.body).scrollTop }
2567
2568 // Converts a {top, bottom, left, right} box from line-local
2569 // coordinates into another coordinate system. Context may be one of
2570 // "line", "div" (display.lineDiv), "local"./null (editor), "window",
2571 // or "page".
2572 function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) {
2573 if (!includeWidgets && lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above) {
2574 var size = widgetHeight(lineObj.widgets[i])
2575 rect.top += size; rect.bottom += size
2576 } } }
2577 if (context == "line") { return rect }
2578 if (!context) { context = "local" }
2579 var yOff = heightAtLine(lineObj)
2580 if (context == "local") { yOff += paddingTop(cm.display) }
2581 else { yOff -= cm.display.viewOffset }
2582 if (context == "page" || context == "window") {
2583 var lOff = cm.display.lineSpace.getBoundingClientRect()
2584 yOff += lOff.top + (context == "window" ? 0 : pageScrollY())
2585 var xOff = lOff.left + (context == "window" ? 0 : pageScrollX())
2586 rect.left += xOff; rect.right += xOff
2587 }
2588 rect.top += yOff; rect.bottom += yOff
2589 return rect
2590 }
2591
2592 // Coverts a box from "div" coords to another coordinate system.
2593 // Context may be "window", "page", "div", or "local"./null.
2594 function fromCoordSystem(cm, coords, context) {
2595 if (context == "div") { return coords }
2596 var left = coords.left, top = coords.top
2597 // First move into "page" coordinate system
2598 if (context == "page") {
2599 left -= pageScrollX()
2600 top -= pageScrollY()
2601 } else if (context == "local" || !context) {
2602 var localBox = cm.display.sizer.getBoundingClientRect()
2603 left += localBox.left
2604 top += localBox.top
2605 }
2606
2607 var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect()
2608 return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top}
2609 }
2610
2611 function charCoords(cm, pos, context, lineObj, bias) {
2612 if (!lineObj) { lineObj = getLine(cm.doc, pos.line) }
2613 return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context)
2614 }
2615
2616 // Returns a box for a given cursor position, which may have an
2617 // 'other' property containing the position of the secondary cursor
2618 // on a bidi boundary.
2619 function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
2620 lineObj = lineObj || getLine(cm.doc, pos.line)
2621 if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj) }
2622 function get(ch, right) {
2623 var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight)
2624 if (right) { m.left = m.right; } else { m.right = m.left }
2625 return intoCoordSystem(cm, lineObj, m, context)
2626 }
2627 function getBidi(ch, partPos) {
2628 var part = order[partPos], right = part.level % 2
2629 if (ch == bidiLeft(part) && partPos && part.level < order[partPos - 1].level) {
2630 part = order[--partPos]
2631 ch = bidiRight(part) - (part.level % 2 ? 0 : 1)
2632 right = true
2633 } else if (ch == bidiRight(part) && partPos < order.length - 1 && part.level < order[partPos + 1].level) {
2634 part = order[++partPos]
2635 ch = bidiLeft(part) - part.level % 2
2636 right = false
2637 }
2638 if (right && ch == part.to && ch > part.from) { return get(ch - 1) }
2639 return get(ch, right)
2640 }
2641 var order = getOrder(lineObj), ch = pos.ch
2642 if (!order) { return get(ch) }
2643 var partPos = getBidiPartAt(order, ch)
2644 var val = getBidi(ch, partPos)
2645 if (bidiOther != null) { val.other = getBidi(ch, bidiOther) }
2646 return val
2647 }
2648
2649 // Used to cheaply estimate the coordinates for a position. Used for
2650 // intermediate scroll updates.
2651 function estimateCoords(cm, pos) {
2652 var left = 0
2653 pos = clipPos(cm.doc, pos)
2654 if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch }
2655 var lineObj = getLine(cm.doc, pos.line)
2656 var top = heightAtLine(lineObj) + paddingTop(cm.display)
2657 return {left: left, right: left, top: top, bottom: top + lineObj.height}
2658 }
2659
2660 // Positions returned by coordsChar contain some extra information.
2661 // xRel is the relative x position of the input coordinates compared
2662 // to the found position (so xRel > 0 means the coordinates are to
2663 // the right of the character position, for example). When outside
2664 // is true, that means the coordinates lie outside the line's
2665 // vertical range.
2666 function PosWithInfo(line, ch, outside, xRel) {
2667 var pos = Pos(line, ch)
2668 pos.xRel = xRel
2669 if (outside) { pos.outside = true }
2670 return pos
2671 }
2672
2673 // Compute the character position closest to the given coordinates.
2674 // Input must be lineSpace-local ("div" coordinate system).
2675 function coordsChar(cm, x, y) {
2676 var doc = cm.doc
2677 y += cm.display.viewOffset
2678 if (y < 0) { return PosWithInfo(doc.first, 0, true, -1) }
2679 var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1
2680 if (lineN > last)
2681 { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, true, 1) }
2682 if (x < 0) { x = 0 }
2683
2684 var lineObj = getLine(doc, lineN)
2685 for (;;) {
2686 var found = coordsCharInner(cm, lineObj, lineN, x, y)
2687 var merged = collapsedSpanAtEnd(lineObj)
2688 var mergedPos = merged && merged.find(0, true)
2689 if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0))
2690 { lineN = lineNo(lineObj = mergedPos.to.line) }
2691 else
2692 { return found }
2693 }
2694 }
2695
2696 function coordsCharInner(cm, lineObj, lineNo$$1, x, y) {
2697 var innerOff = y - heightAtLine(lineObj)
2698 var wrongLine = false, adjust = 2 * cm.display.wrapper.clientWidth
2699 var preparedMeasure = prepareMeasureForLine(cm, lineObj)
2700
2701 function getX(ch) {
2702 var sp = cursorCoords(cm, Pos(lineNo$$1, ch), "line", lineObj, preparedMeasure)
2703 wrongLine = true
2704 if (innerOff > sp.bottom) { return sp.left - adjust }
2705 else if (innerOff < sp.top) { return sp.left + adjust }
2706 else { wrongLine = false }
2707 return sp.left
2708 }
2709
2710 var bidi = getOrder(lineObj), dist = lineObj.text.length
2711 var from = lineLeft(lineObj), to = lineRight(lineObj)
2712 var fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine
2713
2714 if (x > toX) { return PosWithInfo(lineNo$$1, to, toOutside, 1) }
2715 // Do a binary search between these bounds.
2716 for (;;) {
2717 if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
2718 var ch = x < fromX || x - fromX <= toX - x ? from : to
2719 var outside = ch == from ? fromOutside : toOutside
2720 var xDiff = x - (ch == from ? fromX : toX)
2721 // This is a kludge to handle the case where the coordinates
2722 // are after a line-wrapped line. We should replace it with a
2723 // more general handling of cursor positions around line
2724 // breaks. (Issue #4078)
2725 if (toOutside && !bidi && !/\s/.test(lineObj.text.charAt(ch)) && xDiff > 0 &&
2726 ch < lineObj.text.length && preparedMeasure.view.measure.heights.length > 1) {
2727 var charSize = measureCharPrepared(cm, preparedMeasure, ch, "right")
2728 if (innerOff <= charSize.bottom && innerOff >= charSize.top && Math.abs(x - charSize.right) < xDiff) {
2729 outside = false
2730 ch++
2731 xDiff = x - charSize.right
2732 }
2733 }
2734 while (isExtendingChar(lineObj.text.charAt(ch))) { ++ch }
2735 var pos = PosWithInfo(lineNo$$1, ch, outside, xDiff < -1 ? -1 : xDiff > 1 ? 1 : 0)
2736 return pos
2737 }
2738 var step = Math.ceil(dist / 2), middle = from + step
2739 if (bidi) {
2740 middle = from
2741 for (var i = 0; i < step; ++i) { middle = moveVisually(lineObj, middle, 1) }
2742 }
2743 var middleX = getX(middle)
2744 if (middleX > x) {to = middle; toX = middleX; if (toOutside = wrongLine) { toX += 1000; } dist = step}
2745 else {from = middle; fromX = middleX; fromOutside = wrongLine; dist -= step}
2746 }
2747 }
2748
2749 var measureText
2750 // Compute the default text height.
2751 function textHeight(display) {
2752 if (display.cachedTextHeight != null) { return display.cachedTextHeight }
2753 if (measureText == null) {
2754 measureText = elt("pre")
2755 // Measure a bunch of lines, for browsers that compute
2756 // fractional heights.
2757 for (var i = 0; i < 49; ++i) {
2758 measureText.appendChild(document.createTextNode("x"))
2759 measureText.appendChild(elt("br"))
2760 }
2761 measureText.appendChild(document.createTextNode("x"))
2762 }
2763 removeChildrenAndAdd(display.measure, measureText)
2764 var height = measureText.offsetHeight / 50
2765 if (height > 3) { display.cachedTextHeight = height }
2766 removeChildren(display.measure)
2767 return height || 1
2768 }
2769
2770 // Compute the default character width.
2771 function charWidth(display) {
2772 if (display.cachedCharWidth != null) { return display.cachedCharWidth }
2773 var anchor = elt("span", "xxxxxxxxxx")
2774 var pre = elt("pre", [anchor])
2775 removeChildrenAndAdd(display.measure, pre)
2776 var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10
2777 if (width > 2) { display.cachedCharWidth = width }
2778 return width || 10
2779 }
2780
2781 // Do a bulk-read of the DOM positions and sizes needed to draw the
2782 // view, so that we don't interleave reading and writing to the DOM.
2783 function getDimensions(cm) {
2784 var d = cm.display, left = {}, width = {}
2785 var gutterLeft = d.gutters.clientLeft
2786 for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
2787 left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft
2788 width[cm.options.gutters[i]] = n.clientWidth
2789 }
2790 return {fixedPos: compensateForHScroll(d),
2791 gutterTotalWidth: d.gutters.offsetWidth,
2792 gutterLeft: left,
2793 gutterWidth: width,
2794 wrapperWidth: d.wrapper.clientWidth}
2795 }
2796
2797 // Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
2798 // but using getBoundingClientRect to get a sub-pixel-accurate
2799 // result.
2800 function compensateForHScroll(display) {
2801 return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left
2802 }
2803
2804 // Returns a function that estimates the height of a line, to use as
2805 // first approximation until the line becomes visible (and is thus
2806 // properly measurable).
2807 function estimateHeight(cm) {
2808 var th = textHeight(cm.display), wrapping = cm.options.lineWrapping
2809 var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3)
2810 return function (line) {
2811 if (lineIsHidden(cm.doc, line)) { return 0 }
2812
2813 var widgetsHeight = 0
2814 if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) {
2815 if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height }
2816 } }
2817
2818 if (wrapping)
2819 { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th }
2820 else
2821 { return widgetsHeight + th }
2822 }
2823 }
2824
2825 function estimateLineHeights(cm) {
2826 var doc = cm.doc, est = estimateHeight(cm)
2827 doc.iter(function (line) {
2828 var estHeight = est(line)
2829 if (estHeight != line.height) { updateLineHeight(line, estHeight) }
2830 })
2831 }
2832
2833 // Given a mouse event, find the corresponding position. If liberal
2834 // is false, it checks whether a gutter or scrollbar was clicked,
2835 // and returns null if it was. forRect is used by rectangular
2836 // selections, and tries to estimate a character position even for
2837 // coordinates beyond the right of the text.
2838 function posFromMouse(cm, e, liberal, forRect) {
2839 var display = cm.display
2840 if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") { return null }
2841
2842 var x, y, space = display.lineSpace.getBoundingClientRect()
2843 // Fails unpredictably on IE[67] when mouse is dragged around quickly.
2844 try { x = e.clientX - space.left; y = e.clientY - space.top }
2845 catch (e) { return null }
2846 var coords = coordsChar(cm, x, y), line
2847 if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) {
2848 var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length
2849 coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff))
2850 }
2851 return coords
2852 }
2853
2854 // Find the view element corresponding to a given line. Return null
2855 // when the line isn't visible.
2856 function findViewIndex(cm, n) {
2857 if (n >= cm.display.viewTo) { return null }
2858 n -= cm.display.viewFrom
2859 if (n < 0) { return null }
2860 var view = cm.display.view
2861 for (var i = 0; i < view.length; i++) {
2862 n -= view[i].size
2863 if (n < 0) { return i }
2864 }
2865 }
2866
2867 function updateSelection(cm) {
2868 cm.display.input.showSelection(cm.display.input.prepareSelection())
2869 }
2870
2871 function prepareSelection(cm, primary) {
2872 var doc = cm.doc, result = {}
2873 var curFragment = result.cursors = document.createDocumentFragment()
2874 var selFragment = result.selection = document.createDocumentFragment()
2875
2876 for (var i = 0; i < doc.sel.ranges.length; i++) {
2877 if (primary === false && i == doc.sel.primIndex) { continue }
2878 var range$$1 = doc.sel.ranges[i]
2879 if (range$$1.from().line >= cm.display.viewTo || range$$1.to().line < cm.display.viewFrom) { continue }
2880 var collapsed = range$$1.empty()
2881 if (collapsed || cm.options.showCursorWhenSelecting)
2882 { drawSelectionCursor(cm, range$$1.head, curFragment) }
2883 if (!collapsed)
2884 { drawSelectionRange(cm, range$$1, selFragment) }
2885 }
2886 return result
2887 }
2888
2889 // Draws a cursor for the given range
2890 function drawSelectionCursor(cm, head, output) {
2891 var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine)
2892
2893 var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor"))
2894 cursor.style.left = pos.left + "px"
2895 cursor.style.top = pos.top + "px"
2896 cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px"
2897
2898 if (pos.other) {
2899 // Secondary cursor, shown when on a 'jump' in bi-directional text
2900 var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"))
2901 otherCursor.style.display = ""
2902 otherCursor.style.left = pos.other.left + "px"
2903 otherCursor.style.top = pos.other.top + "px"
2904 otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px"
2905 }
2906 }
2907
2908 // Draws the given range as a highlighted selection
2909 function drawSelectionRange(cm, range$$1, output) {
2910 var display = cm.display, doc = cm.doc
2911 var fragment = document.createDocumentFragment()
2912 var padding = paddingH(cm.display), leftSide = padding.left
2913 var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right
2914
2915 function add(left, top, width, bottom) {
2916 if (top < 0) { top = 0 }
2917 top = Math.round(top)
2918 bottom = Math.round(bottom)
2919 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")))
2920 }
2921
2922 function drawForLine(line, fromArg, toArg) {
2923 var lineObj = getLine(doc, line)
2924 var lineLen = lineObj.text.length
2925 var start, end
2926 function coords(ch, bias) {
2927 return charCoords(cm, Pos(line, ch), "div", lineObj, bias)
2928 }
2929
2930 iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir) {
2931 var leftPos = coords(from, "left"), rightPos, left, right
2932 if (from == to) {
2933 rightPos = leftPos
2934 left = right = leftPos.left
2935 } else {
2936 rightPos = coords(to - 1, "right")
2937 if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp }
2938 left = leftPos.left
2939 right = rightPos.right
2940 }
2941 if (fromArg == null && from == 0) { left = leftSide }
2942 if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
2943 add(left, leftPos.top, null, leftPos.bottom)
2944 left = leftSide
2945 if (leftPos.bottom < rightPos.top) { add(left, leftPos.bottom, null, rightPos.top) }
2946 }
2947 if (toArg == null && to == lineLen) { right = rightSide }
2948 if (!start || leftPos.top < start.top || leftPos.top == start.top && leftPos.left < start.left)
2949 { start = leftPos }
2950 if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.bottom && rightPos.right > end.right)
2951 { end = rightPos }
2952 if (left < leftSide + 1) { left = leftSide }
2953 add(left, rightPos.top, right - left, rightPos.bottom)
2954 })
2955 return {start: start, end: end}
2956 }
2957
2958 var sFrom = range$$1.from(), sTo = range$$1.to()
2959 if (sFrom.line == sTo.line) {
2960 drawForLine(sFrom.line, sFrom.ch, sTo.ch)
2961 } else {
2962 var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line)
2963 var singleVLine = visualLine(fromLine) == visualLine(toLine)
2964 var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end
2965 var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start
2966 if (singleVLine) {
2967 if (leftEnd.top < rightStart.top - 2) {
2968 add(leftEnd.right, leftEnd.top, null, leftEnd.bottom)
2969 add(leftSide, rightStart.top, rightStart.left, rightStart.bottom)
2970 } else {
2971 add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom)
2972 }
2973 }
2974 if (leftEnd.bottom < rightStart.top)
2975 { add(leftSide, leftEnd.bottom, null, rightStart.top) }
2976 }
2977
2978 output.appendChild(fragment)
2979 }
2980
2981 // Cursor-blinking
2982 function restartBlink(cm) {
2983 if (!cm.state.focused) { return }
2984 var display = cm.display
2985 clearInterval(display.blinker)
2986 var on = true
2987 display.cursorDiv.style.visibility = ""
2988 if (cm.options.cursorBlinkRate > 0)
2989 { display.blinker = setInterval(function () { return display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; },
2990 cm.options.cursorBlinkRate) }
2991 else if (cm.options.cursorBlinkRate < 0)
2992 { display.cursorDiv.style.visibility = "hidden" }
2993 }
2994
2995 function ensureFocus(cm) {
2996 if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm) }
2997 }
2998
2999 function delayBlurEvent(cm) {
3000 cm.state.delayingBlurEvent = true
3001 setTimeout(function () { if (cm.state.delayingBlurEvent) {
3002 cm.state.delayingBlurEvent = false
3003 onBlur(cm)
3004 } }, 100)
3005 }
3006
3007 function onFocus(cm, e) {
3008 if (cm.state.delayingBlurEvent) { cm.state.delayingBlurEvent = false }
3009
3010 if (cm.options.readOnly == "nocursor") { return }
3011 if (!cm.state.focused) {
3012 signal(cm, "focus", cm, e)
3013 cm.state.focused = true
3014 addClass(cm.display.wrapper, "CodeMirror-focused")
3015 // This test prevents this from firing when a context
3016 // menu is closed (since the input reset would kill the
3017 // select-all detection hack)
3018 if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {
3019 cm.display.input.reset()
3020 if (webkit) { setTimeout(function () { return cm.display.input.reset(true); }, 20) } // Issue #1730
3021 }
3022 cm.display.input.receivedFocus()
3023 }
3024 restartBlink(cm)
3025 }
3026 function onBlur(cm, e) {
3027 if (cm.state.delayingBlurEvent) { return }
3028
3029 if (cm.state.focused) {
3030 signal(cm, "blur", cm, e)
3031 cm.state.focused = false
3032 rmClass(cm.display.wrapper, "CodeMirror-focused")
3033 }
3034 clearInterval(cm.display.blinker)
3035 setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false } }, 150)
3036 }
3037
3038 // Re-align line numbers and gutter marks to compensate for
3039 // horizontal scrolling.
3040 function alignHorizontally(cm) {
3041 var display = cm.display, view = display.view
3042 if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) { return }
3043 var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft
3044 var gutterW = display.gutters.offsetWidth, left = comp + "px"
3045 for (var i = 0; i < view.length; i++) { if (!view[i].hidden) {
3046 if (cm.options.fixedGutter) {
3047 if (view[i].gutter)
3048 { view[i].gutter.style.left = left }
3049 if (view[i].gutterBackground)
3050 { view[i].gutterBackground.style.left = left }
3051 }
3052 var align = view[i].alignable
3053 if (align) { for (var j = 0; j < align.length; j++)
3054 { align[j].style.left = left } }
3055 } }
3056 if (cm.options.fixedGutter)
3057 { display.gutters.style.left = (comp + gutterW) + "px" }
3058 }
3059
3060 // Used to ensure that the line number gutter is still the right
3061 // size for the current document size. Returns true when an update
3062 // is needed.
3063 function maybeUpdateLineNumberWidth(cm) {
3064 if (!cm.options.lineNumbers) { return false }
3065 var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display
3066 if (last.length != display.lineNumChars) {
3067 var test = display.measure.appendChild(elt("div", [elt("div", last)],
3068 "CodeMirror-linenumber CodeMirror-gutter-elt"))
3069 var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW
3070 display.lineGutter.style.width = ""
3071 display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1
3072 display.lineNumWidth = display.lineNumInnerWidth + padding
3073 display.lineNumChars = display.lineNumInnerWidth ? last.length : -1
3074 display.lineGutter.style.width = display.lineNumWidth + "px"
3075 updateGutterSpace(cm)
3076 return true
3077 }
3078 return false
3079 }
3080
3081 // Read the actual heights of the rendered lines, and update their
3082 // stored heights to match.
3083 function updateHeightsInViewport(cm) {
3084 var display = cm.display
3085 var prevBottom = display.lineDiv.offsetTop
3086 for (var i = 0; i < display.view.length; i++) {
3087 var cur = display.view[i], height = void 0
3088 if (cur.hidden) { continue }
3089 if (ie && ie_version < 8) {
3090 var bot = cur.node.offsetTop + cur.node.offsetHeight
3091 height = bot - prevBottom
3092 prevBottom = bot
3093 } else {
3094 var box = cur.node.getBoundingClientRect()
3095 height = box.bottom - box.top
3096 }
3097 var diff = cur.line.height - height
3098 if (height < 2) { height = textHeight(display) }
3099 if (diff > .001 || diff < -.001) {
3100 updateLineHeight(cur.line, height)
3101 updateWidgetHeight(cur.line)
3102 if (cur.rest) { for (var j = 0; j < cur.rest.length; j++)
3103 { updateWidgetHeight(cur.rest[j]) } }
3104 }
3105 }
3106 }
3107
3108 // Read and store the height of line widgets associated with the
3109 // given line.
3110 function updateWidgetHeight(line) {
3111 if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i)
3112 { line.widgets[i].height = line.widgets[i].node.parentNode.offsetHeight } }
3113 }
3114
3115 // Compute the lines that are visible in a given viewport (defaults
3116 // the the current scroll position). viewport may contain top,
3117 // height, and ensure (see op.scrollToPos) properties.
3118 function visibleLines(display, doc, viewport) {
3119 var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop
3120 top = Math.floor(top - paddingTop(display))
3121 var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight
3122
3123 var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom)
3124 // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
3125 // forces those lines into the viewport (if possible).
3126 if (viewport && viewport.ensure) {
3127 var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line
3128 if (ensureFrom < from) {
3129 from = ensureFrom
3130 to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight)
3131 } else if (Math.min(ensureTo, doc.lastLine()) >= to) {
3132 from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight)
3133 to = ensureTo
3134 }
3135 }
3136 return {from: from, to: Math.max(to, from + 1)}
3137 }
3138
3139 // Sync the scrollable area and scrollbars, ensure the viewport
3140 // covers the visible area.
3141 function setScrollTop(cm, val) {
3142 if (Math.abs(cm.doc.scrollTop - val) < 2) { return }
3143 cm.doc.scrollTop = val
3144 if (!gecko) { updateDisplaySimple(cm, {top: val}) }
3145 if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = val }
3146 cm.display.scrollbars.setScrollTop(val)
3147 if (gecko) { updateDisplaySimple(cm) }
3148 startWorker(cm, 100)
3149 }
3150 // Sync scroller and scrollbar, ensure the gutter elements are
3151 // aligned.
3152 function setScrollLeft(cm, val, isScroller) {
3153 if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) { return }
3154 val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth)
3155 cm.doc.scrollLeft = val
3156 alignHorizontally(cm)
3157 if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val }
3158 cm.display.scrollbars.setScrollLeft(val)
3159 }
3160
3161 // Since the delta values reported on mouse wheel events are
3162 // unstandardized between browsers and even browser versions, and
3163 // generally horribly unpredictable, this code starts by measuring
3164 // the scroll effect that the first few mouse wheel events have,
3165 // and, from that, detects the way it can convert deltas to pixel
3166 // offsets afterwards.
3167 //
3168 // The reason we want to know the amount a wheel event will scroll
3169 // is that it gives us a chance to update the display before the
3170 // actual scrolling happens, reducing flickering.
3171
3172 var wheelSamples = 0;
3173 var wheelPixelsPerUnit = null
3174 // Fill in a browser-detected starting value on browsers where we
3175 // know one. These don't have to be accurate -- the result of them
3176 // being wrong would just be a slight flicker on the first wheel
3177 // scroll (if it is large enough).
3178 if (ie) { wheelPixelsPerUnit = -.53 }
3179 else if (gecko) { wheelPixelsPerUnit = 15 }
3180 else if (chrome) { wheelPixelsPerUnit = -.7 }
3181 else if (safari) { wheelPixelsPerUnit = -1/3 }
3182
3183 function wheelEventDelta(e) {
3184 var dx = e.wheelDeltaX, dy = e.wheelDeltaY
3185 if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail }
3186 if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail }
3187 else if (dy == null) { dy = e.wheelDelta }
3188 return {x: dx, y: dy}
3189 }
3190 function wheelEventPixels(e) {
3191 var delta = wheelEventDelta(e)
3192 delta.x *= wheelPixelsPerUnit
3193 delta.y *= wheelPixelsPerUnit
3194 return delta
3195 }
3196
3197 function onScrollWheel(cm, e) {
3198 var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y
3199
3200 var display = cm.display, scroll = display.scroller
3201 // Quit if there's nothing to scroll here
3202 var canScrollX = scroll.scrollWidth > scroll.clientWidth
3203 var canScrollY = scroll.scrollHeight > scroll.clientHeight
3204 if (!(dx && canScrollX || dy && canScrollY)) { return }
3205
3206 // Webkit browsers on OS X abort momentum scrolls when the target
3207 // of the scroll event is removed from the scrollable element.
3208 // This hack (see related code in patchDisplay) makes sure the
3209 // element is kept around.
3210 if (dy && mac && webkit) {
3211 outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {
3212 for (var i = 0; i < view.length; i++) {
3213 if (view[i].node == cur) {
3214 cm.display.currentWheelTarget = cur
3215 break outer
3216 }
3217 }
3218 }
3219 }
3220
3221 // On some browsers, horizontal scrolling will cause redraws to
3222 // happen before the gutter has been realigned, causing it to
3223 // wriggle around in a most unseemly way. When we have an
3224 // estimated pixels/delta value, we just handle horizontal
3225 // scrolling entirely here. It'll be slightly off from native, but
3226 // better than glitching out.
3227 if (dx && !gecko && !presto && wheelPixelsPerUnit != null) {
3228 if (dy && canScrollY)
3229 { setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight))) }
3230 setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth)))
3231 // Only prevent default scrolling if vertical scrolling is
3232 // actually possible. Otherwise, it causes vertical scroll
3233 // jitter on OSX trackpads when deltaX is small and deltaY
3234 // is large (issue #3579)
3235 if (!dy || (dy && canScrollY))
3236 { e_preventDefault(e) }
3237 display.wheelStartX = null // Abort measurement, if in progress
3238 return
3239 }
3240
3241 // 'Project' the visible viewport to cover the area that is being
3242 // scrolled into view (if we know enough to estimate it).
3243 if (dy && wheelPixelsPerUnit != null) {
3244 var pixels = dy * wheelPixelsPerUnit
3245 var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight
3246 if (pixels < 0) { top = Math.max(0, top + pixels - 50) }
3247 else { bot = Math.min(cm.doc.height, bot + pixels + 50) }
3248 updateDisplaySimple(cm, {top: top, bottom: bot})
3249 }
3250
3251 if (wheelSamples < 20) {
3252 if (display.wheelStartX == null) {
3253 display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop
3254 display.wheelDX = dx; display.wheelDY = dy
3255 setTimeout(function () {
3256 if (display.wheelStartX == null) { return }
3257 var movedX = scroll.scrollLeft - display.wheelStartX
3258 var movedY = scroll.scrollTop - display.wheelStartY
3259 var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
3260 (movedX && display.wheelDX && movedX / display.wheelDX)
3261 display.wheelStartX = display.wheelStartY = null
3262 if (!sample) { return }
3263 wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1)
3264 ++wheelSamples
3265 }, 200)
3266 } else {
3267 display.wheelDX += dx; display.wheelDY += dy
3268 }
3269 }
3270 }
3271
3272 // SCROLLBARS
3273
3274 // Prepare DOM reads needed to update the scrollbars. Done in one
3275 // shot to minimize update/measure roundtrips.
3276 function measureForScrollbars(cm) {
3277 var d = cm.display, gutterW = d.gutters.offsetWidth
3278 var docH = Math.round(cm.doc.height + paddingVert(cm.display))
3279 return {
3280 clientHeight: d.scroller.clientHeight,
3281 viewHeight: d.wrapper.clientHeight,
3282 scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,
3283 viewWidth: d.wrapper.clientWidth,
3284 barLeft: cm.options.fixedGutter ? gutterW : 0,
3285 docHeight: docH,
3286 scrollHeight: docH + scrollGap(cm) + d.barHeight,
3287 nativeBarWidth: d.nativeBarWidth,
3288 gutterWidth: gutterW
3289 }
3290 }
3291
3292 function NativeScrollbars(place, scroll, cm) {
3293 this.cm = cm
3294 var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar")
3295 var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar")
3296 place(vert); place(horiz)
3297
3298 on(vert, "scroll", function () {
3299 if (vert.clientHeight) { scroll(vert.scrollTop, "vertical") }
3300 })
3301 on(horiz, "scroll", function () {
3302 if (horiz.clientWidth) { scroll(horiz.scrollLeft, "horizontal") }
3303 })
3304
3305 this.checkedZeroWidth = false
3306 // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
3307 if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = "18px" }
3308 }
3309
3310 NativeScrollbars.prototype = copyObj({
3311 update: function(measure) {
3312 var needsH = measure.scrollWidth > measure.clientWidth + 1
3313 var needsV = measure.scrollHeight > measure.clientHeight + 1
3314 var sWidth = measure.nativeBarWidth
3315
3316 if (needsV) {
3317 this.vert.style.display = "block"
3318 this.vert.style.bottom = needsH ? sWidth + "px" : "0"
3319 var totalHeight = measure.viewHeight - (needsH ? sWidth : 0)
3320 // A bug in IE8 can cause this value to be negative, so guard it.
3321 this.vert.firstChild.style.height =
3322 Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px"
3323 } else {
3324 this.vert.style.display = ""
3325 this.vert.firstChild.style.height = "0"
3326 }
3327
3328 if (needsH) {
3329 this.horiz.style.display = "block"
3330 this.horiz.style.right = needsV ? sWidth + "px" : "0"
3331 this.horiz.style.left = measure.barLeft + "px"
3332 var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0)
3333 this.horiz.firstChild.style.width =
3334 (measure.scrollWidth - measure.clientWidth + totalWidth) + "px"
3335 } else {
3336 this.horiz.style.display = ""
3337 this.horiz.firstChild.style.width = "0"
3338 }
3339
3340 if (!this.checkedZeroWidth && measure.clientHeight > 0) {
3341 if (sWidth == 0) { this.zeroWidthHack() }
3342 this.checkedZeroWidth = true
3343 }
3344
3345 return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0}
3346 },
3347 setScrollLeft: function(pos) {
3348 if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos }
3349 if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz) }
3350 },
3351 setScrollTop: function(pos) {
3352 if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos }
3353 if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert) }
3354 },
3355 zeroWidthHack: function() {
3356 var w = mac && !mac_geMountainLion ? "12px" : "18px"
3357 this.horiz.style.height = this.vert.style.width = w
3358 this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none"
3359 this.disableHoriz = new Delayed
3360 this.disableVert = new Delayed
3361 },
3362 enableZeroWidthBar: function(bar, delay) {
3363 bar.style.pointerEvents = "auto"
3364 function maybeDisable() {
3365 // To find out whether the scrollbar is still visible, we
3366 // check whether the element under the pixel in the bottom
3367 // left corner of the scrollbar box is the scrollbar box
3368 // itself (when the bar is still visible) or its filler child
3369 // (when the bar is hidden). If it is still visible, we keep
3370 // it enabled, if it's hidden, we disable pointer events.
3371 var box = bar.getBoundingClientRect()
3372 var elt$$1 = document.elementFromPoint(box.left + 1, box.bottom - 1)
3373 if (elt$$1 != bar) { bar.style.pointerEvents = "none" }
3374 else { delay.set(1000, maybeDisable) }
3375 }
3376 delay.set(1000, maybeDisable)
3377 },
3378 clear: function() {
3379 var parent = this.horiz.parentNode
3380 parent.removeChild(this.horiz)
3381 parent.removeChild(this.vert)
3382 }
3383 }, NativeScrollbars.prototype)
3384
3385 function NullScrollbars() {}
3386
3387 NullScrollbars.prototype = copyObj({
3388 update: function() { return {bottom: 0, right: 0} },
3389 setScrollLeft: function() {},
3390 setScrollTop: function() {},
3391 clear: function() {}
3392 }, NullScrollbars.prototype)
3393
3394 function updateScrollbars(cm, measure) {
3395 if (!measure) { measure = measureForScrollbars(cm) }
3396 var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight
3397 updateScrollbarsInner(cm, measure)
3398 for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) {
3399 if (startWidth != cm.display.barWidth && cm.options.lineWrapping)
3400 { updateHeightsInViewport(cm) }
3401 updateScrollbarsInner(cm, measureForScrollbars(cm))
3402 startWidth = cm.display.barWidth; startHeight = cm.display.barHeight
3403 }
3404 }
3405
3406 // Re-synchronize the fake scrollbars with the actual size of the
3407 // content.
3408 function updateScrollbarsInner(cm, measure) {
3409 var d = cm.display
3410 var sizes = d.scrollbars.update(measure)
3411
3412 d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px"
3413 d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px"
3414 d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent"
3415
3416 if (sizes.right && sizes.bottom) {
3417 d.scrollbarFiller.style.display = "block"
3418 d.scrollbarFiller.style.height = sizes.bottom + "px"
3419 d.scrollbarFiller.style.width = sizes.right + "px"
3420 } else { d.scrollbarFiller.style.display = "" }
3421 if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
3422 d.gutterFiller.style.display = "block"
3423 d.gutterFiller.style.height = sizes.bottom + "px"
3424 d.gutterFiller.style.width = measure.gutterWidth + "px"
3425 } else { d.gutterFiller.style.display = "" }
3426 }
3427
3428 var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars}
3429
3430 function initScrollbars(cm) {
3431 if (cm.display.scrollbars) {
3432 cm.display.scrollbars.clear()
3433 if (cm.display.scrollbars.addClass)
3434 { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass) }
3435 }
3436
3437 cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) {
3438 cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller)
3439 // Prevent clicks in the scrollbars from killing focus
3440 on(node, "mousedown", function () {
3441 if (cm.state.focused) { setTimeout(function () { return cm.display.input.focus(); }, 0) }
3442 })
3443 node.setAttribute("cm-not-content", "true")
3444 }, function (pos, axis) {
3445 if (axis == "horizontal") { setScrollLeft(cm, pos) }
3446 else { setScrollTop(cm, pos) }
3447 }, cm)
3448 if (cm.display.scrollbars.addClass)
3449 { addClass(cm.display.wrapper, cm.display.scrollbars.addClass) }
3450 }
3451
3452 // SCROLLING THINGS INTO VIEW
3453
3454 // If an editor sits on the top or bottom of the window, partially
3455 // scrolled out of view, this ensures that the cursor is visible.
3456 function maybeScrollWindow(cm, coords) {
3457 if (signalDOMEvent(cm, "scrollCursorIntoView")) { return }
3458
3459 var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null
3460 if (coords.top + box.top < 0) { doScroll = true }
3461 else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) { doScroll = false }
3462 if (doScroll != null && !phantom) {
3463 var scrollNode = elt("div", "\u200b", null, ("position: absolute;\n top: " + (coords.top - display.viewOffset - paddingTop(cm.display)) + "px;\n height: " + (coords.bottom - coords.top + scrollGap(cm) + display.barHeight) + "px;\n left: " + (coords.left) + "px; width: 2px;"))
3464 cm.display.lineSpace.appendChild(scrollNode)
3465 scrollNode.scrollIntoView(doScroll)
3466 cm.display.lineSpace.removeChild(scrollNode)
3467 }
3468 }
3469
3470 // Scroll a given position into view (immediately), verifying that
3471 // it actually became visible (as line heights are accurately
3472 // measured, the position of something may 'drift' during drawing).
3473 function scrollPosIntoView(cm, pos, end, margin) {
3474 if (margin == null) { margin = 0 }
3475 var coords
3476 for (var limit = 0; limit < 5; limit++) {
3477 var changed = false
3478 coords = cursorCoords(cm, pos)
3479 var endCoords = !end || end == pos ? coords : cursorCoords(cm, end)
3480 var scrollPos = calculateScrollPos(cm, Math.min(coords.left, endCoords.left),
3481 Math.min(coords.top, endCoords.top) - margin,
3482 Math.max(coords.left, endCoords.left),
3483 Math.max(coords.bottom, endCoords.bottom) + margin)
3484 var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft
3485 if (scrollPos.scrollTop != null) {
3486 setScrollTop(cm, scrollPos.scrollTop)
3487 if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true }
3488 }
3489 if (scrollPos.scrollLeft != null) {
3490 setScrollLeft(cm, scrollPos.scrollLeft)
3491 if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true }
3492 }
3493 if (!changed) { break }
3494 }
3495 return coords
3496 }
3497
3498 // Scroll a given set of coordinates into view (immediately).
3499 function scrollIntoView(cm, x1, y1, x2, y2) {
3500 var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2)
3501 if (scrollPos.scrollTop != null) { setScrollTop(cm, scrollPos.scrollTop) }
3502 if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft) }
3503 }
3504
3505 // Calculate a new scroll position needed to scroll the given
3506 // rectangle into view. Returns an object with scrollTop and
3507 // scrollLeft properties. When these are undefined, the
3508 // vertical/horizontal position does not need to be adjusted.
3509 function calculateScrollPos(cm, x1, y1, x2, y2) {
3510 var display = cm.display, snapMargin = textHeight(cm.display)
3511 if (y1 < 0) { y1 = 0 }
3512 var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop
3513 var screen = displayHeight(cm), result = {}
3514 if (y2 - y1 > screen) { y2 = y1 + screen }
3515 var docBottom = cm.doc.height + paddingVert(display)
3516 var atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin
3517 if (y1 < screentop) {
3518 result.scrollTop = atTop ? 0 : y1
3519 } else if (y2 > screentop + screen) {
3520 var newTop = Math.min(y1, (atBottom ? docBottom : y2) - screen)
3521 if (newTop != screentop) { result.scrollTop = newTop }
3522 }
3523
3524 var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft
3525 var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0)
3526 var tooWide = x2 - x1 > screenw
3527 if (tooWide) { x2 = x1 + screenw }
3528 if (x1 < 10)
3529 { result.scrollLeft = 0 }
3530 else if (x1 < screenleft)
3531 { result.scrollLeft = Math.max(0, x1 - (tooWide ? 0 : 10)) }
3532 else if (x2 > screenw + screenleft - 3)
3533 { result.scrollLeft = x2 + (tooWide ? 0 : 10) - screenw }
3534 return result
3535 }
3536
3537 // Store a relative adjustment to the scroll position in the current
3538 // operation (to be applied when the operation finishes).
3539 function addToScrollPos(cm, left, top) {
3540 if (left != null || top != null) { resolveScrollToPos(cm) }
3541 if (left != null)
3542 { cm.curOp.scrollLeft = (cm.curOp.scrollLeft == null ? cm.doc.scrollLeft : cm.curOp.scrollLeft) + left }
3543 if (top != null)
3544 { cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top }
3545 }
3546
3547 // Make sure that at the end of the operation the current cursor is
3548 // shown.
3549 function ensureCursorVisible(cm) {
3550 resolveScrollToPos(cm)
3551 var cur = cm.getCursor(), from = cur, to = cur
3552 if (!cm.options.lineWrapping) {
3553 from = cur.ch ? Pos(cur.line, cur.ch - 1) : cur
3554 to = Pos(cur.line, cur.ch + 1)
3555 }
3556 cm.curOp.scrollToPos = {from: from, to: to, margin: cm.options.cursorScrollMargin, isCursor: true}
3557 }
3558
3559 // When an operation has its scrollToPos property set, and another
3560 // scroll action is applied before the end of the operation, this
3561 // 'simulates' scrolling that position into view in a cheap way, so
3562 // that the effect of intermediate scroll commands is not ignored.
3563 function resolveScrollToPos(cm) {
3564 var range$$1 = cm.curOp.scrollToPos
3565 if (range$$1) {
3566 cm.curOp.scrollToPos = null
3567 var from = estimateCoords(cm, range$$1.from), to = estimateCoords(cm, range$$1.to)
3568 var sPos = calculateScrollPos(cm, Math.min(from.left, to.left),
3569 Math.min(from.top, to.top) - range$$1.margin,
3570 Math.max(from.right, to.right),
3571 Math.max(from.bottom, to.bottom) + range$$1.margin)
3572 cm.scrollTo(sPos.scrollLeft, sPos.scrollTop)
3573 }
3574 }
3575
3576 // Operations are used to wrap a series of changes to the editor
3577 // state in such a way that each change won't have to update the
3578 // cursor and display (which would be awkward, slow, and
3579 // error-prone). Instead, display updates are batched and then all
3580 // combined and executed at once.
3581
3582 var nextOpId = 0
3583 // Start a new operation.
3584 function startOperation(cm) {
3585 cm.curOp = {
3586 cm: cm,
3587 viewChanged: false, // Flag that indicates that lines might need to be redrawn
3588 startHeight: cm.doc.height, // Used to detect need to update scrollbar
3589 forceUpdate: false, // Used to force a redraw
3590 updateInput: null, // Whether to reset the input textarea
3591 typing: false, // Whether this reset should be careful to leave existing text (for compositing)
3592 changeObjs: null, // Accumulated changes, for firing change events
3593 cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on
3594 cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
3595 selectionChanged: false, // Whether the selection needs to be redrawn
3596 updateMaxLine: false, // Set when the widest line needs to be determined anew
3597 scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
3598 scrollToPos: null, // Used to scroll to a specific position
3599 focus: false,
3600 id: ++nextOpId // Unique ID
3601 }
3602 pushOperation(cm.curOp)
3603 }
3604
3605 // Finish an operation, updating the display and signalling delayed events
3606 function endOperation(cm) {
3607 var op = cm.curOp
3608 finishOperation(op, function (group) {
3609 for (var i = 0; i < group.ops.length; i++)
3610 { group.ops[i].cm.curOp = null }
3611 endOperations(group)
3612 })
3613 }
3614
3615 // The DOM updates done when an operation finishes are batched so
3616 // that the minimum number of relayouts are required.
3617 function endOperations(group) {
3618 var ops = group.ops
3619 for (var i = 0; i < ops.length; i++) // Read DOM
3620 { endOperation_R1(ops[i]) }
3621 for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe)
3622 { endOperation_W1(ops[i$1]) }
3623 for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM
3624 { endOperation_R2(ops[i$2]) }
3625 for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe)
3626 { endOperation_W2(ops[i$3]) }
3627 for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM
3628 { endOperation_finish(ops[i$4]) }
3629 }
3630
3631 function endOperation_R1(op) {
3632 var cm = op.cm, display = cm.display
3633 maybeClipScrollbars(cm)
3634 if (op.updateMaxLine) { findMaxLine(cm) }
3635
3636 op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||
3637 op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
3638 op.scrollToPos.to.line >= display.viewTo) ||
3639 display.maxLineChanged && cm.options.lineWrapping
3640 op.update = op.mustUpdate &&
3641 new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate)
3642 }
3643
3644 function endOperation_W1(op) {
3645 op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update)
3646 }
3647
3648 function endOperation_R2(op) {
3649 var cm = op.cm, display = cm.display
3650 if (op.updatedDisplay) { updateHeightsInViewport(cm) }
3651
3652 op.barMeasure = measureForScrollbars(cm)
3653
3654 // If the max line changed since it was last measured, measure it,
3655 // and ensure the document's width matches it.
3656 // updateDisplay_W2 will use these properties to do the actual resizing
3657 if (display.maxLineChanged && !cm.options.lineWrapping) {
3658 op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3
3659 cm.display.sizerWidth = op.adjustWidthTo
3660 op.barMeasure.scrollWidth =
3661 Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth)
3662 op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm))
3663 }
3664
3665 if (op.updatedDisplay || op.selectionChanged)
3666 { op.preparedSelection = display.input.prepareSelection(op.focus) }
3667 }
3668
3669 function endOperation_W2(op) {
3670 var cm = op.cm
3671
3672 if (op.adjustWidthTo != null) {
3673 cm.display.sizer.style.minWidth = op.adjustWidthTo + "px"
3674 if (op.maxScrollLeft < cm.doc.scrollLeft)
3675 { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true) }
3676 cm.display.maxLineChanged = false
3677 }
3678
3679 var takeFocus = op.focus && op.focus == activeElt() && (!document.hasFocus || document.hasFocus())
3680 if (op.preparedSelection)
3681 { cm.display.input.showSelection(op.preparedSelection, takeFocus) }
3682 if (op.updatedDisplay || op.startHeight != cm.doc.height)
3683 { updateScrollbars(cm, op.barMeasure) }
3684 if (op.updatedDisplay)
3685 { setDocumentHeight(cm, op.barMeasure) }
3686
3687 if (op.selectionChanged) { restartBlink(cm) }
3688
3689 if (cm.state.focused && op.updateInput)
3690 { cm.display.input.reset(op.typing) }
3691 if (takeFocus) { ensureFocus(op.cm) }
3692 }
3693
3694 function endOperation_finish(op) {
3695 var cm = op.cm, display = cm.display, doc = cm.doc
3696
3697 if (op.updatedDisplay) { postUpdateDisplay(cm, op.update) }
3698
3699 // Abort mouse wheel delta measurement, when scrolling explicitly
3700 if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))
3701 { display.wheelStartX = display.wheelStartY = null }
3702
3703 // Propagate the scroll position to the actual DOM scroller
3704 if (op.scrollTop != null && (display.scroller.scrollTop != op.scrollTop || op.forceScroll)) {
3705 doc.scrollTop = Math.max(0, Math.min(display.scroller.scrollHeight - display.scroller.clientHeight, op.scrollTop))
3706 display.scrollbars.setScrollTop(doc.scrollTop)
3707 display.scroller.scrollTop = doc.scrollTop
3708 }
3709 if (op.scrollLeft != null && (display.scroller.scrollLeft != op.scrollLeft || op.forceScroll)) {
3710 doc.scrollLeft = Math.max(0, Math.min(display.scroller.scrollWidth - display.scroller.clientWidth, op.scrollLeft))
3711 display.scrollbars.setScrollLeft(doc.scrollLeft)
3712 display.scroller.scrollLeft = doc.scrollLeft
3713 alignHorizontally(cm)
3714 }
3715 // If we need to scroll a specific position into view, do so.
3716 if (op.scrollToPos) {
3717 var coords = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
3718 clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin)
3719 if (op.scrollToPos.isCursor && cm.state.focused) { maybeScrollWindow(cm, coords) }
3720 }
3721
3722 // Fire events for markers that are hidden/unidden by editing or
3723 // undoing
3724 var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers
3725 if (hidden) { for (var i = 0; i < hidden.length; ++i)
3726 { if (!hidden[i].lines.length) { signal(hidden[i], "hide") } } }
3727 if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1)
3728 { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], "unhide") } } }
3729
3730 if (display.wrapper.offsetHeight)
3731 { doc.scrollTop = cm.display.scroller.scrollTop }
3732
3733 // Fire change events, and delayed event handlers
3734 if (op.changeObjs)
3735 { signal(cm, "changes", cm, op.changeObjs) }
3736 if (op.update)
3737 { op.update.finish() }
3738 }
3739
3740 // Run the given function in an operation
3741 function runInOp(cm, f) {
3742 if (cm.curOp) { return f() }
3743 startOperation(cm)
3744 try { return f() }
3745 finally { endOperation(cm) }
3746 }
3747 // Wraps a function in an operation. Returns the wrapped function.
3748 function operation(cm, f) {
3749 return function() {
3750 if (cm.curOp) { return f.apply(cm, arguments) }
3751 startOperation(cm)
3752 try { return f.apply(cm, arguments) }
3753 finally { endOperation(cm) }
3754 }
3755 }
3756 // Used to add methods to editor and doc instances, wrapping them in
3757 // operations.
3758 function methodOp(f) {
3759 return function() {
3760 if (this.curOp) { return f.apply(this, arguments) }
3761 startOperation(this)
3762 try { return f.apply(this, arguments) }
3763 finally { endOperation(this) }
3764 }
3765 }
3766 function docMethodOp(f) {
3767 return function() {
3768 var cm = this.cm
3769 if (!cm || cm.curOp) { return f.apply(this, arguments) }
3770 startOperation(cm)
3771 try { return f.apply(this, arguments) }
3772 finally { endOperation(cm) }
3773 }
3774 }
3775
3776 // Updates the display.view data structure for a given change to the
3777 // document. From and to are in pre-change coordinates. Lendiff is
3778 // the amount of lines added or subtracted by the change. This is
3779 // used for changes that span multiple lines, or change the way
3780 // lines are divided into visual lines. regLineChange (below)
3781 // registers single-line changes.
3782 function regChange(cm, from, to, lendiff) {
3783 if (from == null) { from = cm.doc.first }
3784 if (to == null) { to = cm.doc.first + cm.doc.size }
3785 if (!lendiff) { lendiff = 0 }
3786
3787 var display = cm.display
3788 if (lendiff && to < display.viewTo &&
3789 (display.updateLineNumbers == null || display.updateLineNumbers > from))
3790 { display.updateLineNumbers = from }
3791
3792 cm.curOp.viewChanged = true
3793
3794 if (from >= display.viewTo) { // Change after
3795 if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)
3796 { resetView(cm) }
3797 } else if (to <= display.viewFrom) { // Change before
3798 if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {
3799 resetView(cm)
3800 } else {
3801 display.viewFrom += lendiff
3802 display.viewTo += lendiff
3803 }
3804 } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap
3805 resetView(cm)
3806 } else if (from <= display.viewFrom) { // Top overlap
3807 var cut = viewCuttingPoint(cm, to, to + lendiff, 1)
3808 if (cut) {
3809 display.view = display.view.slice(cut.index)
3810 display.viewFrom = cut.lineN
3811 display.viewTo += lendiff
3812 } else {
3813 resetView(cm)
3814 }
3815 } else if (to >= display.viewTo) { // Bottom overlap
3816 var cut$1 = viewCuttingPoint(cm, from, from, -1)
3817 if (cut$1) {
3818 display.view = display.view.slice(0, cut$1.index)
3819 display.viewTo = cut$1.lineN
3820 } else {
3821 resetView(cm)
3822 }
3823 } else { // Gap in the middle
3824 var cutTop = viewCuttingPoint(cm, from, from, -1)
3825 var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1)
3826 if (cutTop && cutBot) {
3827 display.view = display.view.slice(0, cutTop.index)
3828 .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))
3829 .concat(display.view.slice(cutBot.index))
3830 display.viewTo += lendiff
3831 } else {
3832 resetView(cm)
3833 }
3834 }
3835
3836 var ext = display.externalMeasured
3837 if (ext) {
3838 if (to < ext.lineN)
3839 { ext.lineN += lendiff }
3840 else if (from < ext.lineN + ext.size)
3841 { display.externalMeasured = null }
3842 }
3843 }
3844
3845 // Register a change to a single line. Type must be one of "text",
3846 // "gutter", "class", "widget"
3847 function regLineChange(cm, line, type) {
3848 cm.curOp.viewChanged = true
3849 var display = cm.display, ext = cm.display.externalMeasured
3850 if (ext && line >= ext.lineN && line < ext.lineN + ext.size)
3851 { display.externalMeasured = null }
3852
3853 if (line < display.viewFrom || line >= display.viewTo) { return }
3854 var lineView = display.view[findViewIndex(cm, line)]
3855 if (lineView.node == null) { return }
3856 var arr = lineView.changes || (lineView.changes = [])
3857 if (indexOf(arr, type) == -1) { arr.push(type) }
3858 }
3859
3860 // Clear the view.
3861 function resetView(cm) {
3862 cm.display.viewFrom = cm.display.viewTo = cm.doc.first
3863 cm.display.view = []
3864 cm.display.viewOffset = 0
3865 }
3866
3867 function viewCuttingPoint(cm, oldN, newN, dir) {
3868 var index = findViewIndex(cm, oldN), diff, view = cm.display.view
3869 if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)
3870 { return {index: index, lineN: newN} }
3871 var n = cm.display.viewFrom
3872 for (var i = 0; i < index; i++)
3873 { n += view[i].size }
3874 if (n != oldN) {
3875 if (dir > 0) {
3876 if (index == view.length - 1) { return null }
3877 diff = (n + view[index].size) - oldN
3878 index++
3879 } else {
3880 diff = n - oldN
3881 }
3882 oldN += diff; newN += diff
3883 }
3884 while (visualLineNo(cm.doc, newN) != newN) {
3885 if (index == (dir < 0 ? 0 : view.length - 1)) { return null }
3886 newN += dir * view[index - (dir < 0 ? 1 : 0)].size
3887 index += dir
3888 }
3889 return {index: index, lineN: newN}
3890 }
3891
3892 // Force the view to cover a given range, adding empty view element
3893 // or clipping off existing ones as needed.
3894 function adjustView(cm, from, to) {
3895 var display = cm.display, view = display.view
3896 if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {
3897 display.view = buildViewArray(cm, from, to)
3898 display.viewFrom = from
3899 } else {
3900 if (display.viewFrom > from)
3901 { display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view) }
3902 else if (display.viewFrom < from)
3903 { display.view = display.view.slice(findViewIndex(cm, from)) }
3904 display.viewFrom = from
3905 if (display.viewTo < to)
3906 { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)) }
3907 else if (display.viewTo > to)
3908 { display.view = display.view.slice(0, findViewIndex(cm, to)) }
3909 }
3910 display.viewTo = to
3911 }
3912
3913 // Count the number of lines in the view whose DOM representation is
3914 // out of date (or nonexistent).
3915 function countDirtyView(cm) {
3916 var view = cm.display.view, dirty = 0
3917 for (var i = 0; i < view.length; i++) {
3918 var lineView = view[i]
3919 if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty }
3920 }
3921 return dirty
3922 }
3923
3924 // HIGHLIGHT WORKER
3925
3926 function startWorker(cm, time) {
3927 if (cm.doc.mode.startState && cm.doc.frontier < cm.display.viewTo)
3928 { cm.state.highlight.set(time, bind(highlightWorker, cm)) }
3929 }
3930
3931 function highlightWorker(cm) {
3932 var doc = cm.doc
3933 if (doc.frontier < doc.first) { doc.frontier = doc.first }
3934 if (doc.frontier >= cm.display.viewTo) { return }
3935 var end = +new Date + cm.options.workTime
3936 var state = copyState(doc.mode, getStateBefore(cm, doc.frontier))
3937 var changedLines = []
3938
3939 doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function (line) {
3940 if (doc.frontier >= cm.display.viewFrom) { // Visible
3941 var oldStyles = line.styles, tooLong = line.text.length > cm.options.maxHighlightLength
3942 var highlighted = highlightLine(cm, line, tooLong ? copyState(doc.mode, state) : state, true)
3943 line.styles = highlighted.styles
3944 var oldCls = line.styleClasses, newCls = highlighted.classes
3945 if (newCls) { line.styleClasses = newCls }
3946 else if (oldCls) { line.styleClasses = null }
3947 var ischange = !oldStyles || oldStyles.length != line.styles.length ||
3948 oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass)
3949 for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldStyles[i] != line.styles[i] }
3950 if (ischange) { changedLines.push(doc.frontier) }
3951 line.stateAfter = tooLong ? state : copyState(doc.mode, state)
3952 } else {
3953 if (line.text.length <= cm.options.maxHighlightLength)
3954 { processLine(cm, line.text, state) }
3955 line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null
3956 }
3957 ++doc.frontier
3958 if (+new Date > end) {
3959 startWorker(cm, cm.options.workDelay)
3960 return true
3961 }
3962 })
3963 if (changedLines.length) { runInOp(cm, function () {
3964 for (var i = 0; i < changedLines.length; i++)
3965 { regLineChange(cm, changedLines[i], "text") }
3966 }) }
3967 }
3968
3969 // DISPLAY DRAWING
3970
3971 function DisplayUpdate(cm, viewport, force) {
3972 var display = cm.display
3973
3974 this.viewport = viewport
3975 // Store some values that we'll need later (but don't want to force a relayout for)
3976 this.visible = visibleLines(display, cm.doc, viewport)
3977 this.editorIsHidden = !display.wrapper.offsetWidth
3978 this.wrapperHeight = display.wrapper.clientHeight
3979 this.wrapperWidth = display.wrapper.clientWidth
3980 this.oldDisplayWidth = displayWidth(cm)
3981 this.force = force
3982 this.dims = getDimensions(cm)
3983 this.events = []
3984 }
3985
3986 DisplayUpdate.prototype.signal = function(emitter, type) {
3987 if (hasHandler(emitter, type))
3988 { this.events.push(arguments) }
3989 }
3990 DisplayUpdate.prototype.finish = function() {
3991 var this$1 = this;
3992
3993 for (var i = 0; i < this.events.length; i++)
3994 { signal.apply(null, this$1.events[i]) }
3995 }
3996
3997 function maybeClipScrollbars(cm) {
3998 var display = cm.display
3999 if (!display.scrollbarsClipped && display.scroller.offsetWidth) {
4000 display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth
4001 display.heightForcer.style.height = scrollGap(cm) + "px"
4002 display.sizer.style.marginBottom = -display.nativeBarWidth + "px"
4003 display.sizer.style.borderRightWidth = scrollGap(cm) + "px"
4004 display.scrollbarsClipped = true
4005 }
4006 }
4007
4008 // Does the actual updating of the line display. Bails out
4009 // (returning false) when there is nothing to be done and forced is
4010 // false.
4011 function updateDisplayIfNeeded(cm, update) {
4012 var display = cm.display, doc = cm.doc
4013
4014 if (update.editorIsHidden) {
4015 resetView(cm)
4016 return false
4017 }
4018
4019 // Bail out if the visible area is already rendered and nothing changed.
4020 if (!update.force &&
4021 update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&
4022 (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&
4023 display.renderedView == display.view && countDirtyView(cm) == 0)
4024 { return false }
4025
4026 if (maybeUpdateLineNumberWidth(cm)) {
4027 resetView(cm)
4028 update.dims = getDimensions(cm)
4029 }
4030
4031 // Compute a suitable new viewport (from & to)
4032 var end = doc.first + doc.size
4033 var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first)
4034 var to = Math.min(end, update.visible.to + cm.options.viewportMargin)
4035 if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max(doc.first, display.viewFrom) }
4036 if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, display.viewTo) }
4037 if (sawCollapsedSpans) {
4038 from = visualLineNo(cm.doc, from)
4039 to = visualLineEndNo(cm.doc, to)
4040 }
4041
4042 var different = from != display.viewFrom || to != display.viewTo ||
4043 display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth
4044 adjustView(cm, from, to)
4045
4046 display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom))
4047 // Position the mover div to align with the current scroll position
4048 cm.display.mover.style.top = display.viewOffset + "px"
4049
4050 var toUpdate = countDirtyView(cm)
4051 if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view &&
4052 (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))
4053 { return false }
4054
4055 // For big changes, we hide the enclosing element during the
4056 // update, since that speeds up the operations on most browsers.
4057 var focused = activeElt()
4058 if (toUpdate > 4) { display.lineDiv.style.display = "none" }
4059 patchDisplay(cm, display.updateLineNumbers, update.dims)
4060 if (toUpdate > 4) { display.lineDiv.style.display = "" }
4061 display.renderedView = display.view
4062 // There might have been a widget with a focused element that got
4063 // hidden or updated, if so re-focus it.
4064 if (focused && activeElt() != focused && focused.offsetHeight) { focused.focus() }
4065
4066 // Prevent selection and cursors from interfering with the scroll
4067 // width and height.
4068 removeChildren(display.cursorDiv)
4069 removeChildren(display.selectionDiv)
4070 display.gutters.style.height = display.sizer.style.minHeight = 0
4071
4072 if (different) {
4073 display.lastWrapHeight = update.wrapperHeight
4074 display.lastWrapWidth = update.wrapperWidth
4075 startWorker(cm, 400)
4076 }
4077
4078 display.updateLineNumbers = null
4079
4080 return true
4081 }
4082
4083 function postUpdateDisplay(cm, update) {
4084 var viewport = update.viewport
4085
4086 for (var first = true;; first = false) {
4087 if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) {
4088 // Clip forced viewport to actual scrollable area.
4089 if (viewport && viewport.top != null)
4090 { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)} }
4091 // Updated line heights might result in the drawn area not
4092 // actually covering the viewport. Keep looping until it does.
4093 update.visible = visibleLines(cm.display, cm.doc, viewport)
4094 if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)
4095 { break }
4096 }
4097 if (!updateDisplayIfNeeded(cm, update)) { break }
4098 updateHeightsInViewport(cm)
4099 var barMeasure = measureForScrollbars(cm)
4100 updateSelection(cm)
4101 updateScrollbars(cm, barMeasure)
4102 setDocumentHeight(cm, barMeasure)
4103 }
4104
4105 update.signal(cm, "update", cm)
4106 if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {
4107 update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo)
4108 cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo
4109 }
4110 }
4111
4112 function updateDisplaySimple(cm, viewport) {
4113 var update = new DisplayUpdate(cm, viewport)
4114 if (updateDisplayIfNeeded(cm, update)) {
4115 updateHeightsInViewport(cm)
4116 postUpdateDisplay(cm, update)
4117 var barMeasure = measureForScrollbars(cm)
4118 updateSelection(cm)
4119 updateScrollbars(cm, barMeasure)
4120 setDocumentHeight(cm, barMeasure)
4121 update.finish()
4122 }
4123 }
4124
4125 // Sync the actual display DOM structure with display.view, removing
4126 // nodes for lines that are no longer in view, and creating the ones
4127 // that are not there yet, and updating the ones that are out of
4128 // date.
4129 function patchDisplay(cm, updateNumbersFrom, dims) {
4130 var display = cm.display, lineNumbers = cm.options.lineNumbers
4131 var container = display.lineDiv, cur = container.firstChild
4132
4133 function rm(node) {
4134 var next = node.nextSibling
4135 // Works around a throw-scroll bug in OS X Webkit
4136 if (webkit && mac && cm.display.currentWheelTarget == node)
4137 { node.style.display = "none" }
4138 else
4139 { node.parentNode.removeChild(node) }
4140 return next
4141 }
4142
4143 var view = display.view, lineN = display.viewFrom
4144 // Loop over the elements in the view, syncing cur (the DOM nodes
4145 // in display.lineDiv) with the view as we go.
4146 for (var i = 0; i < view.length; i++) {
4147 var lineView = view[i]
4148 if (lineView.hidden) {
4149 } else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet
4150 var node = buildLineElement(cm, lineView, lineN, dims)
4151 container.insertBefore(node, cur)
4152 } else { // Already drawn
4153 while (cur != lineView.node) { cur = rm(cur) }
4154 var updateNumber = lineNumbers && updateNumbersFrom != null &&
4155 updateNumbersFrom <= lineN && lineView.lineNumber
4156 if (lineView.changes) {
4157 if (indexOf(lineView.changes, "gutter") > -1) { updateNumber = false }
4158 updateLineForChanges(cm, lineView, lineN, dims)
4159 }
4160 if (updateNumber) {
4161 removeChildren(lineView.lineNumber)
4162 lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN)))
4163 }
4164 cur = lineView.node.nextSibling
4165 }
4166 lineN += lineView.size
4167 }
4168 while (cur) { cur = rm(cur) }
4169 }
4170
4171 function updateGutterSpace(cm) {
4172 var width = cm.display.gutters.offsetWidth
4173 cm.display.sizer.style.marginLeft = width + "px"
4174 }
4175
4176 function setDocumentHeight(cm, measure) {
4177 cm.display.sizer.style.minHeight = measure.docHeight + "px"
4178 cm.display.heightForcer.style.top = measure.docHeight + "px"
4179 cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px"
4180 }
4181
4182 // Rebuild the gutter elements, ensure the margin to the left of the
4183 // code matches their width.
4184 function updateGutters(cm) {
4185 var gutters = cm.display.gutters, specs = cm.options.gutters
4186 removeChildren(gutters)
4187 var i = 0
4188 for (; i < specs.length; ++i) {
4189 var gutterClass = specs[i]
4190 var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass))
4191 if (gutterClass == "CodeMirror-linenumbers") {
4192 cm.display.lineGutter = gElt
4193 gElt.style.width = (cm.display.lineNumWidth || 1) + "px"
4194 }
4195 }
4196 gutters.style.display = i ? "" : "none"
4197 updateGutterSpace(cm)
4198 }
4199
4200 // Make sure the gutters options contains the element
4201 // "CodeMirror-linenumbers" when the lineNumbers option is true.
4202 function setGuttersForLineNumbers(options) {
4203 var found = indexOf(options.gutters, "CodeMirror-linenumbers")
4204 if (found == -1 && options.lineNumbers) {
4205 options.gutters = options.gutters.concat(["CodeMirror-linenumbers"])
4206 } else if (found > -1 && !options.lineNumbers) {
4207 options.gutters = options.gutters.slice(0)
4208 options.gutters.splice(found, 1)
4209 }
4210 }
4211
4212 // Selection objects are immutable. A new one is created every time
4213 // the selection changes. A selection is one or more non-overlapping
4214 // (and non-touching) ranges, sorted, and an integer that indicates
4215 // which one is the primary selection (the one that's scrolled into
4216 // view, that getCursor returns, etc).
4217 function Selection(ranges, primIndex) {
4218 this.ranges = ranges
4219 this.primIndex = primIndex
4220 }
4221
4222 Selection.prototype = {
4223 primary: function() { return this.ranges[this.primIndex] },
4224 equals: function(other) {
4225 var this$1 = this;
4226
4227 if (other == this) { return true }
4228 if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false }
4229 for (var i = 0; i < this.ranges.length; i++) {
4230 var here = this$1.ranges[i], there = other.ranges[i]
4231 if (cmp(here.anchor, there.anchor) != 0 || cmp(here.head, there.head) != 0) { return false }
4232 }
4233 return true
4234 },
4235 deepCopy: function() {
4236 var this$1 = this;
4237
4238 var out = []
4239 for (var i = 0; i < this.ranges.length; i++)
4240 { out[i] = new Range(copyPos(this$1.ranges[i].anchor), copyPos(this$1.ranges[i].head)) }
4241 return new Selection(out, this.primIndex)
4242 },
4243 somethingSelected: function() {
4244 var this$1 = this;
4245
4246 for (var i = 0; i < this.ranges.length; i++)
4247 { if (!this$1.ranges[i].empty()) { return true } }
4248 return false
4249 },
4250 contains: function(pos, end) {
4251 var this$1 = this;
4252
4253 if (!end) { end = pos }
4254 for (var i = 0; i < this.ranges.length; i++) {
4255 var range = this$1.ranges[i]
4256 if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)
4257 { return i }
4258 }
4259 return -1
4260 }
4261 }
4262
4263 function Range(anchor, head) {
4264 this.anchor = anchor; this.head = head
4265 }
4266
4267 Range.prototype = {
4268 from: function() { return minPos(this.anchor, this.head) },
4269 to: function() { return maxPos(this.anchor, this.head) },
4270 empty: function() {
4271 return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch
4272 }
4273 }
4274
4275 // Take an unsorted, potentially overlapping set of ranges, and
4276 // build a selection out of it. 'Consumes' ranges array (modifying
4277 // it).
4278 function normalizeSelection(ranges, primIndex) {
4279 var prim = ranges[primIndex]
4280 ranges.sort(function (a, b) { return cmp(a.from(), b.from()); })
4281 primIndex = indexOf(ranges, prim)
4282 for (var i = 1; i < ranges.length; i++) {
4283 var cur = ranges[i], prev = ranges[i - 1]
4284 if (cmp(prev.to(), cur.from()) >= 0) {
4285 var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to())
4286 var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head
4287 if (i <= primIndex) { --primIndex }
4288 ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to))
4289 }
4290 }
4291 return new Selection(ranges, primIndex)
4292 }
4293
4294 function simpleSelection(anchor, head) {
4295 return new Selection([new Range(anchor, head || anchor)], 0)
4296 }
4297
4298 // Compute the position of the end of a change (its 'to' property
4299 // refers to the pre-change end).
4300 function changeEnd(change) {
4301 if (!change.text) { return change.to }
4302 return Pos(change.from.line + change.text.length - 1,
4303 lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0))
4304 }
4305
4306 // Adjust a position to refer to the post-change position of the
4307 // same text, or the end of the change if the change covers it.
4308 function adjustForChange(pos, change) {
4309 if (cmp(pos, change.from) < 0) { return pos }
4310 if (cmp(pos, change.to) <= 0) { return changeEnd(change) }
4311
4312 var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch
4313 if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch }
4314 return Pos(line, ch)
4315 }
4316
4317 function computeSelAfterChange(doc, change) {
4318 var out = []
4319 for (var i = 0; i < doc.sel.ranges.length; i++) {
4320 var range = doc.sel.ranges[i]
4321 out.push(new Range(adjustForChange(range.anchor, change),
4322 adjustForChange(range.head, change)))
4323 }
4324 return normalizeSelection(out, doc.sel.primIndex)
4325 }
4326
4327 function offsetPos(pos, old, nw) {
4328 if (pos.line == old.line)
4329 { return Pos(nw.line, pos.ch - old.ch + nw.ch) }
4330 else
4331 { return Pos(nw.line + (pos.line - old.line), pos.ch) }
4332 }
4333
4334 // Used by replaceSelections to allow moving the selection to the
4335 // start or around the replaced test. Hint may be "start" or "around".
4336 function computeReplacedSel(doc, changes, hint) {
4337 var out = []
4338 var oldPrev = Pos(doc.first, 0), newPrev = oldPrev
4339 for (var i = 0; i < changes.length; i++) {
4340 var change = changes[i]
4341 var from = offsetPos(change.from, oldPrev, newPrev)
4342 var to = offsetPos(changeEnd(change), oldPrev, newPrev)
4343 oldPrev = change.to
4344 newPrev = to
4345 if (hint == "around") {
4346 var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0
4347 out[i] = new Range(inv ? to : from, inv ? from : to)
4348 } else {
4349 out[i] = new Range(from, from)
4350 }
4351 }
4352 return new Selection(out, doc.sel.primIndex)
4353 }
4354
4355 // Used to get the editor into a consistent state again when options change.
4356
4357 function loadMode(cm) {
4358 cm.doc.mode = getMode(cm.options, cm.doc.modeOption)
4359 resetModeState(cm)
4360 }
4361
4362 function resetModeState(cm) {
4363 cm.doc.iter(function (line) {
4364 if (line.stateAfter) { line.stateAfter = null }
4365 if (line.styles) { line.styles = null }
4366 })
4367 cm.doc.frontier = cm.doc.first
4368 startWorker(cm, 100)
4369 cm.state.modeGen++
4370 if (cm.curOp) { regChange(cm) }
4371 }
4372
4373 // DOCUMENT DATA STRUCTURE
4374
4375 // By default, updates that start and end at the beginning of a line
4376 // are treated specially, in order to make the association of line
4377 // widgets and marker elements with the text behave more intuitive.
4378 function isWholeLineUpdate(doc, change) {
4379 return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" &&
4380 (!doc.cm || doc.cm.options.wholeLineUpdateBefore)
4381 }
4382
4383 // Perform a change on the document data structure.
4384 function updateDoc(doc, change, markedSpans, estimateHeight$$1) {
4385 function spansFor(n) {return markedSpans ? markedSpans[n] : null}
4386 function update(line, text, spans) {
4387 updateLine(line, text, spans, estimateHeight$$1)
4388 signalLater(line, "change", line, change)
4389 }
4390 function linesFor(start, end) {
4391 var result = []
4392 for (var i = start; i < end; ++i)
4393 { result.push(new Line(text[i], spansFor(i), estimateHeight$$1)) }
4394 return result
4395 }
4396
4397 var from = change.from, to = change.to, text = change.text
4398 var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line)
4399 var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line
4400
4401 // Adjust the line structure
4402 if (change.full) {
4403 doc.insert(0, linesFor(0, text.length))
4404 doc.remove(text.length, doc.size - text.length)
4405 } else if (isWholeLineUpdate(doc, change)) {
4406 // This is a whole-line replace. Treated specially to make
4407 // sure line objects move the way they are supposed to.
4408 var added = linesFor(0, text.length - 1)
4409 update(lastLine, lastLine.text, lastSpans)
4410 if (nlines) { doc.remove(from.line, nlines) }
4411 if (added.length) { doc.insert(from.line, added) }
4412 } else if (firstLine == lastLine) {
4413 if (text.length == 1) {
4414 update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans)
4415 } else {
4416 var added$1 = linesFor(1, text.length - 1)
4417 added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight$$1))
4418 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0))
4419 doc.insert(from.line + 1, added$1)
4420 }
4421 } else if (text.length == 1) {
4422 update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0))
4423 doc.remove(from.line + 1, nlines)
4424 } else {
4425 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0))
4426 update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans)
4427 var added$2 = linesFor(1, text.length - 1)
4428 if (nlines > 1) { doc.remove(from.line + 1, nlines - 1) }
4429 doc.insert(from.line + 1, added$2)
4430 }
4431
4432 signalLater(doc, "change", doc, change)
4433 }
4434
4435 // Call f for all linked documents.
4436 function linkedDocs(doc, f, sharedHistOnly) {
4437 function propagate(doc, skip, sharedHist) {
4438 if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) {
4439 var rel = doc.linked[i]
4440 if (rel.doc == skip) { continue }
4441 var shared = sharedHist && rel.sharedHist
4442 if (sharedHistOnly && !shared) { continue }
4443 f(rel.doc, shared)
4444 propagate(rel.doc, doc, shared)
4445 } }
4446 }
4447 propagate(doc, null, true)
4448 }
4449
4450 // Attach a document to an editor.
4451 function attachDoc(cm, doc) {
4452 if (doc.cm) { throw new Error("This document is already in use.") }
4453 cm.doc = doc
4454 doc.cm = cm
4455 estimateLineHeights(cm)
4456 loadMode(cm)
4457 if (!cm.options.lineWrapping) { findMaxLine(cm) }
4458 cm.options.mode = doc.modeOption
4459 regChange(cm)
4460 }
4461
4462 function History(startGen) {
4463 // Arrays of change events and selections. Doing something adds an
4464 // event to done and clears undo. Undoing moves events from done
4465 // to undone, redoing moves them in the other direction.
4466 this.done = []; this.undone = []
4467 this.undoDepth = Infinity
4468 // Used to track when changes can be merged into a single undo
4469 // event
4470 this.lastModTime = this.lastSelTime = 0
4471 this.lastOp = this.lastSelOp = null
4472 this.lastOrigin = this.lastSelOrigin = null
4473 // Used by the isClean() method
4474 this.generation = this.maxGeneration = startGen || 1
4475 }
4476
4477 // Create a history change event from an updateDoc-style change
4478 // object.
4479 function historyChangeFromChange(doc, change) {
4480 var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)}
4481 attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1)
4482 linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); }, true)
4483 return histChange
4484 }
4485
4486 // Pop all selection events off the end of a history array. Stop at
4487 // a change event.
4488 function clearSelectionEvents(array) {
4489 while (array.length) {
4490 var last = lst(array)
4491 if (last.ranges) { array.pop() }
4492 else { break }
4493 }
4494 }
4495
4496 // Find the top change event in the history. Pop off selection
4497 // events that are in the way.
4498 function lastChangeEvent(hist, force) {
4499 if (force) {
4500 clearSelectionEvents(hist.done)
4501 return lst(hist.done)
4502 } else if (hist.done.length && !lst(hist.done).ranges) {
4503 return lst(hist.done)
4504 } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) {
4505 hist.done.pop()
4506 return lst(hist.done)
4507 }
4508 }
4509
4510 // Register a change in the history. Merges changes that are within
4511 // a single operation, or are close together with an origin that
4512 // allows merging (starting with "+") into a single event.
4513 function addChangeToHistory(doc, change, selAfter, opId) {
4514 var hist = doc.history
4515 hist.undone.length = 0
4516 var time = +new Date, cur
4517 var last
4518
4519 if ((hist.lastOp == opId ||
4520 hist.lastOrigin == change.origin && change.origin &&
4521 ((change.origin.charAt(0) == "+" && doc.cm && hist.lastModTime > time - doc.cm.options.historyEventDelay) ||
4522 change.origin.charAt(0) == "*")) &&
4523 (cur = lastChangeEvent(hist, hist.lastOp == opId))) {
4524 // Merge this change into the last event
4525 last = lst(cur.changes)
4526 if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) {
4527 // Optimized case for simple insertion -- don't want to add
4528 // new changesets for every character typed
4529 last.to = changeEnd(change)
4530 } else {
4531 // Add new sub-event
4532 cur.changes.push(historyChangeFromChange(doc, change))
4533 }
4534 } else {
4535 // Can not be merged, start a new event.
4536 var before = lst(hist.done)
4537 if (!before || !before.ranges)
4538 { pushSelectionToHistory(doc.sel, hist.done) }
4539 cur = {changes: [historyChangeFromChange(doc, change)],
4540 generation: hist.generation}
4541 hist.done.push(cur)
4542 while (hist.done.length > hist.undoDepth) {
4543 hist.done.shift()
4544 if (!hist.done[0].ranges) { hist.done.shift() }
4545 }
4546 }
4547 hist.done.push(selAfter)
4548 hist.generation = ++hist.maxGeneration
4549 hist.lastModTime = hist.lastSelTime = time
4550 hist.lastOp = hist.lastSelOp = opId
4551 hist.lastOrigin = hist.lastSelOrigin = change.origin
4552
4553 if (!last) { signal(doc, "historyAdded") }
4554 }
4555
4556 function selectionEventCanBeMerged(doc, origin, prev, sel) {
4557 var ch = origin.charAt(0)
4558 return ch == "*" ||
4559 ch == "+" &&
4560 prev.ranges.length == sel.ranges.length &&
4561 prev.somethingSelected() == sel.somethingSelected() &&
4562 new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500)
4563 }
4564
4565 // Called whenever the selection changes, sets the new selection as
4566 // the pending selection in the history, and pushes the old pending
4567 // selection into the 'done' array when it was significantly
4568 // different (in number of selected ranges, emptiness, or time).
4569 function addSelectionToHistory(doc, sel, opId, options) {
4570 var hist = doc.history, origin = options && options.origin
4571
4572 // A new event is started when the previous origin does not match
4573 // the current, or the origins don't allow matching. Origins
4574 // starting with * are always merged, those starting with + are
4575 // merged when similar and close together in time.
4576 if (opId == hist.lastSelOp ||
4577 (origin && hist.lastSelOrigin == origin &&
4578 (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||
4579 selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))
4580 { hist.done[hist.done.length - 1] = sel }
4581 else
4582 { pushSelectionToHistory(sel, hist.done) }
4583
4584 hist.lastSelTime = +new Date
4585 hist.lastSelOrigin = origin
4586 hist.lastSelOp = opId
4587 if (options && options.clearRedo !== false)
4588 { clearSelectionEvents(hist.undone) }
4589 }
4590
4591 function pushSelectionToHistory(sel, dest) {
4592 var top = lst(dest)
4593 if (!(top && top.ranges && top.equals(sel)))
4594 { dest.push(sel) }
4595 }
4596
4597 // Used to store marked span information in the history.
4598 function attachLocalSpans(doc, change, from, to) {
4599 var existing = change["spans_" + doc.id], n = 0
4600 doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function (line) {
4601 if (line.markedSpans)
4602 { (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans }
4603 ++n
4604 })
4605 }
4606
4607 // When un/re-doing restores text containing marked spans, those
4608 // that have been explicitly cleared should not be restored.
4609 function removeClearedSpans(spans) {
4610 if (!spans) { return null }
4611 var out
4612 for (var i = 0; i < spans.length; ++i) {
4613 if (spans[i].marker.explicitlyCleared) { if (!out) { out = spans.slice(0, i) } }
4614 else if (out) { out.push(spans[i]) }
4615 }
4616 return !out ? spans : out.length ? out : null
4617 }
4618
4619 // Retrieve and filter the old marked spans stored in a change event.
4620 function getOldSpans(doc, change) {
4621 var found = change["spans_" + doc.id]
4622 if (!found) { return null }
4623 var nw = []
4624 for (var i = 0; i < change.text.length; ++i)
4625 { nw.push(removeClearedSpans(found[i])) }
4626 return nw
4627 }
4628
4629 // Used for un/re-doing changes from the history. Combines the
4630 // result of computing the existing spans with the set of spans that
4631 // existed in the history (so that deleting around a span and then
4632 // undoing brings back the span).
4633 function mergeOldSpans(doc, change) {
4634 var old = getOldSpans(doc, change)
4635 var stretched = stretchSpansOverChange(doc, change)
4636 if (!old) { return stretched }
4637 if (!stretched) { return old }
4638
4639 for (var i = 0; i < old.length; ++i) {
4640 var oldCur = old[i], stretchCur = stretched[i]
4641 if (oldCur && stretchCur) {
4642 spans: for (var j = 0; j < stretchCur.length; ++j) {
4643 var span = stretchCur[j]
4644 for (var k = 0; k < oldCur.length; ++k)
4645 { if (oldCur[k].marker == span.marker) { continue spans } }
4646 oldCur.push(span)
4647 }
4648 } else if (stretchCur) {
4649 old[i] = stretchCur
4650 }
4651 }
4652 return old
4653 }
4654
4655 // Used both to provide a JSON-safe object in .getHistory, and, when
4656 // detaching a document, to split the history in two
4657 function copyHistoryArray(events, newGroup, instantiateSel) {
4658 var copy = []
4659 for (var i = 0; i < events.length; ++i) {
4660 var event = events[i]
4661 if (event.ranges) {
4662 copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event)
4663 continue
4664 }
4665 var changes = event.changes, newChanges = []
4666 copy.push({changes: newChanges})
4667 for (var j = 0; j < changes.length; ++j) {
4668 var change = changes[j], m = void 0
4669 newChanges.push({from: change.from, to: change.to, text: change.text})
4670 if (newGroup) { for (var prop in change) { if (m = prop.match(/^spans_(\d+)$/)) {
4671 if (indexOf(newGroup, Number(m[1])) > -1) {
4672 lst(newChanges)[prop] = change[prop]
4673 delete change[prop]
4674 }
4675 } } }
4676 }
4677 }
4678 return copy
4679 }
4680
4681 // The 'scroll' parameter given to many of these indicated whether
4682 // the new cursor position should be scrolled into view after
4683 // modifying the selection.
4684
4685 // If shift is held or the extend flag is set, extends a range to
4686 // include a given position (and optionally a second position).
4687 // Otherwise, simply returns the range between the given positions.
4688 // Used for cursor motion and such.
4689 function extendRange(doc, range, head, other) {
4690 if (doc.cm && doc.cm.display.shift || doc.extend) {
4691 var anchor = range.anchor
4692 if (other) {
4693 var posBefore = cmp(head, anchor) < 0
4694 if (posBefore != (cmp(other, anchor) < 0)) {
4695 anchor = head
4696 head = other
4697 } else if (posBefore != (cmp(head, other) < 0)) {
4698 head = other
4699 }
4700 }
4701 return new Range(anchor, head)
4702 } else {
4703 return new Range(other || head, head)
4704 }
4705 }
4706
4707 // Extend the primary selection range, discard the rest.
4708 function extendSelection(doc, head, other, options) {
4709 setSelection(doc, new Selection([extendRange(doc, doc.sel.primary(), head, other)], 0), options)
4710 }
4711
4712 // Extend all selections (pos is an array of selections with length
4713 // equal the number of selections)
4714 function extendSelections(doc, heads, options) {
4715 var out = []
4716 for (var i = 0; i < doc.sel.ranges.length; i++)
4717 { out[i] = extendRange(doc, doc.sel.ranges[i], heads[i], null) }
4718 var newSel = normalizeSelection(out, doc.sel.primIndex)
4719 setSelection(doc, newSel, options)
4720 }
4721
4722 // Updates a single range in the selection.
4723 function replaceOneSelection(doc, i, range, options) {
4724 var ranges = doc.sel.ranges.slice(0)
4725 ranges[i] = range
4726 setSelection(doc, normalizeSelection(ranges, doc.sel.primIndex), options)
4727 }
4728
4729 // Reset the selection to a single range.
4730 function setSimpleSelection(doc, anchor, head, options) {
4731 setSelection(doc, simpleSelection(anchor, head), options)
4732 }
4733
4734 // Give beforeSelectionChange handlers a change to influence a
4735 // selection update.
4736 function filterSelectionChange(doc, sel, options) {
4737 var obj = {
4738 ranges: sel.ranges,
4739 update: function(ranges) {
4740 var this$1 = this;
4741
4742 this.ranges = []
4743 for (var i = 0; i < ranges.length; i++)
4744 { this$1.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),
4745 clipPos(doc, ranges[i].head)) }
4746 },
4747 origin: options && options.origin
4748 }
4749 signal(doc, "beforeSelectionChange", doc, obj)
4750 if (doc.cm) { signal(doc.cm, "beforeSelectionChange", doc.cm, obj) }
4751 if (obj.ranges != sel.ranges) { return normalizeSelection(obj.ranges, obj.ranges.length - 1) }
4752 else { return sel }
4753 }
4754
4755 function setSelectionReplaceHistory(doc, sel, options) {
4756 var done = doc.history.done, last = lst(done)
4757 if (last && last.ranges) {
4758 done[done.length - 1] = sel
4759 setSelectionNoUndo(doc, sel, options)
4760 } else {
4761 setSelection(doc, sel, options)
4762 }
4763 }
4764
4765 // Set a new selection.
4766 function setSelection(doc, sel, options) {
4767 setSelectionNoUndo(doc, sel, options)
4768 addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options)
4769 }
4770
4771 function setSelectionNoUndo(doc, sel, options) {
4772 if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange"))
4773 { sel = filterSelectionChange(doc, sel, options) }
4774
4775 var bias = options && options.bias ||
4776 (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1)
4777 setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true))
4778
4779 if (!(options && options.scroll === false) && doc.cm)
4780 { ensureCursorVisible(doc.cm) }
4781 }
4782
4783 function setSelectionInner(doc, sel) {
4784 if (sel.equals(doc.sel)) { return }
4785
4786 doc.sel = sel
4787
4788 if (doc.cm) {
4789 doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true
4790 signalCursorActivity(doc.cm)
4791 }
4792 signalLater(doc, "cursorActivity", doc)
4793 }
4794
4795 // Verify that the selection does not partially select any atomic
4796 // marked ranges.
4797 function reCheckSelection(doc) {
4798 setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false), sel_dontScroll)
4799 }
4800
4801 // Return a selection that does not partially select any atomic
4802 // ranges.
4803 function skipAtomicInSelection(doc, sel, bias, mayClear) {
4804 var out
4805 for (var i = 0; i < sel.ranges.length; i++) {
4806 var range = sel.ranges[i]
4807 var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i]
4808 var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear)
4809 var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear)
4810 if (out || newAnchor != range.anchor || newHead != range.head) {
4811 if (!out) { out = sel.ranges.slice(0, i) }
4812 out[i] = new Range(newAnchor, newHead)
4813 }
4814 }
4815 return out ? normalizeSelection(out, sel.primIndex) : sel
4816 }
4817
4818 function skipAtomicInner(doc, pos, oldPos, dir, mayClear) {
4819 var line = getLine(doc, pos.line)
4820 if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
4821 var sp = line.markedSpans[i], m = sp.marker
4822 if ((sp.from == null || (m.inclusiveLeft ? sp.from <= pos.ch : sp.from < pos.ch)) &&
4823 (sp.to == null || (m.inclusiveRight ? sp.to >= pos.ch : sp.to > pos.ch))) {
4824 if (mayClear) {
4825 signal(m, "beforeCursorEnter")
4826 if (m.explicitlyCleared) {
4827 if (!line.markedSpans) { break }
4828 else {--i; continue}
4829 }
4830 }
4831 if (!m.atomic) { continue }
4832
4833 if (oldPos) {
4834 var near = m.find(dir < 0 ? 1 : -1), diff = void 0
4835 if (dir < 0 ? m.inclusiveRight : m.inclusiveLeft)
4836 { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null) }
4837 if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0))
4838 { return skipAtomicInner(doc, near, pos, dir, mayClear) }
4839 }
4840
4841 var far = m.find(dir < 0 ? -1 : 1)
4842 if (dir < 0 ? m.inclusiveLeft : m.inclusiveRight)
4843 { far = movePos(doc, far, dir, far.line == pos.line ? line : null) }
4844 return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null
4845 }
4846 } }
4847 return pos
4848 }
4849
4850 // Ensure a given position is not inside an atomic range.
4851 function skipAtomic(doc, pos, oldPos, bias, mayClear) {
4852 var dir = bias || 1
4853 var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) ||
4854 (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) ||
4855 skipAtomicInner(doc, pos, oldPos, -dir, mayClear) ||
4856 (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true))
4857 if (!found) {
4858 doc.cantEdit = true
4859 return Pos(doc.first, 0)
4860 }
4861 return found
4862 }
4863
4864 function movePos(doc, pos, dir, line) {
4865 if (dir < 0 && pos.ch == 0) {
4866 if (pos.line > doc.first) { return clipPos(doc, Pos(pos.line - 1)) }
4867 else { return null }
4868 } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) {
4869 if (pos.line < doc.first + doc.size - 1) { return Pos(pos.line + 1, 0) }
4870 else { return null }
4871 } else {
4872 return new Pos(pos.line, pos.ch + dir)
4873 }
4874 }
4875
4876 function selectAll(cm) {
4877 cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll)
4878 }
4879
4880 // UPDATING
4881
4882 // Allow "beforeChange" event handlers to influence a change
4883 function filterChange(doc, change, update) {
4884 var obj = {
4885 canceled: false,
4886 from: change.from,
4887 to: change.to,
4888 text: change.text,
4889 origin: change.origin,
4890 cancel: function () { return obj.canceled = true; }
4891 }
4892 if (update) { obj.update = function (from, to, text, origin) {
4893 if (from) { obj.from = clipPos(doc, from) }
4894 if (to) { obj.to = clipPos(doc, to) }
4895 if (text) { obj.text = text }
4896 if (origin !== undefined) { obj.origin = origin }
4897 } }
4898 signal(doc, "beforeChange", doc, obj)
4899 if (doc.cm) { signal(doc.cm, "beforeChange", doc.cm, obj) }
4900
4901 if (obj.canceled) { return null }
4902 return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin}
4903 }
4904
4905 // Apply a change to a document, and add it to the document's
4906 // history, and propagating it to all linked documents.
4907 function makeChange(doc, change, ignoreReadOnly) {
4908 if (doc.cm) {
4909 if (!doc.cm.curOp) { return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) }
4910 if (doc.cm.state.suppressEdits) { return }
4911 }
4912
4913 if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
4914 change = filterChange(doc, change, true)
4915 if (!change) { return }
4916 }
4917
4918 // Possibly split or suppress the update based on the presence
4919 // of read-only spans in its range.
4920 var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to)
4921 if (split) {
4922 for (var i = split.length - 1; i >= 0; --i)
4923 { makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text}) }
4924 } else {
4925 makeChangeInner(doc, change)
4926 }
4927 }
4928
4929 function makeChangeInner(doc, change) {
4930 if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) { return }
4931 var selAfter = computeSelAfterChange(doc, change)
4932 addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN)
4933
4934 makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change))
4935 var rebased = []
4936
4937 linkedDocs(doc, function (doc, sharedHist) {
4938 if (!sharedHist && indexOf(rebased, doc.history) == -1) {
4939 rebaseHist(doc.history, change)
4940 rebased.push(doc.history)
4941 }
4942 makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change))
4943 })
4944 }
4945
4946 // Revert a change stored in a document's history.
4947 function makeChangeFromHistory(doc, type, allowSelectionOnly) {
4948 if (doc.cm && doc.cm.state.suppressEdits && !allowSelectionOnly) { return }
4949
4950 var hist = doc.history, event, selAfter = doc.sel
4951 var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done
4952
4953 // Verify that there is a useable event (so that ctrl-z won't
4954 // needlessly clear selection events)
4955 var i = 0
4956 for (; i < source.length; i++) {
4957 event = source[i]
4958 if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges)
4959 { break }
4960 }
4961 if (i == source.length) { return }
4962 hist.lastOrigin = hist.lastSelOrigin = null
4963
4964 for (;;) {
4965 event = source.pop()
4966 if (event.ranges) {
4967 pushSelectionToHistory(event, dest)
4968 if (allowSelectionOnly && !event.equals(doc.sel)) {
4969 setSelection(doc, event, {clearRedo: false})
4970 return
4971 }
4972 selAfter = event
4973 }
4974 else { break }
4975 }
4976
4977 // Build up a reverse change object to add to the opposite history
4978 // stack (redo when undoing, and vice versa).
4979 var antiChanges = []
4980 pushSelectionToHistory(selAfter, dest)
4981 dest.push({changes: antiChanges, generation: hist.generation})
4982 hist.generation = event.generation || ++hist.maxGeneration
4983
4984 var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")
4985
4986 var loop = function ( i ) {
4987 var change = event.changes[i]
4988 change.origin = type
4989 if (filter && !filterChange(doc, change, false)) {
4990 source.length = 0
4991 return {}
4992 }
4993
4994 antiChanges.push(historyChangeFromChange(doc, change))
4995
4996 var after = i ? computeSelAfterChange(doc, change) : lst(source)
4997 makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change))
4998 if (!i && doc.cm) { doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}) }
4999 var rebased = []
5000
5001 // Propagate to the linked documents
5002 linkedDocs(doc, function (doc, sharedHist) {
5003 if (!sharedHist && indexOf(rebased, doc.history) == -1) {
5004 rebaseHist(doc.history, change)
5005 rebased.push(doc.history)
5006 }
5007 makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change))
5008 })
5009 };
5010
5011 for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) {
5012 var returned = loop( i$1 );
5013
5014 if ( returned ) return returned.v;
5015 }
5016 }
5017
5018 // Sub-views need their line numbers shifted when text is added
5019 // above or below them in the parent document.
5020 function shiftDoc(doc, distance) {
5021 if (distance == 0) { return }
5022 doc.first += distance
5023 doc.sel = new Selection(map(doc.sel.ranges, function (range) { return new Range(
5024 Pos(range.anchor.line + distance, range.anchor.ch),
5025 Pos(range.head.line + distance, range.head.ch)
5026 ); }), doc.sel.primIndex)
5027 if (doc.cm) {
5028 regChange(doc.cm, doc.first, doc.first - distance, distance)
5029 for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++)
5030 { regLineChange(doc.cm, l, "gutter") }
5031 }
5032 }
5033
5034 // More lower-level change function, handling only a single document
5035 // (not linked ones).
5036 function makeChangeSingleDoc(doc, change, selAfter, spans) {
5037 if (doc.cm && !doc.cm.curOp)
5038 { return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) }
5039
5040 if (change.to.line < doc.first) {
5041 shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line))
5042 return
5043 }
5044 if (change.from.line > doc.lastLine()) { return }
5045
5046 // Clip the change to the size of this doc
5047 if (change.from.line < doc.first) {
5048 var shift = change.text.length - 1 - (doc.first - change.from.line)
5049 shiftDoc(doc, shift)
5050 change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
5051 text: [lst(change.text)], origin: change.origin}
5052 }
5053 var last = doc.lastLine()
5054 if (change.to.line > last) {
5055 change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
5056 text: [change.text[0]], origin: change.origin}
5057 }
5058
5059 change.removed = getBetween(doc, change.from, change.to)
5060
5061 if (!selAfter) { selAfter = computeSelAfterChange(doc, change) }
5062 if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans) }
5063 else { updateDoc(doc, change, spans) }
5064 setSelectionNoUndo(doc, selAfter, sel_dontScroll)
5065 }
5066
5067 // Handle the interaction of a change to a document with the editor
5068 // that this document is part of.
5069 function makeChangeSingleDocInEditor(cm, change, spans) {
5070 var doc = cm.doc, display = cm.display, from = change.from, to = change.to
5071
5072 var recomputeMaxLength = false, checkWidthStart = from.line
5073 if (!cm.options.lineWrapping) {
5074 checkWidthStart = lineNo(visualLine(getLine(doc, from.line)))
5075 doc.iter(checkWidthStart, to.line + 1, function (line) {
5076 if (line == display.maxLine) {
5077 recomputeMaxLength = true
5078 return true
5079 }
5080 })
5081 }
5082
5083 if (doc.sel.contains(change.from, change.to) > -1)
5084 { signalCursorActivity(cm) }
5085
5086 updateDoc(doc, change, spans, estimateHeight(cm))
5087
5088 if (!cm.options.lineWrapping) {
5089 doc.iter(checkWidthStart, from.line + change.text.length, function (line) {
5090 var len = lineLength(line)
5091 if (len > display.maxLineLength) {
5092 display.maxLine = line
5093 display.maxLineLength = len
5094 display.maxLineChanged = true
5095 recomputeMaxLength = false
5096 }
5097 })
5098 if (recomputeMaxLength) { cm.curOp.updateMaxLine = true }
5099 }
5100
5101 // Adjust frontier, schedule worker
5102 doc.frontier = Math.min(doc.frontier, from.line)
5103 startWorker(cm, 400)
5104
5105 var lendiff = change.text.length - (to.line - from.line) - 1
5106 // Remember that these lines changed, for updating the display
5107 if (change.full)
5108 { regChange(cm) }
5109 else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change))
5110 { regLineChange(cm, from.line, "text") }
5111 else
5112 { regChange(cm, from.line, to.line + 1, lendiff) }
5113
5114 var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change")
5115 if (changeHandler || changesHandler) {
5116 var obj = {
5117 from: from, to: to,
5118 text: change.text,
5119 removed: change.removed,
5120 origin: change.origin
5121 }
5122 if (changeHandler) { signalLater(cm, "change", cm, obj) }
5123 if (changesHandler) { (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj) }
5124 }
5125 cm.display.selForContextMenu = null
5126 }
5127
5128 function replaceRange(doc, code, from, to, origin) {
5129 if (!to) { to = from }
5130 if (cmp(to, from) < 0) { var tmp = to; to = from; from = tmp }
5131 if (typeof code == "string") { code = doc.splitLines(code) }
5132 makeChange(doc, {from: from, to: to, text: code, origin: origin})
5133 }
5134
5135 // Rebasing/resetting history to deal with externally-sourced changes
5136
5137 function rebaseHistSelSingle(pos, from, to, diff) {
5138 if (to < pos.line) {
5139 pos.line += diff
5140 } else if (from < pos.line) {
5141 pos.line = from
5142 pos.ch = 0
5143 }
5144 }
5145
5146 // Tries to rebase an array of history events given a change in the
5147 // document. If the change touches the same lines as the event, the
5148 // event, and everything 'behind' it, is discarded. If the change is
5149 // before the event, the event's positions are updated. Uses a
5150 // copy-on-write scheme for the positions, to avoid having to
5151 // reallocate them all on every rebase, but also avoid problems with
5152 // shared position objects being unsafely updated.
5153 function rebaseHistArray(array, from, to, diff) {
5154 for (var i = 0; i < array.length; ++i) {
5155 var sub = array[i], ok = true
5156 if (sub.ranges) {
5157 if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true }
5158 for (var j = 0; j < sub.ranges.length; j++) {
5159 rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff)
5160 rebaseHistSelSingle(sub.ranges[j].head, from, to, diff)
5161 }
5162 continue
5163 }
5164 for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) {
5165 var cur = sub.changes[j$1]
5166 if (to < cur.from.line) {
5167 cur.from = Pos(cur.from.line + diff, cur.from.ch)
5168 cur.to = Pos(cur.to.line + diff, cur.to.ch)
5169 } else if (from <= cur.to.line) {
5170 ok = false
5171 break
5172 }
5173 }
5174 if (!ok) {
5175 array.splice(0, i + 1)
5176 i = 0
5177 }
5178 }
5179 }
5180
5181 function rebaseHist(hist, change) {
5182 var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1
5183 rebaseHistArray(hist.done, from, to, diff)
5184 rebaseHistArray(hist.undone, from, to, diff)
5185 }
5186
5187 // Utility for applying a change to a line by handle or number,
5188 // returning the number and optionally registering the line as
5189 // changed.
5190 function changeLine(doc, handle, changeType, op) {
5191 var no = handle, line = handle
5192 if (typeof handle == "number") { line = getLine(doc, clipLine(doc, handle)) }
5193 else { no = lineNo(handle) }
5194 if (no == null) { return null }
5195 if (op(line, no) && doc.cm) { regLineChange(doc.cm, no, changeType) }
5196 return line
5197 }
5198
5199 // The document is represented as a BTree consisting of leaves, with
5200 // chunk of lines in them, and branches, with up to ten leaves or
5201 // other branch nodes below them. The top node is always a branch
5202 // node, and is the document object itself (meaning it has
5203 // additional methods and properties).
5204 //
5205 // All nodes have parent links. The tree is used both to go from
5206 // line numbers to line objects, and to go from objects to numbers.
5207 // It also indexes by height, and is used to convert between height
5208 // and line object, and to find the total height of the document.
5209 //
5210 // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html
5211
5212 function LeafChunk(lines) {
5213 var this$1 = this;
5214
5215 this.lines = lines
5216 this.parent = null
5217 var height = 0
5218 for (var i = 0; i < lines.length; ++i) {
5219 lines[i].parent = this$1
5220 height += lines[i].height
5221 }
5222 this.height = height
5223 }
5224
5225 LeafChunk.prototype = {
5226 chunkSize: function() { return this.lines.length },
5227 // Remove the n lines at offset 'at'.
5228 removeInner: function(at, n) {
5229 var this$1 = this;
5230
5231 for (var i = at, e = at + n; i < e; ++i) {
5232 var line = this$1.lines[i]
5233 this$1.height -= line.height
5234 cleanUpLine(line)
5235 signalLater(line, "delete")
5236 }
5237 this.lines.splice(at, n)
5238 },
5239 // Helper used to collapse a small branch into a single leaf.
5240 collapse: function(lines) {
5241 lines.push.apply(lines, this.lines)
5242 },
5243 // Insert the given array of lines at offset 'at', count them as
5244 // having the given height.
5245 insertInner: function(at, lines, height) {
5246 var this$1 = this;
5247
5248 this.height += height
5249 this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at))
5250 for (var i = 0; i < lines.length; ++i) { lines[i].parent = this$1 }
5251 },
5252 // Used to iterate over a part of the tree.
5253 iterN: function(at, n, op) {
5254 var this$1 = this;
5255
5256 for (var e = at + n; at < e; ++at)
5257 { if (op(this$1.lines[at])) { return true } }
5258 }
5259 }
5260
5261 function BranchChunk(children) {
5262 var this$1 = this;
5263
5264 this.children = children
5265 var size = 0, height = 0
5266 for (var i = 0; i < children.length; ++i) {
5267 var ch = children[i]
5268 size += ch.chunkSize(); height += ch.height
5269 ch.parent = this$1
5270 }
5271 this.size = size
5272 this.height = height
5273 this.parent = null
5274 }
5275
5276 BranchChunk.prototype = {
5277 chunkSize: function() { return this.size },
5278 removeInner: function(at, n) {
5279 var this$1 = this;
5280
5281 this.size -= n
5282 for (var i = 0; i < this.children.length; ++i) {
5283 var child = this$1.children[i], sz = child.chunkSize()
5284 if (at < sz) {
5285 var rm = Math.min(n, sz - at), oldHeight = child.height
5286 child.removeInner(at, rm)
5287 this$1.height -= oldHeight - child.height
5288 if (sz == rm) { this$1.children.splice(i--, 1); child.parent = null }
5289 if ((n -= rm) == 0) { break }
5290 at = 0
5291 } else { at -= sz }
5292 }
5293 // If the result is smaller than 25 lines, ensure that it is a
5294 // single leaf node.
5295 if (this.size - n < 25 &&
5296 (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) {
5297 var lines = []
5298 this.collapse(lines)
5299 this.children = [new LeafChunk(lines)]
5300 this.children[0].parent = this
5301 }
5302 },
5303 collapse: function(lines) {
5304 var this$1 = this;
5305
5306 for (var i = 0; i < this.children.length; ++i) { this$1.children[i].collapse(lines) }
5307 },
5308 insertInner: function(at, lines, height) {
5309 var this$1 = this;
5310
5311 this.size += lines.length
5312 this.height += height
5313 for (var i = 0; i < this.children.length; ++i) {
5314 var child = this$1.children[i], sz = child.chunkSize()
5315 if (at <= sz) {
5316 child.insertInner(at, lines, height)
5317 if (child.lines && child.lines.length > 50) {
5318 // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced.
5319 // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest.
5320 var remaining = child.lines.length % 25 + 25
5321 for (var pos = remaining; pos < child.lines.length;) {
5322 var leaf = new LeafChunk(child.lines.slice(pos, pos += 25))
5323 child.height -= leaf.height
5324 this$1.children.splice(++i, 0, leaf)
5325 leaf.parent = this$1
5326 }
5327 child.lines = child.lines.slice(0, remaining)
5328 this$1.maybeSpill()
5329 }
5330 break
5331 }
5332 at -= sz
5333 }
5334 },
5335 // When a node has grown, check whether it should be split.
5336 maybeSpill: function() {
5337 if (this.children.length <= 10) { return }
5338 var me = this
5339 do {
5340 var spilled = me.children.splice(me.children.length - 5, 5)
5341 var sibling = new BranchChunk(spilled)
5342 if (!me.parent) { // Become the parent node
5343 var copy = new BranchChunk(me.children)
5344 copy.parent = me
5345 me.children = [copy, sibling]
5346 me = copy
5347 } else {
5348 me.size -= sibling.size
5349 me.height -= sibling.height
5350 var myIndex = indexOf(me.parent.children, me)
5351 me.parent.children.splice(myIndex + 1, 0, sibling)
5352 }
5353 sibling.parent = me.parent
5354 } while (me.children.length > 10)
5355 me.parent.maybeSpill()
5356 },
5357 iterN: function(at, n, op) {
5358 var this$1 = this;
5359
5360 for (var i = 0; i < this.children.length; ++i) {
5361 var child = this$1.children[i], sz = child.chunkSize()
5362 if (at < sz) {
5363 var used = Math.min(n, sz - at)
5364 if (child.iterN(at, used, op)) { return true }
5365 if ((n -= used) == 0) { break }
5366 at = 0
5367 } else { at -= sz }
5368 }
5369 }
5370 }
5371
5372 // Line widgets are block elements displayed above or below a line.
5373
5374 function LineWidget(doc, node, options) {
5375 var this$1 = this;
5376
5377 if (options) { for (var opt in options) { if (options.hasOwnProperty(opt))
5378 { this$1[opt] = options[opt] } } }
5379 this.doc = doc
5380 this.node = node
5381 }
5382 eventMixin(LineWidget)
5383
5384 function adjustScrollWhenAboveVisible(cm, line, diff) {
5385 if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))
5386 { addToScrollPos(cm, null, diff) }
5387 }
5388
5389 LineWidget.prototype.clear = function() {
5390 var this$1 = this;
5391
5392 var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line)
5393 if (no == null || !ws) { return }
5394 for (var i = 0; i < ws.length; ++i) { if (ws[i] == this$1) { ws.splice(i--, 1) } }
5395 if (!ws.length) { line.widgets = null }
5396 var height = widgetHeight(this)
5397 updateLineHeight(line, Math.max(0, line.height - height))
5398 if (cm) { runInOp(cm, function () {
5399 adjustScrollWhenAboveVisible(cm, line, -height)
5400 regLineChange(cm, no, "widget")
5401 }) }
5402 }
5403 LineWidget.prototype.changed = function() {
5404 var oldH = this.height, cm = this.doc.cm, line = this.line
5405 this.height = null
5406 var diff = widgetHeight(this) - oldH
5407 if (!diff) { return }
5408 updateLineHeight(line, line.height + diff)
5409 if (cm) { runInOp(cm, function () {
5410 cm.curOp.forceUpdate = true
5411 adjustScrollWhenAboveVisible(cm, line, diff)
5412 }) }
5413 }
5414
5415 function addLineWidget(doc, handle, node, options) {
5416 var widget = new LineWidget(doc, node, options)
5417 var cm = doc.cm
5418 if (cm && widget.noHScroll) { cm.display.alignWidgets = true }
5419 changeLine(doc, handle, "widget", function (line) {
5420 var widgets = line.widgets || (line.widgets = [])
5421 if (widget.insertAt == null) { widgets.push(widget) }
5422 else { widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget) }
5423 widget.line = line
5424 if (cm && !lineIsHidden(doc, line)) {
5425 var aboveVisible = heightAtLine(line) < doc.scrollTop
5426 updateLineHeight(line, line.height + widgetHeight(widget))
5427 if (aboveVisible) { addToScrollPos(cm, null, widget.height) }
5428 cm.curOp.forceUpdate = true
5429 }
5430 return true
5431 })
5432 return widget
5433 }
5434
5435 // TEXTMARKERS
5436
5437 // Created with markText and setBookmark methods. A TextMarker is a
5438 // handle that can be used to clear or find a marked position in the
5439 // document. Line objects hold arrays (markedSpans) containing
5440 // {from, to, marker} object pointing to such marker objects, and
5441 // indicating that such a marker is present on that line. Multiple
5442 // lines may point to the same marker when it spans across lines.
5443 // The spans will have null for their from/to properties when the
5444 // marker continues beyond the start/end of the line. Markers have
5445 // links back to the lines they currently touch.
5446
5447 // Collapsed markers have unique ids, in order to be able to order
5448 // them, which is needed for uniquely determining an outer marker
5449 // when they overlap (they may nest, but not partially overlap).
5450 var nextMarkerId = 0
5451
5452 function TextMarker(doc, type) {
5453 this.lines = []
5454 this.type = type
5455 this.doc = doc
5456 this.id = ++nextMarkerId
5457 }
5458 eventMixin(TextMarker)
5459
5460 // Clear the marker.
5461 TextMarker.prototype.clear = function() {
5462 var this$1 = this;
5463
5464 if (this.explicitlyCleared) { return }
5465 var cm = this.doc.cm, withOp = cm && !cm.curOp
5466 if (withOp) { startOperation(cm) }
5467 if (hasHandler(this, "clear")) {
5468 var found = this.find()
5469 if (found) { signalLater(this, "clear", found.from, found.to) }
5470 }
5471 var min = null, max = null
5472 for (var i = 0; i < this.lines.length; ++i) {
5473 var line = this$1.lines[i]
5474 var span = getMarkedSpanFor(line.markedSpans, this$1)
5475 if (cm && !this$1.collapsed) { regLineChange(cm, lineNo(line), "text") }
5476 else if (cm) {
5477 if (span.to != null) { max = lineNo(line) }
5478 if (span.from != null) { min = lineNo(line) }
5479 }
5480 line.markedSpans = removeMarkedSpan(line.markedSpans, span)
5481 if (span.from == null && this$1.collapsed && !lineIsHidden(this$1.doc, line) && cm)
5482 { updateLineHeight(line, textHeight(cm.display)) }
5483 }
5484 if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) {
5485 var visual = visualLine(this$1.lines[i$1]), len = lineLength(visual)
5486 if (len > cm.display.maxLineLength) {
5487 cm.display.maxLine = visual
5488 cm.display.maxLineLength = len
5489 cm.display.maxLineChanged = true
5490 }
5491 } }
5492
5493 if (min != null && cm && this.collapsed) { regChange(cm, min, max + 1) }
5494 this.lines.length = 0
5495 this.explicitlyCleared = true
5496 if (this.atomic && this.doc.cantEdit) {
5497 this.doc.cantEdit = false
5498 if (cm) { reCheckSelection(cm.doc) }
5499 }
5500 if (cm) { signalLater(cm, "markerCleared", cm, this) }
5501 if (withOp) { endOperation(cm) }
5502 if (this.parent) { this.parent.clear() }
5503 }
5504
5505 // Find the position of the marker in the document. Returns a {from,
5506 // to} object by default. Side can be passed to get a specific side
5507 // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the
5508 // Pos objects returned contain a line object, rather than a line
5509 // number (used to prevent looking up the same line twice).
5510 TextMarker.prototype.find = function(side, lineObj) {
5511 var this$1 = this;
5512
5513 if (side == null && this.type == "bookmark") { side = 1 }
5514 var from, to
5515 for (var i = 0; i < this.lines.length; ++i) {
5516 var line = this$1.lines[i]
5517 var span = getMarkedSpanFor(line.markedSpans, this$1)
5518 if (span.from != null) {
5519 from = Pos(lineObj ? line : lineNo(line), span.from)
5520 if (side == -1) { return from }
5521 }
5522 if (span.to != null) {
5523 to = Pos(lineObj ? line : lineNo(line), span.to)
5524 if (side == 1) { return to }
5525 }
5526 }
5527 return from && {from: from, to: to}
5528 }
5529
5530 // Signals that the marker's widget changed, and surrounding layout
5531 // should be recomputed.
5532 TextMarker.prototype.changed = function() {
5533 var pos = this.find(-1, true), widget = this, cm = this.doc.cm
5534 if (!pos || !cm) { return }
5535 runInOp(cm, function () {
5536 var line = pos.line, lineN = lineNo(pos.line)
5537 var view = findViewForLine(cm, lineN)
5538 if (view) {
5539 clearLineMeasurementCacheFor(view)
5540 cm.curOp.selectionChanged = cm.curOp.forceUpdate = true
5541 }
5542 cm.curOp.updateMaxLine = true
5543 if (!lineIsHidden(widget.doc, line) && widget.height != null) {
5544 var oldHeight = widget.height
5545 widget.height = null
5546 var dHeight = widgetHeight(widget) - oldHeight
5547 if (dHeight)
5548 { updateLineHeight(line, line.height + dHeight) }
5549 }
5550 })
5551 }
5552
5553 TextMarker.prototype.attachLine = function(line) {
5554 if (!this.lines.length && this.doc.cm) {
5555 var op = this.doc.cm.curOp
5556 if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
5557 { (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this) }
5558 }
5559 this.lines.push(line)
5560 }
5561 TextMarker.prototype.detachLine = function(line) {
5562 this.lines.splice(indexOf(this.lines, line), 1)
5563 if (!this.lines.length && this.doc.cm) {
5564 var op = this.doc.cm.curOp;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this)
5565 }
5566 }
5567
5568 // Create a marker, wire it up to the right lines, and
5569 function markText(doc, from, to, options, type) {
5570 // Shared markers (across linked documents) are handled separately
5571 // (markTextShared will call out to this again, once per
5572 // document).
5573 if (options && options.shared) { return markTextShared(doc, from, to, options, type) }
5574 // Ensure we are in an operation.
5575 if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, markText)(doc, from, to, options, type) }
5576
5577 var marker = new TextMarker(doc, type), diff = cmp(from, to)
5578 if (options) { copyObj(options, marker, false) }
5579 // Don't connect empty markers unless clearWhenEmpty is false
5580 if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false)
5581 { return marker }
5582 if (marker.replacedWith) {
5583 // Showing up as a widget implies collapsed (widget replaces text)
5584 marker.collapsed = true
5585 marker.widgetNode = elt("span", [marker.replacedWith], "CodeMirror-widget")
5586 if (!options.handleMouseEvents) { marker.widgetNode.setAttribute("cm-ignore-events", "true") }
5587 if (options.insertLeft) { marker.widgetNode.insertLeft = true }
5588 }
5589 if (marker.collapsed) {
5590 if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||
5591 from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker))
5592 { throw new Error("Inserting collapsed marker partially overlapping an existing one") }
5593 seeCollapsedSpans()
5594 }
5595
5596 if (marker.addToHistory)
5597 { addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN) }
5598
5599 var curLine = from.line, cm = doc.cm, updateMaxLine
5600 doc.iter(curLine, to.line + 1, function (line) {
5601 if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine)
5602 { updateMaxLine = true }
5603 if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0) }
5604 addMarkedSpan(line, new MarkedSpan(marker,
5605 curLine == from.line ? from.ch : null,
5606 curLine == to.line ? to.ch : null))
5607 ++curLine
5608 })
5609 // lineIsHidden depends on the presence of the spans, so needs a second pass
5610 if (marker.collapsed) { doc.iter(from.line, to.line + 1, function (line) {
5611 if (lineIsHidden(doc, line)) { updateLineHeight(line, 0) }
5612 }) }
5613
5614 if (marker.clearOnEnter) { on(marker, "beforeCursorEnter", function () { return marker.clear(); }) }
5615
5616 if (marker.readOnly) {
5617 seeReadOnlySpans()
5618 if (doc.history.done.length || doc.history.undone.length)
5619 { doc.clearHistory() }
5620 }
5621 if (marker.collapsed) {
5622 marker.id = ++nextMarkerId
5623 marker.atomic = true
5624 }
5625 if (cm) {
5626 // Sync editor state
5627 if (updateMaxLine) { cm.curOp.updateMaxLine = true }
5628 if (marker.collapsed)
5629 { regChange(cm, from.line, to.line + 1) }
5630 else if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.css)
5631 { for (var i = from.line; i <= to.line; i++) { regLineChange(cm, i, "text") } }
5632 if (marker.atomic) { reCheckSelection(cm.doc) }
5633 signalLater(cm, "markerAdded", cm, marker)
5634 }
5635 return marker
5636 }
5637
5638 // SHARED TEXTMARKERS
5639
5640 // A shared marker spans multiple linked documents. It is
5641 // implemented as a meta-marker-object controlling multiple normal
5642 // markers.
5643 function SharedTextMarker(markers, primary) {
5644 var this$1 = this;
5645
5646 this.markers = markers
5647 this.primary = primary
5648 for (var i = 0; i < markers.length; ++i)
5649 { markers[i].parent = this$1 }
5650 }
5651 eventMixin(SharedTextMarker)
5652
5653 SharedTextMarker.prototype.clear = function() {
5654 var this$1 = this;
5655
5656 if (this.explicitlyCleared) { return }
5657 this.explicitlyCleared = true
5658 for (var i = 0; i < this.markers.length; ++i)
5659 { this$1.markers[i].clear() }
5660 signalLater(this, "clear")
5661 }
5662 SharedTextMarker.prototype.find = function(side, lineObj) {
5663 return this.primary.find(side, lineObj)
5664 }
5665
5666 function markTextShared(doc, from, to, options, type) {
5667 options = copyObj(options)
5668 options.shared = false
5669 var markers = [markText(doc, from, to, options, type)], primary = markers[0]
5670 var widget = options.widgetNode
5671 linkedDocs(doc, function (doc) {
5672 if (widget) { options.widgetNode = widget.cloneNode(true) }
5673 markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type))
5674 for (var i = 0; i < doc.linked.length; ++i)
5675 { if (doc.linked[i].isParent) { return } }
5676 primary = lst(markers)
5677 })
5678 return new SharedTextMarker(markers, primary)
5679 }
5680
5681 function findSharedMarkers(doc) {
5682 return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function (m) { return m.parent; })
5683 }
5684
5685 function copySharedMarkers(doc, markers) {
5686 for (var i = 0; i < markers.length; i++) {
5687 var marker = markers[i], pos = marker.find()
5688 var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to)
5689 if (cmp(mFrom, mTo)) {
5690 var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type)
5691 marker.markers.push(subMark)
5692 subMark.parent = marker
5693 }
5694 }
5695 }
5696
5697 function detachSharedMarkers(markers) {
5698 var loop = function ( i ) {
5699 var marker = markers[i], linked = [marker.primary.doc]
5700 linkedDocs(marker.primary.doc, function (d) { return linked.push(d); })
5701 for (var j = 0; j < marker.markers.length; j++) {
5702 var subMarker = marker.markers[j]
5703 if (indexOf(linked, subMarker.doc) == -1) {
5704 subMarker.parent = null
5705 marker.markers.splice(j--, 1)
5706 }
5707 }
5708 };
5709
5710 for (var i = 0; i < markers.length; i++) loop( i );
5711 }
5712
5713 var nextDocId = 0
5714 var Doc = function(text, mode, firstLine, lineSep) {
5715 if (!(this instanceof Doc)) { return new Doc(text, mode, firstLine, lineSep) }
5716 if (firstLine == null) { firstLine = 0 }
5717
5718 BranchChunk.call(this, [new LeafChunk([new Line("", null)])])
5719 this.first = firstLine
5720 this.scrollTop = this.scrollLeft = 0
5721 this.cantEdit = false
5722 this.cleanGeneration = 1
5723 this.frontier = firstLine
5724 var start = Pos(firstLine, 0)
5725 this.sel = simpleSelection(start)
5726 this.history = new History(null)
5727 this.id = ++nextDocId
5728 this.modeOption = mode
5729 this.lineSep = lineSep
5730 this.extend = false
5731
5732 if (typeof text == "string") { text = this.splitLines(text) }
5733 updateDoc(this, {from: start, to: start, text: text})
5734 setSelection(this, simpleSelection(start), sel_dontScroll)
5735 }
5736
5737 Doc.prototype = createObj(BranchChunk.prototype, {
5738 constructor: Doc,
5739 // Iterate over the document. Supports two forms -- with only one
5740 // argument, it calls that for each line in the document. With
5741 // three, it iterates over the range given by the first two (with
5742 // the second being non-inclusive).
5743 iter: function(from, to, op) {
5744 if (op) { this.iterN(from - this.first, to - from, op) }
5745 else { this.iterN(this.first, this.first + this.size, from) }
5746 },
5747
5748 // Non-public interface for adding and removing lines.
5749 insert: function(at, lines) {
5750 var height = 0
5751 for (var i = 0; i < lines.length; ++i) { height += lines[i].height }
5752 this.insertInner(at - this.first, lines, height)
5753 },
5754 remove: function(at, n) { this.removeInner(at - this.first, n) },
5755
5756 // From here, the methods are part of the public interface. Most
5757 // are also available from CodeMirror (editor) instances.
5758
5759 getValue: function(lineSep) {
5760 var lines = getLines(this, this.first, this.first + this.size)
5761 if (lineSep === false) { return lines }
5762 return lines.join(lineSep || this.lineSeparator())
5763 },
5764 setValue: docMethodOp(function(code) {
5765 var top = Pos(this.first, 0), last = this.first + this.size - 1
5766 makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
5767 text: this.splitLines(code), origin: "setValue", full: true}, true)
5768 setSelection(this, simpleSelection(top))
5769 }),
5770 replaceRange: function(code, from, to, origin) {
5771 from = clipPos(this, from)
5772 to = to ? clipPos(this, to) : from
5773 replaceRange(this, code, from, to, origin)
5774 },
5775 getRange: function(from, to, lineSep) {
5776 var lines = getBetween(this, clipPos(this, from), clipPos(this, to))
5777 if (lineSep === false) { return lines }
5778 return lines.join(lineSep || this.lineSeparator())
5779 },
5780
5781 getLine: function(line) {var l = this.getLineHandle(line); return l && l.text},
5782
5783 getLineHandle: function(line) {if (isLine(this, line)) { return getLine(this, line) }},
5784 getLineNumber: function(line) {return lineNo(line)},
5785
5786 getLineHandleVisualStart: function(line) {
5787 if (typeof line == "number") { line = getLine(this, line) }
5788 return visualLine(line)
5789 },
5790
5791 lineCount: function() {return this.size},
5792 firstLine: function() {return this.first},
5793 lastLine: function() {return this.first + this.size - 1},
5794
5795 clipPos: function(pos) {return clipPos(this, pos)},
5796
5797 getCursor: function(start) {
5798 var range$$1 = this.sel.primary(), pos
5799 if (start == null || start == "head") { pos = range$$1.head }
5800 else if (start == "anchor") { pos = range$$1.anchor }
5801 else if (start == "end" || start == "to" || start === false) { pos = range$$1.to() }
5802 else { pos = range$$1.from() }
5803 return pos
5804 },
5805 listSelections: function() { return this.sel.ranges },
5806 somethingSelected: function() {return this.sel.somethingSelected()},
5807
5808 setCursor: docMethodOp(function(line, ch, options) {
5809 setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options)
5810 }),
5811 setSelection: docMethodOp(function(anchor, head, options) {
5812 setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options)
5813 }),
5814 extendSelection: docMethodOp(function(head, other, options) {
5815 extendSelection(this, clipPos(this, head), other && clipPos(this, other), options)
5816 }),
5817 extendSelections: docMethodOp(function(heads, options) {
5818 extendSelections(this, clipPosArray(this, heads), options)
5819 }),
5820 extendSelectionsBy: docMethodOp(function(f, options) {
5821 var heads = map(this.sel.ranges, f)
5822 extendSelections(this, clipPosArray(this, heads), options)
5823 }),
5824 setSelections: docMethodOp(function(ranges, primary, options) {
5825 var this$1 = this;
5826
5827 if (!ranges.length) { return }
5828 var out = []
5829 for (var i = 0; i < ranges.length; i++)
5830 { out[i] = new Range(clipPos(this$1, ranges[i].anchor),
5831 clipPos(this$1, ranges[i].head)) }
5832 if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex) }
5833 setSelection(this, normalizeSelection(out, primary), options)
5834 }),
5835 addSelection: docMethodOp(function(anchor, head, options) {
5836 var ranges = this.sel.ranges.slice(0)
5837 ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)))
5838 setSelection(this, normalizeSelection(ranges, ranges.length - 1), options)
5839 }),
5840
5841 getSelection: function(lineSep) {
5842 var this$1 = this;
5843
5844 var ranges = this.sel.ranges, lines
5845 for (var i = 0; i < ranges.length; i++) {
5846 var sel = getBetween(this$1, ranges[i].from(), ranges[i].to())
5847 lines = lines ? lines.concat(sel) : sel
5848 }
5849 if (lineSep === false) { return lines }
5850 else { return lines.join(lineSep || this.lineSeparator()) }
5851 },
5852 getSelections: function(lineSep) {
5853 var this$1 = this;
5854
5855 var parts = [], ranges = this.sel.ranges
5856 for (var i = 0; i < ranges.length; i++) {
5857 var sel = getBetween(this$1, ranges[i].from(), ranges[i].to())
5858 if (lineSep !== false) { sel = sel.join(lineSep || this$1.lineSeparator()) }
5859 parts[i] = sel
5860 }
5861 return parts
5862 },
5863 replaceSelection: function(code, collapse, origin) {
5864 var dup = []
5865 for (var i = 0; i < this.sel.ranges.length; i++)
5866 { dup[i] = code }
5867 this.replaceSelections(dup, collapse, origin || "+input")
5868 },
5869 replaceSelections: docMethodOp(function(code, collapse, origin) {
5870 var this$1 = this;
5871
5872 var changes = [], sel = this.sel
5873 for (var i = 0; i < sel.ranges.length; i++) {
5874 var range$$1 = sel.ranges[i]
5875 changes[i] = {from: range$$1.from(), to: range$$1.to(), text: this$1.splitLines(code[i]), origin: origin}
5876 }
5877 var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse)
5878 for (var i$1 = changes.length - 1; i$1 >= 0; i$1--)
5879 { makeChange(this$1, changes[i$1]) }
5880 if (newSel) { setSelectionReplaceHistory(this, newSel) }
5881 else if (this.cm) { ensureCursorVisible(this.cm) }
5882 }),
5883 undo: docMethodOp(function() {makeChangeFromHistory(this, "undo")}),
5884 redo: docMethodOp(function() {makeChangeFromHistory(this, "redo")}),
5885 undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true)}),
5886 redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true)}),
5887
5888 setExtending: function(val) {this.extend = val},
5889 getExtending: function() {return this.extend},
5890
5891 historySize: function() {
5892 var hist = this.history, done = 0, undone = 0
5893 for (var i = 0; i < hist.done.length; i++) { if (!hist.done[i].ranges) { ++done } }
5894 for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].ranges) { ++undone } }
5895 return {undo: done, redo: undone}
5896 },
5897 clearHistory: function() {this.history = new History(this.history.maxGeneration)},
5898
5899 markClean: function() {
5900 this.cleanGeneration = this.changeGeneration(true)
5901 },
5902 changeGeneration: function(forceSplit) {
5903 if (forceSplit)
5904 { this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null }
5905 return this.history.generation
5906 },
5907 isClean: function (gen) {
5908 return this.history.generation == (gen || this.cleanGeneration)
5909 },
5910
5911 getHistory: function() {
5912 return {done: copyHistoryArray(this.history.done),
5913 undone: copyHistoryArray(this.history.undone)}
5914 },
5915 setHistory: function(histData) {
5916 var hist = this.history = new History(this.history.maxGeneration)
5917 hist.done = copyHistoryArray(histData.done.slice(0), null, true)
5918 hist.undone = copyHistoryArray(histData.undone.slice(0), null, true)
5919 },
5920
5921 setGutterMarker: docMethodOp(function(line, gutterID, value) {
5922 return changeLine(this, line, "gutter", function (line) {
5923 var markers = line.gutterMarkers || (line.gutterMarkers = {})
5924 markers[gutterID] = value
5925 if (!value && isEmpty(markers)) { line.gutterMarkers = null }
5926 return true
5927 })
5928 }),
5929
5930 clearGutter: docMethodOp(function(gutterID) {
5931 var this$1 = this;
5932
5933 var i = this.first
5934 this.iter(function (line) {
5935 if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
5936 changeLine(this$1, line, "gutter", function () {
5937 line.gutterMarkers[gutterID] = null
5938 if (isEmpty(line.gutterMarkers)) { line.gutterMarkers = null }
5939 return true
5940 })
5941 }
5942 ++i
5943 })
5944 }),
5945
5946 lineInfo: function(line) {
5947 var n
5948 if (typeof line == "number") {
5949 if (!isLine(this, line)) { return null }
5950 n = line
5951 line = getLine(this, line)
5952 if (!line) { return null }
5953 } else {
5954 n = lineNo(line)
5955 if (n == null) { return null }
5956 }
5957 return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
5958 textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
5959 widgets: line.widgets}
5960 },
5961
5962 addLineClass: docMethodOp(function(handle, where, cls) {
5963 return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
5964 var prop = where == "text" ? "textClass"
5965 : where == "background" ? "bgClass"
5966 : where == "gutter" ? "gutterClass" : "wrapClass"
5967 if (!line[prop]) { line[prop] = cls }
5968 else if (classTest(cls).test(line[prop])) { return false }
5969 else { line[prop] += " " + cls }
5970 return true
5971 })
5972 }),
5973 removeLineClass: docMethodOp(function(handle, where, cls) {
5974 return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
5975 var prop = where == "text" ? "textClass"
5976 : where == "background" ? "bgClass"
5977 : where == "gutter" ? "gutterClass" : "wrapClass"
5978 var cur = line[prop]
5979 if (!cur) { return false }
5980 else if (cls == null) { line[prop] = null }
5981 else {
5982 var found = cur.match(classTest(cls))
5983 if (!found) { return false }
5984 var end = found.index + found[0].length
5985 line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null
5986 }
5987 return true
5988 })
5989 }),
5990
5991 addLineWidget: docMethodOp(function(handle, node, options) {
5992 return addLineWidget(this, handle, node, options)
5993 }),
5994 removeLineWidget: function(widget) { widget.clear() },
5995
5996 markText: function(from, to, options) {
5997 return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range")
5998 },
5999 setBookmark: function(pos, options) {
6000 var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
6001 insertLeft: options && options.insertLeft,
6002 clearWhenEmpty: false, shared: options && options.shared,
6003 handleMouseEvents: options && options.handleMouseEvents}
6004 pos = clipPos(this, pos)
6005 return markText(this, pos, pos, realOpts, "bookmark")
6006 },
6007 findMarksAt: function(pos) {
6008 pos = clipPos(this, pos)
6009 var markers = [], spans = getLine(this, pos.line).markedSpans
6010 if (spans) { for (var i = 0; i < spans.length; ++i) {
6011 var span = spans[i]
6012 if ((span.from == null || span.from <= pos.ch) &&
6013 (span.to == null || span.to >= pos.ch))
6014 { markers.push(span.marker.parent || span.marker) }
6015 } }
6016 return markers
6017 },
6018 findMarks: function(from, to, filter) {
6019 from = clipPos(this, from); to = clipPos(this, to)
6020 var found = [], lineNo$$1 = from.line
6021 this.iter(from.line, to.line + 1, function (line) {
6022 var spans = line.markedSpans
6023 if (spans) { for (var i = 0; i < spans.length; i++) {
6024 var span = spans[i]
6025 if (!(span.to != null && lineNo$$1 == from.line && from.ch >= span.to ||
6026 span.from == null && lineNo$$1 != from.line ||
6027 span.from != null && lineNo$$1 == to.line && span.from >= to.ch) &&
6028 (!filter || filter(span.marker)))
6029 { found.push(span.marker.parent || span.marker) }
6030 } }
6031 ++lineNo$$1
6032 })
6033 return found
6034 },
6035 getAllMarks: function() {
6036 var markers = []
6037 this.iter(function (line) {
6038 var sps = line.markedSpans
6039 if (sps) { for (var i = 0; i < sps.length; ++i)
6040 { if (sps[i].from != null) { markers.push(sps[i].marker) } } }
6041 })
6042 return markers
6043 },
6044
6045 posFromIndex: function(off) {
6046 var ch, lineNo$$1 = this.first, sepSize = this.lineSeparator().length
6047 this.iter(function (line) {
6048 var sz = line.text.length + sepSize
6049 if (sz > off) { ch = off; return true }
6050 off -= sz
6051 ++lineNo$$1
6052 })
6053 return clipPos(this, Pos(lineNo$$1, ch))
6054 },
6055 indexFromPos: function (coords) {
6056 coords = clipPos(this, coords)
6057 var index = coords.ch
6058 if (coords.line < this.first || coords.ch < 0) { return 0 }
6059 var sepSize = this.lineSeparator().length
6060 this.iter(this.first, coords.line, function (line) { // iter aborts when callback returns a truthy value
6061 index += line.text.length + sepSize
6062 })
6063 return index
6064 },
6065
6066 copy: function(copyHistory) {
6067 var doc = new Doc(getLines(this, this.first, this.first + this.size),
6068 this.modeOption, this.first, this.lineSep)
6069 doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft
6070 doc.sel = this.sel
6071 doc.extend = false
6072 if (copyHistory) {
6073 doc.history.undoDepth = this.history.undoDepth
6074 doc.setHistory(this.getHistory())
6075 }
6076 return doc
6077 },
6078
6079 linkedDoc: function(options) {
6080 if (!options) { options = {} }
6081 var from = this.first, to = this.first + this.size
6082 if (options.from != null && options.from > from) { from = options.from }
6083 if (options.to != null && options.to < to) { to = options.to }
6084 var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep)
6085 if (options.sharedHist) { copy.history = this.history
6086 ; }(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist})
6087 copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}]
6088 copySharedMarkers(copy, findSharedMarkers(this))
6089 return copy
6090 },
6091 unlinkDoc: function(other) {
6092 var this$1 = this;
6093
6094 if (other instanceof CodeMirror$1) { other = other.doc }
6095 if (this.linked) { for (var i = 0; i < this.linked.length; ++i) {
6096 var link = this$1.linked[i]
6097 if (link.doc != other) { continue }
6098 this$1.linked.splice(i, 1)
6099 other.unlinkDoc(this$1)
6100 detachSharedMarkers(findSharedMarkers(this$1))
6101 break
6102 } }
6103 // If the histories were shared, split them again
6104 if (other.history == this.history) {
6105 var splitIds = [other.id]
6106 linkedDocs(other, function (doc) { return splitIds.push(doc.id); }, true)
6107 other.history = new History(null)
6108 other.history.done = copyHistoryArray(this.history.done, splitIds)
6109 other.history.undone = copyHistoryArray(this.history.undone, splitIds)
6110 }
6111 },
6112 iterLinkedDocs: function(f) {linkedDocs(this, f)},
6113
6114 getMode: function() {return this.mode},
6115 getEditor: function() {return this.cm},
6116
6117 splitLines: function(str) {
6118 if (this.lineSep) { return str.split(this.lineSep) }
6119 return splitLinesAuto(str)
6120 },
6121 lineSeparator: function() { return this.lineSep || "\n" }
6122 })
6123
6124 // Public alias.
6125 Doc.prototype.eachLine = Doc.prototype.iter
6126
6127 // Kludge to work around strange IE behavior where it'll sometimes
6128 // re-fire a series of drag-related events right after the drop (#1551)
6129 var lastDrop = 0
6130
6131 function onDrop(e) {
6132 var cm = this
6133 clearDragCursor(cm)
6134 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))
6135 { return }
6136 e_preventDefault(e)
6137 if (ie) { lastDrop = +new Date }
6138 var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files
6139 if (!pos || cm.isReadOnly()) { return }
6140 // Might be a file drop, in which case we simply extract the text
6141 // and insert it.
6142 if (files && files.length && window.FileReader && window.File) {
6143 var n = files.length, text = Array(n), read = 0
6144 var loadFile = function (file, i) {
6145 if (cm.options.allowDropFileTypes &&
6146 indexOf(cm.options.allowDropFileTypes, file.type) == -1)
6147 { return }
6148
6149 var reader = new FileReader
6150 reader.onload = operation(cm, function () {
6151 var content = reader.result
6152 if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) { content = "" }
6153 text[i] = content
6154 if (++read == n) {
6155 pos = clipPos(cm.doc, pos)
6156 var change = {from: pos, to: pos,
6157 text: cm.doc.splitLines(text.join(cm.doc.lineSeparator())),
6158 origin: "paste"}
6159 makeChange(cm.doc, change)
6160 setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change)))
6161 }
6162 })
6163 reader.readAsText(file)
6164 }
6165 for (var i = 0; i < n; ++i) { loadFile(files[i], i) }
6166 } else { // Normal drop
6167 // Don't do a replace if the drop happened inside of the selected text.
6168 if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {
6169 cm.state.draggingText(e)
6170 // Ensure the editor is re-focused
6171 setTimeout(function () { return cm.display.input.focus(); }, 20)
6172 return
6173 }
6174 try {
6175 var text$1 = e.dataTransfer.getData("Text")
6176 if (text$1) {
6177 var selected
6178 if (cm.state.draggingText && !cm.state.draggingText.copy)
6179 { selected = cm.listSelections() }
6180 setSelectionNoUndo(cm.doc, simpleSelection(pos, pos))
6181 if (selected) { for (var i$1 = 0; i$1 < selected.length; ++i$1)
6182 { replaceRange(cm.doc, "", selected[i$1].anchor, selected[i$1].head, "drag") } }
6183 cm.replaceSelection(text$1, "around", "paste")
6184 cm.display.input.focus()
6185 }
6186 }
6187 catch(e){}
6188 }
6189 }
6190
6191 function onDragStart(cm, e) {
6192 if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return }
6193 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return }
6194
6195 e.dataTransfer.setData("Text", cm.getSelection())
6196 e.dataTransfer.effectAllowed = "copyMove"
6197
6198 // Use dummy image instead of default browsers image.
6199 // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
6200 if (e.dataTransfer.setDragImage && !safari) {
6201 var img = elt("img", null, null, "position: fixed; left: 0; top: 0;")
6202 img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
6203 if (presto) {
6204 img.width = img.height = 1
6205 cm.display.wrapper.appendChild(img)
6206 // Force a relayout, or Opera won't use our image for some obscure reason
6207 img._top = img.offsetTop
6208 }
6209 e.dataTransfer.setDragImage(img, 0, 0)
6210 if (presto) { img.parentNode.removeChild(img) }
6211 }
6212 }
6213
6214 function onDragOver(cm, e) {
6215 var pos = posFromMouse(cm, e)
6216 if (!pos) { return }
6217 var frag = document.createDocumentFragment()
6218 drawSelectionCursor(cm, pos, frag)
6219 if (!cm.display.dragCursor) {
6220 cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors")
6221 cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv)
6222 }
6223 removeChildrenAndAdd(cm.display.dragCursor, frag)
6224 }
6225
6226 function clearDragCursor(cm) {
6227 if (cm.display.dragCursor) {
6228 cm.display.lineSpace.removeChild(cm.display.dragCursor)
6229 cm.display.dragCursor = null
6230 }
6231 }
6232
6233 // These must be handled carefully, because naively registering a
6234 // handler for each editor will cause the editors to never be
6235 // garbage collected.
6236
6237 function forEachCodeMirror(f) {
6238 if (!document.body.getElementsByClassName) { return }
6239 var byClass = document.body.getElementsByClassName("CodeMirror")
6240 for (var i = 0; i < byClass.length; i++) {
6241 var cm = byClass[i].CodeMirror
6242 if (cm) { f(cm) }
6243 }
6244 }
6245
6246 var globalsRegistered = false
6247 function ensureGlobalHandlers() {
6248 if (globalsRegistered) { return }
6249 registerGlobalHandlers()
6250 globalsRegistered = true
6251 }
6252 function registerGlobalHandlers() {
6253 // When the window resizes, we need to refresh active editors.
6254 var resizeTimer
6255 on(window, "resize", function () {
6256 if (resizeTimer == null) { resizeTimer = setTimeout(function () {
6257 resizeTimer = null
6258 forEachCodeMirror(onResize)
6259 }, 100) }
6260 })
6261 // When the window loses focus, we want to show the editor as blurred
6262 on(window, "blur", function () { return forEachCodeMirror(onBlur); })
6263 }
6264 // Called when the window resizes
6265 function onResize(cm) {
6266 var d = cm.display
6267 if (d.lastWrapHeight == d.wrapper.clientHeight && d.lastWrapWidth == d.wrapper.clientWidth)
6268 { return }
6269 // Might be a text scaling operation, clear size caches.
6270 d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null
6271 d.scrollbarsClipped = false
6272 cm.setSize()
6273 }
6274
6275 var keyNames = {
6276 3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
6277 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
6278 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
6279 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod",
6280 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 127: "Delete",
6281 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
6282 221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete",
6283 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert"
6284 }
6285
6286 // Number keys
6287 for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i) }
6288 // Alphabetic keys
6289 for (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1) }
6290 // Function keys
6291 for (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235] = "F" + i$2 }
6292
6293 var keyMap = {}
6294
6295 keyMap.basic = {
6296 "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
6297 "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
6298 "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore",
6299 "Tab": "defaultTab", "Shift-Tab": "indentAuto",
6300 "Enter": "newlineAndIndent", "Insert": "toggleOverwrite",
6301 "Esc": "singleSelection"
6302 }
6303 // Note that the save and find-related commands aren't defined by
6304 // default. User code or addons can define them. Unknown commands
6305 // are simply ignored.
6306 keyMap.pcDefault = {
6307 "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
6308 "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown",
6309 "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
6310 "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
6311 "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
6312 "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
6313 "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection",
6314 fallthrough: "basic"
6315 }
6316 // Very basic readline/emacs-style bindings, which are standard on Mac.
6317 keyMap.emacsy = {
6318 "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
6319 "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
6320 "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
6321 "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars",
6322 "Ctrl-O": "openLine"
6323 }
6324 keyMap.macDefault = {
6325 "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
6326 "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
6327 "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore",
6328 "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
6329 "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
6330 "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight",
6331 "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd",
6332 fallthrough: ["basic", "emacsy"]
6333 }
6334 keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault
6335
6336 // KEYMAP DISPATCH
6337
6338 function normalizeKeyName(name) {
6339 var parts = name.split(/-(?!$)/)
6340 name = parts[parts.length - 1]
6341 var alt, ctrl, shift, cmd
6342 for (var i = 0; i < parts.length - 1; i++) {
6343 var mod = parts[i]
6344 if (/^(cmd|meta|m)$/i.test(mod)) { cmd = true }
6345 else if (/^a(lt)?$/i.test(mod)) { alt = true }
6346 else if (/^(c|ctrl|control)$/i.test(mod)) { ctrl = true }
6347 else if (/^s(hift)?$/i.test(mod)) { shift = true }
6348 else { throw new Error("Unrecognized modifier name: " + mod) }
6349 }
6350 if (alt) { name = "Alt-" + name }
6351 if (ctrl) { name = "Ctrl-" + name }
6352 if (cmd) { name = "Cmd-" + name }
6353 if (shift) { name = "Shift-" + name }
6354 return name
6355 }
6356
6357 // This is a kludge to keep keymaps mostly working as raw objects
6358 // (backwards compatibility) while at the same time support features
6359 // like normalization and multi-stroke key bindings. It compiles a
6360 // new normalized keymap, and then updates the old object to reflect
6361 // this.
6362 function normalizeKeyMap(keymap) {
6363 var copy = {}
6364 for (var keyname in keymap) { if (keymap.hasOwnProperty(keyname)) {
6365 var value = keymap[keyname]
6366 if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) { continue }
6367 if (value == "...") { delete keymap[keyname]; continue }
6368
6369 var keys = map(keyname.split(" "), normalizeKeyName)
6370 for (var i = 0; i < keys.length; i++) {
6371 var val = void 0, name = void 0
6372 if (i == keys.length - 1) {
6373 name = keys.join(" ")
6374 val = value
6375 } else {
6376 name = keys.slice(0, i + 1).join(" ")
6377 val = "..."
6378 }
6379 var prev = copy[name]
6380 if (!prev) { copy[name] = val }
6381 else if (prev != val) { throw new Error("Inconsistent bindings for " + name) }
6382 }
6383 delete keymap[keyname]
6384 } }
6385 for (var prop in copy) { keymap[prop] = copy[prop] }
6386 return keymap
6387 }
6388
6389 function lookupKey(key, map$$1, handle, context) {
6390 map$$1 = getKeyMap(map$$1)
6391 var found = map$$1.call ? map$$1.call(key, context) : map$$1[key]
6392 if (found === false) { return "nothing" }
6393 if (found === "...") { return "multi" }
6394 if (found != null && handle(found)) { return "handled" }
6395
6396 if (map$$1.fallthrough) {
6397 if (Object.prototype.toString.call(map$$1.fallthrough) != "[object Array]")
6398 { return lookupKey(key, map$$1.fallthrough, handle, context) }
6399 for (var i = 0; i < map$$1.fallthrough.length; i++) {
6400 var result = lookupKey(key, map$$1.fallthrough[i], handle, context)
6401 if (result) { return result }
6402 }
6403 }
6404 }
6405
6406 // Modifier key presses don't count as 'real' key presses for the
6407 // purpose of keymap fallthrough.
6408 function isModifierKey(value) {
6409 var name = typeof value == "string" ? value : keyNames[value.keyCode]
6410 return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod"
6411 }
6412
6413 // Look up the name of a key as indicated by an event object.
6414 function keyName(event, noShift) {
6415 if (presto && event.keyCode == 34 && event["char"]) { return false }
6416 var base = keyNames[event.keyCode], name = base
6417 if (name == null || event.altGraphKey) { return false }
6418 if (event.altKey && base != "Alt") { name = "Alt-" + name }
6419 if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") { name = "Ctrl-" + name }
6420 if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") { name = "Cmd-" + name }
6421 if (!noShift && event.shiftKey && base != "Shift") { name = "Shift-" + name }
6422 return name
6423 }
6424
6425 function getKeyMap(val) {
6426 return typeof val == "string" ? keyMap[val] : val
6427 }
6428
6429 // Helper for deleting text near the selection(s), used to implement
6430 // backspace, delete, and similar functionality.
6431 function deleteNearSelection(cm, compute) {
6432 var ranges = cm.doc.sel.ranges, kill = []
6433 // Build up a set of ranges to kill first, merging overlapping
6434 // ranges.
6435 for (var i = 0; i < ranges.length; i++) {
6436 var toKill = compute(ranges[i])
6437 while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {
6438 var replaced = kill.pop()
6439 if (cmp(replaced.from, toKill.from) < 0) {
6440 toKill.from = replaced.from
6441 break
6442 }
6443 }
6444 kill.push(toKill)
6445 }
6446 // Next, remove those actual ranges.
6447 runInOp(cm, function () {
6448 for (var i = kill.length - 1; i >= 0; i--)
6449 { replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete") }
6450 ensureCursorVisible(cm)
6451 })
6452 }
6453
6454 // Commands are parameter-less actions that can be performed on an
6455 // editor, mostly used for keybindings.
6456 var commands = {
6457 selectAll: selectAll,
6458 singleSelection: function (cm) { return cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll); },
6459 killLine: function (cm) { return deleteNearSelection(cm, function (range) {
6460 if (range.empty()) {
6461 var len = getLine(cm.doc, range.head.line).text.length
6462 if (range.head.ch == len && range.head.line < cm.lastLine())
6463 { return {from: range.head, to: Pos(range.head.line + 1, 0)} }
6464 else
6465 { return {from: range.head, to: Pos(range.head.line, len)} }
6466 } else {
6467 return {from: range.from(), to: range.to()}
6468 }
6469 }); },
6470 deleteLine: function (cm) { return deleteNearSelection(cm, function (range) { return ({
6471 from: Pos(range.from().line, 0),
6472 to: clipPos(cm.doc, Pos(range.to().line + 1, 0))
6473 }); }); },
6474 delLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { return ({
6475 from: Pos(range.from().line, 0), to: range.from()
6476 }); }); },
6477 delWrappedLineLeft: function (cm) { return deleteNearSelection(cm, function (range) {
6478 var top = cm.charCoords(range.head, "div").top + 5
6479 var leftPos = cm.coordsChar({left: 0, top: top}, "div")
6480 return {from: leftPos, to: range.from()}
6481 }); },
6482 delWrappedLineRight: function (cm) { return deleteNearSelection(cm, function (range) {
6483 var top = cm.charCoords(range.head, "div").top + 5
6484 var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div")
6485 return {from: range.from(), to: rightPos }
6486 }); },
6487 undo: function (cm) { return cm.undo(); },
6488 redo: function (cm) { return cm.redo(); },
6489 undoSelection: function (cm) { return cm.undoSelection(); },
6490 redoSelection: function (cm) { return cm.redoSelection(); },
6491 goDocStart: function (cm) { return cm.extendSelection(Pos(cm.firstLine(), 0)); },
6492 goDocEnd: function (cm) { return cm.extendSelection(Pos(cm.lastLine())); },
6493 goLineStart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStart(cm, range.head.line); },
6494 {origin: "+move", bias: 1}
6495 ); },
6496 goLineStartSmart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStartSmart(cm, range.head); },
6497 {origin: "+move", bias: 1}
6498 ); },
6499 goLineEnd: function (cm) { return cm.extendSelectionsBy(function (range) { return lineEnd(cm, range.head.line); },
6500 {origin: "+move", bias: -1}
6501 ); },
6502 goLineRight: function (cm) { return cm.extendSelectionsBy(function (range) {
6503 var top = cm.charCoords(range.head, "div").top + 5
6504 return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div")
6505 }, sel_move); },
6506 goLineLeft: function (cm) { return cm.extendSelectionsBy(function (range) {
6507 var top = cm.charCoords(range.head, "div").top + 5
6508 return cm.coordsChar({left: 0, top: top}, "div")
6509 }, sel_move); },
6510 goLineLeftSmart: function (cm) { return cm.extendSelectionsBy(function (range) {
6511 var top = cm.charCoords(range.head, "div").top + 5
6512 var pos = cm.coordsChar({left: 0, top: top}, "div")
6513 if (pos.ch < cm.getLine(pos.line).search(/\S/)) { return lineStartSmart(cm, range.head) }
6514 return pos
6515 }, sel_move); },
6516 goLineUp: function (cm) { return cm.moveV(-1, "line"); },
6517 goLineDown: function (cm) { return cm.moveV(1, "line"); },
6518 goPageUp: function (cm) { return cm.moveV(-1, "page"); },
6519 goPageDown: function (cm) { return cm.moveV(1, "page"); },
6520 goCharLeft: function (cm) { return cm.moveH(-1, "char"); },
6521 goCharRight: function (cm) { return cm.moveH(1, "char"); },
6522 goColumnLeft: function (cm) { return cm.moveH(-1, "column"); },
6523 goColumnRight: function (cm) { return cm.moveH(1, "column"); },
6524 goWordLeft: function (cm) { return cm.moveH(-1, "word"); },
6525 goGroupRight: function (cm) { return cm.moveH(1, "group"); },
6526 goGroupLeft: function (cm) { return cm.moveH(-1, "group"); },
6527 goWordRight: function (cm) { return cm.moveH(1, "word"); },
6528 delCharBefore: function (cm) { return cm.deleteH(-1, "char"); },
6529 delCharAfter: function (cm) { return cm.deleteH(1, "char"); },
6530 delWordBefore: function (cm) { return cm.deleteH(-1, "word"); },
6531 delWordAfter: function (cm) { return cm.deleteH(1, "word"); },
6532 delGroupBefore: function (cm) { return cm.deleteH(-1, "group"); },
6533 delGroupAfter: function (cm) { return cm.deleteH(1, "group"); },
6534 indentAuto: function (cm) { return cm.indentSelection("smart"); },
6535 indentMore: function (cm) { return cm.indentSelection("add"); },
6536 indentLess: function (cm) { return cm.indentSelection("subtract"); },
6537 insertTab: function (cm) { return cm.replaceSelection("\t"); },
6538 insertSoftTab: function (cm) {
6539 var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize
6540 for (var i = 0; i < ranges.length; i++) {
6541 var pos = ranges[i].from()
6542 var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize)
6543 spaces.push(spaceStr(tabSize - col % tabSize))
6544 }
6545 cm.replaceSelections(spaces)
6546 },
6547 defaultTab: function (cm) {
6548 if (cm.somethingSelected()) { cm.indentSelection("add") }
6549 else { cm.execCommand("insertTab") }
6550 },
6551 // Swap the two chars left and right of each selection's head.
6552 // Move cursor behind the two swapped characters afterwards.
6553 //
6554 // Doesn't consider line feeds a character.
6555 // Doesn't scan more than one line above to find a character.
6556 // Doesn't do anything on an empty line.
6557 // Doesn't do anything with non-empty selections.
6558 transposeChars: function (cm) { return runInOp(cm, function () {
6559 var ranges = cm.listSelections(), newSel = []
6560 for (var i = 0; i < ranges.length; i++) {
6561 if (!ranges[i].empty()) { continue }
6562 var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text
6563 if (line) {
6564 if (cur.ch == line.length) { cur = new Pos(cur.line, cur.ch - 1) }
6565 if (cur.ch > 0) {
6566 cur = new Pos(cur.line, cur.ch + 1)
6567 cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),
6568 Pos(cur.line, cur.ch - 2), cur, "+transpose")
6569 } else if (cur.line > cm.doc.first) {
6570 var prev = getLine(cm.doc, cur.line - 1).text
6571 if (prev) {
6572 cur = new Pos(cur.line, 1)
6573 cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() +
6574 prev.charAt(prev.length - 1),
6575 Pos(cur.line - 1, prev.length - 1), cur, "+transpose")
6576 }
6577 }
6578 }
6579 newSel.push(new Range(cur, cur))
6580 }
6581 cm.setSelections(newSel)
6582 }); },
6583 newlineAndIndent: function (cm) { return runInOp(cm, function () {
6584 var sels = cm.listSelections()
6585 for (var i = sels.length - 1; i >= 0; i--)
6586 { cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input") }
6587 sels = cm.listSelections()
6588 for (var i$1 = 0; i$1 < sels.length; i$1++)
6589 { cm.indentLine(sels[i$1].from().line, null, true) }
6590 ensureCursorVisible(cm)
6591 }); },
6592 openLine: function (cm) { return cm.replaceSelection("\n", "start"); },
6593 toggleOverwrite: function (cm) { return cm.toggleOverwrite(); }
6594 }
6595
6596
6597 function lineStart(cm, lineN) {
6598 var line = getLine(cm.doc, lineN)
6599 var visual = visualLine(line)
6600 if (visual != line) { lineN = lineNo(visual) }
6601 var order = getOrder(visual)
6602 var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual)
6603 return Pos(lineN, ch)
6604 }
6605 function lineEnd(cm, lineN) {
6606 var merged, line = getLine(cm.doc, lineN)
6607 while (merged = collapsedSpanAtEnd(line)) {
6608 line = merged.find(1, true).line
6609 lineN = null
6610 }
6611 var order = getOrder(line)
6612 var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line)
6613 return Pos(lineN == null ? lineNo(line) : lineN, ch)
6614 }
6615 function lineStartSmart(cm, pos) {
6616 var start = lineStart(cm, pos.line)
6617 var line = getLine(cm.doc, start.line)
6618 var order = getOrder(line)
6619 if (!order || order[0].level == 0) {
6620 var firstNonWS = Math.max(0, line.text.search(/\S/))
6621 var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch
6622 return Pos(start.line, inWS ? 0 : firstNonWS)
6623 }
6624 return start
6625 }
6626
6627 // Run a handler that was bound to a key.
6628 function doHandleBinding(cm, bound, dropShift) {
6629 if (typeof bound == "string") {
6630 bound = commands[bound]
6631 if (!bound) { return false }
6632 }
6633 // Ensure previous input has been read, so that the handler sees a
6634 // consistent view of the document
6635 cm.display.input.ensurePolled()
6636 var prevShift = cm.display.shift, done = false
6637 try {
6638 if (cm.isReadOnly()) { cm.state.suppressEdits = true }
6639 if (dropShift) { cm.display.shift = false }
6640 done = bound(cm) != Pass
6641 } finally {
6642 cm.display.shift = prevShift
6643 cm.state.suppressEdits = false
6644 }
6645 return done
6646 }
6647
6648 function lookupKeyForEditor(cm, name, handle) {
6649 for (var i = 0; i < cm.state.keyMaps.length; i++) {
6650 var result = lookupKey(name, cm.state.keyMaps[i], handle, cm)
6651 if (result) { return result }
6652 }
6653 return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm))
6654 || lookupKey(name, cm.options.keyMap, handle, cm)
6655 }
6656
6657 var stopSeq = new Delayed
6658 function dispatchKey(cm, name, e, handle) {
6659 var seq = cm.state.keySeq
6660 if (seq) {
6661 if (isModifierKey(name)) { return "handled" }
6662 stopSeq.set(50, function () {
6663 if (cm.state.keySeq == seq) {
6664 cm.state.keySeq = null
6665 cm.display.input.reset()
6666 }
6667 })
6668 name = seq + " " + name
6669 }
6670 var result = lookupKeyForEditor(cm, name, handle)
6671
6672 if (result == "multi")
6673 { cm.state.keySeq = name }
6674 if (result == "handled")
6675 { signalLater(cm, "keyHandled", cm, name, e) }
6676
6677 if (result == "handled" || result == "multi") {
6678 e_preventDefault(e)
6679 restartBlink(cm)
6680 }
6681
6682 if (seq && !result && /\'$/.test(name)) {
6683 e_preventDefault(e)
6684 return true
6685 }
6686 return !!result
6687 }
6688
6689 // Handle a key from the keydown event.
6690 function handleKeyBinding(cm, e) {
6691 var name = keyName(e, true)
6692 if (!name) { return false }
6693
6694 if (e.shiftKey && !cm.state.keySeq) {
6695 // First try to resolve full name (including 'Shift-'). Failing
6696 // that, see if there is a cursor-motion command (starting with
6697 // 'go') bound to the keyname without 'Shift-'.
6698 return dispatchKey(cm, "Shift-" + name, e, function (b) { return doHandleBinding(cm, b, true); })
6699 || dispatchKey(cm, name, e, function (b) {
6700 if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
6701 { return doHandleBinding(cm, b) }
6702 })
6703 } else {
6704 return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b); })
6705 }
6706 }
6707
6708 // Handle a key from the keypress event
6709 function handleCharBinding(cm, e, ch) {
6710 return dispatchKey(cm, "'" + ch + "'", e, function (b) { return doHandleBinding(cm, b, true); })
6711 }
6712
6713 var lastStoppedKey = null
6714 function onKeyDown(e) {
6715 var cm = this
6716 cm.curOp.focus = activeElt()
6717 if (signalDOMEvent(cm, e)) { return }
6718 // IE does strange things with escape.
6719 if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false }
6720 var code = e.keyCode
6721 cm.display.shift = code == 16 || e.shiftKey
6722 var handled = handleKeyBinding(cm, e)
6723 if (presto) {
6724 lastStoppedKey = handled ? code : null
6725 // Opera has no cut event... we try to at least catch the key combo
6726 if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
6727 { cm.replaceSelection("", null, "cut") }
6728 }
6729
6730 // Turn mouse into crosshair when Alt is held on Mac.
6731 if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className))
6732 { showCrossHair(cm) }
6733 }
6734
6735 function showCrossHair(cm) {
6736 var lineDiv = cm.display.lineDiv
6737 addClass(lineDiv, "CodeMirror-crosshair")
6738
6739 function up(e) {
6740 if (e.keyCode == 18 || !e.altKey) {
6741 rmClass(lineDiv, "CodeMirror-crosshair")
6742 off(document, "keyup", up)
6743 off(document, "mouseover", up)
6744 }
6745 }
6746 on(document, "keyup", up)
6747 on(document, "mouseover", up)
6748 }
6749
6750 function onKeyUp(e) {
6751 if (e.keyCode == 16) { this.doc.sel.shift = false }
6752 signalDOMEvent(this, e)
6753 }
6754
6755 function onKeyPress(e) {
6756 var cm = this
6757 if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return }
6758 var keyCode = e.keyCode, charCode = e.charCode
6759 if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return}
6760 if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) { return }
6761 var ch = String.fromCharCode(charCode == null ? keyCode : charCode)
6762 // Some browsers fire keypress events for backspace
6763 if (ch == "\x08") { return }
6764 if (handleCharBinding(cm, e, ch)) { return }
6765 cm.display.input.onKeyPress(e)
6766 }
6767
6768 // A mouse down can be a single click, double click, triple click,
6769 // start of selection drag, start of text drag, new cursor
6770 // (ctrl-click), rectangle drag (alt-drag), or xwin
6771 // middle-click-paste. Or it might be a click on something we should
6772 // not interfere with, such as a scrollbar or widget.
6773 function onMouseDown(e) {
6774 var cm = this, display = cm.display
6775 if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) { return }
6776 display.input.ensurePolled()
6777 display.shift = e.shiftKey
6778
6779 if (eventInWidget(display, e)) {
6780 if (!webkit) {
6781 // Briefly turn off draggability, to allow widgets to do
6782 // normal dragging things.
6783 display.scroller.draggable = false
6784 setTimeout(function () { return display.scroller.draggable = true; }, 100)
6785 }
6786 return
6787 }
6788 if (clickInGutter(cm, e)) { return }
6789 var start = posFromMouse(cm, e)
6790 window.focus()
6791
6792 switch (e_button(e)) {
6793 case 1:
6794 // #3261: make sure, that we're not starting a second selection
6795 if (cm.state.selectingText)
6796 { cm.state.selectingText(e) }
6797 else if (start)
6798 { leftButtonDown(cm, e, start) }
6799 else if (e_target(e) == display.scroller)
6800 { e_preventDefault(e) }
6801 break
6802 case 2:
6803 if (webkit) { cm.state.lastMiddleDown = +new Date }
6804 if (start) { extendSelection(cm.doc, start) }
6805 setTimeout(function () { return display.input.focus(); }, 20)
6806 e_preventDefault(e)
6807 break
6808 case 3:
6809 if (captureRightClick) { onContextMenu(cm, e) }
6810 else { delayBlurEvent(cm) }
6811 break
6812 }
6813 }
6814
6815 var lastClick;
6816 var lastDoubleClick
6817 function leftButtonDown(cm, e, start) {
6818 if (ie) { setTimeout(bind(ensureFocus, cm), 0) }
6819 else { cm.curOp.focus = activeElt() }
6820
6821 var now = +new Date, type
6822 if (lastDoubleClick && lastDoubleClick.time > now - 400 && cmp(lastDoubleClick.pos, start) == 0) {
6823 type = "triple"
6824 } else if (lastClick && lastClick.time > now - 400 && cmp(lastClick.pos, start) == 0) {
6825 type = "double"
6826 lastDoubleClick = {time: now, pos: start}
6827 } else {
6828 type = "single"
6829 lastClick = {time: now, pos: start}
6830 }
6831
6832 var sel = cm.doc.sel, modifier = mac ? e.metaKey : e.ctrlKey, contained
6833 if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() &&
6834 type == "single" && (contained = sel.contains(start)) > -1 &&
6835 (cmp((contained = sel.ranges[contained]).from(), start) < 0 || start.xRel > 0) &&
6836 (cmp(contained.to(), start) > 0 || start.xRel < 0))
6837 { leftButtonStartDrag(cm, e, start, modifier) }
6838 else
6839 { leftButtonSelect(cm, e, start, type, modifier) }
6840 }
6841
6842 // Start a text drag. When it ends, see if any dragging actually
6843 // happen, and treat as a click if it didn't.
6844 function leftButtonStartDrag(cm, e, start, modifier) {
6845 var display = cm.display, startTime = +new Date
6846 var dragEnd = operation(cm, function (e2) {
6847 if (webkit) { display.scroller.draggable = false }
6848 cm.state.draggingText = false
6849 off(document, "mouseup", dragEnd)
6850 off(display.scroller, "drop", dragEnd)
6851 if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
6852 e_preventDefault(e2)
6853 if (!modifier && +new Date - 200 < startTime)
6854 { extendSelection(cm.doc, start) }
6855 // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)
6856 if (webkit || ie && ie_version == 9)
6857 { setTimeout(function () {document.body.focus(); display.input.focus()}, 20) }
6858 else
6859 { display.input.focus() }
6860 }
6861 })
6862 // Let the drag handler handle this.
6863 if (webkit) { display.scroller.draggable = true }
6864 cm.state.draggingText = dragEnd
6865 dragEnd.copy = mac ? e.altKey : e.ctrlKey
6866 // IE's approach to draggable
6867 if (display.scroller.dragDrop) { display.scroller.dragDrop() }
6868 on(document, "mouseup", dragEnd)
6869 on(display.scroller, "drop", dragEnd)
6870 }
6871
6872 // Normal selection, as opposed to text dragging.
6873 function leftButtonSelect(cm, e, start, type, addNew) {
6874 var display = cm.display, doc = cm.doc
6875 e_preventDefault(e)
6876
6877 var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges
6878 if (addNew && !e.shiftKey) {
6879 ourIndex = doc.sel.contains(start)
6880 if (ourIndex > -1)
6881 { ourRange = ranges[ourIndex] }
6882 else
6883 { ourRange = new Range(start, start) }
6884 } else {
6885 ourRange = doc.sel.primary()
6886 ourIndex = doc.sel.primIndex
6887 }
6888
6889 if (chromeOS ? e.shiftKey && e.metaKey : e.altKey) {
6890 type = "rect"
6891 if (!addNew) { ourRange = new Range(start, start) }
6892 start = posFromMouse(cm, e, true, true)
6893 ourIndex = -1
6894 } else if (type == "double") {
6895 var word = cm.findWordAt(start)
6896 if (cm.display.shift || doc.extend)
6897 { ourRange = extendRange(doc, ourRange, word.anchor, word.head) }
6898 else
6899 { ourRange = word }
6900 } else if (type == "triple") {
6901 var line = new Range(Pos(start.line, 0), clipPos(doc, Pos(start.line + 1, 0)))
6902 if (cm.display.shift || doc.extend)
6903 { ourRange = extendRange(doc, ourRange, line.anchor, line.head) }
6904 else
6905 { ourRange = line }
6906 } else {
6907 ourRange = extendRange(doc, ourRange, start)
6908 }
6909
6910 if (!addNew) {
6911 ourIndex = 0
6912 setSelection(doc, new Selection([ourRange], 0), sel_mouse)
6913 startSel = doc.sel
6914 } else if (ourIndex == -1) {
6915 ourIndex = ranges.length
6916 setSelection(doc, normalizeSelection(ranges.concat([ourRange]), ourIndex),
6917 {scroll: false, origin: "*mouse"})
6918 } else if (ranges.length > 1 && ranges[ourIndex].empty() && type == "single" && !e.shiftKey) {
6919 setSelection(doc, normalizeSelection(ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0),
6920 {scroll: false, origin: "*mouse"})
6921 startSel = doc.sel
6922 } else {
6923 replaceOneSelection(doc, ourIndex, ourRange, sel_mouse)
6924 }
6925
6926 var lastPos = start
6927 function extendTo(pos) {
6928 if (cmp(lastPos, pos) == 0) { return }
6929 lastPos = pos
6930
6931 if (type == "rect") {
6932 var ranges = [], tabSize = cm.options.tabSize
6933 var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize)
6934 var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize)
6935 var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol)
6936 for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line));
6937 line <= end; line++) {
6938 var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize)
6939 if (left == right)
6940 { ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))) }
6941 else if (text.length > leftPos)
6942 { ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))) }
6943 }
6944 if (!ranges.length) { ranges.push(new Range(start, start)) }
6945 setSelection(doc, normalizeSelection(startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex),
6946 {origin: "*mouse", scroll: false})
6947 cm.scrollIntoView(pos)
6948 } else {
6949 var oldRange = ourRange
6950 var anchor = oldRange.anchor, head = pos
6951 if (type != "single") {
6952 var range$$1
6953 if (type == "double")
6954 { range$$1 = cm.findWordAt(pos) }
6955 else
6956 { range$$1 = new Range(Pos(pos.line, 0), clipPos(doc, Pos(pos.line + 1, 0))) }
6957 if (cmp(range$$1.anchor, anchor) > 0) {
6958 head = range$$1.head
6959 anchor = minPos(oldRange.from(), range$$1.anchor)
6960 } else {
6961 head = range$$1.anchor
6962 anchor = maxPos(oldRange.to(), range$$1.head)
6963 }
6964 }
6965 var ranges$1 = startSel.ranges.slice(0)
6966 ranges$1[ourIndex] = new Range(clipPos(doc, anchor), head)
6967 setSelection(doc, normalizeSelection(ranges$1, ourIndex), sel_mouse)
6968 }
6969 }
6970
6971 var editorSize = display.wrapper.getBoundingClientRect()
6972 // Used to ensure timeout re-tries don't fire when another extend
6973 // happened in the meantime (clearTimeout isn't reliable -- at
6974 // least on Chrome, the timeouts still happen even when cleared,
6975 // if the clear happens after their scheduled firing time).
6976 var counter = 0
6977
6978 function extend(e) {
6979 var curCount = ++counter
6980 var cur = posFromMouse(cm, e, true, type == "rect")
6981 if (!cur) { return }
6982 if (cmp(cur, lastPos) != 0) {
6983 cm.curOp.focus = activeElt()
6984 extendTo(cur)
6985 var visible = visibleLines(display, doc)
6986 if (cur.line >= visible.to || cur.line < visible.from)
6987 { setTimeout(operation(cm, function () {if (counter == curCount) { extend(e) }}), 150) }
6988 } else {
6989 var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0
6990 if (outside) { setTimeout(operation(cm, function () {
6991 if (counter != curCount) { return }
6992 display.scroller.scrollTop += outside
6993 extend(e)
6994 }), 50) }
6995 }
6996 }
6997
6998 function done(e) {
6999 cm.state.selectingText = false
7000 counter = Infinity
7001 e_preventDefault(e)
7002 display.input.focus()
7003 off(document, "mousemove", move)
7004 off(document, "mouseup", up)
7005 doc.history.lastSelOrigin = null
7006 }
7007
7008 var move = operation(cm, function (e) {
7009 if (!e_button(e)) { done(e) }
7010 else { extend(e) }
7011 })
7012 var up = operation(cm, done)
7013 cm.state.selectingText = up
7014 on(document, "mousemove", move)
7015 on(document, "mouseup", up)
7016 }
7017
7018
7019 // Determines whether an event happened in the gutter, and fires the
7020 // handlers for the corresponding event.
7021 function gutterEvent(cm, e, type, prevent) {
7022 var mX, mY
7023 try { mX = e.clientX; mY = e.clientY }
7024 catch(e) { return false }
7025 if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false }
7026 if (prevent) { e_preventDefault(e) }
7027
7028 var display = cm.display
7029 var lineBox = display.lineDiv.getBoundingClientRect()
7030
7031 if (mY > lineBox.bottom || !hasHandler(cm, type)) { return e_defaultPrevented(e) }
7032 mY -= lineBox.top - display.viewOffset
7033
7034 for (var i = 0; i < cm.options.gutters.length; ++i) {
7035 var g = display.gutters.childNodes[i]
7036 if (g && g.getBoundingClientRect().right >= mX) {
7037 var line = lineAtHeight(cm.doc, mY)
7038 var gutter = cm.options.gutters[i]
7039 signal(cm, type, cm, line, gutter, e)
7040 return e_defaultPrevented(e)
7041 }
7042 }
7043 }
7044
7045 function clickInGutter(cm, e) {
7046 return gutterEvent(cm, e, "gutterClick", true)
7047 }
7048
7049 // CONTEXT MENU HANDLING
7050
7051 // To make the context menu work, we need to briefly unhide the
7052 // textarea (making it as unobtrusive as possible) to let the
7053 // right-click take effect on it.
7054 function onContextMenu(cm, e) {
7055 if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) { return }
7056 if (signalDOMEvent(cm, e, "contextmenu")) { return }
7057 cm.display.input.onContextMenu(e)
7058 }
7059
7060 function contextMenuInGutter(cm, e) {
7061 if (!hasHandler(cm, "gutterContextMenu")) { return false }
7062 return gutterEvent(cm, e, "gutterContextMenu", false)
7063 }
7064
7065 function themeChanged(cm) {
7066 cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
7067 cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-")
7068 clearCaches(cm)
7069 }
7070
7071 var Init = {toString: function(){return "CodeMirror.Init"}}
7072
7073 var defaults = {}
7074 var optionHandlers = {}
7075
7076 function defineOptions(CodeMirror) {
7077 var optionHandlers = CodeMirror.optionHandlers
7078
7079 function option(name, deflt, handle, notOnInit) {
7080 CodeMirror.defaults[name] = deflt
7081 if (handle) { optionHandlers[name] =
7082 notOnInit ? function (cm, val, old) {if (old != Init) { handle(cm, val, old) }} : handle }
7083 }
7084
7085 CodeMirror.defineOption = option
7086
7087 // Passed to option handlers when there is no old value.
7088 CodeMirror.Init = Init
7089
7090 // These two are, on init, called from the constructor because they
7091 // have to be initialized before the editor can start at all.
7092 option("value", "", function (cm, val) { return cm.setValue(val); }, true)
7093 option("mode", null, function (cm, val) {
7094 cm.doc.modeOption = val
7095 loadMode(cm)
7096 }, true)
7097
7098 option("indentUnit", 2, loadMode, true)
7099 option("indentWithTabs", false)
7100 option("smartIndent", true)
7101 option("tabSize", 4, function (cm) {
7102 resetModeState(cm)
7103 clearCaches(cm)
7104 regChange(cm)
7105 }, true)
7106 option("lineSeparator", null, function (cm, val) {
7107 cm.doc.lineSep = val
7108 if (!val) { return }
7109 var newBreaks = [], lineNo = cm.doc.first
7110 cm.doc.iter(function (line) {
7111 for (var pos = 0;;) {
7112 var found = line.text.indexOf(val, pos)
7113 if (found == -1) { break }
7114 pos = found + val.length
7115 newBreaks.push(Pos(lineNo, found))
7116 }
7117 lineNo++
7118 })
7119 for (var i = newBreaks.length - 1; i >= 0; i--)
7120 { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)) }
7121 })
7122 option("specialChars", /[\u0000-\u001f\u007f\u00ad\u200b-\u200f\u2028\u2029\ufeff]/g, function (cm, val, old) {
7123 cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g")
7124 if (old != Init) { cm.refresh() }
7125 })
7126 option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function (cm) { return cm.refresh(); }, true)
7127 option("electricChars", true)
7128 option("inputStyle", mobile ? "contenteditable" : "textarea", function () {
7129 throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME
7130 }, true)
7131 option("spellcheck", false, function (cm, val) { return cm.getInputField().spellcheck = val; }, true)
7132 option("rtlMoveVisually", !windows)
7133 option("wholeLineUpdateBefore", true)
7134
7135 option("theme", "default", function (cm) {
7136 themeChanged(cm)
7137 guttersChanged(cm)
7138 }, true)
7139 option("keyMap", "default", function (cm, val, old) {
7140 var next = getKeyMap(val)
7141 var prev = old != Init && getKeyMap(old)
7142 if (prev && prev.detach) { prev.detach(cm, next) }
7143 if (next.attach) { next.attach(cm, prev || null) }
7144 })
7145 option("extraKeys", null)
7146
7147 option("lineWrapping", false, wrappingChanged, true)
7148 option("gutters", [], function (cm) {
7149 setGuttersForLineNumbers(cm.options)
7150 guttersChanged(cm)
7151 }, true)
7152 option("fixedGutter", true, function (cm, val) {
7153 cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0"
7154 cm.refresh()
7155 }, true)
7156 option("coverGutterNextToScrollbar", false, function (cm) { return updateScrollbars(cm); }, true)
7157 option("scrollbarStyle", "native", function (cm) {
7158 initScrollbars(cm)
7159 updateScrollbars(cm)
7160 cm.display.scrollbars.setScrollTop(cm.doc.scrollTop)
7161 cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft)
7162 }, true)
7163 option("lineNumbers", false, function (cm) {
7164 setGuttersForLineNumbers(cm.options)
7165 guttersChanged(cm)
7166 }, true)
7167 option("firstLineNumber", 1, guttersChanged, true)
7168 option("lineNumberFormatter", function (integer) { return integer; }, guttersChanged, true)
7169 option("showCursorWhenSelecting", false, updateSelection, true)
7170
7171 option("resetSelectionOnContextMenu", true)
7172 option("lineWiseCopyCut", true)
7173
7174 option("readOnly", false, function (cm, val) {
7175 if (val == "nocursor") {
7176 onBlur(cm)
7177 cm.display.input.blur()
7178 cm.display.disabled = true
7179 } else {
7180 cm.display.disabled = false
7181 }
7182 cm.display.input.readOnlyChanged(val)
7183 })
7184 option("disableInput", false, function (cm, val) {if (!val) { cm.display.input.reset() }}, true)
7185 option("dragDrop", true, dragDropChanged)
7186 option("allowDropFileTypes", null)
7187
7188 option("cursorBlinkRate", 530)
7189 option("cursorScrollMargin", 0)
7190 option("cursorHeight", 1, updateSelection, true)
7191 option("singleCursorHeightPerLine", true, updateSelection, true)
7192 option("workTime", 100)
7193 option("workDelay", 100)
7194 option("flattenSpans", true, resetModeState, true)
7195 option("addModeClass", false, resetModeState, true)
7196 option("pollInterval", 100)
7197 option("undoDepth", 200, function (cm, val) { return cm.doc.history.undoDepth = val; })
7198 option("historyEventDelay", 1250)
7199 option("viewportMargin", 10, function (cm) { return cm.refresh(); }, true)
7200 option("maxHighlightLength", 10000, resetModeState, true)
7201 option("moveInputWithCursor", true, function (cm, val) {
7202 if (!val) { cm.display.input.resetPosition() }
7203 })
7204
7205 option("tabindex", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || ""; })
7206 option("autofocus", null)
7207 }
7208
7209 function guttersChanged(cm) {
7210 updateGutters(cm)
7211 regChange(cm)
7212 setTimeout(function () { return alignHorizontally(cm); }, 20)
7213 }
7214
7215 function dragDropChanged(cm, value, old) {
7216 var wasOn = old && old != Init
7217 if (!value != !wasOn) {
7218 var funcs = cm.display.dragFunctions
7219 var toggle = value ? on : off
7220 toggle(cm.display.scroller, "dragstart", funcs.start)
7221 toggle(cm.display.scroller, "dragenter", funcs.enter)
7222 toggle(cm.display.scroller, "dragover", funcs.over)
7223 toggle(cm.display.scroller, "dragleave", funcs.leave)
7224 toggle(cm.display.scroller, "drop", funcs.drop)
7225 }
7226 }
7227
7228 function wrappingChanged(cm) {
7229 if (cm.options.lineWrapping) {
7230 addClass(cm.display.wrapper, "CodeMirror-wrap")
7231 cm.display.sizer.style.minWidth = ""
7232 cm.display.sizerWidth = null
7233 } else {
7234 rmClass(cm.display.wrapper, "CodeMirror-wrap")
7235 findMaxLine(cm)
7236 }
7237 estimateLineHeights(cm)
7238 regChange(cm)
7239 clearCaches(cm)
7240 setTimeout(function () { return updateScrollbars(cm); }, 100)
7241 }
7242
7243 // A CodeMirror instance represents an editor. This is the object
7244 // that user code is usually dealing with.
7245
7246 function CodeMirror$1(place, options) {
7247 var this$1 = this;
7248
7249 if (!(this instanceof CodeMirror$1)) { return new CodeMirror$1(place, options) }
7250
7251 this.options = options = options ? copyObj(options) : {}
7252 // Determine effective options based on given values and defaults.
7253 copyObj(defaults, options, false)
7254 setGuttersForLineNumbers(options)
7255
7256 var doc = options.value
7257 if (typeof doc == "string") { doc = new Doc(doc, options.mode, null, options.lineSeparator) }
7258 this.doc = doc
7259
7260 var input = new CodeMirror$1.inputStyles[options.inputStyle](this)
7261 var display = this.display = new Display(place, doc, input)
7262 display.wrapper.CodeMirror = this
7263 updateGutters(this)
7264 themeChanged(this)
7265 if (options.lineWrapping)
7266 { this.display.wrapper.className += " CodeMirror-wrap" }
7267 if (options.autofocus && !mobile) { display.input.focus() }
7268 initScrollbars(this)
7269
7270 this.state = {
7271 keyMaps: [], // stores maps added by addKeyMap
7272 overlays: [], // highlighting overlays, as added by addOverlay
7273 modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info
7274 overwrite: false,
7275 delayingBlurEvent: false,
7276 focused: false,
7277 suppressEdits: false, // used to disable editing during key handlers when in readOnly mode
7278 pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edits in input.poll
7279 selectingText: false,
7280 draggingText: false,
7281 highlight: new Delayed(), // stores highlight worker timeout
7282 keySeq: null, // Unfinished key sequence
7283 specialChars: null
7284 }
7285
7286 // Override magic textarea content restore that IE sometimes does
7287 // on our hidden textarea on reload
7288 if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20) }
7289
7290 registerEventHandlers(this)
7291 ensureGlobalHandlers()
7292
7293 startOperation(this)
7294 this.curOp.forceUpdate = true
7295 attachDoc(this, doc)
7296
7297 if ((options.autofocus && !mobile) || this.hasFocus())
7298 { setTimeout(bind(onFocus, this), 20) }
7299 else
7300 { onBlur(this) }
7301
7302 for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt))
7303 { optionHandlers[opt](this$1, options[opt], Init) } }
7304 maybeUpdateLineNumberWidth(this)
7305 if (options.finishInit) { options.finishInit(this) }
7306 for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this$1) }
7307 endOperation(this)
7308 // Suppress optimizelegibility in Webkit, since it breaks text
7309 // measuring on line wrapping boundaries.
7310 if (webkit && options.lineWrapping &&
7311 getComputedStyle(display.lineDiv).textRendering == "optimizelegibility")
7312 { display.lineDiv.style.textRendering = "auto" }
7313 }
7314
7315 // The default configuration options.
7316 CodeMirror$1.defaults = defaults
7317 // Functions to run when options are changed.
7318 CodeMirror$1.optionHandlers = optionHandlers
7319
7320 // Attach the necessary event handlers when initializing the editor
7321 function registerEventHandlers(cm) {
7322 var d = cm.display
7323 on(d.scroller, "mousedown", operation(cm, onMouseDown))
7324 // Older IE's will not fire a second mousedown for a double click
7325 if (ie && ie_version < 11)
7326 { on(d.scroller, "dblclick", operation(cm, function (e) {
7327 if (signalDOMEvent(cm, e)) { return }
7328 var pos = posFromMouse(cm, e)
7329 if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) { return }
7330 e_preventDefault(e)
7331 var word = cm.findWordAt(pos)
7332 extendSelection(cm.doc, word.anchor, word.head)
7333 })) }
7334 else
7335 { on(d.scroller, "dblclick", function (e) { return signalDOMEvent(cm, e) || e_preventDefault(e); }) }
7336 // Some browsers fire contextmenu *after* opening the menu, at
7337 // which point we can't mess with it anymore. Context menu is
7338 // handled in onMouseDown for these browsers.
7339 if (!captureRightClick) { on(d.scroller, "contextmenu", function (e) { return onContextMenu(cm, e); }) }
7340
7341 // Used to suppress mouse event handling when a touch happens
7342 var touchFinished, prevTouch = {end: 0}
7343 function finishTouch() {
7344 if (d.activeTouch) {
7345 touchFinished = setTimeout(function () { return d.activeTouch = null; }, 1000)
7346 prevTouch = d.activeTouch
7347 prevTouch.end = +new Date
7348 }
7349 }
7350 function isMouseLikeTouchEvent(e) {
7351 if (e.touches.length != 1) { return false }
7352 var touch = e.touches[0]
7353 return touch.radiusX <= 1 && touch.radiusY <= 1
7354 }
7355 function farAway(touch, other) {
7356 if (other.left == null) { return true }
7357 var dx = other.left - touch.left, dy = other.top - touch.top
7358 return dx * dx + dy * dy > 20 * 20
7359 }
7360 on(d.scroller, "touchstart", function (e) {
7361 if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e)) {
7362 d.input.ensurePolled()
7363 clearTimeout(touchFinished)
7364 var now = +new Date
7365 d.activeTouch = {start: now, moved: false,
7366 prev: now - prevTouch.end <= 300 ? prevTouch : null}
7367 if (e.touches.length == 1) {
7368 d.activeTouch.left = e.touches[0].pageX
7369 d.activeTouch.top = e.touches[0].pageY
7370 }
7371 }
7372 })
7373 on(d.scroller, "touchmove", function () {
7374 if (d.activeTouch) { d.activeTouch.moved = true }
7375 })
7376 on(d.scroller, "touchend", function (e) {
7377 var touch = d.activeTouch
7378 if (touch && !eventInWidget(d, e) && touch.left != null &&
7379 !touch.moved && new Date - touch.start < 300) {
7380 var pos = cm.coordsChar(d.activeTouch, "page"), range
7381 if (!touch.prev || farAway(touch, touch.prev)) // Single tap
7382 { range = new Range(pos, pos) }
7383 else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap
7384 { range = cm.findWordAt(pos) }
7385 else // Triple tap
7386 { range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) }
7387 cm.setSelection(range.anchor, range.head)
7388 cm.focus()
7389 e_preventDefault(e)
7390 }
7391 finishTouch()
7392 })
7393 on(d.scroller, "touchcancel", finishTouch)
7394
7395 // Sync scrolling between fake scrollbars and real scrollable
7396 // area, ensure viewport is updated when scrolling.
7397 on(d.scroller, "scroll", function () {
7398 if (d.scroller.clientHeight) {
7399 setScrollTop(cm, d.scroller.scrollTop)
7400 setScrollLeft(cm, d.scroller.scrollLeft, true)
7401 signal(cm, "scroll", cm)
7402 }
7403 })
7404
7405 // Listen to wheel events in order to try and update the viewport on time.
7406 on(d.scroller, "mousewheel", function (e) { return onScrollWheel(cm, e); })
7407 on(d.scroller, "DOMMouseScroll", function (e) { return onScrollWheel(cm, e); })
7408
7409 // Prevent wrapper from ever scrolling
7410 on(d.wrapper, "scroll", function () { return d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; })
7411
7412 d.dragFunctions = {
7413 enter: function (e) {if (!signalDOMEvent(cm, e)) { e_stop(e) }},
7414 over: function (e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e) }},
7415 start: function (e) { return onDragStart(cm, e); },
7416 drop: operation(cm, onDrop),
7417 leave: function (e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm) }}
7418 }
7419
7420 var inp = d.input.getField()
7421 on(inp, "keyup", function (e) { return onKeyUp.call(cm, e); })
7422 on(inp, "keydown", operation(cm, onKeyDown))
7423 on(inp, "keypress", operation(cm, onKeyPress))
7424 on(inp, "focus", function (e) { return onFocus(cm, e); })
7425 on(inp, "blur", function (e) { return onBlur(cm, e); })
7426 }
7427
7428 var initHooks = []
7429 CodeMirror$1.defineInitHook = function (f) { return initHooks.push(f); }
7430
7431 // Indent the given line. The how parameter can be "smart",
7432 // "add"/null, "subtract", or "prev". When aggressive is false
7433 // (typically set to true for forced single-line indents), empty
7434 // lines are not indented, and places where the mode returns Pass
7435 // are left alone.
7436 function indentLine(cm, n, how, aggressive) {
7437 var doc = cm.doc, state
7438 if (how == null) { how = "add" }
7439 if (how == "smart") {
7440 // Fall back to "prev" when the mode doesn't have an indentation
7441 // method.
7442 if (!doc.mode.indent) { how = "prev" }
7443 else { state = getStateBefore(cm, n) }
7444 }
7445
7446 var tabSize = cm.options.tabSize
7447 var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize)
7448 if (line.stateAfter) { line.stateAfter = null }
7449 var curSpaceString = line.text.match(/^\s*/)[0], indentation
7450 if (!aggressive && !/\S/.test(line.text)) {
7451 indentation = 0
7452 how = "not"
7453 } else if (how == "smart") {
7454 indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text)
7455 if (indentation == Pass || indentation > 150) {
7456 if (!aggressive) { return }
7457 how = "prev"
7458 }
7459 }
7460 if (how == "prev") {
7461 if (n > doc.first) { indentation = countColumn(getLine(doc, n-1).text, null, tabSize) }
7462 else { indentation = 0 }
7463 } else if (how == "add") {
7464 indentation = curSpace + cm.options.indentUnit
7465 } else if (how == "subtract") {
7466 indentation = curSpace - cm.options.indentUnit
7467 } else if (typeof how == "number") {
7468 indentation = curSpace + how
7469 }
7470 indentation = Math.max(0, indentation)
7471
7472 var indentString = "", pos = 0
7473 if (cm.options.indentWithTabs)
7474 { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t"} }
7475 if (pos < indentation) { indentString += spaceStr(indentation - pos) }
7476
7477 if (indentString != curSpaceString) {
7478 replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input")
7479 line.stateAfter = null
7480 return true
7481 } else {
7482 // Ensure that, if the cursor was in the whitespace at the start
7483 // of the line, it is moved to the end of that space.
7484 for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) {
7485 var range = doc.sel.ranges[i$1]
7486 if (range.head.line == n && range.head.ch < curSpaceString.length) {
7487 var pos$1 = Pos(n, curSpaceString.length)
7488 replaceOneSelection(doc, i$1, new Range(pos$1, pos$1))
7489 break
7490 }
7491 }
7492 }
7493 }
7494
7495 // This will be set to a {lineWise: bool, text: [string]} object, so
7496 // that, when pasting, we know what kind of selections the copied
7497 // text was made out of.
7498 var lastCopied = null
7499
7500 function setLastCopied(newLastCopied) {
7501 lastCopied = newLastCopied
7502 }
7503
7504 function applyTextInput(cm, inserted, deleted, sel, origin) {
7505 var doc = cm.doc
7506 cm.display.shift = false
7507 if (!sel) { sel = doc.sel }
7508
7509 var paste = cm.state.pasteIncoming || origin == "paste"
7510 var textLines = splitLinesAuto(inserted), multiPaste = null
7511 // When pasing N lines into N selections, insert one line per selection
7512 if (paste && sel.ranges.length > 1) {
7513 if (lastCopied && lastCopied.text.join("\n") == inserted) {
7514 if (sel.ranges.length % lastCopied.text.length == 0) {
7515 multiPaste = []
7516 for (var i = 0; i < lastCopied.text.length; i++)
7517 { multiPaste.push(doc.splitLines(lastCopied.text[i])) }
7518 }
7519 } else if (textLines.length == sel.ranges.length) {
7520 multiPaste = map(textLines, function (l) { return [l]; })
7521 }
7522 }
7523
7524 var updateInput
7525 // Normal behavior is to insert the new text into every selection
7526 for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) {
7527 var range$$1 = sel.ranges[i$1]
7528 var from = range$$1.from(), to = range$$1.to()
7529 if (range$$1.empty()) {
7530 if (deleted && deleted > 0) // Handle deletion
7531 { from = Pos(from.line, from.ch - deleted) }
7532 else if (cm.state.overwrite && !paste) // Handle overwrite
7533 { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)) }
7534 else if (lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == inserted)
7535 { from = to = Pos(from.line, 0) }
7536 }
7537 updateInput = cm.curOp.updateInput
7538 var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines,
7539 origin: origin || (paste ? "paste" : cm.state.cutIncoming ? "cut" : "+input")}
7540 makeChange(cm.doc, changeEvent)
7541 signalLater(cm, "inputRead", cm, changeEvent)
7542 }
7543 if (inserted && !paste)
7544 { triggerElectric(cm, inserted) }
7545
7546 ensureCursorVisible(cm)
7547 cm.curOp.updateInput = updateInput
7548 cm.curOp.typing = true
7549 cm.state.pasteIncoming = cm.state.cutIncoming = false
7550 }
7551
7552 function handlePaste(e, cm) {
7553 var pasted = e.clipboardData && e.clipboardData.getData("Text")
7554 if (pasted) {
7555 e.preventDefault()
7556 if (!cm.isReadOnly() && !cm.options.disableInput)
7557 { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, "paste"); }) }
7558 return true
7559 }
7560 }
7561
7562 function triggerElectric(cm, inserted) {
7563 // When an 'electric' character is inserted, immediately trigger a reindent
7564 if (!cm.options.electricChars || !cm.options.smartIndent) { return }
7565 var sel = cm.doc.sel
7566
7567 for (var i = sel.ranges.length - 1; i >= 0; i--) {
7568 var range$$1 = sel.ranges[i]
7569 if (range$$1.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range$$1.head.line)) { continue }
7570 var mode = cm.getModeAt(range$$1.head)
7571 var indented = false
7572 if (mode.electricChars) {
7573 for (var j = 0; j < mode.electricChars.length; j++)
7574 { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
7575 indented = indentLine(cm, range$$1.head.line, "smart")
7576 break
7577 } }
7578 } else if (mode.electricInput) {
7579 if (mode.electricInput.test(getLine(cm.doc, range$$1.head.line).text.slice(0, range$$1.head.ch)))
7580 { indented = indentLine(cm, range$$1.head.line, "smart") }
7581 }
7582 if (indented) { signalLater(cm, "electricInput", cm, range$$1.head.line) }
7583 }
7584 }
7585
7586 function copyableRanges(cm) {
7587 var text = [], ranges = []
7588 for (var i = 0; i < cm.doc.sel.ranges.length; i++) {
7589 var line = cm.doc.sel.ranges[i].head.line
7590 var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)}
7591 ranges.push(lineRange)
7592 text.push(cm.getRange(lineRange.anchor, lineRange.head))
7593 }
7594 return {text: text, ranges: ranges}
7595 }
7596
7597 function disableBrowserMagic(field, spellcheck) {
7598 field.setAttribute("autocorrect", "off")
7599 field.setAttribute("autocapitalize", "off")
7600 field.setAttribute("spellcheck", !!spellcheck)
7601 }
7602
7603 function hiddenTextarea() {
7604 var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none")
7605 var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;")
7606 // The textarea is kept positioned near the cursor to prevent the
7607 // fact that it'll be scrolled into view on input from scrolling
7608 // our fake cursor out of view. On webkit, when wrap=off, paste is
7609 // very slow. So make the area wide instead.
7610 if (webkit) { te.style.width = "1000px" }
7611 else { te.setAttribute("wrap", "off") }
7612 // If border: 0; -- iOS fails to open keyboard (issue #1287)
7613 if (ios) { te.style.border = "1px solid black" }
7614 disableBrowserMagic(te)
7615 return div
7616 }
7617
7618 // The publicly visible API. Note that methodOp(f) means
7619 // 'wrap f in an operation, performed on its `this` parameter'.
7620
7621 // This is not the complete set of editor methods. Most of the
7622 // methods defined on the Doc type are also injected into
7623 // CodeMirror.prototype, for backwards compatibility and
7624 // convenience.
7625
7626 var addEditorMethods = function(CodeMirror) {
7627 var optionHandlers = CodeMirror.optionHandlers
7628
7629 var helpers = CodeMirror.helpers = {}
7630
7631 CodeMirror.prototype = {
7632 constructor: CodeMirror,
7633 focus: function(){window.focus(); this.display.input.focus()},
7634
7635 setOption: function(option, value) {
7636 var options = this.options, old = options[option]
7637 if (options[option] == value && option != "mode") { return }
7638 options[option] = value
7639 if (optionHandlers.hasOwnProperty(option))
7640 { operation(this, optionHandlers[option])(this, value, old) }
7641 },
7642
7643 getOption: function(option) {return this.options[option]},
7644 getDoc: function() {return this.doc},
7645
7646 addKeyMap: function(map$$1, bottom) {
7647 this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map$$1))
7648 },
7649 removeKeyMap: function(map$$1) {
7650 var maps = this.state.keyMaps
7651 for (var i = 0; i < maps.length; ++i)
7652 { if (maps[i] == map$$1 || maps[i].name == map$$1) {
7653 maps.splice(i, 1)
7654 return true
7655 } }
7656 },
7657
7658 addOverlay: methodOp(function(spec, options) {
7659 var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec)
7660 if (mode.startState) { throw new Error("Overlays may not be stateful.") }
7661 insertSorted(this.state.overlays,
7662 {mode: mode, modeSpec: spec, opaque: options && options.opaque,
7663 priority: (options && options.priority) || 0},
7664 function (overlay) { return overlay.priority; })
7665 this.state.modeGen++
7666 regChange(this)
7667 }),
7668 removeOverlay: methodOp(function(spec) {
7669 var this$1 = this;
7670
7671 var overlays = this.state.overlays
7672 for (var i = 0; i < overlays.length; ++i) {
7673 var cur = overlays[i].modeSpec
7674 if (cur == spec || typeof spec == "string" && cur.name == spec) {
7675 overlays.splice(i, 1)
7676 this$1.state.modeGen++
7677 regChange(this$1)
7678 return
7679 }
7680 }
7681 }),
7682
7683 indentLine: methodOp(function(n, dir, aggressive) {
7684 if (typeof dir != "string" && typeof dir != "number") {
7685 if (dir == null) { dir = this.options.smartIndent ? "smart" : "prev" }
7686 else { dir = dir ? "add" : "subtract" }
7687 }
7688 if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive) }
7689 }),
7690 indentSelection: methodOp(function(how) {
7691 var this$1 = this;
7692
7693 var ranges = this.doc.sel.ranges, end = -1
7694 for (var i = 0; i < ranges.length; i++) {
7695 var range$$1 = ranges[i]
7696 if (!range$$1.empty()) {
7697 var from = range$$1.from(), to = range$$1.to()
7698 var start = Math.max(end, from.line)
7699 end = Math.min(this$1.lastLine(), to.line - (to.ch ? 0 : 1)) + 1
7700 for (var j = start; j < end; ++j)
7701 { indentLine(this$1, j, how) }
7702 var newRanges = this$1.doc.sel.ranges
7703 if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0)
7704 { replaceOneSelection(this$1.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll) }
7705 } else if (range$$1.head.line > end) {
7706 indentLine(this$1, range$$1.head.line, how, true)
7707 end = range$$1.head.line
7708 if (i == this$1.doc.sel.primIndex) { ensureCursorVisible(this$1) }
7709 }
7710 }
7711 }),
7712
7713 // Fetch the parser token for a given character. Useful for hacks
7714 // that want to inspect the mode state (say, for completion).
7715 getTokenAt: function(pos, precise) {
7716 return takeToken(this, pos, precise)
7717 },
7718
7719 getLineTokens: function(line, precise) {
7720 return takeToken(this, Pos(line), precise, true)
7721 },
7722
7723 getTokenTypeAt: function(pos) {
7724 pos = clipPos(this.doc, pos)
7725 var styles = getLineStyles(this, getLine(this.doc, pos.line))
7726 var before = 0, after = (styles.length - 1) / 2, ch = pos.ch
7727 var type
7728 if (ch == 0) { type = styles[2] }
7729 else { for (;;) {
7730 var mid = (before + after) >> 1
7731 if ((mid ? styles[mid * 2 - 1] : 0) >= ch) { after = mid }
7732 else if (styles[mid * 2 + 1] < ch) { before = mid + 1 }
7733 else { type = styles[mid * 2 + 2]; break }
7734 } }
7735 var cut = type ? type.indexOf("overlay ") : -1
7736 return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1)
7737 },
7738
7739 getModeAt: function(pos) {
7740 var mode = this.doc.mode
7741 if (!mode.innerMode) { return mode }
7742 return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode
7743 },
7744
7745 getHelper: function(pos, type) {
7746 return this.getHelpers(pos, type)[0]
7747 },
7748
7749 getHelpers: function(pos, type) {
7750 var this$1 = this;
7751
7752 var found = []
7753 if (!helpers.hasOwnProperty(type)) { return found }
7754 var help = helpers[type], mode = this.getModeAt(pos)
7755 if (typeof mode[type] == "string") {
7756 if (help[mode[type]]) { found.push(help[mode[type]]) }
7757 } else if (mode[type]) {
7758 for (var i = 0; i < mode[type].length; i++) {
7759 var val = help[mode[type][i]]
7760 if (val) { found.push(val) }
7761 }
7762 } else if (mode.helperType && help[mode.helperType]) {
7763 found.push(help[mode.helperType])
7764 } else if (help[mode.name]) {
7765 found.push(help[mode.name])
7766 }
7767 for (var i$1 = 0; i$1 < help._global.length; i$1++) {
7768 var cur = help._global[i$1]
7769 if (cur.pred(mode, this$1) && indexOf(found, cur.val) == -1)
7770 { found.push(cur.val) }
7771 }
7772 return found
7773 },
7774
7775 getStateAfter: function(line, precise) {
7776 var doc = this.doc
7777 line = clipLine(doc, line == null ? doc.first + doc.size - 1: line)
7778 return getStateBefore(this, line + 1, precise)
7779 },
7780
7781 cursorCoords: function(start, mode) {
7782 var pos, range$$1 = this.doc.sel.primary()
7783 if (start == null) { pos = range$$1.head }
7784 else if (typeof start == "object") { pos = clipPos(this.doc, start) }
7785 else { pos = start ? range$$1.from() : range$$1.to() }
7786 return cursorCoords(this, pos, mode || "page")
7787 },
7788
7789 charCoords: function(pos, mode) {
7790 return charCoords(this, clipPos(this.doc, pos), mode || "page")
7791 },
7792
7793 coordsChar: function(coords, mode) {
7794 coords = fromCoordSystem(this, coords, mode || "page")
7795 return coordsChar(this, coords.left, coords.top)
7796 },
7797
7798 lineAtHeight: function(height, mode) {
7799 height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top
7800 return lineAtHeight(this.doc, height + this.display.viewOffset)
7801 },
7802 heightAtLine: function(line, mode, includeWidgets) {
7803 var end = false, lineObj
7804 if (typeof line == "number") {
7805 var last = this.doc.first + this.doc.size - 1
7806 if (line < this.doc.first) { line = this.doc.first }
7807 else if (line > last) { line = last; end = true }
7808 lineObj = getLine(this.doc, line)
7809 } else {
7810 lineObj = line
7811 }
7812 return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page", includeWidgets).top +
7813 (end ? this.doc.height - heightAtLine(lineObj) : 0)
7814 },
7815
7816 defaultTextHeight: function() { return textHeight(this.display) },
7817 defaultCharWidth: function() { return charWidth(this.display) },
7818
7819 getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}},
7820
7821 addWidget: function(pos, node, scroll, vert, horiz) {
7822 var display = this.display
7823 pos = cursorCoords(this, clipPos(this.doc, pos))
7824 var top = pos.bottom, left = pos.left
7825 node.style.position = "absolute"
7826 node.setAttribute("cm-ignore-events", "true")
7827 this.display.input.setUneditable(node)
7828 display.sizer.appendChild(node)
7829 if (vert == "over") {
7830 top = pos.top
7831 } else if (vert == "above" || vert == "near") {
7832 var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
7833 hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth)
7834 // Default to positioning above (if specified and possible); otherwise default to positioning below
7835 if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
7836 { top = pos.top - node.offsetHeight }
7837 else if (pos.bottom + node.offsetHeight <= vspace)
7838 { top = pos.bottom }
7839 if (left + node.offsetWidth > hspace)
7840 { left = hspace - node.offsetWidth }
7841 }
7842 node.style.top = top + "px"
7843 node.style.left = node.style.right = ""
7844 if (horiz == "right") {
7845 left = display.sizer.clientWidth - node.offsetWidth
7846 node.style.right = "0px"
7847 } else {
7848 if (horiz == "left") { left = 0 }
7849 else if (horiz == "middle") { left = (display.sizer.clientWidth - node.offsetWidth) / 2 }
7850 node.style.left = left + "px"
7851 }
7852 if (scroll)
7853 { scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight) }
7854 },
7855
7856 triggerOnKeyDown: methodOp(onKeyDown),
7857 triggerOnKeyPress: methodOp(onKeyPress),
7858 triggerOnKeyUp: onKeyUp,
7859
7860 execCommand: function(cmd) {
7861 if (commands.hasOwnProperty(cmd))
7862 { return commands[cmd].call(null, this) }
7863 },
7864
7865 triggerElectric: methodOp(function(text) { triggerElectric(this, text) }),
7866
7867 findPosH: function(from, amount, unit, visually) {
7868 var this$1 = this;
7869
7870 var dir = 1
7871 if (amount < 0) { dir = -1; amount = -amount }
7872 var cur = clipPos(this.doc, from)
7873 for (var i = 0; i < amount; ++i) {
7874 cur = findPosH(this$1.doc, cur, dir, unit, visually)
7875 if (cur.hitSide) { break }
7876 }
7877 return cur
7878 },
7879
7880 moveH: methodOp(function(dir, unit) {
7881 var this$1 = this;
7882
7883 this.extendSelectionsBy(function (range$$1) {
7884 if (this$1.display.shift || this$1.doc.extend || range$$1.empty())
7885 { return findPosH(this$1.doc, range$$1.head, dir, unit, this$1.options.rtlMoveVisually) }
7886 else
7887 { return dir < 0 ? range$$1.from() : range$$1.to() }
7888 }, sel_move)
7889 }),
7890
7891 deleteH: methodOp(function(dir, unit) {
7892 var sel = this.doc.sel, doc = this.doc
7893 if (sel.somethingSelected())
7894 { doc.replaceSelection("", null, "+delete") }
7895 else
7896 { deleteNearSelection(this, function (range$$1) {
7897 var other = findPosH(doc, range$$1.head, dir, unit, false)
7898 return dir < 0 ? {from: other, to: range$$1.head} : {from: range$$1.head, to: other}
7899 }) }
7900 }),
7901
7902 findPosV: function(from, amount, unit, goalColumn) {
7903 var this$1 = this;
7904
7905 var dir = 1, x = goalColumn
7906 if (amount < 0) { dir = -1; amount = -amount }
7907 var cur = clipPos(this.doc, from)
7908 for (var i = 0; i < amount; ++i) {
7909 var coords = cursorCoords(this$1, cur, "div")
7910 if (x == null) { x = coords.left }
7911 else { coords.left = x }
7912 cur = findPosV(this$1, coords, dir, unit)
7913 if (cur.hitSide) { break }
7914 }
7915 return cur
7916 },
7917
7918 moveV: methodOp(function(dir, unit) {
7919 var this$1 = this;
7920
7921 var doc = this.doc, goals = []
7922 var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected()
7923 doc.extendSelectionsBy(function (range$$1) {
7924 if (collapse)
7925 { return dir < 0 ? range$$1.from() : range$$1.to() }
7926 var headPos = cursorCoords(this$1, range$$1.head, "div")
7927 if (range$$1.goalColumn != null) { headPos.left = range$$1.goalColumn }
7928 goals.push(headPos.left)
7929 var pos = findPosV(this$1, headPos, dir, unit)
7930 if (unit == "page" && range$$1 == doc.sel.primary())
7931 { addToScrollPos(this$1, null, charCoords(this$1, pos, "div").top - headPos.top) }
7932 return pos
7933 }, sel_move)
7934 if (goals.length) { for (var i = 0; i < doc.sel.ranges.length; i++)
7935 { doc.sel.ranges[i].goalColumn = goals[i] } }
7936 }),
7937
7938 // Find the word at the given position (as returned by coordsChar).
7939 findWordAt: function(pos) {
7940 var doc = this.doc, line = getLine(doc, pos.line).text
7941 var start = pos.ch, end = pos.ch
7942 if (line) {
7943 var helper = this.getHelper(pos, "wordChars")
7944 if ((pos.xRel < 0 || end == line.length) && start) { --start; } else { ++end }
7945 var startChar = line.charAt(start)
7946 var check = isWordChar(startChar, helper)
7947 ? function (ch) { return isWordChar(ch, helper); }
7948 : /\s/.test(startChar) ? function (ch) { return /\s/.test(ch); }
7949 : function (ch) { return (!/\s/.test(ch) && !isWordChar(ch)); }
7950 while (start > 0 && check(line.charAt(start - 1))) { --start }
7951 while (end < line.length && check(line.charAt(end))) { ++end }
7952 }
7953 return new Range(Pos(pos.line, start), Pos(pos.line, end))
7954 },
7955
7956 toggleOverwrite: function(value) {
7957 if (value != null && value == this.state.overwrite) { return }
7958 if (this.state.overwrite = !this.state.overwrite)
7959 { addClass(this.display.cursorDiv, "CodeMirror-overwrite") }
7960 else
7961 { rmClass(this.display.cursorDiv, "CodeMirror-overwrite") }
7962
7963 signal(this, "overwriteToggle", this, this.state.overwrite)
7964 },
7965 hasFocus: function() { return this.display.input.getField() == activeElt() },
7966 isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) },
7967
7968 scrollTo: methodOp(function(x, y) {
7969 if (x != null || y != null) { resolveScrollToPos(this) }
7970 if (x != null) { this.curOp.scrollLeft = x }
7971 if (y != null) { this.curOp.scrollTop = y }
7972 }),
7973 getScrollInfo: function() {
7974 var scroller = this.display.scroller
7975 return {left: scroller.scrollLeft, top: scroller.scrollTop,
7976 height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight,
7977 width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth,
7978 clientHeight: displayHeight(this), clientWidth: displayWidth(this)}
7979 },
7980
7981 scrollIntoView: methodOp(function(range$$1, margin) {
7982 if (range$$1 == null) {
7983 range$$1 = {from: this.doc.sel.primary().head, to: null}
7984 if (margin == null) { margin = this.options.cursorScrollMargin }
7985 } else if (typeof range$$1 == "number") {
7986 range$$1 = {from: Pos(range$$1, 0), to: null}
7987 } else if (range$$1.from == null) {
7988 range$$1 = {from: range$$1, to: null}
7989 }
7990 if (!range$$1.to) { range$$1.to = range$$1.from }
7991 range$$1.margin = margin || 0
7992
7993 if (range$$1.from.line != null) {
7994 resolveScrollToPos(this)
7995 this.curOp.scrollToPos = range$$1
7996 } else {
7997 var sPos = calculateScrollPos(this, Math.min(range$$1.from.left, range$$1.to.left),
7998 Math.min(range$$1.from.top, range$$1.to.top) - range$$1.margin,
7999 Math.max(range$$1.from.right, range$$1.to.right),
8000 Math.max(range$$1.from.bottom, range$$1.to.bottom) + range$$1.margin)
8001 this.scrollTo(sPos.scrollLeft, sPos.scrollTop)
8002 }
8003 }),
8004
8005 setSize: methodOp(function(width, height) {
8006 var this$1 = this;
8007
8008 var interpret = function (val) { return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; }
8009 if (width != null) { this.display.wrapper.style.width = interpret(width) }
8010 if (height != null) { this.display.wrapper.style.height = interpret(height) }
8011 if (this.options.lineWrapping) { clearLineMeasurementCache(this) }
8012 var lineNo$$1 = this.display.viewFrom
8013 this.doc.iter(lineNo$$1, this.display.viewTo, function (line) {
8014 if (line.widgets) { for (var i = 0; i < line.widgets.length; i++)
8015 { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo$$1, "widget"); break } } }
8016 ++lineNo$$1
8017 })
8018 this.curOp.forceUpdate = true
8019 signal(this, "refresh", this)
8020 }),
8021
8022 operation: function(f){return runInOp(this, f)},
8023
8024 refresh: methodOp(function() {
8025 var oldHeight = this.display.cachedTextHeight
8026 regChange(this)
8027 this.curOp.forceUpdate = true
8028 clearCaches(this)
8029 this.scrollTo(this.doc.scrollLeft, this.doc.scrollTop)
8030 updateGutterSpace(this)
8031 if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5)
8032 { estimateLineHeights(this) }
8033 signal(this, "refresh", this)
8034 }),
8035
8036 swapDoc: methodOp(function(doc) {
8037 var old = this.doc
8038 old.cm = null
8039 attachDoc(this, doc)
8040 clearCaches(this)
8041 this.display.input.reset()
8042 this.scrollTo(doc.scrollLeft, doc.scrollTop)
8043 this.curOp.forceScroll = true
8044 signalLater(this, "swapDoc", this, old)
8045 return old
8046 }),
8047
8048 getInputField: function(){return this.display.input.getField()},
8049 getWrapperElement: function(){return this.display.wrapper},
8050 getScrollerElement: function(){return this.display.scroller},
8051 getGutterElement: function(){return this.display.gutters}
8052 }
8053 eventMixin(CodeMirror)
8054
8055 CodeMirror.registerHelper = function(type, name, value) {
8056 if (!helpers.hasOwnProperty(type)) { helpers[type] = CodeMirror[type] = {_global: []} }
8057 helpers[type][name] = value
8058 }
8059 CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {
8060 CodeMirror.registerHelper(type, name, value)
8061 helpers[type]._global.push({pred: predicate, val: value})
8062 }
8063 }
8064
8065 // Used for horizontal relative motion. Dir is -1 or 1 (left or
8066 // right), unit can be "char", "column" (like char, but doesn't
8067 // cross line boundaries), "word" (across next word), or "group" (to
8068 // the start of next group of word or non-word-non-whitespace
8069 // chars). The visually param controls whether, in right-to-left
8070 // text, direction 1 means to move towards the next index in the
8071 // string, or towards the character to the right of the current
8072 // position. The resulting position will have a hitSide=true
8073 // property if it reached the end of the document.
8074 function findPosH(doc, pos, dir, unit, visually) {
8075 var line = pos.line, ch = pos.ch, origDir = dir
8076 var lineObj = getLine(doc, line)
8077 function findNextLine() {
8078 var l = line + dir
8079 if (l < doc.first || l >= doc.first + doc.size) { return false }
8080 line = l
8081 return lineObj = getLine(doc, l)
8082 }
8083 function moveOnce(boundToLine) {
8084 var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, true)
8085 if (next == null) {
8086 if (!boundToLine && findNextLine()) {
8087 if (visually) { ch = (dir < 0 ? lineRight : lineLeft)(lineObj) }
8088 else { ch = dir < 0 ? lineObj.text.length : 0 }
8089 } else { return false }
8090 } else { ch = next }
8091 return true
8092 }
8093
8094 if (unit == "char") {
8095 moveOnce()
8096 } else if (unit == "column") {
8097 moveOnce(true)
8098 } else if (unit == "word" || unit == "group") {
8099 var sawType = null, group = unit == "group"
8100 var helper = doc.cm && doc.cm.getHelper(pos, "wordChars")
8101 for (var first = true;; first = false) {
8102 if (dir < 0 && !moveOnce(!first)) { break }
8103 var cur = lineObj.text.charAt(ch) || "\n"
8104 var type = isWordChar(cur, helper) ? "w"
8105 : group && cur == "\n" ? "n"
8106 : !group || /\s/.test(cur) ? null
8107 : "p"
8108 if (group && !first && !type) { type = "s" }
8109 if (sawType && sawType != type) {
8110 if (dir < 0) {dir = 1; moveOnce()}
8111 break
8112 }
8113
8114 if (type) { sawType = type }
8115 if (dir > 0 && !moveOnce(!first)) { break }
8116 }
8117 }
8118 var result = skipAtomic(doc, Pos(line, ch), pos, origDir, true)
8119 if (!cmp(pos, result)) { result.hitSide = true }
8120 return result
8121 }
8122
8123 // For relative vertical movement. Dir may be -1 or 1. Unit can be
8124 // "page" or "line". The resulting position will have a hitSide=true
8125 // property if it reached the end of the document.
8126 function findPosV(cm, pos, dir, unit) {
8127 var doc = cm.doc, x = pos.left, y
8128 if (unit == "page") {
8129 var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight)
8130 var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3)
8131 y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount
8132
8133 } else if (unit == "line") {
8134 y = dir > 0 ? pos.bottom + 3 : pos.top - 3
8135 }
8136 var target
8137 for (;;) {
8138 target = coordsChar(cm, x, y)
8139 if (!target.outside) { break }
8140 if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break }
8141 y += dir * 5
8142 }
8143 return target
8144 }
8145
8146 // CONTENTEDITABLE INPUT STYLE
8147
8148 function ContentEditableInput(cm) {
8149 this.cm = cm
8150 this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null
8151 this.polling = new Delayed()
8152 this.composing = null
8153 this.gracePeriod = false
8154 this.readDOMTimeout = null
8155 }
8156
8157 ContentEditableInput.prototype = copyObj({
8158 init: function(display) {
8159 var this$1 = this;
8160
8161 var input = this, cm = input.cm
8162 var div = input.div = display.lineDiv
8163 disableBrowserMagic(div, cm.options.spellcheck)
8164
8165 on(div, "paste", function (e) {
8166 if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
8167 // IE doesn't fire input events, so we schedule a read for the pasted content in this way
8168 if (ie_version <= 11) { setTimeout(operation(cm, function () {
8169 if (!input.pollContent()) { regChange(cm) }
8170 }), 20) }
8171 })
8172
8173 on(div, "compositionstart", function (e) {
8174 this$1.composing = {data: e.data}
8175 })
8176 on(div, "compositionupdate", function (e) {
8177 if (!this$1.composing) { this$1.composing = {data: e.data} }
8178 })
8179 on(div, "compositionend", function (e) {
8180 if (this$1.composing) {
8181 if (e.data != this$1.composing.data) { this$1.readFromDOMSoon() }
8182 this$1.composing = null
8183 }
8184 })
8185
8186 on(div, "touchstart", function () { return input.forceCompositionEnd(); })
8187
8188 on(div, "input", function () {
8189 if (!this$1.composing) { this$1.readFromDOMSoon() }
8190 })
8191
8192 function onCopyCut(e) {
8193 if (signalDOMEvent(cm, e)) { return }
8194 if (cm.somethingSelected()) {
8195 setLastCopied({lineWise: false, text: cm.getSelections()})
8196 if (e.type == "cut") { cm.replaceSelection("", null, "cut") }
8197 } else if (!cm.options.lineWiseCopyCut) {
8198 return
8199 } else {
8200 var ranges = copyableRanges(cm)
8201 setLastCopied({lineWise: true, text: ranges.text})
8202 if (e.type == "cut") {
8203 cm.operation(function () {
8204 cm.setSelections(ranges.ranges, 0, sel_dontScroll)
8205 cm.replaceSelection("", null, "cut")
8206 })
8207 }
8208 }
8209 if (e.clipboardData) {
8210 e.clipboardData.clearData()
8211 var content = lastCopied.text.join("\n")
8212 // iOS exposes the clipboard API, but seems to discard content inserted into it
8213 e.clipboardData.setData("Text", content)
8214 if (e.clipboardData.getData("Text") == content) {
8215 e.preventDefault()
8216 return
8217 }
8218 }
8219 // Old-fashioned briefly-focus-a-textarea hack
8220 var kludge = hiddenTextarea(), te = kludge.firstChild
8221 cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild)
8222 te.value = lastCopied.text.join("\n")
8223 var hadFocus = document.activeElement
8224 selectInput(te)
8225 setTimeout(function () {
8226 cm.display.lineSpace.removeChild(kludge)
8227 hadFocus.focus()
8228 if (hadFocus == div) { input.showPrimarySelection() }
8229 }, 50)
8230 }
8231 on(div, "copy", onCopyCut)
8232 on(div, "cut", onCopyCut)
8233 },
8234
8235 prepareSelection: function() {
8236 var result = prepareSelection(this.cm, false)
8237 result.focus = this.cm.state.focused
8238 return result
8239 },
8240
8241 showSelection: function(info, takeFocus) {
8242 if (!info || !this.cm.display.view.length) { return }
8243 if (info.focus || takeFocus) { this.showPrimarySelection() }
8244 this.showMultipleSelections(info)
8245 },
8246
8247 showPrimarySelection: function() {
8248 var sel = window.getSelection(), prim = this.cm.doc.sel.primary()
8249 var curAnchor = domToPos(this.cm, sel.anchorNode, sel.anchorOffset)
8250 var curFocus = domToPos(this.cm, sel.focusNode, sel.focusOffset)
8251 if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad &&
8252 cmp(minPos(curAnchor, curFocus), prim.from()) == 0 &&
8253 cmp(maxPos(curAnchor, curFocus), prim.to()) == 0)
8254 { return }
8255
8256 var start = posToDOM(this.cm, prim.from())
8257 var end = posToDOM(this.cm, prim.to())
8258 if (!start && !end) { return }
8259
8260 var view = this.cm.display.view
8261 var old = sel.rangeCount && sel.getRangeAt(0)
8262 if (!start) {
8263 start = {node: view[0].measure.map[2], offset: 0}
8264 } else if (!end) { // FIXME dangerously hacky
8265 var measure = view[view.length - 1].measure
8266 var map$$1 = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map
8267 end = {node: map$$1[map$$1.length - 1], offset: map$$1[map$$1.length - 2] - map$$1[map$$1.length - 3]}
8268 }
8269
8270 var rng
8271 try { rng = range(start.node, start.offset, end.offset, end.node) }
8272 catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible
8273 if (rng) {
8274 if (!gecko && this.cm.state.focused) {
8275 sel.collapse(start.node, start.offset)
8276 if (!rng.collapsed) {
8277 sel.removeAllRanges()
8278 sel.addRange(rng)
8279 }
8280 } else {
8281 sel.removeAllRanges()
8282 sel.addRange(rng)
8283 }
8284 if (old && sel.anchorNode == null) { sel.addRange(old) }
8285 else if (gecko) { this.startGracePeriod() }
8286 }
8287 this.rememberSelection()
8288 },
8289
8290 startGracePeriod: function() {
8291 var this$1 = this;
8292
8293 clearTimeout(this.gracePeriod)
8294 this.gracePeriod = setTimeout(function () {
8295 this$1.gracePeriod = false
8296 if (this$1.selectionChanged())
8297 { this$1.cm.operation(function () { return this$1.cm.curOp.selectionChanged = true; }) }
8298 }, 20)
8299 },
8300
8301 showMultipleSelections: function(info) {
8302 removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors)
8303 removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection)
8304 },
8305
8306 rememberSelection: function() {
8307 var sel = window.getSelection()
8308 this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset
8309 this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset
8310 },
8311
8312 selectionInEditor: function() {
8313 var sel = window.getSelection()
8314 if (!sel.rangeCount) { return false }
8315 var node = sel.getRangeAt(0).commonAncestorContainer
8316 return contains(this.div, node)
8317 },
8318
8319 focus: function() {
8320 if (this.cm.options.readOnly != "nocursor") {
8321 if (!this.selectionInEditor())
8322 { this.showSelection(this.prepareSelection(), true) }
8323 this.div.focus()
8324 }
8325 },
8326 blur: function() { this.div.blur() },
8327 getField: function() { return this.div },
8328
8329 supportsTouch: function() { return true },
8330
8331 receivedFocus: function() {
8332 var input = this
8333 if (this.selectionInEditor())
8334 { this.pollSelection() }
8335 else
8336 { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }) }
8337
8338 function poll() {
8339 if (input.cm.state.focused) {
8340 input.pollSelection()
8341 input.polling.set(input.cm.options.pollInterval, poll)
8342 }
8343 }
8344 this.polling.set(this.cm.options.pollInterval, poll)
8345 },
8346
8347 selectionChanged: function() {
8348 var sel = window.getSelection()
8349 return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||
8350 sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset
8351 },
8352
8353 pollSelection: function() {
8354 if (!this.composing && this.readDOMTimeout == null && !this.gracePeriod && this.selectionChanged()) {
8355 var sel = window.getSelection(), cm = this.cm
8356 this.rememberSelection()
8357 var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset)
8358 var head = domToPos(cm, sel.focusNode, sel.focusOffset)
8359 if (anchor && head) { runInOp(cm, function () {
8360 setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll)
8361 if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true }
8362 }) }
8363 }
8364 },
8365
8366 pollContent: function() {
8367 if (this.readDOMTimeout != null) {
8368 clearTimeout(this.readDOMTimeout)
8369 this.readDOMTimeout = null
8370 }
8371
8372 var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary()
8373 var from = sel.from(), to = sel.to()
8374 if (from.ch == 0 && from.line > cm.firstLine())
8375 { from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length) }
8376 if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine())
8377 { to = Pos(to.line + 1, 0) }
8378 if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return false }
8379
8380 var fromIndex, fromLine, fromNode
8381 if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) {
8382 fromLine = lineNo(display.view[0].line)
8383 fromNode = display.view[0].node
8384 } else {
8385 fromLine = lineNo(display.view[fromIndex].line)
8386 fromNode = display.view[fromIndex - 1].node.nextSibling
8387 }
8388 var toIndex = findViewIndex(cm, to.line)
8389 var toLine, toNode
8390 if (toIndex == display.view.length - 1) {
8391 toLine = display.viewTo - 1
8392 toNode = display.lineDiv.lastChild
8393 } else {
8394 toLine = lineNo(display.view[toIndex + 1].line) - 1
8395 toNode = display.view[toIndex + 1].node.previousSibling
8396 }
8397
8398 if (!fromNode) { return false }
8399 var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine))
8400 var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length))
8401 while (newText.length > 1 && oldText.length > 1) {
8402 if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine-- }
8403 else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++ }
8404 else { break }
8405 }
8406
8407 var cutFront = 0, cutEnd = 0
8408 var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length)
8409 while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront))
8410 { ++cutFront }
8411 var newBot = lst(newText), oldBot = lst(oldText)
8412 var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0),
8413 oldBot.length - (oldText.length == 1 ? cutFront : 0))
8414 while (cutEnd < maxCutEnd &&
8415 newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1))
8416 { ++cutEnd }
8417
8418 newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, "")
8419 newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, "")
8420
8421 var chFrom = Pos(fromLine, cutFront)
8422 var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0)
8423 if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {
8424 replaceRange(cm.doc, newText, chFrom, chTo, "+input")
8425 return true
8426 }
8427 },
8428
8429 ensurePolled: function() {
8430 this.forceCompositionEnd()
8431 },
8432 reset: function() {
8433 this.forceCompositionEnd()
8434 },
8435 forceCompositionEnd: function() {
8436 if (!this.composing) { return }
8437 this.composing = null
8438 if (!this.pollContent()) { regChange(this.cm) }
8439 this.div.blur()
8440 this.div.focus()
8441 },
8442 readFromDOMSoon: function() {
8443 var this$1 = this;
8444
8445 if (this.readDOMTimeout != null) { return }
8446 this.readDOMTimeout = setTimeout(function () {
8447 this$1.readDOMTimeout = null
8448 if (this$1.composing) { return }
8449 if (this$1.cm.isReadOnly() || !this$1.pollContent())
8450 { runInOp(this$1.cm, function () { return regChange(this$1.cm); }) }
8451 }, 80)
8452 },
8453
8454 setUneditable: function(node) {
8455 node.contentEditable = "false"
8456 },
8457
8458 onKeyPress: function(e) {
8459 e.preventDefault()
8460 if (!this.cm.isReadOnly())
8461 { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0) }
8462 },
8463
8464 readOnlyChanged: function(val) {
8465 this.div.contentEditable = String(val != "nocursor")
8466 },
8467
8468 onContextMenu: nothing,
8469 resetPosition: nothing,
8470
8471 needsContentAttribute: true
8472 }, ContentEditableInput.prototype)
8473
8474 function posToDOM(cm, pos) {
8475 var view = findViewForLine(cm, pos.line)
8476 if (!view || view.hidden) { return null }
8477 var line = getLine(cm.doc, pos.line)
8478 var info = mapFromLineView(view, line, pos.line)
8479
8480 var order = getOrder(line), side = "left"
8481 if (order) {
8482 var partPos = getBidiPartAt(order, pos.ch)
8483 side = partPos % 2 ? "right" : "left"
8484 }
8485 var result = nodeAndOffsetInLineMap(info.map, pos.ch, side)
8486 result.offset = result.collapse == "right" ? result.end : result.start
8487 return result
8488 }
8489
8490 function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos }
8491
8492 function domTextBetween(cm, from, to, fromLine, toLine) {
8493 var text = "", closing = false, lineSep = cm.doc.lineSeparator()
8494 function recognizeMarker(id) { return function (marker) { return marker.id == id; } }
8495 function walk(node) {
8496 if (node.nodeType == 1) {
8497 var cmText = node.getAttribute("cm-text")
8498 if (cmText != null) {
8499 if (cmText == "") { text += node.textContent.replace(/\u200b/g, "") }
8500 else { text += cmText }
8501 return
8502 }
8503 var markerID = node.getAttribute("cm-marker"), range$$1
8504 if (markerID) {
8505 var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID))
8506 if (found.length && (range$$1 = found[0].find()))
8507 { text += getBetween(cm.doc, range$$1.from, range$$1.to).join(lineSep) }
8508 return
8509 }
8510 if (node.getAttribute("contenteditable") == "false") { return }
8511 for (var i = 0; i < node.childNodes.length; i++)
8512 { walk(node.childNodes[i]) }
8513 if (/^(pre|div|p)$/i.test(node.nodeName))
8514 { closing = true }
8515 } else if (node.nodeType == 3) {
8516 var val = node.nodeValue
8517 if (!val) { return }
8518 if (closing) {
8519 text += lineSep
8520 closing = false
8521 }
8522 text += val
8523 }
8524 }
8525 for (;;) {
8526 walk(from)
8527 if (from == to) { break }
8528 from = from.nextSibling
8529 }
8530 return text
8531 }
8532
8533 function domToPos(cm, node, offset) {
8534 var lineNode
8535 if (node == cm.display.lineDiv) {
8536 lineNode = cm.display.lineDiv.childNodes[offset]
8537 if (!lineNode) { return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) }
8538 node = null; offset = 0
8539 } else {
8540 for (lineNode = node;; lineNode = lineNode.parentNode) {
8541 if (!lineNode || lineNode == cm.display.lineDiv) { return null }
8542 if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) { break }
8543 }
8544 }
8545 for (var i = 0; i < cm.display.view.length; i++) {
8546 var lineView = cm.display.view[i]
8547 if (lineView.node == lineNode)
8548 { return locateNodeInLineView(lineView, node, offset) }
8549 }
8550 }
8551
8552 function locateNodeInLineView(lineView, node, offset) {
8553 var wrapper = lineView.text.firstChild, bad = false
8554 if (!node || !contains(wrapper, node)) { return badPos(Pos(lineNo(lineView.line), 0), true) }
8555 if (node == wrapper) {
8556 bad = true
8557 node = wrapper.childNodes[offset]
8558 offset = 0
8559 if (!node) {
8560 var line = lineView.rest ? lst(lineView.rest) : lineView.line
8561 return badPos(Pos(lineNo(line), line.text.length), bad)
8562 }
8563 }
8564
8565 var textNode = node.nodeType == 3 ? node : null, topNode = node
8566 if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {
8567 textNode = node.firstChild
8568 if (offset) { offset = textNode.nodeValue.length }
8569 }
8570 while (topNode.parentNode != wrapper) { topNode = topNode.parentNode }
8571 var measure = lineView.measure, maps = measure.maps
8572
8573 function find(textNode, topNode, offset) {
8574 for (var i = -1; i < (maps ? maps.length : 0); i++) {
8575 var map$$1 = i < 0 ? measure.map : maps[i]
8576 for (var j = 0; j < map$$1.length; j += 3) {
8577 var curNode = map$$1[j + 2]
8578 if (curNode == textNode || curNode == topNode) {
8579 var line = lineNo(i < 0 ? lineView.line : lineView.rest[i])
8580 var ch = map$$1[j] + offset
8581 if (offset < 0 || curNode != textNode) { ch = map$$1[j + (offset ? 1 : 0)] }
8582 return Pos(line, ch)
8583 }
8584 }
8585 }
8586 }
8587 var found = find(textNode, topNode, offset)
8588 if (found) { return badPos(found, bad) }
8589
8590 // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems
8591 for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) {
8592 found = find(after, after.firstChild, 0)
8593 if (found)
8594 { return badPos(Pos(found.line, found.ch - dist), bad) }
8595 else
8596 { dist += after.textContent.length }
8597 }
8598 for (var before = topNode.previousSibling, dist$1 = offset; before; before = before.previousSibling) {
8599 found = find(before, before.firstChild, -1)
8600 if (found)
8601 { return badPos(Pos(found.line, found.ch + dist$1), bad) }
8602 else
8603 { dist$1 += before.textContent.length }
8604 }
8605 }
8606
8607 // TEXTAREA INPUT STYLE
8608
8609 function TextareaInput(cm) {
8610 this.cm = cm
8611 // See input.poll and input.reset
8612 this.prevInput = ""
8613
8614 // Flag that indicates whether we expect input to appear real soon
8615 // now (after some event like 'keypress' or 'input') and are
8616 // polling intensively.
8617 this.pollingFast = false
8618 // Self-resetting timeout for the poller
8619 this.polling = new Delayed()
8620 // Tracks when input.reset has punted to just putting a short
8621 // string into the textarea instead of the full selection.
8622 this.inaccurateSelection = false
8623 // Used to work around IE issue with selection being forgotten when focus moves away from textarea
8624 this.hasSelection = false
8625 this.composing = null
8626 }
8627
8628 TextareaInput.prototype = copyObj({
8629 init: function(display) {
8630 var this$1 = this;
8631
8632 var input = this, cm = this.cm
8633
8634 // Wraps and hides input textarea
8635 var div = this.wrapper = hiddenTextarea()
8636 // The semihidden textarea that is focused when the editor is
8637 // focused, and receives input.
8638 var te = this.textarea = div.firstChild
8639 display.wrapper.insertBefore(div, display.wrapper.firstChild)
8640
8641 // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)
8642 if (ios) { te.style.width = "0px" }
8643
8644 on(te, "input", function () {
8645 if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = null }
8646 input.poll()
8647 })
8648
8649 on(te, "paste", function (e) {
8650 if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
8651
8652 cm.state.pasteIncoming = true
8653 input.fastPoll()
8654 })
8655
8656 function prepareCopyCut(e) {
8657 if (signalDOMEvent(cm, e)) { return }
8658 if (cm.somethingSelected()) {
8659 setLastCopied({lineWise: false, text: cm.getSelections()})
8660 if (input.inaccurateSelection) {
8661 input.prevInput = ""
8662 input.inaccurateSelection = false
8663 te.value = lastCopied.text.join("\n")
8664 selectInput(te)
8665 }
8666 } else if (!cm.options.lineWiseCopyCut) {
8667 return
8668 } else {
8669 var ranges = copyableRanges(cm)
8670 setLastCopied({lineWise: true, text: ranges.text})
8671 if (e.type == "cut") {
8672 cm.setSelections(ranges.ranges, null, sel_dontScroll)
8673 } else {
8674 input.prevInput = ""
8675 te.value = ranges.text.join("\n")
8676 selectInput(te)
8677 }
8678 }
8679 if (e.type == "cut") { cm.state.cutIncoming = true }
8680 }
8681 on(te, "cut", prepareCopyCut)
8682 on(te, "copy", prepareCopyCut)
8683
8684 on(display.scroller, "paste", function (e) {
8685 if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return }
8686 cm.state.pasteIncoming = true
8687 input.focus()
8688 })
8689
8690 // Prevent normal selection in the editor (we handle our own)
8691 on(display.lineSpace, "selectstart", function (e) {
8692 if (!eventInWidget(display, e)) { e_preventDefault(e) }
8693 })
8694
8695 on(te, "compositionstart", function () {
8696 var start = cm.getCursor("from")
8697 if (input.composing) { input.composing.range.clear() }
8698 input.composing = {
8699 start: start,
8700 range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"})
8701 }
8702 })
8703 on(te, "compositionend", function () {
8704 if (input.composing) {
8705 input.poll()
8706 input.composing.range.clear()
8707 input.composing = null
8708 }
8709 })
8710 },
8711
8712 prepareSelection: function() {
8713 // Redraw the selection and/or cursor
8714 var cm = this.cm, display = cm.display, doc = cm.doc
8715 var result = prepareSelection(cm)
8716
8717 // Move the hidden textarea near the cursor to prevent scrolling artifacts
8718 if (cm.options.moveInputWithCursor) {
8719 var headPos = cursorCoords(cm, doc.sel.primary().head, "div")
8720 var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect()
8721 result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
8722 headPos.top + lineOff.top - wrapOff.top))
8723 result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
8724 headPos.left + lineOff.left - wrapOff.left))
8725 }
8726
8727 return result
8728 },
8729
8730 showSelection: function(drawn) {
8731 var cm = this.cm, display = cm.display
8732 removeChildrenAndAdd(display.cursorDiv, drawn.cursors)
8733 removeChildrenAndAdd(display.selectionDiv, drawn.selection)
8734 if (drawn.teTop != null) {
8735 this.wrapper.style.top = drawn.teTop + "px"
8736 this.wrapper.style.left = drawn.teLeft + "px"
8737 }
8738 },
8739
8740 // Reset the input to correspond to the selection (or to be empty,
8741 // when not typing and nothing is selected)
8742 reset: function(typing) {
8743 if (this.contextMenuPending) { return }
8744 var minimal, selected, cm = this.cm, doc = cm.doc
8745 if (cm.somethingSelected()) {
8746 this.prevInput = ""
8747 var range$$1 = doc.sel.primary()
8748 minimal = hasCopyEvent &&
8749 (range$$1.to().line - range$$1.from().line > 100 || (selected = cm.getSelection()).length > 1000)
8750 var content = minimal ? "-" : selected || cm.getSelection()
8751 this.textarea.value = content
8752 if (cm.state.focused) { selectInput(this.textarea) }
8753 if (ie && ie_version >= 9) { this.hasSelection = content }
8754 } else if (!typing) {
8755 this.prevInput = this.textarea.value = ""
8756 if (ie && ie_version >= 9) { this.hasSelection = null }
8757 }
8758 this.inaccurateSelection = minimal
8759 },
8760
8761 getField: function() { return this.textarea },
8762
8763 supportsTouch: function() { return false },
8764
8765 focus: function() {
8766 if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) {
8767 try { this.textarea.focus() }
8768 catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM
8769 }
8770 },
8771
8772 blur: function() { this.textarea.blur() },
8773
8774 resetPosition: function() {
8775 this.wrapper.style.top = this.wrapper.style.left = 0
8776 },
8777
8778 receivedFocus: function() { this.slowPoll() },
8779
8780 // Poll for input changes, using the normal rate of polling. This
8781 // runs as long as the editor is focused.
8782 slowPoll: function() {
8783 var this$1 = this;
8784
8785 if (this.pollingFast) { return }
8786 this.polling.set(this.cm.options.pollInterval, function () {
8787 this$1.poll()
8788 if (this$1.cm.state.focused) { this$1.slowPoll() }
8789 })
8790 },
8791
8792 // When an event has just come in that is likely to add or change
8793 // something in the input textarea, we poll faster, to ensure that
8794 // the change appears on the screen quickly.
8795 fastPoll: function() {
8796 var missed = false, input = this
8797 input.pollingFast = true
8798 function p() {
8799 var changed = input.poll()
8800 if (!changed && !missed) {missed = true; input.polling.set(60, p)}
8801 else {input.pollingFast = false; input.slowPoll()}
8802 }
8803 input.polling.set(20, p)
8804 },
8805
8806 // Read input from the textarea, and update the document to match.
8807 // When something is selected, it is present in the textarea, and
8808 // selected (unless it is huge, in which case a placeholder is
8809 // used). When nothing is selected, the cursor sits after previously
8810 // seen text (can be empty), which is stored in prevInput (we must
8811 // not reset the textarea when typing, because that breaks IME).
8812 poll: function() {
8813 var this$1 = this;
8814
8815 var cm = this.cm, input = this.textarea, prevInput = this.prevInput
8816 // Since this is called a *lot*, try to bail out as cheaply as
8817 // possible when it is clear that nothing happened. hasSelection
8818 // will be the case when there is a lot of text in the textarea,
8819 // in which case reading its value would be expensive.
8820 if (this.contextMenuPending || !cm.state.focused ||
8821 (hasSelection(input) && !prevInput && !this.composing) ||
8822 cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq)
8823 { return false }
8824
8825 var text = input.value
8826 // If nothing changed, bail.
8827 if (text == prevInput && !cm.somethingSelected()) { return false }
8828 // Work around nonsensical selection resetting in IE9/10, and
8829 // inexplicable appearance of private area unicode characters on
8830 // some key combos in Mac (#2689).
8831 if (ie && ie_version >= 9 && this.hasSelection === text ||
8832 mac && /[\uf700-\uf7ff]/.test(text)) {
8833 cm.display.input.reset()
8834 return false
8835 }
8836
8837 if (cm.doc.sel == cm.display.selForContextMenu) {
8838 var first = text.charCodeAt(0)
8839 if (first == 0x200b && !prevInput) { prevInput = "\u200b" }
8840 if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") }
8841 }
8842 // Find the part of the input that is actually new
8843 var same = 0, l = Math.min(prevInput.length, text.length)
8844 while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same }
8845
8846 runInOp(cm, function () {
8847 applyTextInput(cm, text.slice(same), prevInput.length - same,
8848 null, this$1.composing ? "*compose" : null)
8849
8850 // Don't leave long text in the textarea, since it makes further polling slow
8851 if (text.length > 1000 || text.indexOf("\n") > -1) { input.value = this$1.prevInput = "" }
8852 else { this$1.prevInput = text }
8853
8854 if (this$1.composing) {
8855 this$1.composing.range.clear()
8856 this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor("to"),
8857 {className: "CodeMirror-composing"})
8858 }
8859 })
8860 return true
8861 },
8862
8863 ensurePolled: function() {
8864 if (this.pollingFast && this.poll()) { this.pollingFast = false }
8865 },
8866
8867 onKeyPress: function() {
8868 if (ie && ie_version >= 9) { this.hasSelection = null }
8869 this.fastPoll()
8870 },
8871
8872 onContextMenu: function(e) {
8873 var input = this, cm = input.cm, display = cm.display, te = input.textarea
8874 var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop
8875 if (!pos || presto) { return } // Opera is difficult.
8876
8877 // Reset the current text selection only if the click is done outside of the selection
8878 // and 'resetSelectionOnContextMenu' option is true.
8879 var reset = cm.options.resetSelectionOnContextMenu
8880 if (reset && cm.doc.sel.contains(pos) == -1)
8881 { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll) }
8882
8883 var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText
8884 input.wrapper.style.cssText = "position: absolute"
8885 var wrapperBox = input.wrapper.getBoundingClientRect()
8886 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);"
8887 var oldScrollY
8888 if (webkit) { oldScrollY = window.scrollY } // Work around Chrome issue (#2712)
8889 display.input.focus()
8890 if (webkit) { window.scrollTo(null, oldScrollY) }
8891 display.input.reset()
8892 // Adds "Select all" to context menu in FF
8893 if (!cm.somethingSelected()) { te.value = input.prevInput = " " }
8894 input.contextMenuPending = true
8895 display.selForContextMenu = cm.doc.sel
8896 clearTimeout(display.detectingSelectAll)
8897
8898 // Select-all will be greyed out if there's nothing to select, so
8899 // this adds a zero-width space so that we can later check whether
8900 // it got selected.
8901 function prepareSelectAllHack() {
8902 if (te.selectionStart != null) {
8903 var selected = cm.somethingSelected()
8904 var extval = "\u200b" + (selected ? te.value : "")
8905 te.value = "\u21da" // Used to catch context-menu undo
8906 te.value = extval
8907 input.prevInput = selected ? "" : "\u200b"
8908 te.selectionStart = 1; te.selectionEnd = extval.length
8909 // Re-set this, in case some other handler touched the
8910 // selection in the meantime.
8911 display.selForContextMenu = cm.doc.sel
8912 }
8913 }
8914 function rehide() {
8915 input.contextMenuPending = false
8916 input.wrapper.style.cssText = oldWrapperCSS
8917 te.style.cssText = oldCSS
8918 if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos) }
8919
8920 // Try to detect the user choosing select-all
8921 if (te.selectionStart != null) {
8922 if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack() }
8923 var i = 0, poll = function () {
8924 if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 &&
8925 te.selectionEnd > 0 && input.prevInput == "\u200b")
8926 { operation(cm, selectAll)(cm) }
8927 else if (i++ < 10) { display.detectingSelectAll = setTimeout(poll, 500) }
8928 else { display.input.reset() }
8929 }
8930 display.detectingSelectAll = setTimeout(poll, 200)
8931 }
8932 }
8933
8934 if (ie && ie_version >= 9) { prepareSelectAllHack() }
8935 if (captureRightClick) {
8936 e_stop(e)
8937 var mouseup = function () {
8938 off(window, "mouseup", mouseup)
8939 setTimeout(rehide, 20)
8940 }
8941 on(window, "mouseup", mouseup)
8942 } else {
8943 setTimeout(rehide, 50)
8944 }
8945 },
8946
8947 readOnlyChanged: function(val) {
8948 if (!val) { this.reset() }
8949 },
8950
8951 setUneditable: nothing,
8952
8953 needsContentAttribute: false
8954 }, TextareaInput.prototype)
8955
8956 function fromTextArea(textarea, options) {
8957 options = options ? copyObj(options) : {}
8958 options.value = textarea.value
8959 if (!options.tabindex && textarea.tabIndex)
8960 { options.tabindex = textarea.tabIndex }
8961 if (!options.placeholder && textarea.placeholder)
8962 { options.placeholder = textarea.placeholder }
8963 // Set autofocus to true if this textarea is focused, or if it has
8964 // autofocus and no other element is focused.
8965 if (options.autofocus == null) {
8966 var hasFocus = activeElt()
8967 options.autofocus = hasFocus == textarea ||
8968 textarea.getAttribute("autofocus") != null && hasFocus == document.body
8969 }
8970
8971 function save() {textarea.value = cm.getValue()}
8972
8973 var realSubmit
8974 if (textarea.form) {
8975 on(textarea.form, "submit", save)
8976 // Deplorable hack to make the submit method do the right thing.
8977 if (!options.leaveSubmitMethodAlone) {
8978 var form = textarea.form
8979 realSubmit = form.submit
8980 try {
8981 var wrappedSubmit = form.submit = function () {
8982 save()
8983 form.submit = realSubmit
8984 form.submit()
8985 form.submit = wrappedSubmit
8986 }
8987 } catch(e) {}
8988 }
8989 }
8990
8991 options.finishInit = function (cm) {
8992 cm.save = save
8993 cm.getTextArea = function () { return textarea; }
8994 cm.toTextArea = function () {
8995 cm.toTextArea = isNaN // Prevent this from being ran twice
8996 save()
8997 textarea.parentNode.removeChild(cm.getWrapperElement())
8998 textarea.style.display = ""
8999 if (textarea.form) {
9000 off(textarea.form, "submit", save)
9001 if (typeof textarea.form.submit == "function")
9002 { textarea.form.submit = realSubmit }
9003 }
9004 }
9005 }
9006
9007 textarea.style.display = "none"
9008 var cm = CodeMirror$1(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); },
9009 options)
9010 return cm
9011 }
9012
9013 function addLegacyProps(CodeMirror) {
9014 CodeMirror.off = off
9015 CodeMirror.on = on
9016 CodeMirror.wheelEventPixels = wheelEventPixels
9017 CodeMirror.Doc = Doc
9018 CodeMirror.splitLines = splitLinesAuto
9019 CodeMirror.countColumn = countColumn
9020 CodeMirror.findColumn = findColumn
9021 CodeMirror.isWordChar = isWordCharBasic
9022 CodeMirror.Pass = Pass
9023 CodeMirror.signal = signal
9024 CodeMirror.Line = Line
9025 CodeMirror.changeEnd = changeEnd
9026 CodeMirror.scrollbarModel = scrollbarModel
9027 CodeMirror.Pos = Pos
9028 CodeMirror.cmpPos = cmp
9029 CodeMirror.modes = modes
9030 CodeMirror.mimeModes = mimeModes
9031 CodeMirror.resolveMode = resolveMode
9032 CodeMirror.getMode = getMode
9033 CodeMirror.modeExtensions = modeExtensions
9034 CodeMirror.extendMode = extendMode
9035 CodeMirror.copyState = copyState
9036 CodeMirror.startState = startState
9037 CodeMirror.innerMode = innerMode
9038 CodeMirror.commands = commands
9039 CodeMirror.keyMap = keyMap
9040 CodeMirror.keyName = keyName
9041 CodeMirror.isModifierKey = isModifierKey
9042 CodeMirror.lookupKey = lookupKey
9043 CodeMirror.normalizeKeyMap = normalizeKeyMap
9044 CodeMirror.StringStream = StringStream
9045 CodeMirror.SharedTextMarker = SharedTextMarker
9046 CodeMirror.TextMarker = TextMarker
9047 CodeMirror.LineWidget = LineWidget
9048 CodeMirror.e_preventDefault = e_preventDefault
9049 CodeMirror.e_stopPropagation = e_stopPropagation
9050 CodeMirror.e_stop = e_stop
9051 CodeMirror.addClass = addClass
9052 CodeMirror.contains = contains
9053 CodeMirror.rmClass = rmClass
9054 CodeMirror.keyNames = keyNames
9055 }
9056
9057 // EDITOR CONSTRUCTOR
9058
9059 defineOptions(CodeMirror$1)
9060
9061 addEditorMethods(CodeMirror$1)
9062
9063 // Set up methods on CodeMirror's prototype to redirect to the editor's document.
9064 var dontDelegate = "iter insert remove copy getEditor constructor".split(" ")
9065 for (var prop in Doc.prototype) { if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
9066 { CodeMirror$1.prototype[prop] = (function(method) {
9067 return function() {return method.apply(this.doc, arguments)}
9068 })(Doc.prototype[prop]) } }
9069
9070 eventMixin(Doc)
9071
9072 // INPUT HANDLING
9073
9074 CodeMirror$1.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput}
9075
9076 // MODE DEFINITION AND QUERYING
9077
9078 // Extra arguments are stored as the mode's dependencies, which is
9079 // used by (legacy) mechanisms like loadmode.js to automatically
9080 // load a mode. (Preferred mechanism is the require/define calls.)
9081 CodeMirror$1.defineMode = function(name/*, mode, …*/) {
9082 if (!CodeMirror$1.defaults.mode && name != "null") { CodeMirror$1.defaults.mode = name }
9083 defineMode.apply(this, arguments)
9084 }
9085
9086 CodeMirror$1.defineMIME = defineMIME
9087
9088 // Minimal default mode.
9089 CodeMirror$1.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); })
9090 CodeMirror$1.defineMIME("text/plain", "null")
9091
9092 // EXTENSIONS
9093
9094 CodeMirror$1.defineExtension = function (name, func) {
9095 CodeMirror$1.prototype[name] = func
9096 }
9097 CodeMirror$1.defineDocExtension = function (name, func) {
9098 Doc.prototype[name] = func
9099 }
9100
9101 CodeMirror$1.fromTextArea = fromTextArea
9102
9103 addLegacyProps(CodeMirror$1)
9104
9105 CodeMirror$1.version = "5.21.0"
9106
9107 return CodeMirror$1;
9108
9109 })));