· 8 years ago · Aug 15, 2018, 01:10 AM
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/*
11Copyright (C) 2016 by Marijn Haverbeke <marijnh@gmail.com> and others
12
13Permission is hereby granted, free of charge, to any person obtaining a copy
14of this software and associated documentation files (the "Software"), to deal
15in the Software without restriction, including without limitation the rights
16to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17copies of the Software, and to permit persons to whom the Software is
18furnished to do so, subject to the following conditions:
19
20The above copyright notice and this permission notice shall be included in
21all copies or substantial portions of the Software.
22
23THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
29THE SOFTWARE.
30
31*/
32
33(function (global, factory) {
34 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
35 typeof define === 'function' && define.amd ? define(factory) :
36 (global.CodeMirror = factory());
37}(this, (function () { 'use strict';
38
39// Kludges for bugs and behavior differences that can't be feature
40// detected are enabled based on userAgent etc sniffing.
41var userAgent = navigator.userAgent
42var platform = navigator.platform
43
44var gecko = /gecko\/\d/i.test(userAgent)
45var ie_upto10 = /MSIE \d/.test(userAgent)
46var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent)
47var ie = ie_upto10 || ie_11up
48var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : ie_11up[1])
49var webkit = /WebKit\//.test(userAgent)
50var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent)
51var chrome = /Chrome\//.test(userAgent)
52var presto = /Opera\//.test(userAgent)
53var safari = /Apple Computer/.test(navigator.vendor)
54var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent)
55var phantom = /PhantomJS/.test(userAgent)
56
57var ios = /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent)
58// This is woefully incomplete. Suggestions for alternative methods welcome.
59var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent)
60var mac = ios || /Mac/.test(platform)
61var chromeOS = /\bCrOS\b/.test(userAgent)
62var windows = /win/i.test(platform)
63
64var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/)
65if (presto_version) { presto_version = Number(presto_version[1]) }
66if (presto_version && presto_version >= 15) { presto = false; webkit = true }
67// Some browsers use the wrong event properties to signal cmd/ctrl on OS X
68var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11))
69var captureRightClick = gecko || (ie && ie_version >= 9)
70
71function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") }
72
73var rmClass = function(node, cls) {
74 var current = node.className
75 var match = classTest(cls).exec(current)
76 if (match) {
77 var after = current.slice(match.index + match[0].length)
78 node.className = current.slice(0, match.index) + (after ? match[1] + after : "")
79 }
80}
81
82function removeChildren(e) {
83 for (var count = e.childNodes.length; count > 0; --count)
84 { e.removeChild(e.firstChild) }
85 return e
86}
87
88function removeChildrenAndAdd(parent, e) {
89 return removeChildren(parent).appendChild(e)
90}
91
92function elt(tag, content, className, style) {
93 var e = document.createElement(tag)
94 if (className) { e.className = className }
95 if (style) { e.style.cssText = style }
96 if (typeof content == "string") { e.appendChild(document.createTextNode(content)) }
97 else if (content) { for (var i = 0; i < content.length; ++i) { e.appendChild(content[i]) } }
98 return e
99}
100
101var range
102if (document.createRange) { range = function(node, start, end, endNode) {
103 var r = document.createRange()
104 r.setEnd(endNode || node, end)
105 r.setStart(node, start)
106 return r
107} }
108else { range = function(node, start, end) {
109 var r = document.body.createTextRange()
110 try { r.moveToElementText(node.parentNode) }
111 catch(e) { return r }
112 r.collapse(true)
113 r.moveEnd("character", end)
114 r.moveStart("character", start)
115 return r
116} }
117
118function contains(parent, child) {
119 if (child.nodeType == 3) // Android browser always returns false when child is a textnode
120 { child = child.parentNode }
121 if (parent.contains)
122 { return parent.contains(child) }
123 do {
124 if (child.nodeType == 11) { child = child.host }
125 if (child == parent) { return true }
126 } while (child = child.parentNode)
127}
128
129function activeElt() {
130 // IE and Edge may throw an "Unspecified Error" when accessing document.activeElement.
131 // IE < 10 will throw when accessed while the page is loading or in an iframe.
132 // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable.
133 var activeElement
134 try {
135 activeElement = document.activeElement
136 } catch(e) {
137 activeElement = document.body || null
138 }
139 while (activeElement && activeElement.root && activeElement.root.activeElement)
140 { activeElement = activeElement.root.activeElement }
141 return activeElement
142}
143
144function addClass(node, cls) {
145 var current = node.className
146 if (!classTest(cls).test(current)) { node.className += (current ? " " : "") + cls }
147}
148function joinClasses(a, b) {
149 var as = a.split(" ")
150 for (var i = 0; i < as.length; i++)
151 { if (as[i] && !classTest(as[i]).test(b)) { b += " " + as[i] } }
152 return b
153}
154
155var selectInput = function(node) { node.select() }
156if (ios) // Mobile Safari apparently has a bug where select() is broken.
157 { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length } }
158else if (ie) // Suppress mysterious IE10 errors
159 { selectInput = function(node) { try { node.select() } catch(_e) {} } }
160
161function bind(f) {
162 var args = Array.prototype.slice.call(arguments, 1)
163 return function(){return f.apply(null, args)}
164}
165
166function copyObj(obj, target, overwrite) {
167 if (!target) { target = {} }
168 for (var prop in obj)
169 { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
170 { target[prop] = obj[prop] } }
171 return target
172}
173
174// Counts the column offset in a string, taking tabs into account.
175// Used mostly to find indentation.
176function countColumn(string, end, tabSize, startIndex, startValue) {
177 if (end == null) {
178 end = string.search(/[^\s\u00a0]/)
179 if (end == -1) { end = string.length }
180 }
181 for (var i = startIndex || 0, n = startValue || 0;;) {
182 var nextTab = string.indexOf("\t", i)
183 if (nextTab < 0 || nextTab >= end)
184 { return n + (end - i) }
185 n += nextTab - i
186 n += tabSize - (n % tabSize)
187 i = nextTab + 1
188 }
189}
190
191function Delayed() {this.id = null}
192Delayed.prototype.set = function(ms, f) {
193 clearTimeout(this.id)
194 this.id = setTimeout(f, ms)
195}
196
197function indexOf(array, elt) {
198 for (var i = 0; i < array.length; ++i)
199 { if (array[i] == elt) { return i } }
200 return -1
201}
202
203// Number of pixels added to scroller and sizer to hide scrollbar
204var scrollerGap = 30
205
206// Returned or thrown by various protocols to signal 'I'm not
207// handling this'.
208var Pass = {toString: function(){return "CodeMirror.Pass"}}
209
210// Reused option objects for setSelection & friends
211var sel_dontScroll = {scroll: false};
212var sel_mouse = {origin: "*mouse"};
213var sel_move = {origin: "+move"}
214
215// The inverse of countColumn -- find the offset that corresponds to
216// a particular column.
217function findColumn(string, goal, tabSize) {
218 for (var pos = 0, col = 0;;) {
219 var nextTab = string.indexOf("\t", pos)
220 if (nextTab == -1) { nextTab = string.length }
221 var skipped = nextTab - pos
222 if (nextTab == string.length || col + skipped >= goal)
223 { return pos + Math.min(skipped, goal - col) }
224 col += nextTab - pos
225 col += tabSize - (col % tabSize)
226 pos = nextTab + 1
227 if (col >= goal) { return pos }
228 }
229}
230
231var spaceStrs = [""]
232function spaceStr(n) {
233 while (spaceStrs.length <= n)
234 { spaceStrs.push(lst(spaceStrs) + " ") }
235 return spaceStrs[n]
236}
237
238function lst(arr) { return arr[arr.length-1] }
239
240function map(array, f) {
241 var out = []
242 for (var i = 0; i < array.length; i++) { out[i] = f(array[i], i) }
243 return out
244}
245
246function insertSorted(array, value, score) {
247 var pos = 0, priority = score(value)
248 while (pos < array.length && score(array[pos]) <= priority) { pos++ }
249 array.splice(pos, 0, value)
250}
251
252function nothing() {}
253
254function createObj(base, props) {
255 var inst
256 if (Object.create) {
257 inst = Object.create(base)
258 } else {
259 nothing.prototype = base
260 inst = new nothing()
261 }
262 if (props) { copyObj(props, inst) }
263 return inst
264}
265
266var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/
267function isWordCharBasic(ch) {
268 return /\w/.test(ch) || ch > "\x80" &&
269 (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch))
270}
271function isWordChar(ch, helper) {
272 if (!helper) { return isWordCharBasic(ch) }
273 if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) { return true }
274 return helper.test(ch)
275}
276
277function isEmpty(obj) {
278 for (var n in obj) { if (obj.hasOwnProperty(n) && obj[n]) { return false } }
279 return true
280}
281
282// Extending unicode characters. A series of a non-extending char +
283// any number of extending chars is treated as a single unit as far
284// as editing and measuring is concerned. This is not fully correct,
285// since some scripts/fonts/browsers also treat other configurations
286// of code points as a group.
287var 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]/
288function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) }
289
290// The display handles the DOM integration, both for input reading
291// and content drawing. It holds references to DOM nodes and
292// display-related state.
293
294function Display(place, doc, input) {
295 var d = this
296 this.input = input
297
298 // Covers bottom-right square when both scrollbars are present.
299 d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler")
300 d.scrollbarFiller.setAttribute("cm-not-content", "true")
301 // Covers bottom of gutter when coverGutterNextToScrollbar is on
302 // and h scrollbar is present.
303 d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler")
304 d.gutterFiller.setAttribute("cm-not-content", "true")
305 // Will contain the actual code, positioned to cover the viewport.
306 d.lineDiv = elt("div", null, "CodeMirror-code")
307 // Elements are added to these to represent selection and cursors.
308 d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1")
309 d.cursorDiv = elt("div", null, "CodeMirror-cursors")
310 // A visibility: hidden element used to find the size of things.
311 d.measure = elt("div", null, "CodeMirror-measure")
312 // When lines outside of the viewport are measured, they are drawn in this.
313 d.lineMeasure = elt("div", null, "CodeMirror-measure")
314 // Wraps everything that needs to exist inside the vertically-padded coordinate system
315 d.lineSpace = elt("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],
316 null, "position: relative; outline: none")
317 // Moved around its parent to cover visible view.
318 d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null, "position: relative")
319 // Set to the height of the document, allowing scrolling.
320 d.sizer = elt("div", [d.mover], "CodeMirror-sizer")
321 d.sizerWidth = null
322 // Behavior of elts with overflow: auto and padding is
323 // inconsistent across browsers. This is used to ensure the
324 // scrollable area is big enough.
325 d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;")
326 // Will contain the gutters, if any.
327 d.gutters = elt("div", null, "CodeMirror-gutters")
328 d.lineGutter = null
329 // Actual scrollable element.
330 d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll")
331 d.scroller.setAttribute("tabIndex", "-1")
332 // The element in which the editor lives.
333 d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror")
334
335 // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)
336 if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0 }
337 if (!webkit && !(gecko && mobile)) { d.scroller.draggable = true }
338
339 if (place) {
340 if (place.appendChild) { place.appendChild(d.wrapper) }
341 else { place(d.wrapper) }
342 }
343
344 // Current rendered range (may be bigger than the view window).
345 d.viewFrom = d.viewTo = doc.first
346 d.reportedViewFrom = d.reportedViewTo = doc.first
347 // Information about the rendered lines.
348 d.view = []
349 d.renderedView = null
350 // Holds info about a single rendered line when it was rendered
351 // for measurement, while not in view.
352 d.externalMeasured = null
353 // Empty space (in pixels) above the view
354 d.viewOffset = 0
355 d.lastWrapHeight = d.lastWrapWidth = 0
356 d.updateLineNumbers = null
357
358 d.nativeBarWidth = d.barHeight = d.barWidth = 0
359 d.scrollbarsClipped = false
360
361 // Used to only resize the line number gutter when necessary (when
362 // the amount of lines crosses a boundary that makes its width change)
363 d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null
364 // Set to true when a non-horizontal-scrolling line widget is
365 // added. As an optimization, line widget aligning is skipped when
366 // this is false.
367 d.alignWidgets = false
368
369 d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null
370
371 // Tracks the maximum line length so that the horizontal scrollbar
372 // can be kept static when scrolling.
373 d.maxLine = null
374 d.maxLineLength = 0
375 d.maxLineChanged = false
376
377 // Used for measuring wheel scrolling granularity
378 d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null
379
380 // True when shift is held down.
381 d.shift = false
382
383 // Used to track whether anything happened since the context menu
384 // was opened.
385 d.selForContextMenu = null
386
387 d.activeTouch = null
388
389 input.init(d)
390}
391
392// Find the line object corresponding to the given line number.
393function getLine(doc, n) {
394 n -= doc.first
395 if (n < 0 || n >= doc.size) { throw new Error("There is no line " + (n + doc.first) + " in the document.") }
396 var chunk = doc
397 while (!chunk.lines) {
398 for (var i = 0;; ++i) {
399 var child = chunk.children[i], sz = child.chunkSize()
400 if (n < sz) { chunk = child; break }
401 n -= sz
402 }
403 }
404 return chunk.lines[n]
405}
406
407// Get the part of a document between two positions, as an array of
408// strings.
409function getBetween(doc, start, end) {
410 var out = [], n = start.line
411 doc.iter(start.line, end.line + 1, function (line) {
412 var text = line.text
413 if (n == end.line) { text = text.slice(0, end.ch) }
414 if (n == start.line) { text = text.slice(start.ch) }
415 out.push(text)
416 ++n
417 })
418 return out
419}
420// Get the lines between from and to, as array of strings.
421function getLines(doc, from, to) {
422 var out = []
423 doc.iter(from, to, function (line) { out.push(line.text) }) // iter aborts when callback returns truthy value
424 return out
425}
426
427// Update the height of a line, propagating the height change
428// upwards to parent nodes.
429function updateLineHeight(line, height) {
430 var diff = height - line.height
431 if (diff) { for (var n = line; n; n = n.parent) { n.height += diff } }
432}
433
434// Given a line object, find its line number by walking up through
435// its parent links.
436function lineNo(line) {
437 if (line.parent == null) { return null }
438 var cur = line.parent, no = indexOf(cur.lines, line)
439 for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
440 for (var i = 0;; ++i) {
441 if (chunk.children[i] == cur) { break }
442 no += chunk.children[i].chunkSize()
443 }
444 }
445 return no + cur.first
446}
447
448// Find the line at the given vertical position, using the height
449// information in the document tree.
450function lineAtHeight(chunk, h) {
451 var n = chunk.first
452 outer: do {
453 for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) {
454 var child = chunk.children[i$1], ch = child.height
455 if (h < ch) { chunk = child; continue outer }
456 h -= ch
457 n += child.chunkSize()
458 }
459 return n
460 } while (!chunk.lines)
461 var i = 0
462 for (; i < chunk.lines.length; ++i) {
463 var line = chunk.lines[i], lh = line.height
464 if (h < lh) { break }
465 h -= lh
466 }
467 return n + i
468}
469
470function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size}
471
472function lineNumberFor(options, i) {
473 return String(options.lineNumberFormatter(i + options.firstLineNumber))
474}
475
476// A Pos instance represents a position within the text.
477function Pos (line, ch) {
478 if (!(this instanceof Pos)) { return new Pos(line, ch) }
479 this.line = line; this.ch = ch
480}
481
482// Compare two positions, return 0 if they are the same, a negative
483// number when a is less, and a positive number otherwise.
484function cmp(a, b) { return a.line - b.line || a.ch - b.ch }
485
486function copyPos(x) {return Pos(x.line, x.ch)}
487function maxPos(a, b) { return cmp(a, b) < 0 ? b : a }
488function minPos(a, b) { return cmp(a, b) < 0 ? a : b }
489
490// Most of the external API clips given positions to make sure they
491// actually exist within the document.
492function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))}
493function clipPos(doc, pos) {
494 if (pos.line < doc.first) { return Pos(doc.first, 0) }
495 var last = doc.first + doc.size - 1
496 if (pos.line > last) { return Pos(last, getLine(doc, last).text.length) }
497 return clipToLen(pos, getLine(doc, pos.line).text.length)
498}
499function clipToLen(pos, linelen) {
500 var ch = pos.ch
501 if (ch == null || ch > linelen) { return Pos(pos.line, linelen) }
502 else if (ch < 0) { return Pos(pos.line, 0) }
503 else { return pos }
504}
505function clipPosArray(doc, array) {
506 var out = []
507 for (var i = 0; i < array.length; i++) { out[i] = clipPos(doc, array[i]) }
508 return out
509}
510
511// Optimize some code when these features are not used.
512var sawReadOnlySpans = false;
513var sawCollapsedSpans = false
514
515function seeReadOnlySpans() {
516 sawReadOnlySpans = true
517}
518
519function seeCollapsedSpans() {
520 sawCollapsedSpans = true
521}
522
523// TEXTMARKER SPANS
524
525function MarkedSpan(marker, from, to) {
526 this.marker = marker
527 this.from = from; this.to = to
528}
529
530// Search an array of spans for a span matching the given marker.
531function getMarkedSpanFor(spans, marker) {
532 if (spans) { for (var i = 0; i < spans.length; ++i) {
533 var span = spans[i]
534 if (span.marker == marker) { return span }
535 } }
536}
537// Remove a span from an array, returning undefined if no spans are
538// left (we don't store arrays for lines without spans).
539function removeMarkedSpan(spans, span) {
540 var r
541 for (var i = 0; i < spans.length; ++i)
542 { if (spans[i] != span) { (r || (r = [])).push(spans[i]) } }
543 return r
544}
545// Add a span to a line.
546function addMarkedSpan(line, span) {
547 line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span]
548 span.marker.attachLine(line)
549}
550
551// Used for the algorithm that adjusts markers for a change in the
552// document. These functions cut an array of spans at a given
553// character position, returning an array of remaining chunks (or
554// undefined if nothing remains).
555function markedSpansBefore(old, startCh, isInsert) {
556 var nw
557 if (old) { for (var i = 0; i < old.length; ++i) {
558 var span = old[i], marker = span.marker
559 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh)
560 if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) {
561 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))
562 }
563 } }
564 return nw
565}
566function markedSpansAfter(old, endCh, isInsert) {
567 var nw
568 if (old) { for (var i = 0; i < old.length; ++i) {
569 var span = old[i], marker = span.marker
570 var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh)
571 if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) {
572 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh);(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh,
573 span.to == null ? null : span.to - endCh))
574 }
575 } }
576 return nw
577}
578
579// Given a change object, compute the new set of marker spans that
580// cover the line in which the change took place. Removes spans
581// entirely within the change, reconnects spans belonging to the
582// same marker that appear on both sides of the change, and cuts off
583// spans partially within the change. Returns an array of span
584// arrays with one element for each line in (after) the change.
585function stretchSpansOverChange(doc, change) {
586 if (change.full) { return null }
587 var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans
588 var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans
589 if (!oldFirst && !oldLast) { return null }
590
591 var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0
592 // Get the spans that 'stick out' on both sides
593 var first = markedSpansBefore(oldFirst, startCh, isInsert)
594 var last = markedSpansAfter(oldLast, endCh, isInsert)
595
596 // Next, merge those two ends
597 var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0)
598 if (first) {
599 // Fix up .to properties of first
600 for (var i = 0; i < first.length; ++i) {
601 var span = first[i]
602 if (span.to == null) {
603 var found = getMarkedSpanFor(last, span.marker)
604 if (!found) { span.to = startCh }
605 else if (sameLine) { span.to = found.to == null ? null : found.to + offset }
606 }
607 }
608 }
609 if (last) {
610 // Fix up .from in last (or move them into first in case of sameLine)
611 for (var i$1 = 0; i$1 < last.length; ++i$1) {
612 var span$1 = last[i$1]
613 if (span$1.to != null) { span$1.to += offset }
614 if (span$1.from == null) {
615 var found$1 = getMarkedSpanFor(first, span$1.marker)
616 if (!found$1) {
617 span$1.from = offset
618 if (sameLine) { (first || (first = [])).push(span$1) }
619 }
620 } else {
621 span$1.from += offset
622 if (sameLine) { (first || (first = [])).push(span$1) }
623 }
624 }
625 }
626 // Make sure we didn't create any zero-length spans
627 if (first) { first = clearEmptySpans(first) }
628 if (last && last != first) { last = clearEmptySpans(last) }
629
630 var newMarkers = [first]
631 if (!sameLine) {
632 // Fill gap with whole-line-spans
633 var gap = change.text.length - 2, gapMarkers
634 if (gap > 0 && first)
635 { for (var i$2 = 0; i$2 < first.length; ++i$2)
636 { if (first[i$2].to == null)
637 { (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i$2].marker, null, null)) } } }
638 for (var i$3 = 0; i$3 < gap; ++i$3)
639 { newMarkers.push(gapMarkers) }
640 newMarkers.push(last)
641 }
642 return newMarkers
643}
644
645// Remove spans that are empty and don't have a clearWhenEmpty
646// option of false.
647function clearEmptySpans(spans) {
648 for (var i = 0; i < spans.length; ++i) {
649 var span = spans[i]
650 if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)
651 { spans.splice(i--, 1) }
652 }
653 if (!spans.length) { return null }
654 return spans
655}
656
657// Used to 'clip' out readOnly ranges when making a change.
658function removeReadOnlyRanges(doc, from, to) {
659 var markers = null
660 doc.iter(from.line, to.line + 1, function (line) {
661 if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
662 var mark = line.markedSpans[i].marker
663 if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
664 { (markers || (markers = [])).push(mark) }
665 } }
666 })
667 if (!markers) { return null }
668 var parts = [{from: from, to: to}]
669 for (var i = 0; i < markers.length; ++i) {
670 var mk = markers[i], m = mk.find(0)
671 for (var j = 0; j < parts.length; ++j) {
672 var p = parts[j]
673 if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) { continue }
674 var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to)
675 if (dfrom < 0 || !mk.inclusiveLeft && !dfrom)
676 { newParts.push({from: p.from, to: m.from}) }
677 if (dto > 0 || !mk.inclusiveRight && !dto)
678 { newParts.push({from: m.to, to: p.to}) }
679 parts.splice.apply(parts, newParts)
680 j += newParts.length - 1
681 }
682 }
683 return parts
684}
685
686// Connect or disconnect spans from a line.
687function detachMarkedSpans(line) {
688 var spans = line.markedSpans
689 if (!spans) { return }
690 for (var i = 0; i < spans.length; ++i)
691 { spans[i].marker.detachLine(line) }
692 line.markedSpans = null
693}
694function attachMarkedSpans(line, spans) {
695 if (!spans) { return }
696 for (var i = 0; i < spans.length; ++i)
697 { spans[i].marker.attachLine(line) }
698 line.markedSpans = spans
699}
700
701// Helpers used when computing which overlapping collapsed span
702// counts as the larger one.
703function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 }
704function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 }
705
706// Returns a number indicating which of two overlapping collapsed
707// spans is larger (and thus includes the other). Falls back to
708// comparing ids when the spans cover exactly the same range.
709function compareCollapsedMarkers(a, b) {
710 var lenDiff = a.lines.length - b.lines.length
711 if (lenDiff != 0) { return lenDiff }
712 var aPos = a.find(), bPos = b.find()
713 var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b)
714 if (fromCmp) { return -fromCmp }
715 var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b)
716 if (toCmp) { return toCmp }
717 return b.id - a.id
718}
719
720// Find out whether a line ends or starts in a collapsed span. If
721// so, return the marker for that span.
722function collapsedSpanAtSide(line, start) {
723 var sps = sawCollapsedSpans && line.markedSpans, found
724 if (sps) { for (var sp = void 0, i = 0; i < sps.length; ++i) {
725 sp = sps[i]
726 if (sp.marker.collapsed && (start ? sp.from : sp.to) == null &&
727 (!found || compareCollapsedMarkers(found, sp.marker) < 0))
728 { found = sp.marker }
729 } }
730 return found
731}
732function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) }
733function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) }
734
735// Test whether there exists a collapsed span that partially
736// overlaps (covers the start or end, but not both) of a new span.
737// Such overlap is not allowed.
738function conflictingCollapsedRange(doc, lineNo$$1, from, to, marker) {
739 var line = getLine(doc, lineNo$$1)
740 var sps = sawCollapsedSpans && line.markedSpans
741 if (sps) { for (var i = 0; i < sps.length; ++i) {
742 var sp = sps[i]
743 if (!sp.marker.collapsed) { continue }
744 var found = sp.marker.find(0)
745 var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker)
746 var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker)
747 if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue }
748 if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) ||
749 fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0))
750 { return true }
751 } }
752}
753
754// A visual line is a line as drawn on the screen. Folding, for
755// example, can cause multiple logical lines to appear on the same
756// visual line. This finds the start of the visual line that the
757// given line is part of (usually that is the line itself).
758function visualLine(line) {
759 var merged
760 while (merged = collapsedSpanAtStart(line))
761 { line = merged.find(-1, true).line }
762 return line
763}
764
765// Returns an array of logical lines that continue the visual line
766// started by the argument, or undefined if there are no such lines.
767function visualLineContinued(line) {
768 var merged, lines
769 while (merged = collapsedSpanAtEnd(line)) {
770 line = merged.find(1, true).line
771 ;(lines || (lines = [])).push(line)
772 }
773 return lines
774}
775
776// Get the line number of the start of the visual line that the
777// given line number is part of.
778function visualLineNo(doc, lineN) {
779 var line = getLine(doc, lineN), vis = visualLine(line)
780 if (line == vis) { return lineN }
781 return lineNo(vis)
782}
783
784// Get the line number of the start of the next visual line after
785// the given line.
786function visualLineEndNo(doc, lineN) {
787 if (lineN > doc.lastLine()) { return lineN }
788 var line = getLine(doc, lineN), merged
789 if (!lineIsHidden(doc, line)) { return lineN }
790 while (merged = collapsedSpanAtEnd(line))
791 { line = merged.find(1, true).line }
792 return lineNo(line) + 1
793}
794
795// Compute whether a line is hidden. Lines count as hidden when they
796// are part of a visual line that starts with another line, or when
797// they are entirely covered by collapsed, non-widget span.
798function lineIsHidden(doc, line) {
799 var sps = sawCollapsedSpans && line.markedSpans
800 if (sps) { for (var sp = void 0, i = 0; i < sps.length; ++i) {
801 sp = sps[i]
802 if (!sp.marker.collapsed) { continue }
803 if (sp.from == null) { return true }
804 if (sp.marker.widgetNode) { continue }
805 if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
806 { return true }
807 } }
808}
809function lineIsHiddenInner(doc, line, span) {
810 if (span.to == null) {
811 var end = span.marker.find(1, true)
812 return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker))
813 }
814 if (span.marker.inclusiveRight && span.to == line.text.length)
815 { return true }
816 for (var sp = void 0, i = 0; i < line.markedSpans.length; ++i) {
817 sp = line.markedSpans[i]
818 if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to &&
819 (sp.to == null || sp.to != span.from) &&
820 (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
821 lineIsHiddenInner(doc, line, sp)) { return true }
822 }
823}
824
825// Find the height above the given line.
826function heightAtLine(lineObj) {
827 lineObj = visualLine(lineObj)
828
829 var h = 0, chunk = lineObj.parent
830 for (var i = 0; i < chunk.lines.length; ++i) {
831 var line = chunk.lines[i]
832 if (line == lineObj) { break }
833 else { h += line.height }
834 }
835 for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
836 for (var i$1 = 0; i$1 < p.children.length; ++i$1) {
837 var cur = p.children[i$1]
838 if (cur == chunk) { break }
839 else { h += cur.height }
840 }
841 }
842 return h
843}
844
845// Compute the character length of a line, taking into account
846// collapsed ranges (see markText) that might hide parts, and join
847// other lines onto it.
848function lineLength(line) {
849 if (line.height == 0) { return 0 }
850 var len = line.text.length, merged, cur = line
851 while (merged = collapsedSpanAtStart(cur)) {
852 var found = merged.find(0, true)
853 cur = found.from.line
854 len += found.from.ch - found.to.ch
855 }
856 cur = line
857 while (merged = collapsedSpanAtEnd(cur)) {
858 var found$1 = merged.find(0, true)
859 len -= cur.text.length - found$1.from.ch
860 cur = found$1.to.line
861 len += cur.text.length - found$1.to.ch
862 }
863 return len
864}
865
866// Find the longest line in the document.
867function findMaxLine(cm) {
868 var d = cm.display, doc = cm.doc
869 d.maxLine = getLine(doc, doc.first)
870 d.maxLineLength = lineLength(d.maxLine)
871 d.maxLineChanged = true
872 doc.iter(function (line) {
873 var len = lineLength(line)
874 if (len > d.maxLineLength) {
875 d.maxLineLength = len
876 d.maxLine = line
877 }
878 })
879}
880
881// BIDI HELPERS
882
883function iterateBidiSections(order, from, to, f) {
884 if (!order) { return f(from, to, "ltr") }
885 var found = false
886 for (var i = 0; i < order.length; ++i) {
887 var part = order[i]
888 if (part.from < to && part.to > from || from == to && part.to == from) {
889 f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr")
890 found = true
891 }
892 }
893 if (!found) { f(from, to, "ltr") }
894}
895
896function bidiLeft(part) { return part.level % 2 ? part.to : part.from }
897function bidiRight(part) { return part.level % 2 ? part.from : part.to }
898
899function lineLeft(line) { var order = getOrder(line); return order ? bidiLeft(order[0]) : 0 }
900function lineRight(line) {
901 var order = getOrder(line)
902 if (!order) { return line.text.length }
903 return bidiRight(lst(order))
904}
905
906function compareBidiLevel(order, a, b) {
907 var linedir = order[0].level
908 if (a == linedir) { return true }
909 if (b == linedir) { return false }
910 return a < b
911}
912
913var bidiOther = null
914function getBidiPartAt(order, pos) {
915 var found
916 bidiOther = null
917 for (var i = 0; i < order.length; ++i) {
918 var cur = order[i]
919 if (cur.from < pos && cur.to > pos) { return i }
920 if ((cur.from == pos || cur.to == pos)) {
921 if (found == null) {
922 found = i
923 } else if (compareBidiLevel(order, cur.level, order[found].level)) {
924 if (cur.from != cur.to) { bidiOther = found }
925 return i
926 } else {
927 if (cur.from != cur.to) { bidiOther = i }
928 return found
929 }
930 }
931 }
932 return found
933}
934
935function moveInLine(line, pos, dir, byUnit) {
936 if (!byUnit) { return pos + dir }
937 do { pos += dir }
938 while (pos > 0 && isExtendingChar(line.text.charAt(pos)))
939 return pos
940}
941
942// This is needed in order to move 'visually' through bi-directional
943// text -- i.e., pressing left should make the cursor go left, even
944// when in RTL text. The tricky part is the 'jumps', where RTL and
945// LTR text touch each other. This often requires the cursor offset
946// to move more than one unit, in order to visually move one unit.
947function moveVisually(line, start, dir, byUnit) {
948 var bidi = getOrder(line)
949 if (!bidi) { return moveLogically(line, start, dir, byUnit) }
950 var pos = getBidiPartAt(bidi, start), part = bidi[pos]
951 var target = moveInLine(line, start, part.level % 2 ? -dir : dir, byUnit)
952
953 for (;;) {
954 if (target > part.from && target < part.to) { return target }
955 if (target == part.from || target == part.to) {
956 if (getBidiPartAt(bidi, target) == pos) { return target }
957 part = bidi[pos += dir]
958 return (dir > 0) == part.level % 2 ? part.to : part.from
959 } else {
960 part = bidi[pos += dir]
961 if (!part) { return null }
962 if ((dir > 0) == part.level % 2)
963 { target = moveInLine(line, part.to, -1, byUnit) }
964 else
965 { target = moveInLine(line, part.from, 1, byUnit) }
966 }
967 }
968}
969
970function moveLogically(line, start, dir, byUnit) {
971 var target = start + dir
972 if (byUnit) { while (target > 0 && isExtendingChar(line.text.charAt(target))) { target += dir } }
973 return target < 0 || target > line.text.length ? null : target
974}
975
976// Bidirectional ordering algorithm
977// See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
978// that this (partially) implements.
979
980// One-char codes used for character types:
981// L (L): Left-to-Right
982// R (R): Right-to-Left
983// r (AL): Right-to-Left Arabic
984// 1 (EN): European Number
985// + (ES): European Number Separator
986// % (ET): European Number Terminator
987// n (AN): Arabic Number
988// , (CS): Common Number Separator
989// m (NSM): Non-Spacing Mark
990// b (BN): Boundary Neutral
991// s (B): Paragraph Separator
992// t (S): Segment Separator
993// w (WS): Whitespace
994// N (ON): Other Neutrals
995
996// Returns null if characters are ordered as they appear
997// (left-to-right), or an array of sections ({from, to, level}
998// objects) in the order in which they occur visually.
999var bidiOrdering = (function() {
1000 // Character types for codepoints 0 to 0xff
1001 var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN"
1002 // Character types for codepoints 0x600 to 0x6ff
1003 var arabicTypes = "rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmm"
1004 function charType(code) {
1005 if (code <= 0xf7) { return lowTypes.charAt(code) }
1006 else if (0x590 <= code && code <= 0x5f4) { return "R" }
1007 else if (0x600 <= code && code <= 0x6ed) { return arabicTypes.charAt(code - 0x600) }
1008 else if (0x6ee <= code && code <= 0x8ac) { return "r" }
1009 else if (0x2000 <= code && code <= 0x200b) { return "w" }
1010 else if (code == 0x200c) { return "b" }
1011 else { return "L" }
1012 }
1013
1014 var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/
1015 var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/
1016 // Browsers seem to always treat the boundaries of block elements as being L.
1017 var outerType = "L"
1018
1019 function BidiSpan(level, from, to) {
1020 this.level = level
1021 this.from = from; this.to = to
1022 }
1023
1024 return function(str) {
1025 if (!bidiRE.test(str)) { return false }
1026 var len = str.length, types = []
1027 for (var i = 0; i < len; ++i)
1028 { types.push(charType(str.charCodeAt(i))) }
1029
1030 // W1. Examine each non-spacing mark (NSM) in the level run, and
1031 // change the type of the NSM to the type of the previous
1032 // character. If the NSM is at the start of the level run, it will
1033 // get the type of sor.
1034 for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) {
1035 var type = types[i$1]
1036 if (type == "m") { types[i$1] = prev }
1037 else { prev = type }
1038 }
1039
1040 // W2. Search backwards from each instance of a European number
1041 // until the first strong type (R, L, AL, or sor) is found. If an
1042 // AL is found, change the type of the European number to Arabic
1043 // number.
1044 // W3. Change all ALs to R.
1045 for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) {
1046 var type$1 = types[i$2]
1047 if (type$1 == "1" && cur == "r") { types[i$2] = "n" }
1048 else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == "r") { types[i$2] = "R" } }
1049 }
1050
1051 // W4. A single European separator between two European numbers
1052 // changes to a European number. A single common separator between
1053 // two numbers of the same type changes to that type.
1054 for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) {
1055 var type$2 = types[i$3]
1056 if (type$2 == "+" && prev$1 == "1" && types[i$3+1] == "1") { types[i$3] = "1" }
1057 else if (type$2 == "," && prev$1 == types[i$3+1] &&
1058 (prev$1 == "1" || prev$1 == "n")) { types[i$3] = prev$1 }
1059 prev$1 = type$2
1060 }
1061
1062 // W5. A sequence of European terminators adjacent to European
1063 // numbers changes to all European numbers.
1064 // W6. Otherwise, separators and terminators change to Other
1065 // Neutral.
1066 for (var i$4 = 0; i$4 < len; ++i$4) {
1067 var type$3 = types[i$4]
1068 if (type$3 == ",") { types[i$4] = "N" }
1069 else if (type$3 == "%") {
1070 var end = void 0
1071 for (end = i$4 + 1; end < len && types[end] == "%"; ++end) {}
1072 var replace = (i$4 && types[i$4-1] == "!") || (end < len && types[end] == "1") ? "1" : "N"
1073 for (var j = i$4; j < end; ++j) { types[j] = replace }
1074 i$4 = end - 1
1075 }
1076 }
1077
1078 // W7. Search backwards from each instance of a European number
1079 // until the first strong type (R, L, or sor) is found. If an L is
1080 // found, then change the type of the European number to L.
1081 for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) {
1082 var type$4 = types[i$5]
1083 if (cur$1 == "L" && type$4 == "1") { types[i$5] = "L" }
1084 else if (isStrong.test(type$4)) { cur$1 = type$4 }
1085 }
1086
1087 // N1. A sequence of neutrals takes the direction of the
1088 // surrounding strong text if the text on both sides has the same
1089 // direction. European and Arabic numbers act as if they were R in
1090 // terms of their influence on neutrals. Start-of-level-run (sor)
1091 // and end-of-level-run (eor) are used at level run boundaries.
1092 // N2. Any remaining neutrals take the embedding direction.
1093 for (var i$6 = 0; i$6 < len; ++i$6) {
1094 if (isNeutral.test(types[i$6])) {
1095 var end$1 = void 0
1096 for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {}
1097 var before = (i$6 ? types[i$6-1] : outerType) == "L"
1098 var after = (end$1 < len ? types[end$1] : outerType) == "L"
1099 var replace$1 = before || after ? "L" : "R"
1100 for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1 }
1101 i$6 = end$1 - 1
1102 }
1103 }
1104
1105 // Here we depart from the documented algorithm, in order to avoid
1106 // building up an actual levels array. Since there are only three
1107 // levels (0, 1, 2) in an implementation that doesn't take
1108 // explicit embedding into account, we can build up the order on
1109 // the fly, without following the level-based algorithm.
1110 var order = [], m
1111 for (var i$7 = 0; i$7 < len;) {
1112 if (countsAsLeft.test(types[i$7])) {
1113 var start = i$7
1114 for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {}
1115 order.push(new BidiSpan(0, start, i$7))
1116 } else {
1117 var pos = i$7, at = order.length
1118 for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {}
1119 for (var j$2 = pos; j$2 < i$7;) {
1120 if (countsAsNum.test(types[j$2])) {
1121 if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)) }
1122 var nstart = j$2
1123 for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {}
1124 order.splice(at, 0, new BidiSpan(2, nstart, j$2))
1125 pos = j$2
1126 } else { ++j$2 }
1127 }
1128 if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)) }
1129 }
1130 }
1131 if (order[0].level == 1 && (m = str.match(/^\s+/))) {
1132 order[0].from = m[0].length
1133 order.unshift(new BidiSpan(0, 0, m[0].length))
1134 }
1135 if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
1136 lst(order).to -= m[0].length
1137 order.push(new BidiSpan(0, len - m[0].length, len))
1138 }
1139 if (order[0].level == 2)
1140 { order.unshift(new BidiSpan(1, order[0].to, order[0].to)) }
1141 if (order[0].level != lst(order).level)
1142 { order.push(new BidiSpan(order[0].level, len, len)) }
1143
1144 return order
1145 }
1146})()
1147
1148// Get the bidi ordering for the given line (and cache it). Returns
1149// false for lines that are fully left-to-right, and an array of
1150// BidiSpan objects otherwise.
1151function getOrder(line) {
1152 var order = line.order
1153 if (order == null) { order = line.order = bidiOrdering(line.text) }
1154 return order
1155}
1156
1157// EVENT HANDLING
1158
1159// Lightweight event framework. on/off also work on DOM nodes,
1160// registering native DOM handlers.
1161
1162var noHandlers = []
1163
1164var on = function(emitter, type, f) {
1165 if (emitter.addEventListener) {
1166 emitter.addEventListener(type, f, false)
1167 } else if (emitter.attachEvent) {
1168 emitter.attachEvent("on" + type, f)
1169 } else {
1170 var map$$1 = emitter._handlers || (emitter._handlers = {})
1171 map$$1[type] = (map$$1[type] || noHandlers).concat(f)
1172 }
1173}
1174
1175function getHandlers(emitter, type) {
1176 return emitter._handlers && emitter._handlers[type] || noHandlers
1177}
1178
1179function off(emitter, type, f) {
1180 if (emitter.removeEventListener) {
1181 emitter.removeEventListener(type, f, false)
1182 } else if (emitter.detachEvent) {
1183 emitter.detachEvent("on" + type, f)
1184 } else {
1185 var map$$1 = emitter._handlers, arr = map$$1 && map$$1[type]
1186 if (arr) {
1187 var index = indexOf(arr, f)
1188 if (index > -1)
1189 { map$$1[type] = arr.slice(0, index).concat(arr.slice(index + 1)) }
1190 }
1191 }
1192}
1193
1194function signal(emitter, type /*, values...*/) {
1195 var handlers = getHandlers(emitter, type)
1196 if (!handlers.length) { return }
1197 var args = Array.prototype.slice.call(arguments, 2)
1198 for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args) }
1199}
1200
1201// The DOM events that CodeMirror handles can be overridden by
1202// registering a (non-DOM) handler on the editor for the event name,
1203// and preventDefault-ing the event in that handler.
1204function signalDOMEvent(cm, e, override) {
1205 if (typeof e == "string")
1206 { e = {type: e, preventDefault: function() { this.defaultPrevented = true }} }
1207 signal(cm, override || e.type, cm, e)
1208 return e_defaultPrevented(e) || e.codemirrorIgnore
1209}
1210
1211function signalCursorActivity(cm) {
1212 var arr = cm._handlers && cm._handlers.cursorActivity
1213 if (!arr) { return }
1214 var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = [])
1215 for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1)
1216 { set.push(arr[i]) } }
1217}
1218
1219function hasHandler(emitter, type) {
1220 return getHandlers(emitter, type).length > 0
1221}
1222
1223// Add on and off methods to a constructor's prototype, to make
1224// registering events on such objects more convenient.
1225function eventMixin(ctor) {
1226 ctor.prototype.on = function(type, f) {on(this, type, f)}
1227 ctor.prototype.off = function(type, f) {off(this, type, f)}
1228}
1229
1230// Due to the fact that we still support jurassic IE versions, some
1231// compatibility wrappers are needed.
1232
1233function e_preventDefault(e) {
1234 if (e.preventDefault) { e.preventDefault() }
1235 else { e.returnValue = false }
1236}
1237function e_stopPropagation(e) {
1238 if (e.stopPropagation) { e.stopPropagation() }
1239 else { e.cancelBubble = true }
1240}
1241function e_defaultPrevented(e) {
1242 return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false
1243}
1244function e_stop(e) {e_preventDefault(e); e_stopPropagation(e)}
1245
1246function e_target(e) {return e.target || e.srcElement}
1247function e_button(e) {
1248 var b = e.which
1249 if (b == null) {
1250 if (e.button & 1) { b = 1 }
1251 else if (e.button & 2) { b = 3 }
1252 else if (e.button & 4) { b = 2 }
1253 }
1254 if (mac && e.ctrlKey && b == 1) { b = 3 }
1255 return b
1256}
1257
1258// Detect drag-and-drop
1259var dragAndDrop = function() {
1260 // There is *some* kind of drag-and-drop support in IE6-8, but I
1261 // couldn't get it to work yet.
1262 if (ie && ie_version < 9) { return false }
1263 var div = elt('div')
1264 return "draggable" in div || "dragDrop" in div
1265}()
1266
1267var zwspSupported
1268function zeroWidthElement(measure) {
1269 if (zwspSupported == null) {
1270 var test = elt("span", "\u200b")
1271 removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]))
1272 if (measure.firstChild.offsetHeight != 0)
1273 { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8) }
1274 }
1275 var node = zwspSupported ? elt("span", "\u200b") :
1276 elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px")
1277 node.setAttribute("cm-text", "")
1278 return node
1279}
1280
1281// Feature-detect IE's crummy client rect reporting for bidi text
1282var badBidiRects
1283function hasBadBidiRects(measure) {
1284 if (badBidiRects != null) { return badBidiRects }
1285 var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA"))
1286 var r0 = range(txt, 0, 1).getBoundingClientRect()
1287 var r1 = range(txt, 1, 2).getBoundingClientRect()
1288 removeChildren(measure)
1289 if (!r0 || r0.left == r0.right) { return false } // Safari returns null in some cases (#2780)
1290 return badBidiRects = (r1.right - r0.right < 3)
1291}
1292
1293// See if "".split is the broken IE version, if so, provide an
1294// alternative way to split lines.
1295var splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function (string) {
1296 var pos = 0, result = [], l = string.length
1297 while (pos <= l) {
1298 var nl = string.indexOf("\n", pos)
1299 if (nl == -1) { nl = string.length }
1300 var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl)
1301 var rt = line.indexOf("\r")
1302 if (rt != -1) {
1303 result.push(line.slice(0, rt))
1304 pos += rt + 1
1305 } else {
1306 result.push(line)
1307 pos = nl + 1
1308 }
1309 }
1310 return result
1311} : function (string) { return string.split(/\r\n?|\n/); }
1312
1313var hasSelection = window.getSelection ? function (te) {
1314 try { return te.selectionStart != te.selectionEnd }
1315 catch(e) { return false }
1316} : function (te) {
1317 var range$$1
1318 try {range$$1 = te.ownerDocument.selection.createRange()}
1319 catch(e) {}
1320 if (!range$$1 || range$$1.parentElement() != te) { return false }
1321 return range$$1.compareEndPoints("StartToEnd", range$$1) != 0
1322}
1323
1324var hasCopyEvent = (function () {
1325 var e = elt("div")
1326 if ("oncopy" in e) { return true }
1327 e.setAttribute("oncopy", "return;")
1328 return typeof e.oncopy == "function"
1329})()
1330
1331var badZoomedRects = null
1332function hasBadZoomedRects(measure) {
1333 if (badZoomedRects != null) { return badZoomedRects }
1334 var node = removeChildrenAndAdd(measure, elt("span", "x"))
1335 var normal = node.getBoundingClientRect()
1336 var fromRange = range(node, 0, 1).getBoundingClientRect()
1337 return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1
1338}
1339
1340// Known modes, by name and by MIME
1341var modes = {};
1342var mimeModes = {}
1343
1344// Extra arguments are stored as the mode's dependencies, which is
1345// used by (legacy) mechanisms like loadmode.js to automatically
1346// load a mode. (Preferred mechanism is the require/define calls.)
1347function defineMode(name, mode) {
1348 if (arguments.length > 2)
1349 { mode.dependencies = Array.prototype.slice.call(arguments, 2) }
1350 modes[name] = mode
1351}
1352
1353function defineMIME(mime, spec) {
1354 mimeModes[mime] = spec
1355}
1356
1357// Given a MIME type, a {name, ...options} config object, or a name
1358// string, return a mode config object.
1359function resolveMode(spec) {
1360 if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
1361 spec = mimeModes[spec]
1362 } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
1363 var found = mimeModes[spec.name]
1364 if (typeof found == "string") { found = {name: found} }
1365 spec = createObj(found, spec)
1366 spec.name = found.name
1367 } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
1368 return resolveMode("application/xml")
1369 } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) {
1370 return resolveMode("application/json")
1371 }
1372 if (typeof spec == "string") { return {name: spec} }
1373 else { return spec || {name: "null"} }
1374}
1375
1376// Given a mode spec (anything that resolveMode accepts), find and
1377// initialize an actual mode object.
1378function getMode(options, spec) {
1379 spec = resolveMode(spec)
1380 var mfactory = modes[spec.name]
1381 if (!mfactory) { return getMode(options, "text/plain") }
1382 var modeObj = mfactory(options, spec)
1383 if (modeExtensions.hasOwnProperty(spec.name)) {
1384 var exts = modeExtensions[spec.name]
1385 for (var prop in exts) {
1386 if (!exts.hasOwnProperty(prop)) { continue }
1387 if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop] }
1388 modeObj[prop] = exts[prop]
1389 }
1390 }
1391 modeObj.name = spec.name
1392 if (spec.helperType) { modeObj.helperType = spec.helperType }
1393 if (spec.modeProps) { for (var prop$1 in spec.modeProps)
1394 { modeObj[prop$1] = spec.modeProps[prop$1] } }
1395
1396 return modeObj
1397}
1398
1399// This can be used to attach properties to mode objects from
1400// outside the actual mode definition.
1401var modeExtensions = {}
1402function extendMode(mode, properties) {
1403 var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {})
1404 copyObj(properties, exts)
1405}
1406
1407function copyState(mode, state) {
1408 if (state === true) { return state }
1409 if (mode.copyState) { return mode.copyState(state) }
1410 var nstate = {}
1411 for (var n in state) {
1412 var val = state[n]
1413 if (val instanceof Array) { val = val.concat([]) }
1414 nstate[n] = val
1415 }
1416 return nstate
1417}
1418
1419// Given a mode and a state (for that mode), find the inner mode and
1420// state at the position that the state refers to.
1421function innerMode(mode, state) {
1422 var info
1423 while (mode.innerMode) {
1424 info = mode.innerMode(state)
1425 if (!info || info.mode == mode) { break }
1426 state = info.state
1427 mode = info.mode
1428 }
1429 return info || {mode: mode, state: state}
1430}
1431
1432function startState(mode, a1, a2) {
1433 return mode.startState ? mode.startState(a1, a2) : true
1434}
1435
1436// STRING STREAM
1437
1438// Fed to the mode parsers, provides helper functions to make
1439// parsers more succinct.
1440
1441var StringStream = function(string, tabSize) {
1442 this.pos = this.start = 0
1443 this.string = string
1444 this.tabSize = tabSize || 8
1445 this.lastColumnPos = this.lastColumnValue = 0
1446 this.lineStart = 0
1447}
1448
1449StringStream.prototype = {
1450 eol: function() {return this.pos >= this.string.length},
1451 sol: function() {return this.pos == this.lineStart},
1452 peek: function() {return this.string.charAt(this.pos) || undefined},
1453 next: function() {
1454 if (this.pos < this.string.length)
1455 { return this.string.charAt(this.pos++) }
1456 },
1457 eat: function(match) {
1458 var ch = this.string.charAt(this.pos)
1459 var ok
1460 if (typeof match == "string") { ok = ch == match }
1461 else { ok = ch && (match.test ? match.test(ch) : match(ch)) }
1462 if (ok) {++this.pos; return ch}
1463 },
1464 eatWhile: function(match) {
1465 var start = this.pos
1466 while (this.eat(match)){}
1467 return this.pos > start
1468 },
1469 eatSpace: function() {
1470 var this$1 = this;
1471
1472 var start = this.pos
1473 while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this$1.pos }
1474 return this.pos > start
1475 },
1476 skipToEnd: function() {this.pos = this.string.length},
1477 skipTo: function(ch) {
1478 var found = this.string.indexOf(ch, this.pos)
1479 if (found > -1) {this.pos = found; return true}
1480 },
1481 backUp: function(n) {this.pos -= n},
1482 column: function() {
1483 if (this.lastColumnPos < this.start) {
1484 this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue)
1485 this.lastColumnPos = this.start
1486 }
1487 return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
1488 },
1489 indentation: function() {
1490 return countColumn(this.string, null, this.tabSize) -
1491 (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
1492 },
1493 match: function(pattern, consume, caseInsensitive) {
1494 if (typeof pattern == "string") {
1495 var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; }
1496 var substr = this.string.substr(this.pos, pattern.length)
1497 if (cased(substr) == cased(pattern)) {
1498 if (consume !== false) { this.pos += pattern.length }
1499 return true
1500 }
1501 } else {
1502 var match = this.string.slice(this.pos).match(pattern)
1503 if (match && match.index > 0) { return null }
1504 if (match && consume !== false) { this.pos += match[0].length }
1505 return match
1506 }
1507 },
1508 current: function(){return this.string.slice(this.start, this.pos)},
1509 hideFirstChars: function(n, inner) {
1510 this.lineStart += n
1511 try { return inner() }
1512 finally { this.lineStart -= n }
1513 }
1514}
1515
1516// Compute a style array (an array starting with a mode generation
1517// -- for invalidation -- followed by pairs of end positions and
1518// style strings), which is used to highlight the tokens on the
1519// line.
1520function highlightLine(cm, line, state, forceToEnd) {
1521 // A styles array always starts with a number identifying the
1522 // mode/overlays that it is based on (for easy invalidation).
1523 var st = [cm.state.modeGen], lineClasses = {}
1524 // Compute the base array of styles
1525 runMode(cm, line.text, cm.doc.mode, state, function (end, style) { return st.push(end, style); },
1526 lineClasses, forceToEnd)
1527
1528 // Run overlays, adjust style array.
1529 var loop = function ( o ) {
1530 var overlay = cm.state.overlays[o], i = 1, at = 0
1531 runMode(cm, line.text, overlay.mode, true, function (end, style) {
1532 var start = i
1533 // Ensure there's a token end at the current position, and that i points at it
1534 while (at < end) {
1535 var i_end = st[i]
1536 if (i_end > end)
1537 { st.splice(i, 1, end, st[i+1], i_end) }
1538 i += 2
1539 at = Math.min(end, i_end)
1540 }
1541 if (!style) { return }
1542 if (overlay.opaque) {
1543 st.splice(start, i - start, end, "overlay " + style)
1544 i = start + 2
1545 } else {
1546 for (; start < i; start += 2) {
1547 var cur = st[start+1]
1548 st[start+1] = (cur ? cur + " " : "") + "overlay " + style
1549 }
1550 }
1551 }, lineClasses)
1552 };
1553
1554 for (var o = 0; o < cm.state.overlays.length; ++o) loop( o );
1555
1556 return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null}
1557}
1558
1559function getLineStyles(cm, line, updateFrontier) {
1560 if (!line.styles || line.styles[0] != cm.state.modeGen) {
1561 var state = getStateBefore(cm, lineNo(line))
1562 var result = highlightLine(cm, line, line.text.length > cm.options.maxHighlightLength ? copyState(cm.doc.mode, state) : state)
1563 line.stateAfter = state
1564 line.styles = result.styles
1565 if (result.classes) { line.styleClasses = result.classes }
1566 else if (line.styleClasses) { line.styleClasses = null }
1567 if (updateFrontier === cm.doc.frontier) { cm.doc.frontier++ }
1568 }
1569 return line.styles
1570}
1571
1572function getStateBefore(cm, n, precise) {
1573 var doc = cm.doc, display = cm.display
1574 if (!doc.mode.startState) { return true }
1575 var pos = findStartLine(cm, n, precise), state = pos > doc.first && getLine(doc, pos-1).stateAfter
1576 if (!state) { state = startState(doc.mode) }
1577 else { state = copyState(doc.mode, state) }
1578 doc.iter(pos, n, function (line) {
1579 processLine(cm, line.text, state)
1580 var save = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo
1581 line.stateAfter = save ? copyState(doc.mode, state) : null
1582 ++pos
1583 })
1584 if (precise) { doc.frontier = pos }
1585 return state
1586}
1587
1588// Lightweight form of highlight -- proceed over this line and
1589// update state, but don't save a style array. Used for lines that
1590// aren't currently visible.
1591function processLine(cm, text, state, startAt) {
1592 var mode = cm.doc.mode
1593 var stream = new StringStream(text, cm.options.tabSize)
1594 stream.start = stream.pos = startAt || 0
1595 if (text == "") { callBlankLine(mode, state) }
1596 while (!stream.eol()) {
1597 readToken(mode, stream, state)
1598 stream.start = stream.pos
1599 }
1600}
1601
1602function callBlankLine(mode, state) {
1603 if (mode.blankLine) { return mode.blankLine(state) }
1604 if (!mode.innerMode) { return }
1605 var inner = innerMode(mode, state)
1606 if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) }
1607}
1608
1609function readToken(mode, stream, state, inner) {
1610 for (var i = 0; i < 10; i++) {
1611 if (inner) { inner[0] = innerMode(mode, state).mode }
1612 var style = mode.token(stream, state)
1613 if (stream.pos > stream.start) { return style }
1614 }
1615 throw new Error("Mode " + mode.name + " failed to advance stream.")
1616}
1617
1618// Utility for getTokenAt and getLineTokens
1619function takeToken(cm, pos, precise, asArray) {
1620 var getObj = function (copy) { return ({
1621 start: stream.start, end: stream.pos,
1622 string: stream.current(),
1623 type: style || null,
1624 state: copy ? copyState(doc.mode, state) : state
1625 }); }
1626
1627 var doc = cm.doc, mode = doc.mode, style
1628 pos = clipPos(doc, pos)
1629 var line = getLine(doc, pos.line), state = getStateBefore(cm, pos.line, precise)
1630 var stream = new StringStream(line.text, cm.options.tabSize), tokens
1631 if (asArray) { tokens = [] }
1632 while ((asArray || stream.pos < pos.ch) && !stream.eol()) {
1633 stream.start = stream.pos
1634 style = readToken(mode, stream, state)
1635 if (asArray) { tokens.push(getObj(true)) }
1636 }
1637 return asArray ? tokens : getObj()
1638}
1639
1640function extractLineClasses(type, output) {
1641 if (type) { for (;;) {
1642 var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/)
1643 if (!lineClass) { break }
1644 type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length)
1645 var prop = lineClass[1] ? "bgClass" : "textClass"
1646 if (output[prop] == null)
1647 { output[prop] = lineClass[2] }
1648 else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop]))
1649 { output[prop] += " " + lineClass[2] }
1650 } }
1651 return type
1652}
1653
1654// Run the given mode's parser over a line, calling f for each token.
1655function runMode(cm, text, mode, state, f, lineClasses, forceToEnd) {
1656 var flattenSpans = mode.flattenSpans
1657 if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans }
1658 var curStart = 0, curStyle = null
1659 var stream = new StringStream(text, cm.options.tabSize), style
1660 var inner = cm.options.addModeClass && [null]
1661 if (text == "") { extractLineClasses(callBlankLine(mode, state), lineClasses) }
1662 while (!stream.eol()) {
1663 if (stream.pos > cm.options.maxHighlightLength) {
1664 flattenSpans = false
1665 if (forceToEnd) { processLine(cm, text, state, stream.pos) }
1666 stream.pos = text.length
1667 style = null
1668 } else {
1669 style = extractLineClasses(readToken(mode, stream, state, inner), lineClasses)
1670 }
1671 if (inner) {
1672 var mName = inner[0].name
1673 if (mName) { style = "m-" + (style ? mName + " " + style : mName) }
1674 }
1675 if (!flattenSpans || curStyle != style) {
1676 while (curStart < stream.start) {
1677 curStart = Math.min(stream.start, curStart + 5000)
1678 f(curStart, curStyle)
1679 }
1680 curStyle = style
1681 }
1682 stream.start = stream.pos
1683 }
1684 while (curStart < stream.pos) {
1685 // Webkit seems to refuse to render text nodes longer than 57444
1686 // characters, and returns inaccurate measurements in nodes
1687 // starting around 5000 chars.
1688 var pos = Math.min(stream.pos, curStart + 5000)
1689 f(pos, curStyle)
1690 curStart = pos
1691 }
1692}
1693
1694// Finds the line to start with when starting a parse. Tries to
1695// find a line with a stateAfter, so that it can start with a
1696// valid state. If that fails, it returns the line with the
1697// smallest indentation, which tends to need the least context to
1698// parse correctly.
1699function findStartLine(cm, n, precise) {
1700 var minindent, minline, doc = cm.doc
1701 var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100)
1702 for (var search = n; search > lim; --search) {
1703 if (search <= doc.first) { return doc.first }
1704 var line = getLine(doc, search - 1)
1705 if (line.stateAfter && (!precise || search <= doc.frontier)) { return search }
1706 var indented = countColumn(line.text, null, cm.options.tabSize)
1707 if (minline == null || minindent > indented) {
1708 minline = search - 1
1709 minindent = indented
1710 }
1711 }
1712 return minline
1713}
1714
1715// LINE DATA STRUCTURE
1716
1717// Line objects. These hold state related to a line, including
1718// highlighting info (the styles array).
1719function Line(text, markedSpans, estimateHeight) {
1720 this.text = text
1721 attachMarkedSpans(this, markedSpans)
1722 this.height = estimateHeight ? estimateHeight(this) : 1
1723}
1724eventMixin(Line)
1725Line.prototype.lineNo = function() { return lineNo(this) }
1726
1727// Change the content (text, markers) of a line. Automatically
1728// invalidates cached information and tries to re-estimate the
1729// line's height.
1730function updateLine(line, text, markedSpans, estimateHeight) {
1731 line.text = text
1732 if (line.stateAfter) { line.stateAfter = null }
1733 if (line.styles) { line.styles = null }
1734 if (line.order != null) { line.order = null }
1735 detachMarkedSpans(line)
1736 attachMarkedSpans(line, markedSpans)
1737 var estHeight = estimateHeight ? estimateHeight(line) : 1
1738 if (estHeight != line.height) { updateLineHeight(line, estHeight) }
1739}
1740
1741// Detach a line from the document tree and its markers.
1742function cleanUpLine(line) {
1743 line.parent = null
1744 detachMarkedSpans(line)
1745}
1746
1747// Convert a style as returned by a mode (either null, or a string
1748// containing one or more styles) to a CSS style. This is cached,
1749// and also looks for line-wide styles.
1750var styleToClassCache = {};
1751var styleToClassCacheWithMode = {}
1752function interpretTokenStyle(style, options) {
1753 if (!style || /^\s*$/.test(style)) { return null }
1754 var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache
1755 return cache[style] ||
1756 (cache[style] = style.replace(/\S+/g, "cm-$&"))
1757}
1758
1759// Render the DOM representation of the text of a line. Also builds
1760// up a 'line map', which points at the DOM nodes that represent
1761// specific stretches of text, and is used by the measuring code.
1762// The returned object contains the DOM node, this map, and
1763// information about line-wide styles that were set by the mode.
1764function buildLineContent(cm, lineView) {
1765 // The padding-right forces the element to have a 'border', which
1766 // is needed on Webkit to be able to get line-level bounding
1767 // rectangles for it (in measureChar).
1768 var content = elt("span", null, null, webkit ? "padding-right: .1px" : null)
1769 var builder = {pre: elt("pre", [content], "CodeMirror-line"), content: content,
1770 col: 0, pos: 0, cm: cm,
1771 trailingSpace: false,
1772 splitSpaces: (ie || webkit) && cm.getOption("lineWrapping")}
1773 lineView.measure = {}
1774
1775 // Iterate over the logical lines that make up this visual line.
1776 for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {
1777 var line = i ? lineView.rest[i - 1] : lineView.line, order = void 0
1778 builder.pos = 0
1779 builder.addToken = buildToken
1780 // Optionally wire in some hacks into the token-rendering
1781 // algorithm, to deal with browser quirks.
1782 if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line)))
1783 { builder.addToken = buildTokenBadBidi(builder.addToken, order) }
1784 builder.map = []
1785 var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line)
1786 insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate))
1787 if (line.styleClasses) {
1788 if (line.styleClasses.bgClass)
1789 { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || "") }
1790 if (line.styleClasses.textClass)
1791 { builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || "") }
1792 }
1793
1794 // Ensure at least a single node is present, for measuring.
1795 if (builder.map.length == 0)
1796 { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))) }
1797
1798 // Store the map and a cache object for the current logical line
1799 if (i == 0) {
1800 lineView.measure.map = builder.map
1801 lineView.measure.cache = {}
1802 } else {
1803 (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map)
1804 ;(lineView.measure.caches || (lineView.measure.caches = [])).push({})
1805 }
1806 }
1807
1808 // See issue #2901
1809 if (webkit) {
1810 var last = builder.content.lastChild
1811 if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab")))
1812 { builder.content.className = "cm-tab-wrap-hack" }
1813 }
1814
1815 signal(cm, "renderLine", cm, lineView.line, builder.pre)
1816 if (builder.pre.className)
1817 { builder.textClass = joinClasses(builder.pre.className, builder.textClass || "") }
1818
1819 return builder
1820}
1821
1822function defaultSpecialCharPlaceholder(ch) {
1823 var token = elt("span", "\u2022", "cm-invalidchar")
1824 token.title = "\\u" + ch.charCodeAt(0).toString(16)
1825 token.setAttribute("aria-label", token.title)
1826 return token
1827}
1828
1829// Build up the DOM representation for a single token, and add it to
1830// the line map. Takes care to render special characters separately.
1831function buildToken(builder, text, style, startStyle, endStyle, title, css) {
1832 if (!text) { return }
1833 var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text
1834 var special = builder.cm.state.specialChars, mustWrap = false
1835 var content
1836 if (!special.test(text)) {
1837 builder.col += text.length
1838 content = document.createTextNode(displayText)
1839 builder.map.push(builder.pos, builder.pos + text.length, content)
1840 if (ie && ie_version < 9) { mustWrap = true }
1841 builder.pos += text.length
1842 } else {
1843 content = document.createDocumentFragment()
1844 var pos = 0
1845 while (true) {
1846 special.lastIndex = pos
1847 var m = special.exec(text)
1848 var skipped = m ? m.index - pos : text.length - pos
1849 if (skipped) {
1850 var txt = document.createTextNode(displayText.slice(pos, pos + skipped))
1851 if (ie && ie_version < 9) { content.appendChild(elt("span", [txt])) }
1852 else { content.appendChild(txt) }
1853 builder.map.push(builder.pos, builder.pos + skipped, txt)
1854 builder.col += skipped
1855 builder.pos += skipped
1856 }
1857 if (!m) { break }
1858 pos += skipped + 1
1859 var txt$1 = void 0
1860 if (m[0] == "\t") {
1861 var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize
1862 txt$1 = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"))
1863 txt$1.setAttribute("role", "presentation")
1864 txt$1.setAttribute("cm-text", "\t")
1865 builder.col += tabWidth
1866 } else if (m[0] == "\r" || m[0] == "\n") {
1867 txt$1 = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar"))
1868 txt$1.setAttribute("cm-text", m[0])
1869 builder.col += 1
1870 } else {
1871 txt$1 = builder.cm.options.specialCharPlaceholder(m[0])
1872 txt$1.setAttribute("cm-text", m[0])
1873 if (ie && ie_version < 9) { content.appendChild(elt("span", [txt$1])) }
1874 else { content.appendChild(txt$1) }
1875 builder.col += 1
1876 }
1877 builder.map.push(builder.pos, builder.pos + 1, txt$1)
1878 builder.pos++
1879 }
1880 }
1881 builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32
1882 if (style || startStyle || endStyle || mustWrap || css) {
1883 var fullStyle = style || ""
1884 if (startStyle) { fullStyle += startStyle }
1885 if (endStyle) { fullStyle += endStyle }
1886 var token = elt("span", [content], fullStyle, css)
1887 if (title) { token.title = title }
1888 return builder.content.appendChild(token)
1889 }
1890 builder.content.appendChild(content)
1891}
1892
1893function splitSpaces(text, trailingBefore) {
1894 if (text.length > 1 && !/ /.test(text)) { return text }
1895 var spaceBefore = trailingBefore, result = ""
1896 for (var i = 0; i < text.length; i++) {
1897 var ch = text.charAt(i)
1898 if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32))
1899 { ch = "\u00a0" }
1900 result += ch
1901 spaceBefore = ch == " "
1902 }
1903 return result
1904}
1905
1906// Work around nonsense dimensions being reported for stretches of
1907// right-to-left text.
1908function buildTokenBadBidi(inner, order) {
1909 return function (builder, text, style, startStyle, endStyle, title, css) {
1910 style = style ? style + " cm-force-border" : "cm-force-border"
1911 var start = builder.pos, end = start + text.length
1912 for (;;) {
1913 // Find the part that overlaps with the start of this text
1914 var part = void 0
1915 for (var i = 0; i < order.length; i++) {
1916 part = order[i]
1917 if (part.to > start && part.from <= start) { break }
1918 }
1919 if (part.to >= end) { return inner(builder, text, style, startStyle, endStyle, title, css) }
1920 inner(builder, text.slice(0, part.to - start), style, startStyle, null, title, css)
1921 startStyle = null
1922 text = text.slice(part.to - start)
1923 start = part.to
1924 }
1925 }
1926}
1927
1928function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
1929 var widget = !ignoreWidget && marker.widgetNode
1930 if (widget) { builder.map.push(builder.pos, builder.pos + size, widget) }
1931 if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {
1932 if (!widget)
1933 { widget = builder.content.appendChild(document.createElement("span")) }
1934 widget.setAttribute("cm-marker", marker.id)
1935 }
1936 if (widget) {
1937 builder.cm.display.input.setUneditable(widget)
1938 builder.content.appendChild(widget)
1939 }
1940 builder.pos += size
1941 builder.trailingSpace = false
1942}
1943
1944// Outputs a number of spans to make up a line, taking highlighting
1945// and marked text into account.
1946function insertLineContent(line, builder, styles) {
1947 var spans = line.markedSpans, allText = line.text, at = 0
1948 if (!spans) {
1949 for (var i$1 = 1; i$1 < styles.length; i$1+=2)
1950 { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpretTokenStyle(styles[i$1+1], builder.cm.options)) }
1951 return
1952 }
1953
1954 var len = allText.length, pos = 0, i = 1, text = "", style, css
1955 var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed
1956 for (;;) {
1957 if (nextChange == pos) { // Update current marker set
1958 spanStyle = spanEndStyle = spanStartStyle = title = css = ""
1959 collapsed = null; nextChange = Infinity
1960 var foundBookmarks = [], endStyles = void 0
1961 for (var j = 0; j < spans.length; ++j) {
1962 var sp = spans[j], m = sp.marker
1963 if (m.type == "bookmark" && sp.from == pos && m.widgetNode) {
1964 foundBookmarks.push(m)
1965 } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) {
1966 if (sp.to != null && sp.to != pos && nextChange > sp.to) {
1967 nextChange = sp.to
1968 spanEndStyle = ""
1969 }
1970 if (m.className) { spanStyle += " " + m.className }
1971 if (m.css) { css = (css ? css + ";" : "") + m.css }
1972 if (m.startStyle && sp.from == pos) { spanStartStyle += " " + m.startStyle }
1973 if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [])).push(m.endStyle, sp.to) }
1974 if (m.title && !title) { title = m.title }
1975 if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))
1976 { collapsed = sp }
1977 } else if (sp.from > pos && nextChange > sp.from) {
1978 nextChange = sp.from
1979 }
1980 }
1981 if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2)
1982 { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += " " + endStyles[j$1] } } }
1983
1984 if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2)
1985 { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]) } }
1986 if (collapsed && (collapsed.from || 0) == pos) {
1987 buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos,
1988 collapsed.marker, collapsed.from == null)
1989 if (collapsed.to == null) { return }
1990 if (collapsed.to == pos) { collapsed = false }
1991 }
1992 }
1993 if (pos >= len) { break }
1994
1995 var upto = Math.min(len, nextChange)
1996 while (true) {
1997 if (text) {
1998 var end = pos + text.length
1999 if (!collapsed) {
2000 var tokenText = end > upto ? text.slice(0, upto - pos) : text
2001 builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
2002 spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", title, css)
2003 }
2004 if (end >= upto) {text = text.slice(upto - pos); pos = upto; break}
2005 pos = end
2006 spanStartStyle = ""
2007 }
2008 text = allText.slice(at, at = styles[i++])
2009 style = interpretTokenStyle(styles[i++], builder.cm.options)
2010 }
2011 }
2012}
2013
2014
2015// These objects are used to represent the visible (currently drawn)
2016// part of the document. A LineView may correspond to multiple
2017// logical lines, if those are connected by collapsed ranges.
2018function LineView(doc, line, lineN) {
2019 // The starting line
2020 this.line = line
2021 // Continuing lines, if any
2022 this.rest = visualLineContinued(line)
2023 // Number of logical lines in this visual line
2024 this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1
2025 this.node = this.text = null
2026 this.hidden = lineIsHidden(doc, line)
2027}
2028
2029// Create a range of LineView objects for the given lines.
2030function buildViewArray(cm, from, to) {
2031 var array = [], nextPos
2032 for (var pos = from; pos < to; pos = nextPos) {
2033 var view = new LineView(cm.doc, getLine(cm.doc, pos), pos)
2034 nextPos = pos + view.size
2035 array.push(view)
2036 }
2037 return array
2038}
2039
2040var operationGroup = null
2041
2042function pushOperation(op) {
2043 if (operationGroup) {
2044 operationGroup.ops.push(op)
2045 } else {
2046 op.ownsGroup = operationGroup = {
2047 ops: [op],
2048 delayedCallbacks: []
2049 }
2050 }
2051}
2052
2053function fireCallbacksForOps(group) {
2054 // Calls delayed callbacks and cursorActivity handlers until no
2055 // new ones appear
2056 var callbacks = group.delayedCallbacks, i = 0
2057 do {
2058 for (; i < callbacks.length; i++)
2059 { callbacks[i].call(null) }
2060 for (var j = 0; j < group.ops.length; j++) {
2061 var op = group.ops[j]
2062 if (op.cursorActivityHandlers)
2063 { while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
2064 { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm) } }
2065 }
2066 } while (i < callbacks.length)
2067}
2068
2069function finishOperation(op, endCb) {
2070 var group = op.ownsGroup
2071 if (!group) { return }
2072
2073 try { fireCallbacksForOps(group) }
2074 finally {
2075 operationGroup = null
2076 endCb(group)
2077 }
2078}
2079
2080var orphanDelayedCallbacks = null
2081
2082// Often, we want to signal events at a point where we are in the
2083// middle of some work, but don't want the handler to start calling
2084// other methods on the editor, which might be in an inconsistent
2085// state or simply not expect any other events to happen.
2086// signalLater looks whether there are any handlers, and schedules
2087// them to be executed when the last operation ends, or, if no
2088// operation is active, when a timeout fires.
2089function signalLater(emitter, type /*, values...*/) {
2090 var arr = getHandlers(emitter, type)
2091 if (!arr.length) { return }
2092 var args = Array.prototype.slice.call(arguments, 2), list
2093 if (operationGroup) {
2094 list = operationGroup.delayedCallbacks
2095 } else if (orphanDelayedCallbacks) {
2096 list = orphanDelayedCallbacks
2097 } else {
2098 list = orphanDelayedCallbacks = []
2099 setTimeout(fireOrphanDelayed, 0)
2100 }
2101 var loop = function ( i ) {
2102 list.push(function () { return arr[i].apply(null, args); })
2103 };
2104
2105 for (var i = 0; i < arr.length; ++i)
2106 loop( i );
2107}
2108
2109function fireOrphanDelayed() {
2110 var delayed = orphanDelayedCallbacks
2111 orphanDelayedCallbacks = null
2112 for (var i = 0; i < delayed.length; ++i) { delayed[i]() }
2113}
2114
2115// When an aspect of a line changes, a string is added to
2116// lineView.changes. This updates the relevant part of the line's
2117// DOM structure.
2118function updateLineForChanges(cm, lineView, lineN, dims) {
2119 for (var j = 0; j < lineView.changes.length; j++) {
2120 var type = lineView.changes[j]
2121 if (type == "text") { updateLineText(cm, lineView) }
2122 else if (type == "gutter") { updateLineGutter(cm, lineView, lineN, dims) }
2123 else if (type == "class") { updateLineClasses(lineView) }
2124 else if (type == "widget") { updateLineWidgets(cm, lineView, dims) }
2125 }
2126 lineView.changes = null
2127}
2128
2129// Lines with gutter elements, widgets or a background class need to
2130// be wrapped, and have the extra elements added to the wrapper div
2131function ensureLineWrapped(lineView) {
2132 if (lineView.node == lineView.text) {
2133 lineView.node = elt("div", null, null, "position: relative")
2134 if (lineView.text.parentNode)
2135 { lineView.text.parentNode.replaceChild(lineView.node, lineView.text) }
2136 lineView.node.appendChild(lineView.text)
2137 if (ie && ie_version < 8) { lineView.node.style.zIndex = 2 }
2138 }
2139 return lineView.node
2140}
2141
2142function updateLineBackground(lineView) {
2143 var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass
2144 if (cls) { cls += " CodeMirror-linebackground" }
2145 if (lineView.background) {
2146 if (cls) { lineView.background.className = cls }
2147 else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null }
2148 } else if (cls) {
2149 var wrap = ensureLineWrapped(lineView)
2150 lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild)
2151 }
2152}
2153
2154// Wrapper around buildLineContent which will reuse the structure
2155// in display.externalMeasured when possible.
2156function getLineContent(cm, lineView) {
2157 var ext = cm.display.externalMeasured
2158 if (ext && ext.line == lineView.line) {
2159 cm.display.externalMeasured = null
2160 lineView.measure = ext.measure
2161 return ext.built
2162 }
2163 return buildLineContent(cm, lineView)
2164}
2165
2166// Redraw the line's text. Interacts with the background and text
2167// classes because the mode may output tokens that influence these
2168// classes.
2169function updateLineText(cm, lineView) {
2170 var cls = lineView.text.className
2171 var built = getLineContent(cm, lineView)
2172 if (lineView.text == lineView.node) { lineView.node = built.pre }
2173 lineView.text.parentNode.replaceChild(built.pre, lineView.text)
2174 lineView.text = built.pre
2175 if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {
2176 lineView.bgClass = built.bgClass
2177 lineView.textClass = built.textClass
2178 updateLineClasses(lineView)
2179 } else if (cls) {
2180 lineView.text.className = cls
2181 }
2182}
2183
2184function updateLineClasses(lineView) {
2185 updateLineBackground(lineView)
2186 if (lineView.line.wrapClass)
2187 { ensureLineWrapped(lineView).className = lineView.line.wrapClass }
2188 else if (lineView.node != lineView.text)
2189 { lineView.node.className = "" }
2190 var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass
2191 lineView.text.className = textClass || ""
2192}
2193
2194function updateLineGutter(cm, lineView, lineN, dims) {
2195 if (lineView.gutter) {
2196 lineView.node.removeChild(lineView.gutter)
2197 lineView.gutter = null
2198 }
2199 if (lineView.gutterBackground) {
2200 lineView.node.removeChild(lineView.gutterBackground)
2201 lineView.gutterBackground = null
2202 }
2203 if (lineView.line.gutterClass) {
2204 var wrap = ensureLineWrapped(lineView)
2205 lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass,
2206 ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px; width: " + (dims.gutterTotalWidth) + "px"))
2207 wrap.insertBefore(lineView.gutterBackground, lineView.text)
2208 }
2209 var markers = lineView.line.gutterMarkers
2210 if (cm.options.lineNumbers || markers) {
2211 var wrap$1 = ensureLineWrapped(lineView)
2212 var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"))
2213 cm.display.input.setUneditable(gutterWrap)
2214 wrap$1.insertBefore(gutterWrap, lineView.text)
2215 if (lineView.line.gutterClass)
2216 { gutterWrap.className += " " + lineView.line.gutterClass }
2217 if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
2218 { lineView.lineNumber = gutterWrap.appendChild(
2219 elt("div", lineNumberFor(cm.options, lineN),
2220 "CodeMirror-linenumber CodeMirror-gutter-elt",
2221 ("left: " + (dims.gutterLeft["CodeMirror-linenumbers"]) + "px; width: " + (cm.display.lineNumInnerWidth) + "px"))) }
2222 if (markers) { for (var k = 0; k < cm.options.gutters.length; ++k) {
2223 var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id]
2224 if (found)
2225 { gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt",
2226 ("left: " + (dims.gutterLeft[id]) + "px; width: " + (dims.gutterWidth[id]) + "px"))) }
2227 } }
2228 }
2229}
2230
2231function updateLineWidgets(cm, lineView, dims) {
2232 if (lineView.alignable) { lineView.alignable = null }
2233 for (var node = lineView.node.firstChild, next = void 0; node; node = next) {
2234 next = node.nextSibling
2235 if (node.className == "CodeMirror-linewidget")
2236 { lineView.node.removeChild(node) }
2237 }
2238 insertLineWidgets(cm, lineView, dims)
2239}
2240
2241// Build a line's DOM representation from scratch
2242function buildLineElement(cm, lineView, lineN, dims) {
2243 var built = getLineContent(cm, lineView)
2244 lineView.text = lineView.node = built.pre
2245 if (built.bgClass) { lineView.bgClass = built.bgClass }
2246 if (built.textClass) { lineView.textClass = built.textClass }
2247
2248 updateLineClasses(lineView)
2249 updateLineGutter(cm, lineView, lineN, dims)
2250 insertLineWidgets(cm, lineView, dims)
2251 return lineView.node
2252}
2253
2254// A lineView may contain multiple logical lines (when merged by
2255// collapsed spans). The widgets for all of them need to be drawn.
2256function insertLineWidgets(cm, lineView, dims) {
2257 insertLineWidgetsFor(cm, lineView.line, lineView, dims, true)
2258 if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
2259 { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false) } }
2260}
2261
2262function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {
2263 if (!line.widgets) { return }
2264 var wrap = ensureLineWrapped(lineView)
2265 for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
2266 var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget")
2267 if (!widget.handleMouseEvents) { node.setAttribute("cm-ignore-events", "true") }
2268 positionLineWidget(widget, node, lineView, dims)
2269 cm.display.input.setUneditable(node)
2270 if (allowAbove && widget.above)
2271 { wrap.insertBefore(node, lineView.gutter || lineView.text) }
2272 else
2273 { wrap.appendChild(node) }
2274 signalLater(widget, "redraw")
2275 }
2276}
2277
2278function positionLineWidget(widget, node, lineView, dims) {
2279 if (widget.noHScroll) {
2280 (lineView.alignable || (lineView.alignable = [])).push(node)
2281 var width = dims.wrapperWidth
2282 node.style.left = dims.fixedPos + "px"
2283 if (!widget.coverGutter) {
2284 width -= dims.gutterTotalWidth
2285 node.style.paddingLeft = dims.gutterTotalWidth + "px"
2286 }
2287 node.style.width = width + "px"
2288 }
2289 if (widget.coverGutter) {
2290 node.style.zIndex = 5
2291 node.style.position = "relative"
2292 if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + "px" }
2293 }
2294}
2295
2296function widgetHeight(widget) {
2297 if (widget.height != null) { return widget.height }
2298 var cm = widget.doc.cm
2299 if (!cm) { return 0 }
2300 if (!contains(document.body, widget.node)) {
2301 var parentStyle = "position: relative;"
2302 if (widget.coverGutter)
2303 { parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;" }
2304 if (widget.noHScroll)
2305 { parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;" }
2306 removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle))
2307 }
2308 return widget.height = widget.node.parentNode.offsetHeight
2309}
2310
2311// Return true when the given mouse event happened in a widget
2312function eventInWidget(display, e) {
2313 for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
2314 if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") ||
2315 (n.parentNode == display.sizer && n != display.mover))
2316 { return true }
2317 }
2318}
2319
2320// POSITION MEASUREMENT
2321
2322function paddingTop(display) {return display.lineSpace.offsetTop}
2323function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight}
2324function paddingH(display) {
2325 if (display.cachedPaddingH) { return display.cachedPaddingH }
2326 var e = removeChildrenAndAdd(display.measure, elt("pre", "x"))
2327 var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle
2328 var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)}
2329 if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data }
2330 return data
2331}
2332
2333function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth }
2334function displayWidth(cm) {
2335 return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth
2336}
2337function displayHeight(cm) {
2338 return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight
2339}
2340
2341// Ensure the lineView.wrapping.heights array is populated. This is
2342// an array of bottom offsets for the lines that make up a drawn
2343// line. When lineWrapping is on, there might be more than one
2344// height.
2345function ensureLineHeights(cm, lineView, rect) {
2346 var wrapping = cm.options.lineWrapping
2347 var curWidth = wrapping && displayWidth(cm)
2348 if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {
2349 var heights = lineView.measure.heights = []
2350 if (wrapping) {
2351 lineView.measure.width = curWidth
2352 var rects = lineView.text.firstChild.getClientRects()
2353 for (var i = 0; i < rects.length - 1; i++) {
2354 var cur = rects[i], next = rects[i + 1]
2355 if (Math.abs(cur.bottom - next.bottom) > 2)
2356 { heights.push((cur.bottom + next.top) / 2 - rect.top) }
2357 }
2358 }
2359 heights.push(rect.bottom - rect.top)
2360 }
2361}
2362
2363// Find a line map (mapping character offsets to text nodes) and a
2364// measurement cache for the given line number. (A line view might
2365// contain multiple lines when collapsed ranges are present.)
2366function mapFromLineView(lineView, line, lineN) {
2367 if (lineView.line == line)
2368 { return {map: lineView.measure.map, cache: lineView.measure.cache} }
2369 for (var i = 0; i < lineView.rest.length; i++)
2370 { if (lineView.rest[i] == line)
2371 { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } }
2372 for (var i$1 = 0; i$1 < lineView.rest.length; i$1++)
2373 { if (lineNo(lineView.rest[i$1]) > lineN)
2374 { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } }
2375}
2376
2377// Render a line into the hidden node display.externalMeasured. Used
2378// when measurement is needed for a line that's not in the viewport.
2379function updateExternalMeasurement(cm, line) {
2380 line = visualLine(line)
2381 var lineN = lineNo(line)
2382 var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN)
2383 view.lineN = lineN
2384 var built = view.built = buildLineContent(cm, view)
2385 view.text = built.pre
2386 removeChildrenAndAdd(cm.display.lineMeasure, built.pre)
2387 return view
2388}
2389
2390// Get a {top, bottom, left, right} box (in line-local coordinates)
2391// for a given character.
2392function measureChar(cm, line, ch, bias) {
2393 return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias)
2394}
2395
2396// Find a line view that corresponds to the given line number.
2397function findViewForLine(cm, lineN) {
2398 if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo)
2399 { return cm.display.view[findViewIndex(cm, lineN)] }
2400 var ext = cm.display.externalMeasured
2401 if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size)
2402 { return ext }
2403}
2404
2405// Measurement can be split in two steps, the set-up work that
2406// applies to the whole line, and the measurement of the actual
2407// character. Functions like coordsChar, that need to do a lot of
2408// measurements in a row, can thus ensure that the set-up work is
2409// only done once.
2410function prepareMeasureForLine(cm, line) {
2411 var lineN = lineNo(line)
2412 var view = findViewForLine(cm, lineN)
2413 if (view && !view.text) {
2414 view = null
2415 } else if (view && view.changes) {
2416 updateLineForChanges(cm, view, lineN, getDimensions(cm))
2417 cm.curOp.forceUpdate = true
2418 }
2419 if (!view)
2420 { view = updateExternalMeasurement(cm, line) }
2421
2422 var info = mapFromLineView(view, line, lineN)
2423 return {
2424 line: line, view: view, rect: null,
2425 map: info.map, cache: info.cache, before: info.before,
2426 hasHeights: false
2427 }
2428}
2429
2430// Given a prepared measurement object, measures the position of an
2431// actual character (or fetches it from the cache).
2432function measureCharPrepared(cm, prepared, ch, bias, varHeight) {
2433 if (prepared.before) { ch = -1 }
2434 var key = ch + (bias || ""), found
2435 if (prepared.cache.hasOwnProperty(key)) {
2436 found = prepared.cache[key]
2437 } else {
2438 if (!prepared.rect)
2439 { prepared.rect = prepared.view.text.getBoundingClientRect() }
2440 if (!prepared.hasHeights) {
2441 ensureLineHeights(cm, prepared.view, prepared.rect)
2442 prepared.hasHeights = true
2443 }
2444 found = measureCharInner(cm, prepared, ch, bias)
2445 if (!found.bogus) { prepared.cache[key] = found }
2446 }
2447 return {left: found.left, right: found.right,
2448 top: varHeight ? found.rtop : found.top,
2449 bottom: varHeight ? found.rbottom : found.bottom}
2450}
2451
2452var nullRect = {left: 0, right: 0, top: 0, bottom: 0}
2453
2454function nodeAndOffsetInLineMap(map$$1, ch, bias) {
2455 var node, start, end, collapse, mStart, mEnd
2456 // First, search the line map for the text node corresponding to,
2457 // or closest to, the target character.
2458 for (var i = 0; i < map$$1.length; i += 3) {
2459 mStart = map$$1[i]
2460 mEnd = map$$1[i + 1]
2461 if (ch < mStart) {
2462 start = 0; end = 1
2463 collapse = "left"
2464 } else if (ch < mEnd) {
2465 start = ch - mStart
2466 end = start + 1
2467 } else if (i == map$$1.length - 3 || ch == mEnd && map$$1[i + 3] > ch) {
2468 end = mEnd - mStart
2469 start = end - 1
2470 if (ch >= mEnd) { collapse = "right" }
2471 }
2472 if (start != null) {
2473 node = map$$1[i + 2]
2474 if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right"))
2475 { collapse = bias }
2476 if (bias == "left" && start == 0)
2477 { while (i && map$$1[i - 2] == map$$1[i - 3] && map$$1[i - 1].insertLeft) {
2478 node = map$$1[(i -= 3) + 2]
2479 collapse = "left"
2480 } }
2481 if (bias == "right" && start == mEnd - mStart)
2482 { while (i < map$$1.length - 3 && map$$1[i + 3] == map$$1[i + 4] && !map$$1[i + 5].insertLeft) {
2483 node = map$$1[(i += 3) + 2]
2484 collapse = "right"
2485 } }
2486 break
2487 }
2488 }
2489 return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd}
2490}
2491
2492function getUsefulRect(rects, bias) {
2493 var rect = nullRect
2494 if (bias == "left") { for (var i = 0; i < rects.length; i++) {
2495 if ((rect = rects[i]).left != rect.right) { break }
2496 } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) {
2497 if ((rect = rects[i$1]).left != rect.right) { break }
2498 } }
2499 return rect
2500}
2501
2502function measureCharInner(cm, prepared, ch, bias) {
2503 var place = nodeAndOffsetInLineMap(prepared.map, ch, bias)
2504 var node = place.node, start = place.start, end = place.end, collapse = place.collapse
2505
2506 var rect
2507 if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.
2508 for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonsense rectangles are returned
2509 while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start }
2510 while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) { ++end }
2511 if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart)
2512 { rect = node.parentNode.getBoundingClientRect() }
2513 else
2514 { rect = getUsefulRect(range(node, start, end).getClientRects(), bias) }
2515 if (rect.left || rect.right || start == 0) { break }
2516 end = start
2517 start = start - 1
2518 collapse = "right"
2519 }
2520 if (ie && ie_version < 11) { rect = maybeUpdateRectForZooming(cm.display.measure, rect) }
2521 } else { // If it is a widget, simply get the box for the whole widget.
2522 if (start > 0) { collapse = bias = "right" }
2523 var rects
2524 if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)
2525 { rect = rects[bias == "right" ? rects.length - 1 : 0] }
2526 else
2527 { rect = node.getBoundingClientRect() }
2528 }
2529 if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {
2530 var rSpan = node.parentNode.getClientRects()[0]
2531 if (rSpan)
2532 { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom} }
2533 else
2534 { rect = nullRect }
2535 }
2536
2537 var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top
2538 var mid = (rtop + rbot) / 2
2539 var heights = prepared.view.measure.heights
2540 var i = 0
2541 for (; i < heights.length - 1; i++)
2542 { if (mid < heights[i]) { break } }
2543 var top = i ? heights[i - 1] : 0, bot = heights[i]
2544 var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left,
2545 right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left,
2546 top: top, bottom: bot}
2547 if (!rect.left && !rect.right) { result.bogus = true }
2548 if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot }
2549
2550 return result
2551}
2552
2553// Work around problem with bounding client rects on ranges being
2554// returned incorrectly when zoomed on IE10 and below.
2555function maybeUpdateRectForZooming(measure, rect) {
2556 if (!window.screen || screen.logicalXDPI == null ||
2557 screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))
2558 { return rect }
2559 var scaleX = screen.logicalXDPI / screen.deviceXDPI
2560 var scaleY = screen.logicalYDPI / screen.deviceYDPI
2561 return {left: rect.left * scaleX, right: rect.right * scaleX,
2562 top: rect.top * scaleY, bottom: rect.bottom * scaleY}
2563}
2564
2565function clearLineMeasurementCacheFor(lineView) {
2566 if (lineView.measure) {
2567 lineView.measure.cache = {}
2568 lineView.measure.heights = null
2569 if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
2570 { lineView.measure.caches[i] = {} } }
2571 }
2572}
2573
2574function clearLineMeasurementCache(cm) {
2575 cm.display.externalMeasure = null
2576 removeChildren(cm.display.lineMeasure)
2577 for (var i = 0; i < cm.display.view.length; i++)
2578 { clearLineMeasurementCacheFor(cm.display.view[i]) }
2579}
2580
2581function clearCaches(cm) {
2582 clearLineMeasurementCache(cm)
2583 cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null
2584 if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true }
2585 cm.display.lineNumChars = null
2586}
2587
2588function pageScrollX() { return window.pageXOffset || (document.documentElement || document.body).scrollLeft }
2589function pageScrollY() { return window.pageYOffset || (document.documentElement || document.body).scrollTop }
2590
2591// Converts a {top, bottom, left, right} box from line-local
2592// coordinates into another coordinate system. Context may be one of
2593// "line", "div" (display.lineDiv), "local"./null (editor), "window",
2594// or "page".
2595function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) {
2596 if (!includeWidgets && lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above) {
2597 var size = widgetHeight(lineObj.widgets[i])
2598 rect.top += size; rect.bottom += size
2599 } } }
2600 if (context == "line") { return rect }
2601 if (!context) { context = "local" }
2602 var yOff = heightAtLine(lineObj)
2603 if (context == "local") { yOff += paddingTop(cm.display) }
2604 else { yOff -= cm.display.viewOffset }
2605 if (context == "page" || context == "window") {
2606 var lOff = cm.display.lineSpace.getBoundingClientRect()
2607 yOff += lOff.top + (context == "window" ? 0 : pageScrollY())
2608 var xOff = lOff.left + (context == "window" ? 0 : pageScrollX())
2609 rect.left += xOff; rect.right += xOff
2610 }
2611 rect.top += yOff; rect.bottom += yOff
2612 return rect
2613}
2614
2615// Coverts a box from "div" coords to another coordinate system.
2616// Context may be "window", "page", "div", or "local"./null.
2617function fromCoordSystem(cm, coords, context) {
2618 if (context == "div") { return coords }
2619 var left = coords.left, top = coords.top
2620 // First move into "page" coordinate system
2621 if (context == "page") {
2622 left -= pageScrollX()
2623 top -= pageScrollY()
2624 } else if (context == "local" || !context) {
2625 var localBox = cm.display.sizer.getBoundingClientRect()
2626 left += localBox.left
2627 top += localBox.top
2628 }
2629
2630 var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect()
2631 return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top}
2632}
2633
2634function charCoords(cm, pos, context, lineObj, bias) {
2635 if (!lineObj) { lineObj = getLine(cm.doc, pos.line) }
2636 return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context)
2637}
2638
2639// Returns a box for a given cursor position, which may have an
2640// 'other' property containing the position of the secondary cursor
2641// on a bidi boundary.
2642function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
2643 lineObj = lineObj || getLine(cm.doc, pos.line)
2644 if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj) }
2645 function get(ch, right) {
2646 var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight)
2647 if (right) { m.left = m.right; } else { m.right = m.left }
2648 return intoCoordSystem(cm, lineObj, m, context)
2649 }
2650 function getBidi(ch, partPos) {
2651 var part = order[partPos], right = part.level % 2
2652 if (ch == bidiLeft(part) && partPos && part.level < order[partPos - 1].level) {
2653 part = order[--partPos]
2654 ch = bidiRight(part) - (part.level % 2 ? 0 : 1)
2655 right = true
2656 } else if (ch == bidiRight(part) && partPos < order.length - 1 && part.level < order[partPos + 1].level) {
2657 part = order[++partPos]
2658 ch = bidiLeft(part) - part.level % 2
2659 right = false
2660 }
2661 if (right && ch == part.to && ch > part.from) { return get(ch - 1) }
2662 return get(ch, right)
2663 }
2664 var order = getOrder(lineObj), ch = pos.ch
2665 if (!order) { return get(ch) }
2666 var partPos = getBidiPartAt(order, ch)
2667 var val = getBidi(ch, partPos)
2668 if (bidiOther != null) { val.other = getBidi(ch, bidiOther) }
2669 return val
2670}
2671
2672// Used to cheaply estimate the coordinates for a position. Used for
2673// intermediate scroll updates.
2674function estimateCoords(cm, pos) {
2675 var left = 0
2676 pos = clipPos(cm.doc, pos)
2677 if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch }
2678 var lineObj = getLine(cm.doc, pos.line)
2679 var top = heightAtLine(lineObj) + paddingTop(cm.display)
2680 return {left: left, right: left, top: top, bottom: top + lineObj.height}
2681}
2682
2683// Positions returned by coordsChar contain some extra information.
2684// xRel is the relative x position of the input coordinates compared
2685// to the found position (so xRel > 0 means the coordinates are to
2686// the right of the character position, for example). When outside
2687// is true, that means the coordinates lie outside the line's
2688// vertical range.
2689function PosWithInfo(line, ch, outside, xRel) {
2690 var pos = Pos(line, ch)
2691 pos.xRel = xRel
2692 if (outside) { pos.outside = true }
2693 return pos
2694}
2695
2696// Compute the character position closest to the given coordinates.
2697// Input must be lineSpace-local ("div" coordinate system).
2698function coordsChar(cm, x, y) {
2699 var doc = cm.doc
2700 y += cm.display.viewOffset
2701 if (y < 0) { return PosWithInfo(doc.first, 0, true, -1) }
2702 var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1
2703 if (lineN > last)
2704 { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, true, 1) }
2705 if (x < 0) { x = 0 }
2706
2707 var lineObj = getLine(doc, lineN)
2708 for (;;) {
2709 var found = coordsCharInner(cm, lineObj, lineN, x, y)
2710 var merged = collapsedSpanAtEnd(lineObj)
2711 var mergedPos = merged && merged.find(0, true)
2712 if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0))
2713 { lineN = lineNo(lineObj = mergedPos.to.line) }
2714 else
2715 { return found }
2716 }
2717}
2718
2719function coordsCharInner(cm, lineObj, lineNo$$1, x, y) {
2720 var innerOff = y - heightAtLine(lineObj)
2721 var wrongLine = false, adjust = 2 * cm.display.wrapper.clientWidth
2722 var preparedMeasure = prepareMeasureForLine(cm, lineObj)
2723
2724 function getX(ch) {
2725 var sp = cursorCoords(cm, Pos(lineNo$$1, ch), "line", lineObj, preparedMeasure)
2726 wrongLine = true
2727 if (innerOff > sp.bottom) { return sp.left - adjust }
2728 else if (innerOff < sp.top) { return sp.left + adjust }
2729 else { wrongLine = false }
2730 return sp.left
2731 }
2732
2733 var bidi = getOrder(lineObj), dist = lineObj.text.length
2734 var from = lineLeft(lineObj), to = lineRight(lineObj)
2735 var fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine
2736
2737 if (x > toX) { return PosWithInfo(lineNo$$1, to, toOutside, 1) }
2738 // Do a binary search between these bounds.
2739 for (;;) {
2740 if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
2741 var ch = x < fromX || x - fromX <= toX - x ? from : to
2742 var outside = ch == from ? fromOutside : toOutside
2743 var xDiff = x - (ch == from ? fromX : toX)
2744 // This is a kludge to handle the case where the coordinates
2745 // are after a line-wrapped line. We should replace it with a
2746 // more general handling of cursor positions around line
2747 // breaks. (Issue #4078)
2748 if (toOutside && !bidi && !/\s/.test(lineObj.text.charAt(ch)) && xDiff > 0 &&
2749 ch < lineObj.text.length && preparedMeasure.view.measure.heights.length > 1) {
2750 var charSize = measureCharPrepared(cm, preparedMeasure, ch, "right")
2751 if (innerOff <= charSize.bottom && innerOff >= charSize.top && Math.abs(x - charSize.right) < xDiff) {
2752 outside = false
2753 ch++
2754 xDiff = x - charSize.right
2755 }
2756 }
2757 while (isExtendingChar(lineObj.text.charAt(ch))) { ++ch }
2758 var pos = PosWithInfo(lineNo$$1, ch, outside, xDiff < -1 ? -1 : xDiff > 1 ? 1 : 0)
2759 return pos
2760 }
2761 var step = Math.ceil(dist / 2), middle = from + step
2762 if (bidi) {
2763 middle = from
2764 for (var i = 0; i < step; ++i) { middle = moveVisually(lineObj, middle, 1) }
2765 }
2766 var middleX = getX(middle)
2767 if (middleX > x) {to = middle; toX = middleX; if (toOutside = wrongLine) { toX += 1000; } dist = step}
2768 else {from = middle; fromX = middleX; fromOutside = wrongLine; dist -= step}
2769 }
2770}
2771
2772var measureText
2773// Compute the default text height.
2774function textHeight(display) {
2775 if (display.cachedTextHeight != null) { return display.cachedTextHeight }
2776 if (measureText == null) {
2777 measureText = elt("pre")
2778 // Measure a bunch of lines, for browsers that compute
2779 // fractional heights.
2780 for (var i = 0; i < 49; ++i) {
2781 measureText.appendChild(document.createTextNode("x"))
2782 measureText.appendChild(elt("br"))
2783 }
2784 measureText.appendChild(document.createTextNode("x"))
2785 }
2786 removeChildrenAndAdd(display.measure, measureText)
2787 var height = measureText.offsetHeight / 50
2788 if (height > 3) { display.cachedTextHeight = height }
2789 removeChildren(display.measure)
2790 return height || 1
2791}
2792
2793// Compute the default character width.
2794function charWidth(display) {
2795 if (display.cachedCharWidth != null) { return display.cachedCharWidth }
2796 var anchor = elt("span", "xxxxxxxxxx")
2797 var pre = elt("pre", [anchor])
2798 removeChildrenAndAdd(display.measure, pre)
2799 var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10
2800 if (width > 2) { display.cachedCharWidth = width }
2801 return width || 10
2802}
2803
2804// Do a bulk-read of the DOM positions and sizes needed to draw the
2805// view, so that we don't interleave reading and writing to the DOM.
2806function getDimensions(cm) {
2807 var d = cm.display, left = {}, width = {}
2808 var gutterLeft = d.gutters.clientLeft
2809 for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
2810 left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft
2811 width[cm.options.gutters[i]] = n.clientWidth
2812 }
2813 return {fixedPos: compensateForHScroll(d),
2814 gutterTotalWidth: d.gutters.offsetWidth,
2815 gutterLeft: left,
2816 gutterWidth: width,
2817 wrapperWidth: d.wrapper.clientWidth}
2818}
2819
2820// Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
2821// but using getBoundingClientRect to get a sub-pixel-accurate
2822// result.
2823function compensateForHScroll(display) {
2824 return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left
2825}
2826
2827// Returns a function that estimates the height of a line, to use as
2828// first approximation until the line becomes visible (and is thus
2829// properly measurable).
2830function estimateHeight(cm) {
2831 var th = textHeight(cm.display), wrapping = cm.options.lineWrapping
2832 var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3)
2833 return function (line) {
2834 if (lineIsHidden(cm.doc, line)) { return 0 }
2835
2836 var widgetsHeight = 0
2837 if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) {
2838 if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height }
2839 } }
2840
2841 if (wrapping)
2842 { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th }
2843 else
2844 { return widgetsHeight + th }
2845 }
2846}
2847
2848function estimateLineHeights(cm) {
2849 var doc = cm.doc, est = estimateHeight(cm)
2850 doc.iter(function (line) {
2851 var estHeight = est(line)
2852 if (estHeight != line.height) { updateLineHeight(line, estHeight) }
2853 })
2854}
2855
2856// Given a mouse event, find the corresponding position. If liberal
2857// is false, it checks whether a gutter or scrollbar was clicked,
2858// and returns null if it was. forRect is used by rectangular
2859// selections, and tries to estimate a character position even for
2860// coordinates beyond the right of the text.
2861function posFromMouse(cm, e, liberal, forRect) {
2862 var display = cm.display
2863 if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") { return null }
2864
2865 var x, y, space = display.lineSpace.getBoundingClientRect()
2866 // Fails unpredictably on IE[67] when mouse is dragged around quickly.
2867 try { x = e.clientX - space.left; y = e.clientY - space.top }
2868 catch (e) { return null }
2869 var coords = coordsChar(cm, x, y), line
2870 if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) {
2871 var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length
2872 coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff))
2873 }
2874 return coords
2875}
2876
2877// Find the view element corresponding to a given line. Return null
2878// when the line isn't visible.
2879function findViewIndex(cm, n) {
2880 if (n >= cm.display.viewTo) { return null }
2881 n -= cm.display.viewFrom
2882 if (n < 0) { return null }
2883 var view = cm.display.view
2884 for (var i = 0; i < view.length; i++) {
2885 n -= view[i].size
2886 if (n < 0) { return i }
2887 }
2888}
2889
2890function updateSelection(cm) {
2891 cm.display.input.showSelection(cm.display.input.prepareSelection())
2892}
2893
2894function prepareSelection(cm, primary) {
2895 var doc = cm.doc, result = {}
2896 var curFragment = result.cursors = document.createDocumentFragment()
2897 var selFragment = result.selection = document.createDocumentFragment()
2898
2899 for (var i = 0; i < doc.sel.ranges.length; i++) {
2900 if (primary === false && i == doc.sel.primIndex) { continue }
2901 var range$$1 = doc.sel.ranges[i]
2902 if (range$$1.from().line >= cm.display.viewTo || range$$1.to().line < cm.display.viewFrom) { continue }
2903 var collapsed = range$$1.empty()
2904 if (collapsed || cm.options.showCursorWhenSelecting)
2905 { drawSelectionCursor(cm, range$$1.head, curFragment) }
2906 if (!collapsed)
2907 { drawSelectionRange(cm, range$$1, selFragment) }
2908 }
2909 return result
2910}
2911
2912// Draws a cursor for the given range
2913function drawSelectionCursor(cm, head, output) {
2914 var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine)
2915
2916 var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor"))
2917 cursor.style.left = pos.left + "px"
2918 cursor.style.top = pos.top + "px"
2919 cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px"
2920
2921 if (pos.other) {
2922 // Secondary cursor, shown when on a 'jump' in bi-directional text
2923 var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"))
2924 otherCursor.style.display = ""
2925 otherCursor.style.left = pos.other.left + "px"
2926 otherCursor.style.top = pos.other.top + "px"
2927 otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px"
2928 }
2929}
2930
2931// Draws the given range as a highlighted selection
2932function drawSelectionRange(cm, range$$1, output) {
2933 var display = cm.display, doc = cm.doc
2934 var fragment = document.createDocumentFragment()
2935 var padding = paddingH(cm.display), leftSide = padding.left
2936 var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right
2937
2938 function add(left, top, width, bottom) {
2939 if (top < 0) { top = 0 }
2940 top = Math.round(top)
2941 bottom = Math.round(bottom)
2942 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")))
2943 }
2944
2945 function drawForLine(line, fromArg, toArg) {
2946 var lineObj = getLine(doc, line)
2947 var lineLen = lineObj.text.length
2948 var start, end
2949 function coords(ch, bias) {
2950 return charCoords(cm, Pos(line, ch), "div", lineObj, bias)
2951 }
2952
2953 iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir) {
2954 var leftPos = coords(from, "left"), rightPos, left, right
2955 if (from == to) {
2956 rightPos = leftPos
2957 left = right = leftPos.left
2958 } else {
2959 rightPos = coords(to - 1, "right")
2960 if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp }
2961 left = leftPos.left
2962 right = rightPos.right
2963 }
2964 if (fromArg == null && from == 0) { left = leftSide }
2965 if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
2966 add(left, leftPos.top, null, leftPos.bottom)
2967 left = leftSide
2968 if (leftPos.bottom < rightPos.top) { add(left, leftPos.bottom, null, rightPos.top) }
2969 }
2970 if (toArg == null && to == lineLen) { right = rightSide }
2971 if (!start || leftPos.top < start.top || leftPos.top == start.top && leftPos.left < start.left)
2972 { start = leftPos }
2973 if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.bottom && rightPos.right > end.right)
2974 { end = rightPos }
2975 if (left < leftSide + 1) { left = leftSide }
2976 add(left, rightPos.top, right - left, rightPos.bottom)
2977 })
2978 return {start: start, end: end}
2979 }
2980
2981 var sFrom = range$$1.from(), sTo = range$$1.to()
2982 if (sFrom.line == sTo.line) {
2983 drawForLine(sFrom.line, sFrom.ch, sTo.ch)
2984 } else {
2985 var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line)
2986 var singleVLine = visualLine(fromLine) == visualLine(toLine)
2987 var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end
2988 var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start
2989 if (singleVLine) {
2990 if (leftEnd.top < rightStart.top - 2) {
2991 add(leftEnd.right, leftEnd.top, null, leftEnd.bottom)
2992 add(leftSide, rightStart.top, rightStart.left, rightStart.bottom)
2993 } else {
2994 add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom)
2995 }
2996 }
2997 if (leftEnd.bottom < rightStart.top)
2998 { add(leftSide, leftEnd.bottom, null, rightStart.top) }
2999 }
3000
3001 output.appendChild(fragment)
3002}
3003
3004// Cursor-blinking
3005function restartBlink(cm) {
3006 if (!cm.state.focused) { return }
3007 var display = cm.display
3008 clearInterval(display.blinker)
3009 var on = true
3010 display.cursorDiv.style.visibility = ""
3011 if (cm.options.cursorBlinkRate > 0)
3012 { display.blinker = setInterval(function () { return display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; },
3013 cm.options.cursorBlinkRate) }
3014 else if (cm.options.cursorBlinkRate < 0)
3015 { display.cursorDiv.style.visibility = "hidden" }
3016}
3017
3018function ensureFocus(cm) {
3019 if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm) }
3020}
3021
3022function delayBlurEvent(cm) {
3023 cm.state.delayingBlurEvent = true
3024 setTimeout(function () { if (cm.state.delayingBlurEvent) {
3025 cm.state.delayingBlurEvent = false
3026 onBlur(cm)
3027 } }, 100)
3028}
3029
3030function onFocus(cm, e) {
3031 if (cm.state.delayingBlurEvent) { cm.state.delayingBlurEvent = false }
3032
3033 if (cm.options.readOnly == "nocursor") { return }
3034 if (!cm.state.focused) {
3035 signal(cm, "focus", cm, e)
3036 cm.state.focused = true
3037 addClass(cm.display.wrapper, "CodeMirror-focused")
3038 // This test prevents this from firing when a context
3039 // menu is closed (since the input reset would kill the
3040 // select-all detection hack)
3041 if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {
3042 cm.display.input.reset()
3043 if (webkit) { setTimeout(function () { return cm.display.input.reset(true); }, 20) } // Issue #1730
3044 }
3045 cm.display.input.receivedFocus()
3046 }
3047 restartBlink(cm)
3048}
3049function onBlur(cm, e) {
3050 if (cm.state.delayingBlurEvent) { return }
3051
3052 if (cm.state.focused) {
3053 signal(cm, "blur", cm, e)
3054 cm.state.focused = false
3055 rmClass(cm.display.wrapper, "CodeMirror-focused")
3056 }
3057 clearInterval(cm.display.blinker)
3058 setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false } }, 150)
3059}
3060
3061// Re-align line numbers and gutter marks to compensate for
3062// horizontal scrolling.
3063function alignHorizontally(cm) {
3064 var display = cm.display, view = display.view
3065 if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) { return }
3066 var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft
3067 var gutterW = display.gutters.offsetWidth, left = comp + "px"
3068 for (var i = 0; i < view.length; i++) { if (!view[i].hidden) {
3069 if (cm.options.fixedGutter) {
3070 if (view[i].gutter)
3071 { view[i].gutter.style.left = left }
3072 if (view[i].gutterBackground)
3073 { view[i].gutterBackground.style.left = left }
3074 }
3075 var align = view[i].alignable
3076 if (align) { for (var j = 0; j < align.length; j++)
3077 { align[j].style.left = left } }
3078 } }
3079 if (cm.options.fixedGutter)
3080 { display.gutters.style.left = (comp + gutterW) + "px" }
3081}
3082
3083// Used to ensure that the line number gutter is still the right
3084// size for the current document size. Returns true when an update
3085// is needed.
3086function maybeUpdateLineNumberWidth(cm) {
3087 if (!cm.options.lineNumbers) { return false }
3088 var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display
3089 if (last.length != display.lineNumChars) {
3090 var test = display.measure.appendChild(elt("div", [elt("div", last)],
3091 "CodeMirror-linenumber CodeMirror-gutter-elt"))
3092 var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW
3093 display.lineGutter.style.width = ""
3094 display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1
3095 display.lineNumWidth = display.lineNumInnerWidth + padding
3096 display.lineNumChars = display.lineNumInnerWidth ? last.length : -1
3097 display.lineGutter.style.width = display.lineNumWidth + "px"
3098 updateGutterSpace(cm)
3099 return true
3100 }
3101 return false
3102}
3103
3104// Read the actual heights of the rendered lines, and update their
3105// stored heights to match.
3106function updateHeightsInViewport(cm) {
3107 var display = cm.display
3108 var prevBottom = display.lineDiv.offsetTop
3109 for (var i = 0; i < display.view.length; i++) {
3110 var cur = display.view[i], height = void 0
3111 if (cur.hidden) { continue }
3112 if (ie && ie_version < 8) {
3113 var bot = cur.node.offsetTop + cur.node.offsetHeight
3114 height = bot - prevBottom
3115 prevBottom = bot
3116 } else {
3117 var box = cur.node.getBoundingClientRect()
3118 height = box.bottom - box.top
3119 }
3120 var diff = cur.line.height - height
3121 if (height < 2) { height = textHeight(display) }
3122 if (diff > .001 || diff < -.001) {
3123 updateLineHeight(cur.line, height)
3124 updateWidgetHeight(cur.line)
3125 if (cur.rest) { for (var j = 0; j < cur.rest.length; j++)
3126 { updateWidgetHeight(cur.rest[j]) } }
3127 }
3128 }
3129}
3130
3131// Read and store the height of line widgets associated with the
3132// given line.
3133function updateWidgetHeight(line) {
3134 if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i)
3135 { line.widgets[i].height = line.widgets[i].node.parentNode.offsetHeight } }
3136}
3137
3138// Compute the lines that are visible in a given viewport (defaults
3139// the the current scroll position). viewport may contain top,
3140// height, and ensure (see op.scrollToPos) properties.
3141function visibleLines(display, doc, viewport) {
3142 var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop
3143 top = Math.floor(top - paddingTop(display))
3144 var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight
3145
3146 var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom)
3147 // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
3148 // forces those lines into the viewport (if possible).
3149 if (viewport && viewport.ensure) {
3150 var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line
3151 if (ensureFrom < from) {
3152 from = ensureFrom
3153 to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight)
3154 } else if (Math.min(ensureTo, doc.lastLine()) >= to) {
3155 from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight)
3156 to = ensureTo
3157 }
3158 }
3159 return {from: from, to: Math.max(to, from + 1)}
3160}
3161
3162// Sync the scrollable area and scrollbars, ensure the viewport
3163// covers the visible area.
3164function setScrollTop(cm, val) {
3165 if (Math.abs(cm.doc.scrollTop - val) < 2) { return }
3166 cm.doc.scrollTop = val
3167 if (!gecko) { updateDisplaySimple(cm, {top: val}) }
3168 if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = val }
3169 cm.display.scrollbars.setScrollTop(val)
3170 if (gecko) { updateDisplaySimple(cm) }
3171 startWorker(cm, 100)
3172}
3173// Sync scroller and scrollbar, ensure the gutter elements are
3174// aligned.
3175function setScrollLeft(cm, val, isScroller) {
3176 if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) { return }
3177 val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth)
3178 cm.doc.scrollLeft = val
3179 alignHorizontally(cm)
3180 if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val }
3181 cm.display.scrollbars.setScrollLeft(val)
3182}
3183
3184// Since the delta values reported on mouse wheel events are
3185// unstandardized between browsers and even browser versions, and
3186// generally horribly unpredictable, this code starts by measuring
3187// the scroll effect that the first few mouse wheel events have,
3188// and, from that, detects the way it can convert deltas to pixel
3189// offsets afterwards.
3190//
3191// The reason we want to know the amount a wheel event will scroll
3192// is that it gives us a chance to update the display before the
3193// actual scrolling happens, reducing flickering.
3194
3195var wheelSamples = 0;
3196var wheelPixelsPerUnit = null
3197// Fill in a browser-detected starting value on browsers where we
3198// know one. These don't have to be accurate -- the result of them
3199// being wrong would just be a slight flicker on the first wheel
3200// scroll (if it is large enough).
3201if (ie) { wheelPixelsPerUnit = -.53 }
3202else if (gecko) { wheelPixelsPerUnit = 15 }
3203else if (chrome) { wheelPixelsPerUnit = -.7 }
3204else if (safari) { wheelPixelsPerUnit = -1/3 }
3205
3206function wheelEventDelta(e) {
3207 var dx = e.wheelDeltaX, dy = e.wheelDeltaY
3208 if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail }
3209 if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail }
3210 else if (dy == null) { dy = e.wheelDelta }
3211 return {x: dx, y: dy}
3212}
3213function wheelEventPixels(e) {
3214 var delta = wheelEventDelta(e)
3215 delta.x *= wheelPixelsPerUnit
3216 delta.y *= wheelPixelsPerUnit
3217 return delta
3218}
3219
3220function onScrollWheel(cm, e) {
3221 var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y
3222
3223 var display = cm.display, scroll = display.scroller
3224 // Quit if there's nothing to scroll here
3225 var canScrollX = scroll.scrollWidth > scroll.clientWidth
3226 var canScrollY = scroll.scrollHeight > scroll.clientHeight
3227 if (!(dx && canScrollX || dy && canScrollY)) { return }
3228
3229 // Webkit browsers on OS X abort momentum scrolls when the target
3230 // of the scroll event is removed from the scrollable element.
3231 // This hack (see related code in patchDisplay) makes sure the
3232 // element is kept around.
3233 if (dy && mac && webkit) {
3234 outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {
3235 for (var i = 0; i < view.length; i++) {
3236 if (view[i].node == cur) {
3237 cm.display.currentWheelTarget = cur
3238 break outer
3239 }
3240 }
3241 }
3242 }
3243
3244 // On some browsers, horizontal scrolling will cause redraws to
3245 // happen before the gutter has been realigned, causing it to
3246 // wriggle around in a most unseemly way. When we have an
3247 // estimated pixels/delta value, we just handle horizontal
3248 // scrolling entirely here. It'll be slightly off from native, but
3249 // better than glitching out.
3250 if (dx && !gecko && !presto && wheelPixelsPerUnit != null) {
3251 if (dy && canScrollY)
3252 { setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight))) }
3253 setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth)))
3254 // Only prevent default scrolling if vertical scrolling is
3255 // actually possible. Otherwise, it causes vertical scroll
3256 // jitter on OSX trackpads when deltaX is small and deltaY
3257 // is large (issue #3579)
3258 if (!dy || (dy && canScrollY))
3259 { e_preventDefault(e) }
3260 display.wheelStartX = null // Abort measurement, if in progress
3261 return
3262 }
3263
3264 // 'Project' the visible viewport to cover the area that is being
3265 // scrolled into view (if we know enough to estimate it).
3266 if (dy && wheelPixelsPerUnit != null) {
3267 var pixels = dy * wheelPixelsPerUnit
3268 var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight
3269 if (pixels < 0) { top = Math.max(0, top + pixels - 50) }
3270 else { bot = Math.min(cm.doc.height, bot + pixels + 50) }
3271 updateDisplaySimple(cm, {top: top, bottom: bot})
3272 }
3273
3274 if (wheelSamples < 20) {
3275 if (display.wheelStartX == null) {
3276 display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop
3277 display.wheelDX = dx; display.wheelDY = dy
3278 setTimeout(function () {
3279 if (display.wheelStartX == null) { return }
3280 var movedX = scroll.scrollLeft - display.wheelStartX
3281 var movedY = scroll.scrollTop - display.wheelStartY
3282 var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
3283 (movedX && display.wheelDX && movedX / display.wheelDX)
3284 display.wheelStartX = display.wheelStartY = null
3285 if (!sample) { return }
3286 wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1)
3287 ++wheelSamples
3288 }, 200)
3289 } else {
3290 display.wheelDX += dx; display.wheelDY += dy
3291 }
3292 }
3293}
3294
3295// SCROLLBARS
3296
3297// Prepare DOM reads needed to update the scrollbars. Done in one
3298// shot to minimize update/measure roundtrips.
3299function measureForScrollbars(cm) {
3300 var d = cm.display, gutterW = d.gutters.offsetWidth
3301 var docH = Math.round(cm.doc.height + paddingVert(cm.display))
3302 return {
3303 clientHeight: d.scroller.clientHeight,
3304 viewHeight: d.wrapper.clientHeight,
3305 scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,
3306 viewWidth: d.wrapper.clientWidth,
3307 barLeft: cm.options.fixedGutter ? gutterW : 0,
3308 docHeight: docH,
3309 scrollHeight: docH + scrollGap(cm) + d.barHeight,
3310 nativeBarWidth: d.nativeBarWidth,
3311 gutterWidth: gutterW
3312 }
3313}
3314
3315function NativeScrollbars(place, scroll, cm) {
3316 this.cm = cm
3317 var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar")
3318 var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar")
3319 place(vert); place(horiz)
3320
3321 on(vert, "scroll", function () {
3322 if (vert.clientHeight) { scroll(vert.scrollTop, "vertical") }
3323 })
3324 on(horiz, "scroll", function () {
3325 if (horiz.clientWidth) { scroll(horiz.scrollLeft, "horizontal") }
3326 })
3327
3328 this.checkedZeroWidth = false
3329 // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
3330 if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = "18px" }
3331}
3332
3333NativeScrollbars.prototype = copyObj({
3334 update: function(measure) {
3335 var needsH = measure.scrollWidth > measure.clientWidth + 1
3336 var needsV = measure.scrollHeight > measure.clientHeight + 1
3337 var sWidth = measure.nativeBarWidth
3338
3339 if (needsV) {
3340 this.vert.style.display = "block"
3341 this.vert.style.bottom = needsH ? sWidth + "px" : "0"
3342 var totalHeight = measure.viewHeight - (needsH ? sWidth : 0)
3343 // A bug in IE8 can cause this value to be negative, so guard it.
3344 this.vert.firstChild.style.height =
3345 Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px"
3346 } else {
3347 this.vert.style.display = ""
3348 this.vert.firstChild.style.height = "0"
3349 }
3350
3351 if (needsH) {
3352 this.horiz.style.display = "block"
3353 this.horiz.style.right = needsV ? sWidth + "px" : "0"
3354 this.horiz.style.left = measure.barLeft + "px"
3355 var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0)
3356 this.horiz.firstChild.style.width =
3357 (measure.scrollWidth - measure.clientWidth + totalWidth) + "px"
3358 } else {
3359 this.horiz.style.display = ""
3360 this.horiz.firstChild.style.width = "0"
3361 }
3362
3363 if (!this.checkedZeroWidth && measure.clientHeight > 0) {
3364 if (sWidth == 0) { this.zeroWidthHack() }
3365 this.checkedZeroWidth = true
3366 }
3367
3368 return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0}
3369 },
3370 setScrollLeft: function(pos) {
3371 if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos }
3372 if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz) }
3373 },
3374 setScrollTop: function(pos) {
3375 if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos }
3376 if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert) }
3377 },
3378 zeroWidthHack: function() {
3379 var w = mac && !mac_geMountainLion ? "12px" : "18px"
3380 this.horiz.style.height = this.vert.style.width = w
3381 this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none"
3382 this.disableHoriz = new Delayed
3383 this.disableVert = new Delayed
3384 },
3385 enableZeroWidthBar: function(bar, delay) {
3386 bar.style.pointerEvents = "auto"
3387 function maybeDisable() {
3388 // To find out whether the scrollbar is still visible, we
3389 // check whether the element under the pixel in the bottom
3390 // left corner of the scrollbar box is the scrollbar box
3391 // itself (when the bar is still visible) or its filler child
3392 // (when the bar is hidden). If it is still visible, we keep
3393 // it enabled, if it's hidden, we disable pointer events.
3394 var box = bar.getBoundingClientRect()
3395 var elt$$1 = document.elementFromPoint(box.left + 1, box.bottom - 1)
3396 if (elt$$1 != bar) { bar.style.pointerEvents = "none" }
3397 else { delay.set(1000, maybeDisable) }
3398 }
3399 delay.set(1000, maybeDisable)
3400 },
3401 clear: function() {
3402 var parent = this.horiz.parentNode
3403 parent.removeChild(this.horiz)
3404 parent.removeChild(this.vert)
3405 }
3406}, NativeScrollbars.prototype)
3407
3408function NullScrollbars() {}
3409
3410NullScrollbars.prototype = copyObj({
3411 update: function() { return {bottom: 0, right: 0} },
3412 setScrollLeft: function() {},
3413 setScrollTop: function() {},
3414 clear: function() {}
3415}, NullScrollbars.prototype)
3416
3417function updateScrollbars(cm, measure) {
3418 if (!measure) { measure = measureForScrollbars(cm) }
3419 var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight
3420 updateScrollbarsInner(cm, measure)
3421 for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) {
3422 if (startWidth != cm.display.barWidth && cm.options.lineWrapping)
3423 { updateHeightsInViewport(cm) }
3424 updateScrollbarsInner(cm, measureForScrollbars(cm))
3425 startWidth = cm.display.barWidth; startHeight = cm.display.barHeight
3426 }
3427}
3428
3429// Re-synchronize the fake scrollbars with the actual size of the
3430// content.
3431function updateScrollbarsInner(cm, measure) {
3432 var d = cm.display
3433 var sizes = d.scrollbars.update(measure)
3434
3435 d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px"
3436 d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px"
3437 d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent"
3438
3439 if (sizes.right && sizes.bottom) {
3440 d.scrollbarFiller.style.display = "block"
3441 d.scrollbarFiller.style.height = sizes.bottom + "px"
3442 d.scrollbarFiller.style.width = sizes.right + "px"
3443 } else { d.scrollbarFiller.style.display = "" }
3444 if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
3445 d.gutterFiller.style.display = "block"
3446 d.gutterFiller.style.height = sizes.bottom + "px"
3447 d.gutterFiller.style.width = measure.gutterWidth + "px"
3448 } else { d.gutterFiller.style.display = "" }
3449}
3450
3451var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars}
3452
3453function initScrollbars(cm) {
3454 if (cm.display.scrollbars) {
3455 cm.display.scrollbars.clear()
3456 if (cm.display.scrollbars.addClass)
3457 { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass) }
3458 }
3459
3460 cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) {
3461 cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller)
3462 // Prevent clicks in the scrollbars from killing focus
3463 on(node, "mousedown", function () {
3464 if (cm.state.focused) { setTimeout(function () { return cm.display.input.focus(); }, 0) }
3465 })
3466 node.setAttribute("cm-not-content", "true")
3467 }, function (pos, axis) {
3468 if (axis == "horizontal") { setScrollLeft(cm, pos) }
3469 else { setScrollTop(cm, pos) }
3470 }, cm)
3471 if (cm.display.scrollbars.addClass)
3472 { addClass(cm.display.wrapper, cm.display.scrollbars.addClass) }
3473}
3474
3475// SCROLLING THINGS INTO VIEW
3476
3477// If an editor sits on the top or bottom of the window, partially
3478// scrolled out of view, this ensures that the cursor is visible.
3479function maybeScrollWindow(cm, coords) {
3480 if (signalDOMEvent(cm, "scrollCursorIntoView")) { return }
3481
3482 var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null
3483 if (coords.top + box.top < 0) { doScroll = true }
3484 else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) { doScroll = false }
3485 if (doScroll != null && !phantom) {
3486 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;"))
3487 cm.display.lineSpace.appendChild(scrollNode)
3488 scrollNode.scrollIntoView(doScroll)
3489 cm.display.lineSpace.removeChild(scrollNode)
3490 }
3491}
3492
3493// Scroll a given position into view (immediately), verifying that
3494// it actually became visible (as line heights are accurately
3495// measured, the position of something may 'drift' during drawing).
3496function scrollPosIntoView(cm, pos, end, margin) {
3497 if (margin == null) { margin = 0 }
3498 var coords
3499 for (var limit = 0; limit < 5; limit++) {
3500 var changed = false
3501 coords = cursorCoords(cm, pos)
3502 var endCoords = !end || end == pos ? coords : cursorCoords(cm, end)
3503 var scrollPos = calculateScrollPos(cm, Math.min(coords.left, endCoords.left),
3504 Math.min(coords.top, endCoords.top) - margin,
3505 Math.max(coords.left, endCoords.left),
3506 Math.max(coords.bottom, endCoords.bottom) + margin)
3507 var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft
3508 if (scrollPos.scrollTop != null) {
3509 setScrollTop(cm, scrollPos.scrollTop)
3510 if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true }
3511 }
3512 if (scrollPos.scrollLeft != null) {
3513 setScrollLeft(cm, scrollPos.scrollLeft)
3514 if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true }
3515 }
3516 if (!changed) { break }
3517 }
3518 return coords
3519}
3520
3521// Scroll a given set of coordinates into view (immediately).
3522function scrollIntoView(cm, x1, y1, x2, y2) {
3523 var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2)
3524 if (scrollPos.scrollTop != null) { setScrollTop(cm, scrollPos.scrollTop) }
3525 if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft) }
3526}
3527
3528// Calculate a new scroll position needed to scroll the given
3529// rectangle into view. Returns an object with scrollTop and
3530// scrollLeft properties. When these are undefined, the
3531// vertical/horizontal position does not need to be adjusted.
3532function calculateScrollPos(cm, x1, y1, x2, y2) {
3533 var display = cm.display, snapMargin = textHeight(cm.display)
3534 if (y1 < 0) { y1 = 0 }
3535 var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop
3536 var screen = displayHeight(cm), result = {}
3537 if (y2 - y1 > screen) { y2 = y1 + screen }
3538 var docBottom = cm.doc.height + paddingVert(display)
3539 var atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin
3540 if (y1 < screentop) {
3541 result.scrollTop = atTop ? 0 : y1
3542 } else if (y2 > screentop + screen) {
3543 var newTop = Math.min(y1, (atBottom ? docBottom : y2) - screen)
3544 if (newTop != screentop) { result.scrollTop = newTop }
3545 }
3546
3547 var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft
3548 var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0)
3549 var tooWide = x2 - x1 > screenw
3550 if (tooWide) { x2 = x1 + screenw }
3551 if (x1 < 10)
3552 { result.scrollLeft = 0 }
3553 else if (x1 < screenleft)
3554 { result.scrollLeft = Math.max(0, x1 - (tooWide ? 0 : 10)) }
3555 else if (x2 > screenw + screenleft - 3)
3556 { result.scrollLeft = x2 + (tooWide ? 0 : 10) - screenw }
3557 return result
3558}
3559
3560// Store a relative adjustment to the scroll position in the current
3561// operation (to be applied when the operation finishes).
3562function addToScrollPos(cm, left, top) {
3563 if (left != null || top != null) { resolveScrollToPos(cm) }
3564 if (left != null)
3565 { cm.curOp.scrollLeft = (cm.curOp.scrollLeft == null ? cm.doc.scrollLeft : cm.curOp.scrollLeft) + left }
3566 if (top != null)
3567 { cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top }
3568}
3569
3570// Make sure that at the end of the operation the current cursor is
3571// shown.
3572function ensureCursorVisible(cm) {
3573 resolveScrollToPos(cm)
3574 var cur = cm.getCursor(), from = cur, to = cur
3575 if (!cm.options.lineWrapping) {
3576 from = cur.ch ? Pos(cur.line, cur.ch - 1) : cur
3577 to = Pos(cur.line, cur.ch + 1)
3578 }
3579 cm.curOp.scrollToPos = {from: from, to: to, margin: cm.options.cursorScrollMargin, isCursor: true}
3580}
3581
3582// When an operation has its scrollToPos property set, and another
3583// scroll action is applied before the end of the operation, this
3584// 'simulates' scrolling that position into view in a cheap way, so
3585// that the effect of intermediate scroll commands is not ignored.
3586function resolveScrollToPos(cm) {
3587 var range$$1 = cm.curOp.scrollToPos
3588 if (range$$1) {
3589 cm.curOp.scrollToPos = null
3590 var from = estimateCoords(cm, range$$1.from), to = estimateCoords(cm, range$$1.to)
3591 var sPos = calculateScrollPos(cm, Math.min(from.left, to.left),
3592 Math.min(from.top, to.top) - range$$1.margin,
3593 Math.max(from.right, to.right),
3594 Math.max(from.bottom, to.bottom) + range$$1.margin)
3595 cm.scrollTo(sPos.scrollLeft, sPos.scrollTop)
3596 }
3597}
3598
3599// Operations are used to wrap a series of changes to the editor
3600// state in such a way that each change won't have to update the
3601// cursor and display (which would be awkward, slow, and
3602// error-prone). Instead, display updates are batched and then all
3603// combined and executed at once.
3604
3605var nextOpId = 0
3606// Start a new operation.
3607function startOperation(cm) {
3608 cm.curOp = {
3609 cm: cm,
3610 viewChanged: false, // Flag that indicates that lines might need to be redrawn
3611 startHeight: cm.doc.height, // Used to detect need to update scrollbar
3612 forceUpdate: false, // Used to force a redraw
3613 updateInput: null, // Whether to reset the input textarea
3614 typing: false, // Whether this reset should be careful to leave existing text (for compositing)
3615 changeObjs: null, // Accumulated changes, for firing change events
3616 cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on
3617 cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
3618 selectionChanged: false, // Whether the selection needs to be redrawn
3619 updateMaxLine: false, // Set when the widest line needs to be determined anew
3620 scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
3621 scrollToPos: null, // Used to scroll to a specific position
3622 focus: false,
3623 id: ++nextOpId // Unique ID
3624 }
3625 pushOperation(cm.curOp)
3626}
3627
3628// Finish an operation, updating the display and signalling delayed events
3629function endOperation(cm) {
3630 var op = cm.curOp
3631 finishOperation(op, function (group) {
3632 for (var i = 0; i < group.ops.length; i++)
3633 { group.ops[i].cm.curOp = null }
3634 endOperations(group)
3635 })
3636}
3637
3638// The DOM updates done when an operation finishes are batched so
3639// that the minimum number of relayouts are required.
3640function endOperations(group) {
3641 var ops = group.ops
3642 for (var i = 0; i < ops.length; i++) // Read DOM
3643 { endOperation_R1(ops[i]) }
3644 for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe)
3645 { endOperation_W1(ops[i$1]) }
3646 for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM
3647 { endOperation_R2(ops[i$2]) }
3648 for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe)
3649 { endOperation_W2(ops[i$3]) }
3650 for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM
3651 { endOperation_finish(ops[i$4]) }
3652}
3653
3654function endOperation_R1(op) {
3655 var cm = op.cm, display = cm.display
3656 maybeClipScrollbars(cm)
3657 if (op.updateMaxLine) { findMaxLine(cm) }
3658
3659 op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||
3660 op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
3661 op.scrollToPos.to.line >= display.viewTo) ||
3662 display.maxLineChanged && cm.options.lineWrapping
3663 op.update = op.mustUpdate &&
3664 new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate)
3665}
3666
3667function endOperation_W1(op) {
3668 op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update)
3669}
3670
3671function endOperation_R2(op) {
3672 var cm = op.cm, display = cm.display
3673 if (op.updatedDisplay) { updateHeightsInViewport(cm) }
3674
3675 op.barMeasure = measureForScrollbars(cm)
3676
3677 // If the max line changed since it was last measured, measure it,
3678 // and ensure the document's width matches it.
3679 // updateDisplay_W2 will use these properties to do the actual resizing
3680 if (display.maxLineChanged && !cm.options.lineWrapping) {
3681 op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3
3682 cm.display.sizerWidth = op.adjustWidthTo
3683 op.barMeasure.scrollWidth =
3684 Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth)
3685 op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm))
3686 }
3687
3688 if (op.updatedDisplay || op.selectionChanged)
3689 { op.preparedSelection = display.input.prepareSelection(op.focus) }
3690}
3691
3692function endOperation_W2(op) {
3693 var cm = op.cm
3694
3695 if (op.adjustWidthTo != null) {
3696 cm.display.sizer.style.minWidth = op.adjustWidthTo + "px"
3697 if (op.maxScrollLeft < cm.doc.scrollLeft)
3698 { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true) }
3699 cm.display.maxLineChanged = false
3700 }
3701
3702 var takeFocus = op.focus && op.focus == activeElt() && (!document.hasFocus || document.hasFocus())
3703 if (op.preparedSelection)
3704 { cm.display.input.showSelection(op.preparedSelection, takeFocus) }
3705 if (op.updatedDisplay || op.startHeight != cm.doc.height)
3706 { updateScrollbars(cm, op.barMeasure) }
3707 if (op.updatedDisplay)
3708 { setDocumentHeight(cm, op.barMeasure) }
3709
3710 if (op.selectionChanged) { restartBlink(cm) }
3711
3712 if (cm.state.focused && op.updateInput)
3713 { cm.display.input.reset(op.typing) }
3714 if (takeFocus) { ensureFocus(op.cm) }
3715}
3716
3717function endOperation_finish(op) {
3718 var cm = op.cm, display = cm.display, doc = cm.doc
3719
3720 if (op.updatedDisplay) { postUpdateDisplay(cm, op.update) }
3721
3722 // Abort mouse wheel delta measurement, when scrolling explicitly
3723 if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))
3724 { display.wheelStartX = display.wheelStartY = null }
3725
3726 // Propagate the scroll position to the actual DOM scroller
3727 if (op.scrollTop != null && (display.scroller.scrollTop != op.scrollTop || op.forceScroll)) {
3728 doc.scrollTop = Math.max(0, Math.min(display.scroller.scrollHeight - display.scroller.clientHeight, op.scrollTop))
3729 display.scrollbars.setScrollTop(doc.scrollTop)
3730 display.scroller.scrollTop = doc.scrollTop
3731 }
3732 if (op.scrollLeft != null && (display.scroller.scrollLeft != op.scrollLeft || op.forceScroll)) {
3733 doc.scrollLeft = Math.max(0, Math.min(display.scroller.scrollWidth - display.scroller.clientWidth, op.scrollLeft))
3734 display.scrollbars.setScrollLeft(doc.scrollLeft)
3735 display.scroller.scrollLeft = doc.scrollLeft
3736 alignHorizontally(cm)
3737 }
3738 // If we need to scroll a specific position into view, do so.
3739 if (op.scrollToPos) {
3740 var coords = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
3741 clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin)
3742 if (op.scrollToPos.isCursor && cm.state.focused) { maybeScrollWindow(cm, coords) }
3743 }
3744
3745 // Fire events for markers that are hidden/unidden by editing or
3746 // undoing
3747 var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers
3748 if (hidden) { for (var i = 0; i < hidden.length; ++i)
3749 { if (!hidden[i].lines.length) { signal(hidden[i], "hide") } } }
3750 if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1)
3751 { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], "unhide") } } }
3752
3753 if (display.wrapper.offsetHeight)
3754 { doc.scrollTop = cm.display.scroller.scrollTop }
3755
3756 // Fire change events, and delayed event handlers
3757 if (op.changeObjs)
3758 { signal(cm, "changes", cm, op.changeObjs) }
3759 if (op.update)
3760 { op.update.finish() }
3761}
3762
3763// Run the given function in an operation
3764function runInOp(cm, f) {
3765 if (cm.curOp) { return f() }
3766 startOperation(cm)
3767 try { return f() }
3768 finally { endOperation(cm) }
3769}
3770// Wraps a function in an operation. Returns the wrapped function.
3771function operation(cm, f) {
3772 return function() {
3773 if (cm.curOp) { return f.apply(cm, arguments) }
3774 startOperation(cm)
3775 try { return f.apply(cm, arguments) }
3776 finally { endOperation(cm) }
3777 }
3778}
3779// Used to add methods to editor and doc instances, wrapping them in
3780// operations.
3781function methodOp(f) {
3782 return function() {
3783 if (this.curOp) { return f.apply(this, arguments) }
3784 startOperation(this)
3785 try { return f.apply(this, arguments) }
3786 finally { endOperation(this) }
3787 }
3788}
3789function docMethodOp(f) {
3790 return function() {
3791 var cm = this.cm
3792 if (!cm || cm.curOp) { return f.apply(this, arguments) }
3793 startOperation(cm)
3794 try { return f.apply(this, arguments) }
3795 finally { endOperation(cm) }
3796 }
3797}
3798
3799// Updates the display.view data structure for a given change to the
3800// document. From and to are in pre-change coordinates. Lendiff is
3801// the amount of lines added or subtracted by the change. This is
3802// used for changes that span multiple lines, or change the way
3803// lines are divided into visual lines. regLineChange (below)
3804// registers single-line changes.
3805function regChange(cm, from, to, lendiff) {
3806 if (from == null) { from = cm.doc.first }
3807 if (to == null) { to = cm.doc.first + cm.doc.size }
3808 if (!lendiff) { lendiff = 0 }
3809
3810 var display = cm.display
3811 if (lendiff && to < display.viewTo &&
3812 (display.updateLineNumbers == null || display.updateLineNumbers > from))
3813 { display.updateLineNumbers = from }
3814
3815 cm.curOp.viewChanged = true
3816
3817 if (from >= display.viewTo) { // Change after
3818 if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)
3819 { resetView(cm) }
3820 } else if (to <= display.viewFrom) { // Change before
3821 if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {
3822 resetView(cm)
3823 } else {
3824 display.viewFrom += lendiff
3825 display.viewTo += lendiff
3826 }
3827 } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap
3828 resetView(cm)
3829 } else if (from <= display.viewFrom) { // Top overlap
3830 var cut = viewCuttingPoint(cm, to, to + lendiff, 1)
3831 if (cut) {
3832 display.view = display.view.slice(cut.index)
3833 display.viewFrom = cut.lineN
3834 display.viewTo += lendiff
3835 } else {
3836 resetView(cm)
3837 }
3838 } else if (to >= display.viewTo) { // Bottom overlap
3839 var cut$1 = viewCuttingPoint(cm, from, from, -1)
3840 if (cut$1) {
3841 display.view = display.view.slice(0, cut$1.index)
3842 display.viewTo = cut$1.lineN
3843 } else {
3844 resetView(cm)
3845 }
3846 } else { // Gap in the middle
3847 var cutTop = viewCuttingPoint(cm, from, from, -1)
3848 var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1)
3849 if (cutTop && cutBot) {
3850 display.view = display.view.slice(0, cutTop.index)
3851 .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))
3852 .concat(display.view.slice(cutBot.index))
3853 display.viewTo += lendiff
3854 } else {
3855 resetView(cm)
3856 }
3857 }
3858
3859 var ext = display.externalMeasured
3860 if (ext) {
3861 if (to < ext.lineN)
3862 { ext.lineN += lendiff }
3863 else if (from < ext.lineN + ext.size)
3864 { display.externalMeasured = null }
3865 }
3866}
3867
3868// Register a change to a single line. Type must be one of "text",
3869// "gutter", "class", "widget"
3870function regLineChange(cm, line, type) {
3871 cm.curOp.viewChanged = true
3872 var display = cm.display, ext = cm.display.externalMeasured
3873 if (ext && line >= ext.lineN && line < ext.lineN + ext.size)
3874 { display.externalMeasured = null }
3875
3876 if (line < display.viewFrom || line >= display.viewTo) { return }
3877 var lineView = display.view[findViewIndex(cm, line)]
3878 if (lineView.node == null) { return }
3879 var arr = lineView.changes || (lineView.changes = [])
3880 if (indexOf(arr, type) == -1) { arr.push(type) }
3881}
3882
3883// Clear the view.
3884function resetView(cm) {
3885 cm.display.viewFrom = cm.display.viewTo = cm.doc.first
3886 cm.display.view = []
3887 cm.display.viewOffset = 0
3888}
3889
3890function viewCuttingPoint(cm, oldN, newN, dir) {
3891 var index = findViewIndex(cm, oldN), diff, view = cm.display.view
3892 if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)
3893 { return {index: index, lineN: newN} }
3894 var n = cm.display.viewFrom
3895 for (var i = 0; i < index; i++)
3896 { n += view[i].size }
3897 if (n != oldN) {
3898 if (dir > 0) {
3899 if (index == view.length - 1) { return null }
3900 diff = (n + view[index].size) - oldN
3901 index++
3902 } else {
3903 diff = n - oldN
3904 }
3905 oldN += diff; newN += diff
3906 }
3907 while (visualLineNo(cm.doc, newN) != newN) {
3908 if (index == (dir < 0 ? 0 : view.length - 1)) { return null }
3909 newN += dir * view[index - (dir < 0 ? 1 : 0)].size
3910 index += dir
3911 }
3912 return {index: index, lineN: newN}
3913}
3914
3915// Force the view to cover a given range, adding empty view element
3916// or clipping off existing ones as needed.
3917function adjustView(cm, from, to) {
3918 var display = cm.display, view = display.view
3919 if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {
3920 display.view = buildViewArray(cm, from, to)
3921 display.viewFrom = from
3922 } else {
3923 if (display.viewFrom > from)
3924 { display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view) }
3925 else if (display.viewFrom < from)
3926 { display.view = display.view.slice(findViewIndex(cm, from)) }
3927 display.viewFrom = from
3928 if (display.viewTo < to)
3929 { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)) }
3930 else if (display.viewTo > to)
3931 { display.view = display.view.slice(0, findViewIndex(cm, to)) }
3932 }
3933 display.viewTo = to
3934}
3935
3936// Count the number of lines in the view whose DOM representation is
3937// out of date (or nonexistent).
3938function countDirtyView(cm) {
3939 var view = cm.display.view, dirty = 0
3940 for (var i = 0; i < view.length; i++) {
3941 var lineView = view[i]
3942 if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty }
3943 }
3944 return dirty
3945}
3946
3947// HIGHLIGHT WORKER
3948
3949function startWorker(cm, time) {
3950 if (cm.doc.mode.startState && cm.doc.frontier < cm.display.viewTo)
3951 { cm.state.highlight.set(time, bind(highlightWorker, cm)) }
3952}
3953
3954function highlightWorker(cm) {
3955 var doc = cm.doc
3956 if (doc.frontier < doc.first) { doc.frontier = doc.first }
3957 if (doc.frontier >= cm.display.viewTo) { return }
3958 var end = +new Date + cm.options.workTime
3959 var state = copyState(doc.mode, getStateBefore(cm, doc.frontier))
3960 var changedLines = []
3961
3962 doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function (line) {
3963 if (doc.frontier >= cm.display.viewFrom) { // Visible
3964 var oldStyles = line.styles, tooLong = line.text.length > cm.options.maxHighlightLength
3965 var highlighted = highlightLine(cm, line, tooLong ? copyState(doc.mode, state) : state, true)
3966 line.styles = highlighted.styles
3967 var oldCls = line.styleClasses, newCls = highlighted.classes
3968 if (newCls) { line.styleClasses = newCls }
3969 else if (oldCls) { line.styleClasses = null }
3970 var ischange = !oldStyles || oldStyles.length != line.styles.length ||
3971 oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass)
3972 for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldStyles[i] != line.styles[i] }
3973 if (ischange) { changedLines.push(doc.frontier) }
3974 line.stateAfter = tooLong ? state : copyState(doc.mode, state)
3975 } else {
3976 if (line.text.length <= cm.options.maxHighlightLength)
3977 { processLine(cm, line.text, state) }
3978 line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null
3979 }
3980 ++doc.frontier
3981 if (+new Date > end) {
3982 startWorker(cm, cm.options.workDelay)
3983 return true
3984 }
3985 })
3986 if (changedLines.length) { runInOp(cm, function () {
3987 for (var i = 0; i < changedLines.length; i++)
3988 { regLineChange(cm, changedLines[i], "text") }
3989 }) }
3990}
3991
3992// DISPLAY DRAWING
3993
3994function DisplayUpdate(cm, viewport, force) {
3995 var display = cm.display
3996
3997 this.viewport = viewport
3998 // Store some values that we'll need later (but don't want to force a relayout for)
3999 this.visible = visibleLines(display, cm.doc, viewport)
4000 this.editorIsHidden = !display.wrapper.offsetWidth
4001 this.wrapperHeight = display.wrapper.clientHeight
4002 this.wrapperWidth = display.wrapper.clientWidth
4003 this.oldDisplayWidth = displayWidth(cm)
4004 this.force = force
4005 this.dims = getDimensions(cm)
4006 this.events = []
4007}
4008
4009DisplayUpdate.prototype.signal = function(emitter, type) {
4010 if (hasHandler(emitter, type))
4011 { this.events.push(arguments) }
4012}
4013DisplayUpdate.prototype.finish = function() {
4014 var this$1 = this;
4015
4016 for (var i = 0; i < this.events.length; i++)
4017 { signal.apply(null, this$1.events[i]) }
4018}
4019
4020function maybeClipScrollbars(cm) {
4021 var display = cm.display
4022 if (!display.scrollbarsClipped && display.scroller.offsetWidth) {
4023 display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth
4024 display.heightForcer.style.height = scrollGap(cm) + "px"
4025 display.sizer.style.marginBottom = -display.nativeBarWidth + "px"
4026 display.sizer.style.borderRightWidth = scrollGap(cm) + "px"
4027 display.scrollbarsClipped = true
4028 }
4029}
4030
4031// Does the actual updating of the line display. Bails out
4032// (returning false) when there is nothing to be done and forced is
4033// false.
4034function updateDisplayIfNeeded(cm, update) {
4035 var display = cm.display, doc = cm.doc
4036
4037 if (update.editorIsHidden) {
4038 resetView(cm)
4039 return false
4040 }
4041
4042 // Bail out if the visible area is already rendered and nothing changed.
4043 if (!update.force &&
4044 update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&
4045 (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&
4046 display.renderedView == display.view && countDirtyView(cm) == 0)
4047 { return false }
4048
4049 if (maybeUpdateLineNumberWidth(cm)) {
4050 resetView(cm)
4051 update.dims = getDimensions(cm)
4052 }
4053
4054 // Compute a suitable new viewport (from & to)
4055 var end = doc.first + doc.size
4056 var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first)
4057 var to = Math.min(end, update.visible.to + cm.options.viewportMargin)
4058 if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max(doc.first, display.viewFrom) }
4059 if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, display.viewTo) }
4060 if (sawCollapsedSpans) {
4061 from = visualLineNo(cm.doc, from)
4062 to = visualLineEndNo(cm.doc, to)
4063 }
4064
4065 var different = from != display.viewFrom || to != display.viewTo ||
4066 display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth
4067 adjustView(cm, from, to)
4068
4069 display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom))
4070 // Position the mover div to align with the current scroll position
4071 cm.display.mover.style.top = display.viewOffset + "px"
4072
4073 var toUpdate = countDirtyView(cm)
4074 if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view &&
4075 (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))
4076 { return false }
4077
4078 // For big changes, we hide the enclosing element during the
4079 // update, since that speeds up the operations on most browsers.
4080 var focused = activeElt()
4081 if (toUpdate > 4) { display.lineDiv.style.display = "none" }
4082 patchDisplay(cm, display.updateLineNumbers, update.dims)
4083 if (toUpdate > 4) { display.lineDiv.style.display = "" }
4084 display.renderedView = display.view
4085 // There might have been a widget with a focused element that got
4086 // hidden or updated, if so re-focus it.
4087 if (focused && activeElt() != focused && focused.offsetHeight) { focused.focus() }
4088
4089 // Prevent selection and cursors from interfering with the scroll
4090 // width and height.
4091 removeChildren(display.cursorDiv)
4092 removeChildren(display.selectionDiv)
4093 display.gutters.style.height = display.sizer.style.minHeight = 0
4094
4095 if (different) {
4096 display.lastWrapHeight = update.wrapperHeight
4097 display.lastWrapWidth = update.wrapperWidth
4098 startWorker(cm, 400)
4099 }
4100
4101 display.updateLineNumbers = null
4102
4103 return true
4104}
4105
4106function postUpdateDisplay(cm, update) {
4107 var viewport = update.viewport
4108
4109 for (var first = true;; first = false) {
4110 if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) {
4111 // Clip forced viewport to actual scrollable area.
4112 if (viewport && viewport.top != null)
4113 { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)} }
4114 // Updated line heights might result in the drawn area not
4115 // actually covering the viewport. Keep looping until it does.
4116 update.visible = visibleLines(cm.display, cm.doc, viewport)
4117 if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)
4118 { break }
4119 }
4120 if (!updateDisplayIfNeeded(cm, update)) { break }
4121 updateHeightsInViewport(cm)
4122 var barMeasure = measureForScrollbars(cm)
4123 updateSelection(cm)
4124 updateScrollbars(cm, barMeasure)
4125 setDocumentHeight(cm, barMeasure)
4126 }
4127
4128 update.signal(cm, "update", cm)
4129 if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {
4130 update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo)
4131 cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo
4132 }
4133}
4134
4135function updateDisplaySimple(cm, viewport) {
4136 var update = new DisplayUpdate(cm, viewport)
4137 if (updateDisplayIfNeeded(cm, update)) {
4138 updateHeightsInViewport(cm)
4139 postUpdateDisplay(cm, update)
4140 var barMeasure = measureForScrollbars(cm)
4141 updateSelection(cm)
4142 updateScrollbars(cm, barMeasure)
4143 setDocumentHeight(cm, barMeasure)
4144 update.finish()
4145 }
4146}
4147
4148// Sync the actual display DOM structure with display.view, removing
4149// nodes for lines that are no longer in view, and creating the ones
4150// that are not there yet, and updating the ones that are out of
4151// date.
4152function patchDisplay(cm, updateNumbersFrom, dims) {
4153 var display = cm.display, lineNumbers = cm.options.lineNumbers
4154 var container = display.lineDiv, cur = container.firstChild
4155
4156 function rm(node) {
4157 var next = node.nextSibling
4158 // Works around a throw-scroll bug in OS X Webkit
4159 if (webkit && mac && cm.display.currentWheelTarget == node)
4160 { node.style.display = "none" }
4161 else
4162 { node.parentNode.removeChild(node) }
4163 return next
4164 }
4165
4166 var view = display.view, lineN = display.viewFrom
4167 // Loop over the elements in the view, syncing cur (the DOM nodes
4168 // in display.lineDiv) with the view as we go.
4169 for (var i = 0; i < view.length; i++) {
4170 var lineView = view[i]
4171 if (lineView.hidden) {
4172 } else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet
4173 var node = buildLineElement(cm, lineView, lineN, dims)
4174 container.insertBefore(node, cur)
4175 } else { // Already drawn
4176 while (cur != lineView.node) { cur = rm(cur) }
4177 var updateNumber = lineNumbers && updateNumbersFrom != null &&
4178 updateNumbersFrom <= lineN && lineView.lineNumber
4179 if (lineView.changes) {
4180 if (indexOf(lineView.changes, "gutter") > -1) { updateNumber = false }
4181 updateLineForChanges(cm, lineView, lineN, dims)
4182 }
4183 if (updateNumber) {
4184 removeChildren(lineView.lineNumber)
4185 lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN)))
4186 }
4187 cur = lineView.node.nextSibling
4188 }
4189 lineN += lineView.size
4190 }
4191 while (cur) { cur = rm(cur) }
4192}
4193
4194function updateGutterSpace(cm) {
4195 var width = cm.display.gutters.offsetWidth
4196 cm.display.sizer.style.marginLeft = width + "px"
4197}
4198
4199function setDocumentHeight(cm, measure) {
4200 cm.display.sizer.style.minHeight = measure.docHeight + "px"
4201 cm.display.heightForcer.style.top = measure.docHeight + "px"
4202 cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px"
4203}
4204
4205// Rebuild the gutter elements, ensure the margin to the left of the
4206// code matches their width.
4207function updateGutters(cm) {
4208 var gutters = cm.display.gutters, specs = cm.options.gutters
4209 removeChildren(gutters)
4210 var i = 0
4211 for (; i < specs.length; ++i) {
4212 var gutterClass = specs[i]
4213 var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass))
4214 if (gutterClass == "CodeMirror-linenumbers") {
4215 cm.display.lineGutter = gElt
4216 gElt.style.width = (cm.display.lineNumWidth || 1) + "px"
4217 }
4218 }
4219 gutters.style.display = i ? "" : "none"
4220 updateGutterSpace(cm)
4221}
4222
4223// Make sure the gutters options contains the element
4224// "CodeMirror-linenumbers" when the lineNumbers option is true.
4225function setGuttersForLineNumbers(options) {
4226 var found = indexOf(options.gutters, "CodeMirror-linenumbers")
4227 if (found == -1 && options.lineNumbers) {
4228 options.gutters = options.gutters.concat(["CodeMirror-linenumbers"])
4229 } else if (found > -1 && !options.lineNumbers) {
4230 options.gutters = options.gutters.slice(0)
4231 options.gutters.splice(found, 1)
4232 }
4233}
4234
4235// Selection objects are immutable. A new one is created every time
4236// the selection changes. A selection is one or more non-overlapping
4237// (and non-touching) ranges, sorted, and an integer that indicates
4238// which one is the primary selection (the one that's scrolled into
4239// view, that getCursor returns, etc).
4240function Selection(ranges, primIndex) {
4241 this.ranges = ranges
4242 this.primIndex = primIndex
4243}
4244
4245Selection.prototype = {
4246 primary: function() { return this.ranges[this.primIndex] },
4247 equals: function(other) {
4248 var this$1 = this;
4249
4250 if (other == this) { return true }
4251 if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false }
4252 for (var i = 0; i < this.ranges.length; i++) {
4253 var here = this$1.ranges[i], there = other.ranges[i]
4254 if (cmp(here.anchor, there.anchor) != 0 || cmp(here.head, there.head) != 0) { return false }
4255 }
4256 return true
4257 },
4258 deepCopy: function() {
4259 var this$1 = this;
4260
4261 var out = []
4262 for (var i = 0; i < this.ranges.length; i++)
4263 { out[i] = new Range(copyPos(this$1.ranges[i].anchor), copyPos(this$1.ranges[i].head)) }
4264 return new Selection(out, this.primIndex)
4265 },
4266 somethingSelected: function() {
4267 var this$1 = this;
4268
4269 for (var i = 0; i < this.ranges.length; i++)
4270 { if (!this$1.ranges[i].empty()) { return true } }
4271 return false
4272 },
4273 contains: function(pos, end) {
4274 var this$1 = this;
4275
4276 if (!end) { end = pos }
4277 for (var i = 0; i < this.ranges.length; i++) {
4278 var range = this$1.ranges[i]
4279 if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)
4280 { return i }
4281 }
4282 return -1
4283 }
4284}
4285
4286function Range(anchor, head) {
4287 this.anchor = anchor; this.head = head
4288}
4289
4290Range.prototype = {
4291 from: function() { return minPos(this.anchor, this.head) },
4292 to: function() { return maxPos(this.anchor, this.head) },
4293 empty: function() {
4294 return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch
4295 }
4296}
4297
4298// Take an unsorted, potentially overlapping set of ranges, and
4299// build a selection out of it. 'Consumes' ranges array (modifying
4300// it).
4301function normalizeSelection(ranges, primIndex) {
4302 var prim = ranges[primIndex]
4303 ranges.sort(function (a, b) { return cmp(a.from(), b.from()); })
4304 primIndex = indexOf(ranges, prim)
4305 for (var i = 1; i < ranges.length; i++) {
4306 var cur = ranges[i], prev = ranges[i - 1]
4307 if (cmp(prev.to(), cur.from()) >= 0) {
4308 var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to())
4309 var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head
4310 if (i <= primIndex) { --primIndex }
4311 ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to))
4312 }
4313 }
4314 return new Selection(ranges, primIndex)
4315}
4316
4317function simpleSelection(anchor, head) {
4318 return new Selection([new Range(anchor, head || anchor)], 0)
4319}
4320
4321// Compute the position of the end of a change (its 'to' property
4322// refers to the pre-change end).
4323function changeEnd(change) {
4324 if (!change.text) { return change.to }
4325 return Pos(change.from.line + change.text.length - 1,
4326 lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0))
4327}
4328
4329// Adjust a position to refer to the post-change position of the
4330// same text, or the end of the change if the change covers it.
4331function adjustForChange(pos, change) {
4332 if (cmp(pos, change.from) < 0) { return pos }
4333 if (cmp(pos, change.to) <= 0) { return changeEnd(change) }
4334
4335 var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch
4336 if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch }
4337 return Pos(line, ch)
4338}
4339
4340function computeSelAfterChange(doc, change) {
4341 var out = []
4342 for (var i = 0; i < doc.sel.ranges.length; i++) {
4343 var range = doc.sel.ranges[i]
4344 out.push(new Range(adjustForChange(range.anchor, change),
4345 adjustForChange(range.head, change)))
4346 }
4347 return normalizeSelection(out, doc.sel.primIndex)
4348}
4349
4350function offsetPos(pos, old, nw) {
4351 if (pos.line == old.line)
4352 { return Pos(nw.line, pos.ch - old.ch + nw.ch) }
4353 else
4354 { return Pos(nw.line + (pos.line - old.line), pos.ch) }
4355}
4356
4357// Used by replaceSelections to allow moving the selection to the
4358// start or around the replaced test. Hint may be "start" or "around".
4359function computeReplacedSel(doc, changes, hint) {
4360 var out = []
4361 var oldPrev = Pos(doc.first, 0), newPrev = oldPrev
4362 for (var i = 0; i < changes.length; i++) {
4363 var change = changes[i]
4364 var from = offsetPos(change.from, oldPrev, newPrev)
4365 var to = offsetPos(changeEnd(change), oldPrev, newPrev)
4366 oldPrev = change.to
4367 newPrev = to
4368 if (hint == "around") {
4369 var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0
4370 out[i] = new Range(inv ? to : from, inv ? from : to)
4371 } else {
4372 out[i] = new Range(from, from)
4373 }
4374 }
4375 return new Selection(out, doc.sel.primIndex)
4376}
4377
4378// Used to get the editor into a consistent state again when options change.
4379
4380function loadMode(cm) {
4381 cm.doc.mode = getMode(cm.options, cm.doc.modeOption)
4382 resetModeState(cm)
4383}
4384
4385function resetModeState(cm) {
4386 cm.doc.iter(function (line) {
4387 if (line.stateAfter) { line.stateAfter = null }
4388 if (line.styles) { line.styles = null }
4389 })
4390 cm.doc.frontier = cm.doc.first
4391 startWorker(cm, 100)
4392 cm.state.modeGen++
4393 if (cm.curOp) { regChange(cm) }
4394}
4395
4396// DOCUMENT DATA STRUCTURE
4397
4398// By default, updates that start and end at the beginning of a line
4399// are treated specially, in order to make the association of line
4400// widgets and marker elements with the text behave more intuitive.
4401function isWholeLineUpdate(doc, change) {
4402 return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" &&
4403 (!doc.cm || doc.cm.options.wholeLineUpdateBefore)
4404}
4405
4406// Perform a change on the document data structure.
4407function updateDoc(doc, change, markedSpans, estimateHeight$$1) {
4408 function spansFor(n) {return markedSpans ? markedSpans[n] : null}
4409 function update(line, text, spans) {
4410 updateLine(line, text, spans, estimateHeight$$1)
4411 signalLater(line, "change", line, change)
4412 }
4413 function linesFor(start, end) {
4414 var result = []
4415 for (var i = start; i < end; ++i)
4416 { result.push(new Line(text[i], spansFor(i), estimateHeight$$1)) }
4417 return result
4418 }
4419
4420 var from = change.from, to = change.to, text = change.text
4421 var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line)
4422 var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line
4423
4424 // Adjust the line structure
4425 if (change.full) {
4426 doc.insert(0, linesFor(0, text.length))
4427 doc.remove(text.length, doc.size - text.length)
4428 } else if (isWholeLineUpdate(doc, change)) {
4429 // This is a whole-line replace. Treated specially to make
4430 // sure line objects move the way they are supposed to.
4431 var added = linesFor(0, text.length - 1)
4432 update(lastLine, lastLine.text, lastSpans)
4433 if (nlines) { doc.remove(from.line, nlines) }
4434 if (added.length) { doc.insert(from.line, added) }
4435 } else if (firstLine == lastLine) {
4436 if (text.length == 1) {
4437 update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans)
4438 } else {
4439 var added$1 = linesFor(1, text.length - 1)
4440 added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight$$1))
4441 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0))
4442 doc.insert(from.line + 1, added$1)
4443 }
4444 } else if (text.length == 1) {
4445 update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0))
4446 doc.remove(from.line + 1, nlines)
4447 } else {
4448 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0))
4449 update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans)
4450 var added$2 = linesFor(1, text.length - 1)
4451 if (nlines > 1) { doc.remove(from.line + 1, nlines - 1) }
4452 doc.insert(from.line + 1, added$2)
4453 }
4454
4455 signalLater(doc, "change", doc, change)
4456}
4457
4458// Call f for all linked documents.
4459function linkedDocs(doc, f, sharedHistOnly) {
4460 function propagate(doc, skip, sharedHist) {
4461 if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) {
4462 var rel = doc.linked[i]
4463 if (rel.doc == skip) { continue }
4464 var shared = sharedHist && rel.sharedHist
4465 if (sharedHistOnly && !shared) { continue }
4466 f(rel.doc, shared)
4467 propagate(rel.doc, doc, shared)
4468 } }
4469 }
4470 propagate(doc, null, true)
4471}
4472
4473// Attach a document to an editor.
4474function attachDoc(cm, doc) {
4475 if (doc.cm) { throw new Error("This document is already in use.") }
4476 cm.doc = doc
4477 doc.cm = cm
4478 estimateLineHeights(cm)
4479 loadMode(cm)
4480 if (!cm.options.lineWrapping) { findMaxLine(cm) }
4481 cm.options.mode = doc.modeOption
4482 regChange(cm)
4483}
4484
4485function History(startGen) {
4486 // Arrays of change events and selections. Doing something adds an
4487 // event to done and clears undo. Undoing moves events from done
4488 // to undone, redoing moves them in the other direction.
4489 this.done = []; this.undone = []
4490 this.undoDepth = Infinity
4491 // Used to track when changes can be merged into a single undo
4492 // event
4493 this.lastModTime = this.lastSelTime = 0
4494 this.lastOp = this.lastSelOp = null
4495 this.lastOrigin = this.lastSelOrigin = null
4496 // Used by the isClean() method
4497 this.generation = this.maxGeneration = startGen || 1
4498}
4499
4500// Create a history change event from an updateDoc-style change
4501// object.
4502function historyChangeFromChange(doc, change) {
4503 var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)}
4504 attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1)
4505 linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); }, true)
4506 return histChange
4507}
4508
4509// Pop all selection events off the end of a history array. Stop at
4510// a change event.
4511function clearSelectionEvents(array) {
4512 while (array.length) {
4513 var last = lst(array)
4514 if (last.ranges) { array.pop() }
4515 else { break }
4516 }
4517}
4518
4519// Find the top change event in the history. Pop off selection
4520// events that are in the way.
4521function lastChangeEvent(hist, force) {
4522 if (force) {
4523 clearSelectionEvents(hist.done)
4524 return lst(hist.done)
4525 } else if (hist.done.length && !lst(hist.done).ranges) {
4526 return lst(hist.done)
4527 } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) {
4528 hist.done.pop()
4529 return lst(hist.done)
4530 }
4531}
4532
4533// Register a change in the history. Merges changes that are within
4534// a single operation, or are close together with an origin that
4535// allows merging (starting with "+") into a single event.
4536function addChangeToHistory(doc, change, selAfter, opId) {
4537 var hist = doc.history
4538 hist.undone.length = 0
4539 var time = +new Date, cur
4540 var last
4541
4542 if ((hist.lastOp == opId ||
4543 hist.lastOrigin == change.origin && change.origin &&
4544 ((change.origin.charAt(0) == "+" && doc.cm && hist.lastModTime > time - doc.cm.options.historyEventDelay) ||
4545 change.origin.charAt(0) == "*")) &&
4546 (cur = lastChangeEvent(hist, hist.lastOp == opId))) {
4547 // Merge this change into the last event
4548 last = lst(cur.changes)
4549 if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) {
4550 // Optimized case for simple insertion -- don't want to add
4551 // new changesets for every character typed
4552 last.to = changeEnd(change)
4553 } else {
4554 // Add new sub-event
4555 cur.changes.push(historyChangeFromChange(doc, change))
4556 }
4557 } else {
4558 // Can not be merged, start a new event.
4559 var before = lst(hist.done)
4560 if (!before || !before.ranges)
4561 { pushSelectionToHistory(doc.sel, hist.done) }
4562 cur = {changes: [historyChangeFromChange(doc, change)],
4563 generation: hist.generation}
4564 hist.done.push(cur)
4565 while (hist.done.length > hist.undoDepth) {
4566 hist.done.shift()
4567 if (!hist.done[0].ranges) { hist.done.shift() }
4568 }
4569 }
4570 hist.done.push(selAfter)
4571 hist.generation = ++hist.maxGeneration
4572 hist.lastModTime = hist.lastSelTime = time
4573 hist.lastOp = hist.lastSelOp = opId
4574 hist.lastOrigin = hist.lastSelOrigin = change.origin
4575
4576 if (!last) { signal(doc, "historyAdded") }
4577}
4578
4579function selectionEventCanBeMerged(doc, origin, prev, sel) {
4580 var ch = origin.charAt(0)
4581 return ch == "*" ||
4582 ch == "+" &&
4583 prev.ranges.length == sel.ranges.length &&
4584 prev.somethingSelected() == sel.somethingSelected() &&
4585 new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500)
4586}
4587
4588// Called whenever the selection changes, sets the new selection as
4589// the pending selection in the history, and pushes the old pending
4590// selection into the 'done' array when it was significantly
4591// different (in number of selected ranges, emptiness, or time).
4592function addSelectionToHistory(doc, sel, opId, options) {
4593 var hist = doc.history, origin = options && options.origin
4594
4595 // A new event is started when the previous origin does not match
4596 // the current, or the origins don't allow matching. Origins
4597 // starting with * are always merged, those starting with + are
4598 // merged when similar and close together in time.
4599 if (opId == hist.lastSelOp ||
4600 (origin && hist.lastSelOrigin == origin &&
4601 (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||
4602 selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))
4603 { hist.done[hist.done.length - 1] = sel }
4604 else
4605 { pushSelectionToHistory(sel, hist.done) }
4606
4607 hist.lastSelTime = +new Date
4608 hist.lastSelOrigin = origin
4609 hist.lastSelOp = opId
4610 if (options && options.clearRedo !== false)
4611 { clearSelectionEvents(hist.undone) }
4612}
4613
4614function pushSelectionToHistory(sel, dest) {
4615 var top = lst(dest)
4616 if (!(top && top.ranges && top.equals(sel)))
4617 { dest.push(sel) }
4618}
4619
4620// Used to store marked span information in the history.
4621function attachLocalSpans(doc, change, from, to) {
4622 var existing = change["spans_" + doc.id], n = 0
4623 doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function (line) {
4624 if (line.markedSpans)
4625 { (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans }
4626 ++n
4627 })
4628}
4629
4630// When un/re-doing restores text containing marked spans, those
4631// that have been explicitly cleared should not be restored.
4632function removeClearedSpans(spans) {
4633 if (!spans) { return null }
4634 var out
4635 for (var i = 0; i < spans.length; ++i) {
4636 if (spans[i].marker.explicitlyCleared) { if (!out) { out = spans.slice(0, i) } }
4637 else if (out) { out.push(spans[i]) }
4638 }
4639 return !out ? spans : out.length ? out : null
4640}
4641
4642// Retrieve and filter the old marked spans stored in a change event.
4643function getOldSpans(doc, change) {
4644 var found = change["spans_" + doc.id]
4645 if (!found) { return null }
4646 var nw = []
4647 for (var i = 0; i < change.text.length; ++i)
4648 { nw.push(removeClearedSpans(found[i])) }
4649 return nw
4650}
4651
4652// Used for un/re-doing changes from the history. Combines the
4653// result of computing the existing spans with the set of spans that
4654// existed in the history (so that deleting around a span and then
4655// undoing brings back the span).
4656function mergeOldSpans(doc, change) {
4657 var old = getOldSpans(doc, change)
4658 var stretched = stretchSpansOverChange(doc, change)
4659 if (!old) { return stretched }
4660 if (!stretched) { return old }
4661
4662 for (var i = 0; i < old.length; ++i) {
4663 var oldCur = old[i], stretchCur = stretched[i]
4664 if (oldCur && stretchCur) {
4665 spans: for (var j = 0; j < stretchCur.length; ++j) {
4666 var span = stretchCur[j]
4667 for (var k = 0; k < oldCur.length; ++k)
4668 { if (oldCur[k].marker == span.marker) { continue spans } }
4669 oldCur.push(span)
4670 }
4671 } else if (stretchCur) {
4672 old[i] = stretchCur
4673 }
4674 }
4675 return old
4676}
4677
4678// Used both to provide a JSON-safe object in .getHistory, and, when
4679// detaching a document, to split the history in two
4680function copyHistoryArray(events, newGroup, instantiateSel) {
4681 var copy = []
4682 for (var i = 0; i < events.length; ++i) {
4683 var event = events[i]
4684 if (event.ranges) {
4685 copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event)
4686 continue
4687 }
4688 var changes = event.changes, newChanges = []
4689 copy.push({changes: newChanges})
4690 for (var j = 0; j < changes.length; ++j) {
4691 var change = changes[j], m = void 0
4692 newChanges.push({from: change.from, to: change.to, text: change.text})
4693 if (newGroup) { for (var prop in change) { if (m = prop.match(/^spans_(\d+)$/)) {
4694 if (indexOf(newGroup, Number(m[1])) > -1) {
4695 lst(newChanges)[prop] = change[prop]
4696 delete change[prop]
4697 }
4698 } } }
4699 }
4700 }
4701 return copy
4702}
4703
4704// The 'scroll' parameter given to many of these indicated whether
4705// the new cursor position should be scrolled into view after
4706// modifying the selection.
4707
4708// If shift is held or the extend flag is set, extends a range to
4709// include a given position (and optionally a second position).
4710// Otherwise, simply returns the range between the given positions.
4711// Used for cursor motion and such.
4712function extendRange(doc, range, head, other) {
4713 if (doc.cm && doc.cm.display.shift || doc.extend) {
4714 var anchor = range.anchor
4715 if (other) {
4716 var posBefore = cmp(head, anchor) < 0
4717 if (posBefore != (cmp(other, anchor) < 0)) {
4718 anchor = head
4719 head = other
4720 } else if (posBefore != (cmp(head, other) < 0)) {
4721 head = other
4722 }
4723 }
4724 return new Range(anchor, head)
4725 } else {
4726 return new Range(other || head, head)
4727 }
4728}
4729
4730// Extend the primary selection range, discard the rest.
4731function extendSelection(doc, head, other, options) {
4732 setSelection(doc, new Selection([extendRange(doc, doc.sel.primary(), head, other)], 0), options)
4733}
4734
4735// Extend all selections (pos is an array of selections with length
4736// equal the number of selections)
4737function extendSelections(doc, heads, options) {
4738 var out = []
4739 for (var i = 0; i < doc.sel.ranges.length; i++)
4740 { out[i] = extendRange(doc, doc.sel.ranges[i], heads[i], null) }
4741 var newSel = normalizeSelection(out, doc.sel.primIndex)
4742 setSelection(doc, newSel, options)
4743}
4744
4745// Updates a single range in the selection.
4746function replaceOneSelection(doc, i, range, options) {
4747 var ranges = doc.sel.ranges.slice(0)
4748 ranges[i] = range
4749 setSelection(doc, normalizeSelection(ranges, doc.sel.primIndex), options)
4750}
4751
4752// Reset the selection to a single range.
4753function setSimpleSelection(doc, anchor, head, options) {
4754 setSelection(doc, simpleSelection(anchor, head), options)
4755}
4756
4757// Give beforeSelectionChange handlers a change to influence a
4758// selection update.
4759function filterSelectionChange(doc, sel, options) {
4760 var obj = {
4761 ranges: sel.ranges,
4762 update: function(ranges) {
4763 var this$1 = this;
4764
4765 this.ranges = []
4766 for (var i = 0; i < ranges.length; i++)
4767 { this$1.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),
4768 clipPos(doc, ranges[i].head)) }
4769 },
4770 origin: options && options.origin
4771 }
4772 signal(doc, "beforeSelectionChange", doc, obj)
4773 if (doc.cm) { signal(doc.cm, "beforeSelectionChange", doc.cm, obj) }
4774 if (obj.ranges != sel.ranges) { return normalizeSelection(obj.ranges, obj.ranges.length - 1) }
4775 else { return sel }
4776}
4777
4778function setSelectionReplaceHistory(doc, sel, options) {
4779 var done = doc.history.done, last = lst(done)
4780 if (last && last.ranges) {
4781 done[done.length - 1] = sel
4782 setSelectionNoUndo(doc, sel, options)
4783 } else {
4784 setSelection(doc, sel, options)
4785 }
4786}
4787
4788// Set a new selection.
4789function setSelection(doc, sel, options) {
4790 setSelectionNoUndo(doc, sel, options)
4791 addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options)
4792}
4793
4794function setSelectionNoUndo(doc, sel, options) {
4795 if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange"))
4796 { sel = filterSelectionChange(doc, sel, options) }
4797
4798 var bias = options && options.bias ||
4799 (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1)
4800 setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true))
4801
4802 if (!(options && options.scroll === false) && doc.cm)
4803 { ensureCursorVisible(doc.cm) }
4804}
4805
4806function setSelectionInner(doc, sel) {
4807 if (sel.equals(doc.sel)) { return }
4808
4809 doc.sel = sel
4810
4811 if (doc.cm) {
4812 doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true
4813 signalCursorActivity(doc.cm)
4814 }
4815 signalLater(doc, "cursorActivity", doc)
4816}
4817
4818// Verify that the selection does not partially select any atomic
4819// marked ranges.
4820function reCheckSelection(doc) {
4821 setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false), sel_dontScroll)
4822}
4823
4824// Return a selection that does not partially select any atomic
4825// ranges.
4826function skipAtomicInSelection(doc, sel, bias, mayClear) {
4827 var out
4828 for (var i = 0; i < sel.ranges.length; i++) {
4829 var range = sel.ranges[i]
4830 var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i]
4831 var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear)
4832 var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear)
4833 if (out || newAnchor != range.anchor || newHead != range.head) {
4834 if (!out) { out = sel.ranges.slice(0, i) }
4835 out[i] = new Range(newAnchor, newHead)
4836 }
4837 }
4838 return out ? normalizeSelection(out, sel.primIndex) : sel
4839}
4840
4841function skipAtomicInner(doc, pos, oldPos, dir, mayClear) {
4842 var line = getLine(doc, pos.line)
4843 if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
4844 var sp = line.markedSpans[i], m = sp.marker
4845 if ((sp.from == null || (m.inclusiveLeft ? sp.from <= pos.ch : sp.from < pos.ch)) &&
4846 (sp.to == null || (m.inclusiveRight ? sp.to >= pos.ch : sp.to > pos.ch))) {
4847 if (mayClear) {
4848 signal(m, "beforeCursorEnter")
4849 if (m.explicitlyCleared) {
4850 if (!line.markedSpans) { break }
4851 else {--i; continue}
4852 }
4853 }
4854 if (!m.atomic) { continue }
4855
4856 if (oldPos) {
4857 var near = m.find(dir < 0 ? 1 : -1), diff = void 0
4858 if (dir < 0 ? m.inclusiveRight : m.inclusiveLeft)
4859 { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null) }
4860 if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0))
4861 { return skipAtomicInner(doc, near, pos, dir, mayClear) }
4862 }
4863
4864 var far = m.find(dir < 0 ? -1 : 1)
4865 if (dir < 0 ? m.inclusiveLeft : m.inclusiveRight)
4866 { far = movePos(doc, far, dir, far.line == pos.line ? line : null) }
4867 return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null
4868 }
4869 } }
4870 return pos
4871}
4872
4873// Ensure a given position is not inside an atomic range.
4874function skipAtomic(doc, pos, oldPos, bias, mayClear) {
4875 var dir = bias || 1
4876 var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) ||
4877 (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) ||
4878 skipAtomicInner(doc, pos, oldPos, -dir, mayClear) ||
4879 (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true))
4880 if (!found) {
4881 doc.cantEdit = true
4882 return Pos(doc.first, 0)
4883 }
4884 return found
4885}
4886
4887function movePos(doc, pos, dir, line) {
4888 if (dir < 0 && pos.ch == 0) {
4889 if (pos.line > doc.first) { return clipPos(doc, Pos(pos.line - 1)) }
4890 else { return null }
4891 } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) {
4892 if (pos.line < doc.first + doc.size - 1) { return Pos(pos.line + 1, 0) }
4893 else { return null }
4894 } else {
4895 return new Pos(pos.line, pos.ch + dir)
4896 }
4897}
4898
4899function selectAll(cm) {
4900 cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll)
4901}
4902
4903// UPDATING
4904
4905// Allow "beforeChange" event handlers to influence a change
4906function filterChange(doc, change, update) {
4907 var obj = {
4908 canceled: false,
4909 from: change.from,
4910 to: change.to,
4911 text: change.text,
4912 origin: change.origin,
4913 cancel: function () { return obj.canceled = true; }
4914 }
4915 if (update) { obj.update = function (from, to, text, origin) {
4916 if (from) { obj.from = clipPos(doc, from) }
4917 if (to) { obj.to = clipPos(doc, to) }
4918 if (text) { obj.text = text }
4919 if (origin !== undefined) { obj.origin = origin }
4920 } }
4921 signal(doc, "beforeChange", doc, obj)
4922 if (doc.cm) { signal(doc.cm, "beforeChange", doc.cm, obj) }
4923
4924 if (obj.canceled) { return null }
4925 return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin}
4926}
4927
4928// Apply a change to a document, and add it to the document's
4929// history, and propagating it to all linked documents.
4930function makeChange(doc, change, ignoreReadOnly) {
4931 if (doc.cm) {
4932 if (!doc.cm.curOp) { return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) }
4933 if (doc.cm.state.suppressEdits) { return }
4934 }
4935
4936 if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
4937 change = filterChange(doc, change, true)
4938 if (!change) { return }
4939 }
4940
4941 // Possibly split or suppress the update based on the presence
4942 // of read-only spans in its range.
4943 var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to)
4944 if (split) {
4945 for (var i = split.length - 1; i >= 0; --i)
4946 { makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text}) }
4947 } else {
4948 makeChangeInner(doc, change)
4949 }
4950}
4951
4952function makeChangeInner(doc, change) {
4953 if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) { return }
4954 var selAfter = computeSelAfterChange(doc, change)
4955 addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN)
4956
4957 makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change))
4958 var rebased = []
4959
4960 linkedDocs(doc, function (doc, sharedHist) {
4961 if (!sharedHist && indexOf(rebased, doc.history) == -1) {
4962 rebaseHist(doc.history, change)
4963 rebased.push(doc.history)
4964 }
4965 makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change))
4966 })
4967}
4968
4969// Revert a change stored in a document's history.
4970function makeChangeFromHistory(doc, type, allowSelectionOnly) {
4971 if (doc.cm && doc.cm.state.suppressEdits && !allowSelectionOnly) { return }
4972
4973 var hist = doc.history, event, selAfter = doc.sel
4974 var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done
4975
4976 // Verify that there is a useable event (so that ctrl-z won't
4977 // needlessly clear selection events)
4978 var i = 0
4979 for (; i < source.length; i++) {
4980 event = source[i]
4981 if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges)
4982 { break }
4983 }
4984 if (i == source.length) { return }
4985 hist.lastOrigin = hist.lastSelOrigin = null
4986
4987 for (;;) {
4988 event = source.pop()
4989 if (event.ranges) {
4990 pushSelectionToHistory(event, dest)
4991 if (allowSelectionOnly && !event.equals(doc.sel)) {
4992 setSelection(doc, event, {clearRedo: false})
4993 return
4994 }
4995 selAfter = event
4996 }
4997 else { break }
4998 }
4999
5000 // Build up a reverse change object to add to the opposite history
5001 // stack (redo when undoing, and vice versa).
5002 var antiChanges = []
5003 pushSelectionToHistory(selAfter, dest)
5004 dest.push({changes: antiChanges, generation: hist.generation})
5005 hist.generation = event.generation || ++hist.maxGeneration
5006
5007 var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")
5008
5009 var loop = function ( i ) {
5010 var change = event.changes[i]
5011 change.origin = type
5012 if (filter && !filterChange(doc, change, false)) {
5013 source.length = 0
5014 return {}
5015 }
5016
5017 antiChanges.push(historyChangeFromChange(doc, change))
5018
5019 var after = i ? computeSelAfterChange(doc, change) : lst(source)
5020 makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change))
5021 if (!i && doc.cm) { doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}) }
5022 var rebased = []
5023
5024 // Propagate to the linked documents
5025 linkedDocs(doc, function (doc, sharedHist) {
5026 if (!sharedHist && indexOf(rebased, doc.history) == -1) {
5027 rebaseHist(doc.history, change)
5028 rebased.push(doc.history)
5029 }
5030 makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change))
5031 })
5032 };
5033
5034 for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) {
5035 var returned = loop( i$1 );
5036
5037 if ( returned ) return returned.v;
5038 }
5039}
5040
5041// Sub-views need their line numbers shifted when text is added
5042// above or below them in the parent document.
5043function shiftDoc(doc, distance) {
5044 if (distance == 0) { return }
5045 doc.first += distance
5046 doc.sel = new Selection(map(doc.sel.ranges, function (range) { return new Range(
5047 Pos(range.anchor.line + distance, range.anchor.ch),
5048 Pos(range.head.line + distance, range.head.ch)
5049 ); }), doc.sel.primIndex)
5050 if (doc.cm) {
5051 regChange(doc.cm, doc.first, doc.first - distance, distance)
5052 for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++)
5053 { regLineChange(doc.cm, l, "gutter") }
5054 }
5055}
5056
5057// More lower-level change function, handling only a single document
5058// (not linked ones).
5059function makeChangeSingleDoc(doc, change, selAfter, spans) {
5060 if (doc.cm && !doc.cm.curOp)
5061 { return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) }
5062
5063 if (change.to.line < doc.first) {
5064 shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line))
5065 return
5066 }
5067 if (change.from.line > doc.lastLine()) { return }
5068
5069 // Clip the change to the size of this doc
5070 if (change.from.line < doc.first) {
5071 var shift = change.text.length - 1 - (doc.first - change.from.line)
5072 shiftDoc(doc, shift)
5073 change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
5074 text: [lst(change.text)], origin: change.origin}
5075 }
5076 var last = doc.lastLine()
5077 if (change.to.line > last) {
5078 change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
5079 text: [change.text[0]], origin: change.origin}
5080 }
5081
5082 change.removed = getBetween(doc, change.from, change.to)
5083
5084 if (!selAfter) { selAfter = computeSelAfterChange(doc, change) }
5085 if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans) }
5086 else { updateDoc(doc, change, spans) }
5087 setSelectionNoUndo(doc, selAfter, sel_dontScroll)
5088}
5089
5090// Handle the interaction of a change to a document with the editor
5091// that this document is part of.
5092function makeChangeSingleDocInEditor(cm, change, spans) {
5093 var doc = cm.doc, display = cm.display, from = change.from, to = change.to
5094
5095 var recomputeMaxLength = false, checkWidthStart = from.line
5096 if (!cm.options.lineWrapping) {
5097 checkWidthStart = lineNo(visualLine(getLine(doc, from.line)))
5098 doc.iter(checkWidthStart, to.line + 1, function (line) {
5099 if (line == display.maxLine) {
5100 recomputeMaxLength = true
5101 return true
5102 }
5103 })
5104 }
5105
5106 if (doc.sel.contains(change.from, change.to) > -1)
5107 { signalCursorActivity(cm) }
5108
5109 updateDoc(doc, change, spans, estimateHeight(cm))
5110
5111 if (!cm.options.lineWrapping) {
5112 doc.iter(checkWidthStart, from.line + change.text.length, function (line) {
5113 var len = lineLength(line)
5114 if (len > display.maxLineLength) {
5115 display.maxLine = line
5116 display.maxLineLength = len
5117 display.maxLineChanged = true
5118 recomputeMaxLength = false
5119 }
5120 })
5121 if (recomputeMaxLength) { cm.curOp.updateMaxLine = true }
5122 }
5123
5124 // Adjust frontier, schedule worker
5125 doc.frontier = Math.min(doc.frontier, from.line)
5126 startWorker(cm, 400)
5127
5128 var lendiff = change.text.length - (to.line - from.line) - 1
5129 // Remember that these lines changed, for updating the display
5130 if (change.full)
5131 { regChange(cm) }
5132 else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change))
5133 { regLineChange(cm, from.line, "text") }
5134 else
5135 { regChange(cm, from.line, to.line + 1, lendiff) }
5136
5137 var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change")
5138 if (changeHandler || changesHandler) {
5139 var obj = {
5140 from: from, to: to,
5141 text: change.text,
5142 removed: change.removed,
5143 origin: change.origin
5144 }
5145 if (changeHandler) { signalLater(cm, "change", cm, obj) }
5146 if (changesHandler) { (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj) }
5147 }
5148 cm.display.selForContextMenu = null
5149}
5150
5151function replaceRange(doc, code, from, to, origin) {
5152 if (!to) { to = from }
5153 if (cmp(to, from) < 0) { var tmp = to; to = from; from = tmp }
5154 if (typeof code == "string") { code = doc.splitLines(code) }
5155 makeChange(doc, {from: from, to: to, text: code, origin: origin})
5156}
5157
5158// Rebasing/resetting history to deal with externally-sourced changes
5159
5160function rebaseHistSelSingle(pos, from, to, diff) {
5161 if (to < pos.line) {
5162 pos.line += diff
5163 } else if (from < pos.line) {
5164 pos.line = from
5165 pos.ch = 0
5166 }
5167}
5168
5169// Tries to rebase an array of history events given a change in the
5170// document. If the change touches the same lines as the event, the
5171// event, and everything 'behind' it, is discarded. If the change is
5172// before the event, the event's positions are updated. Uses a
5173// copy-on-write scheme for the positions, to avoid having to
5174// reallocate them all on every rebase, but also avoid problems with
5175// shared position objects being unsafely updated.
5176function rebaseHistArray(array, from, to, diff) {
5177 for (var i = 0; i < array.length; ++i) {
5178 var sub = array[i], ok = true
5179 if (sub.ranges) {
5180 if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true }
5181 for (var j = 0; j < sub.ranges.length; j++) {
5182 rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff)
5183 rebaseHistSelSingle(sub.ranges[j].head, from, to, diff)
5184 }
5185 continue
5186 }
5187 for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) {
5188 var cur = sub.changes[j$1]
5189 if (to < cur.from.line) {
5190 cur.from = Pos(cur.from.line + diff, cur.from.ch)
5191 cur.to = Pos(cur.to.line + diff, cur.to.ch)
5192 } else if (from <= cur.to.line) {
5193 ok = false
5194 break
5195 }
5196 }
5197 if (!ok) {
5198 array.splice(0, i + 1)
5199 i = 0
5200 }
5201 }
5202}
5203
5204function rebaseHist(hist, change) {
5205 var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1
5206 rebaseHistArray(hist.done, from, to, diff)
5207 rebaseHistArray(hist.undone, from, to, diff)
5208}
5209
5210// Utility for applying a change to a line by handle or number,
5211// returning the number and optionally registering the line as
5212// changed.
5213function changeLine(doc, handle, changeType, op) {
5214 var no = handle, line = handle
5215 if (typeof handle == "number") { line = getLine(doc, clipLine(doc, handle)) }
5216 else { no = lineNo(handle) }
5217 if (no == null) { return null }
5218 if (op(line, no) && doc.cm) { regLineChange(doc.cm, no, changeType) }
5219 return line
5220}
5221
5222// The document is represented as a BTree consisting of leaves, with
5223// chunk of lines in them, and branches, with up to ten leaves or
5224// other branch nodes below them. The top node is always a branch
5225// node, and is the document object itself (meaning it has
5226// additional methods and properties).
5227//
5228// All nodes have parent links. The tree is used both to go from
5229// line numbers to line objects, and to go from objects to numbers.
5230// It also indexes by height, and is used to convert between height
5231// and line object, and to find the total height of the document.
5232//
5233// See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html
5234
5235function LeafChunk(lines) {
5236 var this$1 = this;
5237
5238 this.lines = lines
5239 this.parent = null
5240 var height = 0
5241 for (var i = 0; i < lines.length; ++i) {
5242 lines[i].parent = this$1
5243 height += lines[i].height
5244 }
5245 this.height = height
5246}
5247
5248LeafChunk.prototype = {
5249 chunkSize: function() { return this.lines.length },
5250 // Remove the n lines at offset 'at'.
5251 removeInner: function(at, n) {
5252 var this$1 = this;
5253
5254 for (var i = at, e = at + n; i < e; ++i) {
5255 var line = this$1.lines[i]
5256 this$1.height -= line.height
5257 cleanUpLine(line)
5258 signalLater(line, "delete")
5259 }
5260 this.lines.splice(at, n)
5261 },
5262 // Helper used to collapse a small branch into a single leaf.
5263 collapse: function(lines) {
5264 lines.push.apply(lines, this.lines)
5265 },
5266 // Insert the given array of lines at offset 'at', count them as
5267 // having the given height.
5268 insertInner: function(at, lines, height) {
5269 var this$1 = this;
5270
5271 this.height += height
5272 this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at))
5273 for (var i = 0; i < lines.length; ++i) { lines[i].parent = this$1 }
5274 },
5275 // Used to iterate over a part of the tree.
5276 iterN: function(at, n, op) {
5277 var this$1 = this;
5278
5279 for (var e = at + n; at < e; ++at)
5280 { if (op(this$1.lines[at])) { return true } }
5281 }
5282}
5283
5284function BranchChunk(children) {
5285 var this$1 = this;
5286
5287 this.children = children
5288 var size = 0, height = 0
5289 for (var i = 0; i < children.length; ++i) {
5290 var ch = children[i]
5291 size += ch.chunkSize(); height += ch.height
5292 ch.parent = this$1
5293 }
5294 this.size = size
5295 this.height = height
5296 this.parent = null
5297}
5298
5299BranchChunk.prototype = {
5300 chunkSize: function() { return this.size },
5301 removeInner: function(at, n) {
5302 var this$1 = this;
5303
5304 this.size -= n
5305 for (var i = 0; i < this.children.length; ++i) {
5306 var child = this$1.children[i], sz = child.chunkSize()
5307 if (at < sz) {
5308 var rm = Math.min(n, sz - at), oldHeight = child.height
5309 child.removeInner(at, rm)
5310 this$1.height -= oldHeight - child.height
5311 if (sz == rm) { this$1.children.splice(i--, 1); child.parent = null }
5312 if ((n -= rm) == 0) { break }
5313 at = 0
5314 } else { at -= sz }
5315 }
5316 // If the result is smaller than 25 lines, ensure that it is a
5317 // single leaf node.
5318 if (this.size - n < 25 &&
5319 (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) {
5320 var lines = []
5321 this.collapse(lines)
5322 this.children = [new LeafChunk(lines)]
5323 this.children[0].parent = this
5324 }
5325 },
5326 collapse: function(lines) {
5327 var this$1 = this;
5328
5329 for (var i = 0; i < this.children.length; ++i) { this$1.children[i].collapse(lines) }
5330 },
5331 insertInner: function(at, lines, height) {
5332 var this$1 = this;
5333
5334 this.size += lines.length
5335 this.height += height
5336 for (var i = 0; i < this.children.length; ++i) {
5337 var child = this$1.children[i], sz = child.chunkSize()
5338 if (at <= sz) {
5339 child.insertInner(at, lines, height)
5340 if (child.lines && child.lines.length > 50) {
5341 // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced.
5342 // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest.
5343 var remaining = child.lines.length % 25 + 25
5344 for (var pos = remaining; pos < child.lines.length;) {
5345 var leaf = new LeafChunk(child.lines.slice(pos, pos += 25))
5346 child.height -= leaf.height
5347 this$1.children.splice(++i, 0, leaf)
5348 leaf.parent = this$1
5349 }
5350 child.lines = child.lines.slice(0, remaining)
5351 this$1.maybeSpill()
5352 }
5353 break
5354 }
5355 at -= sz
5356 }
5357 },
5358 // When a node has grown, check whether it should be split.
5359 maybeSpill: function() {
5360 if (this.children.length <= 10) { return }
5361 var me = this
5362 do {
5363 var spilled = me.children.splice(me.children.length - 5, 5)
5364 var sibling = new BranchChunk(spilled)
5365 if (!me.parent) { // Become the parent node
5366 var copy = new BranchChunk(me.children)
5367 copy.parent = me
5368 me.children = [copy, sibling]
5369 me = copy
5370 } else {
5371 me.size -= sibling.size
5372 me.height -= sibling.height
5373 var myIndex = indexOf(me.parent.children, me)
5374 me.parent.children.splice(myIndex + 1, 0, sibling)
5375 }
5376 sibling.parent = me.parent
5377 } while (me.children.length > 10)
5378 me.parent.maybeSpill()
5379 },
5380 iterN: function(at, n, op) {
5381 var this$1 = this;
5382
5383 for (var i = 0; i < this.children.length; ++i) {
5384 var child = this$1.children[i], sz = child.chunkSize()
5385 if (at < sz) {
5386 var used = Math.min(n, sz - at)
5387 if (child.iterN(at, used, op)) { return true }
5388 if ((n -= used) == 0) { break }
5389 at = 0
5390 } else { at -= sz }
5391 }
5392 }
5393}
5394
5395// Line widgets are block elements displayed above or below a line.
5396
5397function LineWidget(doc, node, options) {
5398 var this$1 = this;
5399
5400 if (options) { for (var opt in options) { if (options.hasOwnProperty(opt))
5401 { this$1[opt] = options[opt] } } }
5402 this.doc = doc
5403 this.node = node
5404}
5405eventMixin(LineWidget)
5406
5407function adjustScrollWhenAboveVisible(cm, line, diff) {
5408 if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))
5409 { addToScrollPos(cm, null, diff) }
5410}
5411
5412LineWidget.prototype.clear = function() {
5413 var this$1 = this;
5414
5415 var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line)
5416 if (no == null || !ws) { return }
5417 for (var i = 0; i < ws.length; ++i) { if (ws[i] == this$1) { ws.splice(i--, 1) } }
5418 if (!ws.length) { line.widgets = null }
5419 var height = widgetHeight(this)
5420 updateLineHeight(line, Math.max(0, line.height - height))
5421 if (cm) { runInOp(cm, function () {
5422 adjustScrollWhenAboveVisible(cm, line, -height)
5423 regLineChange(cm, no, "widget")
5424 }) }
5425}
5426LineWidget.prototype.changed = function() {
5427 var oldH = this.height, cm = this.doc.cm, line = this.line
5428 this.height = null
5429 var diff = widgetHeight(this) - oldH
5430 if (!diff) { return }
5431 updateLineHeight(line, line.height + diff)
5432 if (cm) { runInOp(cm, function () {
5433 cm.curOp.forceUpdate = true
5434 adjustScrollWhenAboveVisible(cm, line, diff)
5435 }) }
5436}
5437
5438function addLineWidget(doc, handle, node, options) {
5439 var widget = new LineWidget(doc, node, options)
5440 var cm = doc.cm
5441 if (cm && widget.noHScroll) { cm.display.alignWidgets = true }
5442 changeLine(doc, handle, "widget", function (line) {
5443 var widgets = line.widgets || (line.widgets = [])
5444 if (widget.insertAt == null) { widgets.push(widget) }
5445 else { widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget) }
5446 widget.line = line
5447 if (cm && !lineIsHidden(doc, line)) {
5448 var aboveVisible = heightAtLine(line) < doc.scrollTop
5449 updateLineHeight(line, line.height + widgetHeight(widget))
5450 if (aboveVisible) { addToScrollPos(cm, null, widget.height) }
5451 cm.curOp.forceUpdate = true
5452 }
5453 return true
5454 })
5455 return widget
5456}
5457
5458// TEXTMARKERS
5459
5460// Created with markText and setBookmark methods. A TextMarker is a
5461// handle that can be used to clear or find a marked position in the
5462// document. Line objects hold arrays (markedSpans) containing
5463// {from, to, marker} object pointing to such marker objects, and
5464// indicating that such a marker is present on that line. Multiple
5465// lines may point to the same marker when it spans across lines.
5466// The spans will have null for their from/to properties when the
5467// marker continues beyond the start/end of the line. Markers have
5468// links back to the lines they currently touch.
5469
5470// Collapsed markers have unique ids, in order to be able to order
5471// them, which is needed for uniquely determining an outer marker
5472// when they overlap (they may nest, but not partially overlap).
5473var nextMarkerId = 0
5474
5475function TextMarker(doc, type) {
5476 this.lines = []
5477 this.type = type
5478 this.doc = doc
5479 this.id = ++nextMarkerId
5480}
5481eventMixin(TextMarker)
5482
5483// Clear the marker.
5484TextMarker.prototype.clear = function() {
5485 var this$1 = this;
5486
5487 if (this.explicitlyCleared) { return }
5488 var cm = this.doc.cm, withOp = cm && !cm.curOp
5489 if (withOp) { startOperation(cm) }
5490 if (hasHandler(this, "clear")) {
5491 var found = this.find()
5492 if (found) { signalLater(this, "clear", found.from, found.to) }
5493 }
5494 var min = null, max = null
5495 for (var i = 0; i < this.lines.length; ++i) {
5496 var line = this$1.lines[i]
5497 var span = getMarkedSpanFor(line.markedSpans, this$1)
5498 if (cm && !this$1.collapsed) { regLineChange(cm, lineNo(line), "text") }
5499 else if (cm) {
5500 if (span.to != null) { max = lineNo(line) }
5501 if (span.from != null) { min = lineNo(line) }
5502 }
5503 line.markedSpans = removeMarkedSpan(line.markedSpans, span)
5504 if (span.from == null && this$1.collapsed && !lineIsHidden(this$1.doc, line) && cm)
5505 { updateLineHeight(line, textHeight(cm.display)) }
5506 }
5507 if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) {
5508 var visual = visualLine(this$1.lines[i$1]), len = lineLength(visual)
5509 if (len > cm.display.maxLineLength) {
5510 cm.display.maxLine = visual
5511 cm.display.maxLineLength = len
5512 cm.display.maxLineChanged = true
5513 }
5514 } }
5515
5516 if (min != null && cm && this.collapsed) { regChange(cm, min, max + 1) }
5517 this.lines.length = 0
5518 this.explicitlyCleared = true
5519 if (this.atomic && this.doc.cantEdit) {
5520 this.doc.cantEdit = false
5521 if (cm) { reCheckSelection(cm.doc) }
5522 }
5523 if (cm) { signalLater(cm, "markerCleared", cm, this) }
5524 if (withOp) { endOperation(cm) }
5525 if (this.parent) { this.parent.clear() }
5526}
5527
5528// Find the position of the marker in the document. Returns a {from,
5529// to} object by default. Side can be passed to get a specific side
5530// -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the
5531// Pos objects returned contain a line object, rather than a line
5532// number (used to prevent looking up the same line twice).
5533TextMarker.prototype.find = function(side, lineObj) {
5534 var this$1 = this;
5535
5536 if (side == null && this.type == "bookmark") { side = 1 }
5537 var from, to
5538 for (var i = 0; i < this.lines.length; ++i) {
5539 var line = this$1.lines[i]
5540 var span = getMarkedSpanFor(line.markedSpans, this$1)
5541 if (span.from != null) {
5542 from = Pos(lineObj ? line : lineNo(line), span.from)
5543 if (side == -1) { return from }
5544 }
5545 if (span.to != null) {
5546 to = Pos(lineObj ? line : lineNo(line), span.to)
5547 if (side == 1) { return to }
5548 }
5549 }
5550 return from && {from: from, to: to}
5551}
5552
5553// Signals that the marker's widget changed, and surrounding layout
5554// should be recomputed.
5555TextMarker.prototype.changed = function() {
5556 var pos = this.find(-1, true), widget = this, cm = this.doc.cm
5557 if (!pos || !cm) { return }
5558 runInOp(cm, function () {
5559 var line = pos.line, lineN = lineNo(pos.line)
5560 var view = findViewForLine(cm, lineN)
5561 if (view) {
5562 clearLineMeasurementCacheFor(view)
5563 cm.curOp.selectionChanged = cm.curOp.forceUpdate = true
5564 }
5565 cm.curOp.updateMaxLine = true
5566 if (!lineIsHidden(widget.doc, line) && widget.height != null) {
5567 var oldHeight = widget.height
5568 widget.height = null
5569 var dHeight = widgetHeight(widget) - oldHeight
5570 if (dHeight)
5571 { updateLineHeight(line, line.height + dHeight) }
5572 }
5573 })
5574}
5575
5576TextMarker.prototype.attachLine = function(line) {
5577 if (!this.lines.length && this.doc.cm) {
5578 var op = this.doc.cm.curOp
5579 if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
5580 { (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this) }
5581 }
5582 this.lines.push(line)
5583}
5584TextMarker.prototype.detachLine = function(line) {
5585 this.lines.splice(indexOf(this.lines, line), 1)
5586 if (!this.lines.length && this.doc.cm) {
5587 var op = this.doc.cm.curOp;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this)
5588 }
5589}
5590
5591// Create a marker, wire it up to the right lines, and
5592function markText(doc, from, to, options, type) {
5593 // Shared markers (across linked documents) are handled separately
5594 // (markTextShared will call out to this again, once per
5595 // document).
5596 if (options && options.shared) { return markTextShared(doc, from, to, options, type) }
5597 // Ensure we are in an operation.
5598 if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, markText)(doc, from, to, options, type) }
5599
5600 var marker = new TextMarker(doc, type), diff = cmp(from, to)
5601 if (options) { copyObj(options, marker, false) }
5602 // Don't connect empty markers unless clearWhenEmpty is false
5603 if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false)
5604 { return marker }
5605 if (marker.replacedWith) {
5606 // Showing up as a widget implies collapsed (widget replaces text)
5607 marker.collapsed = true
5608 marker.widgetNode = elt("span", [marker.replacedWith], "CodeMirror-widget")
5609 if (!options.handleMouseEvents) { marker.widgetNode.setAttribute("cm-ignore-events", "true") }
5610 if (options.insertLeft) { marker.widgetNode.insertLeft = true }
5611 }
5612 if (marker.collapsed) {
5613 if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||
5614 from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker))
5615 { throw new Error("Inserting collapsed marker partially overlapping an existing one") }
5616 seeCollapsedSpans()
5617 }
5618
5619 if (marker.addToHistory)
5620 { addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN) }
5621
5622 var curLine = from.line, cm = doc.cm, updateMaxLine
5623 doc.iter(curLine, to.line + 1, function (line) {
5624 if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine)
5625 { updateMaxLine = true }
5626 if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0) }
5627 addMarkedSpan(line, new MarkedSpan(marker,
5628 curLine == from.line ? from.ch : null,
5629 curLine == to.line ? to.ch : null))
5630 ++curLine
5631 })
5632 // lineIsHidden depends on the presence of the spans, so needs a second pass
5633 if (marker.collapsed) { doc.iter(from.line, to.line + 1, function (line) {
5634 if (lineIsHidden(doc, line)) { updateLineHeight(line, 0) }
5635 }) }
5636
5637 if (marker.clearOnEnter) { on(marker, "beforeCursorEnter", function () { return marker.clear(); }) }
5638
5639 if (marker.readOnly) {
5640 seeReadOnlySpans()
5641 if (doc.history.done.length || doc.history.undone.length)
5642 { doc.clearHistory() }
5643 }
5644 if (marker.collapsed) {
5645 marker.id = ++nextMarkerId
5646 marker.atomic = true
5647 }
5648 if (cm) {
5649 // Sync editor state
5650 if (updateMaxLine) { cm.curOp.updateMaxLine = true }
5651 if (marker.collapsed)
5652 { regChange(cm, from.line, to.line + 1) }
5653 else if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.css)
5654 { for (var i = from.line; i <= to.line; i++) { regLineChange(cm, i, "text") } }
5655 if (marker.atomic) { reCheckSelection(cm.doc) }
5656 signalLater(cm, "markerAdded", cm, marker)
5657 }
5658 return marker
5659}
5660
5661// SHARED TEXTMARKERS
5662
5663// A shared marker spans multiple linked documents. It is
5664// implemented as a meta-marker-object controlling multiple normal
5665// markers.
5666function SharedTextMarker(markers, primary) {
5667 var this$1 = this;
5668
5669 this.markers = markers
5670 this.primary = primary
5671 for (var i = 0; i < markers.length; ++i)
5672 { markers[i].parent = this$1 }
5673}
5674eventMixin(SharedTextMarker)
5675
5676SharedTextMarker.prototype.clear = function() {
5677 var this$1 = this;
5678
5679 if (this.explicitlyCleared) { return }
5680 this.explicitlyCleared = true
5681 for (var i = 0; i < this.markers.length; ++i)
5682 { this$1.markers[i].clear() }
5683 signalLater(this, "clear")
5684}
5685SharedTextMarker.prototype.find = function(side, lineObj) {
5686 return this.primary.find(side, lineObj)
5687}
5688
5689function markTextShared(doc, from, to, options, type) {
5690 options = copyObj(options)
5691 options.shared = false
5692 var markers = [markText(doc, from, to, options, type)], primary = markers[0]
5693 var widget = options.widgetNode
5694 linkedDocs(doc, function (doc) {
5695 if (widget) { options.widgetNode = widget.cloneNode(true) }
5696 markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type))
5697 for (var i = 0; i < doc.linked.length; ++i)
5698 { if (doc.linked[i].isParent) { return } }
5699 primary = lst(markers)
5700 })
5701 return new SharedTextMarker(markers, primary)
5702}
5703
5704function findSharedMarkers(doc) {
5705 return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function (m) { return m.parent; })
5706}
5707
5708function copySharedMarkers(doc, markers) {
5709 for (var i = 0; i < markers.length; i++) {
5710 var marker = markers[i], pos = marker.find()
5711 var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to)
5712 if (cmp(mFrom, mTo)) {
5713 var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type)
5714 marker.markers.push(subMark)
5715 subMark.parent = marker
5716 }
5717 }
5718}
5719
5720function detachSharedMarkers(markers) {
5721 var loop = function ( i ) {
5722 var marker = markers[i], linked = [marker.primary.doc]
5723 linkedDocs(marker.primary.doc, function (d) { return linked.push(d); })
5724 for (var j = 0; j < marker.markers.length; j++) {
5725 var subMarker = marker.markers[j]
5726 if (indexOf(linked, subMarker.doc) == -1) {
5727 subMarker.parent = null
5728 marker.markers.splice(j--, 1)
5729 }
5730 }
5731 };
5732
5733 for (var i = 0; i < markers.length; i++) loop( i );
5734}
5735
5736var nextDocId = 0
5737var Doc = function(text, mode, firstLine, lineSep) {
5738 if (!(this instanceof Doc)) { return new Doc(text, mode, firstLine, lineSep) }
5739 if (firstLine == null) { firstLine = 0 }
5740
5741 BranchChunk.call(this, [new LeafChunk([new Line("", null)])])
5742 this.first = firstLine
5743 this.scrollTop = this.scrollLeft = 0
5744 this.cantEdit = false
5745 this.cleanGeneration = 1
5746 this.frontier = firstLine
5747 var start = Pos(firstLine, 0)
5748 this.sel = simpleSelection(start)
5749 this.history = new History(null)
5750 this.id = ++nextDocId
5751 this.modeOption = mode
5752 this.lineSep = lineSep
5753 this.extend = false
5754
5755 if (typeof text == "string") { text = this.splitLines(text) }
5756 updateDoc(this, {from: start, to: start, text: text})
5757 setSelection(this, simpleSelection(start), sel_dontScroll)
5758}
5759
5760Doc.prototype = createObj(BranchChunk.prototype, {
5761 constructor: Doc,
5762 // Iterate over the document. Supports two forms -- with only one
5763 // argument, it calls that for each line in the document. With
5764 // three, it iterates over the range given by the first two (with
5765 // the second being non-inclusive).
5766 iter: function(from, to, op) {
5767 if (op) { this.iterN(from - this.first, to - from, op) }
5768 else { this.iterN(this.first, this.first + this.size, from) }
5769 },
5770
5771 // Non-public interface for adding and removing lines.
5772 insert: function(at, lines) {
5773 var height = 0
5774 for (var i = 0; i < lines.length; ++i) { height += lines[i].height }
5775 this.insertInner(at - this.first, lines, height)
5776 },
5777 remove: function(at, n) { this.removeInner(at - this.first, n) },
5778
5779 // From here, the methods are part of the public interface. Most
5780 // are also available from CodeMirror (editor) instances.
5781
5782 getValue: function(lineSep) {
5783 var lines = getLines(this, this.first, this.first + this.size)
5784 if (lineSep === false) { return lines }
5785 return lines.join(lineSep || this.lineSeparator())
5786 },
5787 setValue: docMethodOp(function(code) {
5788 var top = Pos(this.first, 0), last = this.first + this.size - 1
5789 makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
5790 text: this.splitLines(code), origin: "setValue", full: true}, true)
5791 setSelection(this, simpleSelection(top))
5792 }),
5793 replaceRange: function(code, from, to, origin) {
5794 from = clipPos(this, from)
5795 to = to ? clipPos(this, to) : from
5796 replaceRange(this, code, from, to, origin)
5797 },
5798 getRange: function(from, to, lineSep) {
5799 var lines = getBetween(this, clipPos(this, from), clipPos(this, to))
5800 if (lineSep === false) { return lines }
5801 return lines.join(lineSep || this.lineSeparator())
5802 },
5803
5804 getLine: function(line) {var l = this.getLineHandle(line); return l && l.text},
5805
5806 getLineHandle: function(line) {if (isLine(this, line)) { return getLine(this, line) }},
5807 getLineNumber: function(line) {return lineNo(line)},
5808
5809 getLineHandleVisualStart: function(line) {
5810 if (typeof line == "number") { line = getLine(this, line) }
5811 return visualLine(line)
5812 },
5813
5814 lineCount: function() {return this.size},
5815 firstLine: function() {return this.first},
5816 lastLine: function() {return this.first + this.size - 1},
5817
5818 clipPos: function(pos) {return clipPos(this, pos)},
5819
5820 getCursor: function(start) {
5821 var range$$1 = this.sel.primary(), pos
5822 if (start == null || start == "head") { pos = range$$1.head }
5823 else if (start == "anchor") { pos = range$$1.anchor }
5824 else if (start == "end" || start == "to" || start === false) { pos = range$$1.to() }
5825 else { pos = range$$1.from() }
5826 return pos
5827 },
5828 listSelections: function() { return this.sel.ranges },
5829 somethingSelected: function() {return this.sel.somethingSelected()},
5830
5831 setCursor: docMethodOp(function(line, ch, options) {
5832 setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options)
5833 }),
5834 setSelection: docMethodOp(function(anchor, head, options) {
5835 setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options)
5836 }),
5837 extendSelection: docMethodOp(function(head, other, options) {
5838 extendSelection(this, clipPos(this, head), other && clipPos(this, other), options)
5839 }),
5840 extendSelections: docMethodOp(function(heads, options) {
5841 extendSelections(this, clipPosArray(this, heads), options)
5842 }),
5843 extendSelectionsBy: docMethodOp(function(f, options) {
5844 var heads = map(this.sel.ranges, f)
5845 extendSelections(this, clipPosArray(this, heads), options)
5846 }),
5847 setSelections: docMethodOp(function(ranges, primary, options) {
5848 var this$1 = this;
5849
5850 if (!ranges.length) { return }
5851 var out = []
5852 for (var i = 0; i < ranges.length; i++)
5853 { out[i] = new Range(clipPos(this$1, ranges[i].anchor),
5854 clipPos(this$1, ranges[i].head)) }
5855 if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex) }
5856 setSelection(this, normalizeSelection(out, primary), options)
5857 }),
5858 addSelection: docMethodOp(function(anchor, head, options) {
5859 var ranges = this.sel.ranges.slice(0)
5860 ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)))
5861 setSelection(this, normalizeSelection(ranges, ranges.length - 1), options)
5862 }),
5863
5864 getSelection: function(lineSep) {
5865 var this$1 = this;
5866
5867 var ranges = this.sel.ranges, lines
5868 for (var i = 0; i < ranges.length; i++) {
5869 var sel = getBetween(this$1, ranges[i].from(), ranges[i].to())
5870 lines = lines ? lines.concat(sel) : sel
5871 }
5872 if (lineSep === false) { return lines }
5873 else { return lines.join(lineSep || this.lineSeparator()) }
5874 },
5875 getSelections: function(lineSep) {
5876 var this$1 = this;
5877
5878 var parts = [], ranges = this.sel.ranges
5879 for (var i = 0; i < ranges.length; i++) {
5880 var sel = getBetween(this$1, ranges[i].from(), ranges[i].to())
5881 if (lineSep !== false) { sel = sel.join(lineSep || this$1.lineSeparator()) }
5882 parts[i] = sel
5883 }
5884 return parts
5885 },
5886 replaceSelection: function(code, collapse, origin) {
5887 var dup = []
5888 for (var i = 0; i < this.sel.ranges.length; i++)
5889 { dup[i] = code }
5890 this.replaceSelections(dup, collapse, origin || "+input")
5891 },
5892 replaceSelections: docMethodOp(function(code, collapse, origin) {
5893 var this$1 = this;
5894
5895 var changes = [], sel = this.sel
5896 for (var i = 0; i < sel.ranges.length; i++) {
5897 var range$$1 = sel.ranges[i]
5898 changes[i] = {from: range$$1.from(), to: range$$1.to(), text: this$1.splitLines(code[i]), origin: origin}
5899 }
5900 var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse)
5901 for (var i$1 = changes.length - 1; i$1 >= 0; i$1--)
5902 { makeChange(this$1, changes[i$1]) }
5903 if (newSel) { setSelectionReplaceHistory(this, newSel) }
5904 else if (this.cm) { ensureCursorVisible(this.cm) }
5905 }),
5906 undo: docMethodOp(function() {makeChangeFromHistory(this, "undo")}),
5907 redo: docMethodOp(function() {makeChangeFromHistory(this, "redo")}),
5908 undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true)}),
5909 redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true)}),
5910
5911 setExtending: function(val) {this.extend = val},
5912 getExtending: function() {return this.extend},
5913
5914 historySize: function() {
5915 var hist = this.history, done = 0, undone = 0
5916 for (var i = 0; i < hist.done.length; i++) { if (!hist.done[i].ranges) { ++done } }
5917 for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].ranges) { ++undone } }
5918 return {undo: done, redo: undone}
5919 },
5920 clearHistory: function() {this.history = new History(this.history.maxGeneration)},
5921
5922 markClean: function() {
5923 this.cleanGeneration = this.changeGeneration(true)
5924 },
5925 changeGeneration: function(forceSplit) {
5926 if (forceSplit)
5927 { this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null }
5928 return this.history.generation
5929 },
5930 isClean: function (gen) {
5931 return this.history.generation == (gen || this.cleanGeneration)
5932 },
5933
5934 getHistory: function() {
5935 return {done: copyHistoryArray(this.history.done),
5936 undone: copyHistoryArray(this.history.undone)}
5937 },
5938 setHistory: function(histData) {
5939 var hist = this.history = new History(this.history.maxGeneration)
5940 hist.done = copyHistoryArray(histData.done.slice(0), null, true)
5941 hist.undone = copyHistoryArray(histData.undone.slice(0), null, true)
5942 },
5943
5944 setGutterMarker: docMethodOp(function(line, gutterID, value) {
5945 return changeLine(this, line, "gutter", function (line) {
5946 var markers = line.gutterMarkers || (line.gutterMarkers = {})
5947 markers[gutterID] = value
5948 if (!value && isEmpty(markers)) { line.gutterMarkers = null }
5949 return true
5950 })
5951 }),
5952
5953 clearGutter: docMethodOp(function(gutterID) {
5954 var this$1 = this;
5955
5956 var i = this.first
5957 this.iter(function (line) {
5958 if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
5959 changeLine(this$1, line, "gutter", function () {
5960 line.gutterMarkers[gutterID] = null
5961 if (isEmpty(line.gutterMarkers)) { line.gutterMarkers = null }
5962 return true
5963 })
5964 }
5965 ++i
5966 })
5967 }),
5968
5969 lineInfo: function(line) {
5970 var n
5971 if (typeof line == "number") {
5972 if (!isLine(this, line)) { return null }
5973 n = line
5974 line = getLine(this, line)
5975 if (!line) { return null }
5976 } else {
5977 n = lineNo(line)
5978 if (n == null) { return null }
5979 }
5980 return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
5981 textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
5982 widgets: line.widgets}
5983 },
5984
5985 addLineClass: docMethodOp(function(handle, where, cls) {
5986 return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
5987 var prop = where == "text" ? "textClass"
5988 : where == "background" ? "bgClass"
5989 : where == "gutter" ? "gutterClass" : "wrapClass"
5990 if (!line[prop]) { line[prop] = cls }
5991 else if (classTest(cls).test(line[prop])) { return false }
5992 else { line[prop] += " " + cls }
5993 return true
5994 })
5995 }),
5996 removeLineClass: docMethodOp(function(handle, where, cls) {
5997 return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
5998 var prop = where == "text" ? "textClass"
5999 : where == "background" ? "bgClass"
6000 : where == "gutter" ? "gutterClass" : "wrapClass"
6001 var cur = line[prop]
6002 if (!cur) { return false }
6003 else if (cls == null) { line[prop] = null }
6004 else {
6005 var found = cur.match(classTest(cls))
6006 if (!found) { return false }
6007 var end = found.index + found[0].length
6008 line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null
6009 }
6010 return true
6011 })
6012 }),
6013
6014 addLineWidget: docMethodOp(function(handle, node, options) {
6015 return addLineWidget(this, handle, node, options)
6016 }),
6017 removeLineWidget: function(widget) { widget.clear() },
6018
6019 markText: function(from, to, options) {
6020 return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range")
6021 },
6022 setBookmark: function(pos, options) {
6023 var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
6024 insertLeft: options && options.insertLeft,
6025 clearWhenEmpty: false, shared: options && options.shared,
6026 handleMouseEvents: options && options.handleMouseEvents}
6027 pos = clipPos(this, pos)
6028 return markText(this, pos, pos, realOpts, "bookmark")
6029 },
6030 findMarksAt: function(pos) {
6031 pos = clipPos(this, pos)
6032 var markers = [], spans = getLine(this, pos.line).markedSpans
6033 if (spans) { for (var i = 0; i < spans.length; ++i) {
6034 var span = spans[i]
6035 if ((span.from == null || span.from <= pos.ch) &&
6036 (span.to == null || span.to >= pos.ch))
6037 { markers.push(span.marker.parent || span.marker) }
6038 } }
6039 return markers
6040 },
6041 findMarks: function(from, to, filter) {
6042 from = clipPos(this, from); to = clipPos(this, to)
6043 var found = [], lineNo$$1 = from.line
6044 this.iter(from.line, to.line + 1, function (line) {
6045 var spans = line.markedSpans
6046 if (spans) { for (var i = 0; i < spans.length; i++) {
6047 var span = spans[i]
6048 if (!(span.to != null && lineNo$$1 == from.line && from.ch >= span.to ||
6049 span.from == null && lineNo$$1 != from.line ||
6050 span.from != null && lineNo$$1 == to.line && span.from >= to.ch) &&
6051 (!filter || filter(span.marker)))
6052 { found.push(span.marker.parent || span.marker) }
6053 } }
6054 ++lineNo$$1
6055 })
6056 return found
6057 },
6058 getAllMarks: function() {
6059 var markers = []
6060 this.iter(function (line) {
6061 var sps = line.markedSpans
6062 if (sps) { for (var i = 0; i < sps.length; ++i)
6063 { if (sps[i].from != null) { markers.push(sps[i].marker) } } }
6064 })
6065 return markers
6066 },
6067
6068 posFromIndex: function(off) {
6069 var ch, lineNo$$1 = this.first, sepSize = this.lineSeparator().length
6070 this.iter(function (line) {
6071 var sz = line.text.length + sepSize
6072 if (sz > off) { ch = off; return true }
6073 off -= sz
6074 ++lineNo$$1
6075 })
6076 return clipPos(this, Pos(lineNo$$1, ch))
6077 },
6078 indexFromPos: function (coords) {
6079 coords = clipPos(this, coords)
6080 var index = coords.ch
6081 if (coords.line < this.first || coords.ch < 0) { return 0 }
6082 var sepSize = this.lineSeparator().length
6083 this.iter(this.first, coords.line, function (line) { // iter aborts when callback returns a truthy value
6084 index += line.text.length + sepSize
6085 })
6086 return index
6087 },
6088
6089 copy: function(copyHistory) {
6090 var doc = new Doc(getLines(this, this.first, this.first + this.size),
6091 this.modeOption, this.first, this.lineSep)
6092 doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft
6093 doc.sel = this.sel
6094 doc.extend = false
6095 if (copyHistory) {
6096 doc.history.undoDepth = this.history.undoDepth
6097 doc.setHistory(this.getHistory())
6098 }
6099 return doc
6100 },
6101
6102 linkedDoc: function(options) {
6103 if (!options) { options = {} }
6104 var from = this.first, to = this.first + this.size
6105 if (options.from != null && options.from > from) { from = options.from }
6106 if (options.to != null && options.to < to) { to = options.to }
6107 var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep)
6108 if (options.sharedHist) { copy.history = this.history
6109 ; }(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist})
6110 copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}]
6111 copySharedMarkers(copy, findSharedMarkers(this))
6112 return copy
6113 },
6114 unlinkDoc: function(other) {
6115 var this$1 = this;
6116
6117 if (other instanceof CodeMirror$1) { other = other.doc }
6118 if (this.linked) { for (var i = 0; i < this.linked.length; ++i) {
6119 var link = this$1.linked[i]
6120 if (link.doc != other) { continue }
6121 this$1.linked.splice(i, 1)
6122 other.unlinkDoc(this$1)
6123 detachSharedMarkers(findSharedMarkers(this$1))
6124 break
6125 } }
6126 // If the histories were shared, split them again
6127 if (other.history == this.history) {
6128 var splitIds = [other.id]
6129 linkedDocs(other, function (doc) { return splitIds.push(doc.id); }, true)
6130 other.history = new History(null)
6131 other.history.done = copyHistoryArray(this.history.done, splitIds)
6132 other.history.undone = copyHistoryArray(this.history.undone, splitIds)
6133 }
6134 },
6135 iterLinkedDocs: function(f) {linkedDocs(this, f)},
6136
6137 getMode: function() {return this.mode},
6138 getEditor: function() {return this.cm},
6139
6140 splitLines: function(str) {
6141 if (this.lineSep) { return str.split(this.lineSep) }
6142 return splitLinesAuto(str)
6143 },
6144 lineSeparator: function() { return this.lineSep || "\n" }
6145})
6146
6147// Public alias.
6148Doc.prototype.eachLine = Doc.prototype.iter
6149
6150// Kludge to work around strange IE behavior where it'll sometimes
6151// re-fire a series of drag-related events right after the drop (#1551)
6152var lastDrop = 0
6153
6154function onDrop(e) {
6155 var cm = this
6156 clearDragCursor(cm)
6157 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))
6158 { return }
6159 e_preventDefault(e)
6160 if (ie) { lastDrop = +new Date }
6161 var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files
6162 if (!pos || cm.isReadOnly()) { return }
6163 // Might be a file drop, in which case we simply extract the text
6164 // and insert it.
6165 if (files && files.length && window.FileReader && window.File) {
6166 var n = files.length, text = Array(n), read = 0
6167 var loadFile = function (file, i) {
6168 if (cm.options.allowDropFileTypes &&
6169 indexOf(cm.options.allowDropFileTypes, file.type) == -1)
6170 { return }
6171
6172 var reader = new FileReader
6173 reader.onload = operation(cm, function () {
6174 var content = reader.result
6175 if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) { content = "" }
6176 text[i] = content
6177 if (++read == n) {
6178 pos = clipPos(cm.doc, pos)
6179 var change = {from: pos, to: pos,
6180 text: cm.doc.splitLines(text.join(cm.doc.lineSeparator())),
6181 origin: "paste"}
6182 makeChange(cm.doc, change)
6183 setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change)))
6184 }
6185 })
6186 reader.readAsText(file)
6187 }
6188 for (var i = 0; i < n; ++i) { loadFile(files[i], i) }
6189 } else { // Normal drop
6190 // Don't do a replace if the drop happened inside of the selected text.
6191 if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {
6192 cm.state.draggingText(e)
6193 // Ensure the editor is re-focused
6194 setTimeout(function () { return cm.display.input.focus(); }, 20)
6195 return
6196 }
6197 try {
6198 var text$1 = e.dataTransfer.getData("Text")
6199 if (text$1) {
6200 var selected
6201 if (cm.state.draggingText && !cm.state.draggingText.copy)
6202 { selected = cm.listSelections() }
6203 setSelectionNoUndo(cm.doc, simpleSelection(pos, pos))
6204 if (selected) { for (var i$1 = 0; i$1 < selected.length; ++i$1)
6205 { replaceRange(cm.doc, "", selected[i$1].anchor, selected[i$1].head, "drag") } }
6206 cm.replaceSelection(text$1, "around", "paste")
6207 cm.display.input.focus()
6208 }
6209 }
6210 catch(e){}
6211 }
6212}
6213
6214function onDragStart(cm, e) {
6215 if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return }
6216 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return }
6217
6218 e.dataTransfer.setData("Text", cm.getSelection())
6219 e.dataTransfer.effectAllowed = "copyMove"
6220
6221 // Use dummy image instead of default browsers image.
6222 // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
6223 if (e.dataTransfer.setDragImage && !safari) {
6224 var img = elt("img", null, null, "position: fixed; left: 0; top: 0;")
6225 img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
6226 if (presto) {
6227 img.width = img.height = 1
6228 cm.display.wrapper.appendChild(img)
6229 // Force a relayout, or Opera won't use our image for some obscure reason
6230 img._top = img.offsetTop
6231 }
6232 e.dataTransfer.setDragImage(img, 0, 0)
6233 if (presto) { img.parentNode.removeChild(img) }
6234 }
6235}
6236
6237function onDragOver(cm, e) {
6238 var pos = posFromMouse(cm, e)
6239 if (!pos) { return }
6240 var frag = document.createDocumentFragment()
6241 drawSelectionCursor(cm, pos, frag)
6242 if (!cm.display.dragCursor) {
6243 cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors")
6244 cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv)
6245 }
6246 removeChildrenAndAdd(cm.display.dragCursor, frag)
6247}
6248
6249function clearDragCursor(cm) {
6250 if (cm.display.dragCursor) {
6251 cm.display.lineSpace.removeChild(cm.display.dragCursor)
6252 cm.display.dragCursor = null
6253 }
6254}
6255
6256// These must be handled carefully, because naively registering a
6257// handler for each editor will cause the editors to never be
6258// garbage collected.
6259
6260function forEachCodeMirror(f) {
6261 if (!document.body.getElementsByClassName) { return }
6262 var byClass = document.body.getElementsByClassName("CodeMirror")
6263 for (var i = 0; i < byClass.length; i++) {
6264 var cm = byClass[i].CodeMirror
6265 if (cm) { f(cm) }
6266 }
6267}
6268
6269var globalsRegistered = false
6270function ensureGlobalHandlers() {
6271 if (globalsRegistered) { return }
6272 registerGlobalHandlers()
6273 globalsRegistered = true
6274}
6275function registerGlobalHandlers() {
6276 // When the window resizes, we need to refresh active editors.
6277 var resizeTimer
6278 on(window, "resize", function () {
6279 if (resizeTimer == null) { resizeTimer = setTimeout(function () {
6280 resizeTimer = null
6281 forEachCodeMirror(onResize)
6282 }, 100) }
6283 })
6284 // When the window loses focus, we want to show the editor as blurred
6285 on(window, "blur", function () { return forEachCodeMirror(onBlur); })
6286}
6287// Called when the window resizes
6288function onResize(cm) {
6289 var d = cm.display
6290 if (d.lastWrapHeight == d.wrapper.clientHeight && d.lastWrapWidth == d.wrapper.clientWidth)
6291 { return }
6292 // Might be a text scaling operation, clear size caches.
6293 d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null
6294 d.scrollbarsClipped = false
6295 cm.setSize()
6296}
6297
6298var keyNames = {
6299 3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
6300 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
6301 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
6302 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod",
6303 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 127: "Delete",
6304 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
6305 221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete",
6306 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert"
6307}
6308
6309// Number keys
6310for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i) }
6311// Alphabetic keys
6312for (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1) }
6313// Function keys
6314for (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235] = "F" + i$2 }
6315
6316var keyMap = {}
6317
6318keyMap.basic = {
6319 "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
6320 "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
6321 "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore",
6322 "Tab": "defaultTab", "Shift-Tab": "indentAuto",
6323 "Enter": "newlineAndIndent", "Insert": "toggleOverwrite",
6324 "Esc": "singleSelection"
6325}
6326// Note that the save and find-related commands aren't defined by
6327// default. User code or addons can define them. Unknown commands
6328// are simply ignored.
6329keyMap.pcDefault = {
6330 "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
6331 "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown",
6332 "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
6333 "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
6334 "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
6335 "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
6336 "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection",
6337 fallthrough: "basic"
6338}
6339// Very basic readline/emacs-style bindings, which are standard on Mac.
6340keyMap.emacsy = {
6341 "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
6342 "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
6343 "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
6344 "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars",
6345 "Ctrl-O": "openLine"
6346}
6347keyMap.macDefault = {
6348 "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
6349 "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
6350 "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore",
6351 "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
6352 "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
6353 "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight",
6354 "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd",
6355 fallthrough: ["basic", "emacsy"]
6356}
6357keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault
6358
6359// KEYMAP DISPATCH
6360
6361function normalizeKeyName(name) {
6362 var parts = name.split(/-(?!$)/)
6363 name = parts[parts.length - 1]
6364 var alt, ctrl, shift, cmd
6365 for (var i = 0; i < parts.length - 1; i++) {
6366 var mod = parts[i]
6367 if (/^(cmd|meta|m)$/i.test(mod)) { cmd = true }
6368 else if (/^a(lt)?$/i.test(mod)) { alt = true }
6369 else if (/^(c|ctrl|control)$/i.test(mod)) { ctrl = true }
6370 else if (/^s(hift)?$/i.test(mod)) { shift = true }
6371 else { throw new Error("Unrecognized modifier name: " + mod) }
6372 }
6373 if (alt) { name = "Alt-" + name }
6374 if (ctrl) { name = "Ctrl-" + name }
6375 if (cmd) { name = "Cmd-" + name }
6376 if (shift) { name = "Shift-" + name }
6377 return name
6378}
6379
6380// This is a kludge to keep keymaps mostly working as raw objects
6381// (backwards compatibility) while at the same time support features
6382// like normalization and multi-stroke key bindings. It compiles a
6383// new normalized keymap, and then updates the old object to reflect
6384// this.
6385function normalizeKeyMap(keymap) {
6386 var copy = {}
6387 for (var keyname in keymap) { if (keymap.hasOwnProperty(keyname)) {
6388 var value = keymap[keyname]
6389 if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) { continue }
6390 if (value == "...") { delete keymap[keyname]; continue }
6391
6392 var keys = map(keyname.split(" "), normalizeKeyName)
6393 for (var i = 0; i < keys.length; i++) {
6394 var val = void 0, name = void 0
6395 if (i == keys.length - 1) {
6396 name = keys.join(" ")
6397 val = value
6398 } else {
6399 name = keys.slice(0, i + 1).join(" ")
6400 val = "..."
6401 }
6402 var prev = copy[name]
6403 if (!prev) { copy[name] = val }
6404 else if (prev != val) { throw new Error("Inconsistent bindings for " + name) }
6405 }
6406 delete keymap[keyname]
6407 } }
6408 for (var prop in copy) { keymap[prop] = copy[prop] }
6409 return keymap
6410}
6411
6412function lookupKey(key, map$$1, handle, context) {
6413 map$$1 = getKeyMap(map$$1)
6414 var found = map$$1.call ? map$$1.call(key, context) : map$$1[key]
6415 if (found === false) { return "nothing" }
6416 if (found === "...") { return "multi" }
6417 if (found != null && handle(found)) { return "handled" }
6418
6419 if (map$$1.fallthrough) {
6420 if (Object.prototype.toString.call(map$$1.fallthrough) != "[object Array]")
6421 { return lookupKey(key, map$$1.fallthrough, handle, context) }
6422 for (var i = 0; i < map$$1.fallthrough.length; i++) {
6423 var result = lookupKey(key, map$$1.fallthrough[i], handle, context)
6424 if (result) { return result }
6425 }
6426 }
6427}
6428
6429// Modifier key presses don't count as 'real' key presses for the
6430// purpose of keymap fallthrough.
6431function isModifierKey(value) {
6432 var name = typeof value == "string" ? value : keyNames[value.keyCode]
6433 return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod"
6434}
6435
6436// Look up the name of a key as indicated by an event object.
6437function keyName(event, noShift) {
6438 if (presto && event.keyCode == 34 && event["char"]) { return false }
6439 var base = keyNames[event.keyCode], name = base
6440 if (name == null || event.altGraphKey) { return false }
6441 if (event.altKey && base != "Alt") { name = "Alt-" + name }
6442 if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") { name = "Ctrl-" + name }
6443 if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") { name = "Cmd-" + name }
6444 if (!noShift && event.shiftKey && base != "Shift") { name = "Shift-" + name }
6445 return name
6446}
6447
6448function getKeyMap(val) {
6449 return typeof val == "string" ? keyMap[val] : val
6450}
6451
6452// Helper for deleting text near the selection(s), used to implement
6453// backspace, delete, and similar functionality.
6454function deleteNearSelection(cm, compute) {
6455 var ranges = cm.doc.sel.ranges, kill = []
6456 // Build up a set of ranges to kill first, merging overlapping
6457 // ranges.
6458 for (var i = 0; i < ranges.length; i++) {
6459 var toKill = compute(ranges[i])
6460 while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {
6461 var replaced = kill.pop()
6462 if (cmp(replaced.from, toKill.from) < 0) {
6463 toKill.from = replaced.from
6464 break
6465 }
6466 }
6467 kill.push(toKill)
6468 }
6469 // Next, remove those actual ranges.
6470 runInOp(cm, function () {
6471 for (var i = kill.length - 1; i >= 0; i--)
6472 { replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete") }
6473 ensureCursorVisible(cm)
6474 })
6475}
6476
6477// Commands are parameter-less actions that can be performed on an
6478// editor, mostly used for keybindings.
6479var commands = {
6480 selectAll: selectAll,
6481 singleSelection: function (cm) { return cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll); },
6482 killLine: function (cm) { return deleteNearSelection(cm, function (range) {
6483 if (range.empty()) {
6484 var len = getLine(cm.doc, range.head.line).text.length
6485 if (range.head.ch == len && range.head.line < cm.lastLine())
6486 { return {from: range.head, to: Pos(range.head.line + 1, 0)} }
6487 else
6488 { return {from: range.head, to: Pos(range.head.line, len)} }
6489 } else {
6490 return {from: range.from(), to: range.to()}
6491 }
6492 }); },
6493 deleteLine: function (cm) { return deleteNearSelection(cm, function (range) { return ({
6494 from: Pos(range.from().line, 0),
6495 to: clipPos(cm.doc, Pos(range.to().line + 1, 0))
6496 }); }); },
6497 delLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { return ({
6498 from: Pos(range.from().line, 0), to: range.from()
6499 }); }); },
6500 delWrappedLineLeft: function (cm) { return deleteNearSelection(cm, function (range) {
6501 var top = cm.charCoords(range.head, "div").top + 5
6502 var leftPos = cm.coordsChar({left: 0, top: top}, "div")
6503 return {from: leftPos, to: range.from()}
6504 }); },
6505 delWrappedLineRight: function (cm) { return deleteNearSelection(cm, function (range) {
6506 var top = cm.charCoords(range.head, "div").top + 5
6507 var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div")
6508 return {from: range.from(), to: rightPos }
6509 }); },
6510 undo: function (cm) { return cm.undo(); },
6511 redo: function (cm) { return cm.redo(); },
6512 undoSelection: function (cm) { return cm.undoSelection(); },
6513 redoSelection: function (cm) { return cm.redoSelection(); },
6514 goDocStart: function (cm) { return cm.extendSelection(Pos(cm.firstLine(), 0)); },
6515 goDocEnd: function (cm) { return cm.extendSelection(Pos(cm.lastLine())); },
6516 goLineStart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStart(cm, range.head.line); },
6517 {origin: "+move", bias: 1}
6518 ); },
6519 goLineStartSmart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStartSmart(cm, range.head); },
6520 {origin: "+move", bias: 1}
6521 ); },
6522 goLineEnd: function (cm) { return cm.extendSelectionsBy(function (range) { return lineEnd(cm, range.head.line); },
6523 {origin: "+move", bias: -1}
6524 ); },
6525 goLineRight: function (cm) { return cm.extendSelectionsBy(function (range) {
6526 var top = cm.charCoords(range.head, "div").top + 5
6527 return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div")
6528 }, sel_move); },
6529 goLineLeft: function (cm) { return cm.extendSelectionsBy(function (range) {
6530 var top = cm.charCoords(range.head, "div").top + 5
6531 return cm.coordsChar({left: 0, top: top}, "div")
6532 }, sel_move); },
6533 goLineLeftSmart: function (cm) { return cm.extendSelectionsBy(function (range) {
6534 var top = cm.charCoords(range.head, "div").top + 5
6535 var pos = cm.coordsChar({left: 0, top: top}, "div")
6536 if (pos.ch < cm.getLine(pos.line).search(/\S/)) { return lineStartSmart(cm, range.head) }
6537 return pos
6538 }, sel_move); },
6539 goLineUp: function (cm) { return cm.moveV(-1, "line"); },
6540 goLineDown: function (cm) { return cm.moveV(1, "line"); },
6541 goPageUp: function (cm) { return cm.moveV(-1, "page"); },
6542 goPageDown: function (cm) { return cm.moveV(1, "page"); },
6543 goCharLeft: function (cm) { return cm.moveH(-1, "char"); },
6544 goCharRight: function (cm) { return cm.moveH(1, "char"); },
6545 goColumnLeft: function (cm) { return cm.moveH(-1, "column"); },
6546 goColumnRight: function (cm) { return cm.moveH(1, "column"); },
6547 goWordLeft: function (cm) { return cm.moveH(-1, "word"); },
6548 goGroupRight: function (cm) { return cm.moveH(1, "group"); },
6549 goGroupLeft: function (cm) { return cm.moveH(-1, "group"); },
6550 goWordRight: function (cm) { return cm.moveH(1, "word"); },
6551 delCharBefore: function (cm) { return cm.deleteH(-1, "char"); },
6552 delCharAfter: function (cm) { return cm.deleteH(1, "char"); },
6553 delWordBefore: function (cm) { return cm.deleteH(-1, "word"); },
6554 delWordAfter: function (cm) { return cm.deleteH(1, "word"); },
6555 delGroupBefore: function (cm) { return cm.deleteH(-1, "group"); },
6556 delGroupAfter: function (cm) { return cm.deleteH(1, "group"); },
6557 indentAuto: function (cm) { return cm.indentSelection("smart"); },
6558 indentMore: function (cm) { return cm.indentSelection("add"); },
6559 indentLess: function (cm) { return cm.indentSelection("subtract"); },
6560 insertTab: function (cm) { return cm.replaceSelection("\t"); },
6561 insertSoftTab: function (cm) {
6562 var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize
6563 for (var i = 0; i < ranges.length; i++) {
6564 var pos = ranges[i].from()
6565 var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize)
6566 spaces.push(spaceStr(tabSize - col % tabSize))
6567 }
6568 cm.replaceSelections(spaces)
6569 },
6570 defaultTab: function (cm) {
6571 if (cm.somethingSelected()) { cm.indentSelection("add") }
6572 else { cm.execCommand("insertTab") }
6573 },
6574 // Swap the two chars left and right of each selection's head.
6575 // Move cursor behind the two swapped characters afterwards.
6576 //
6577 // Doesn't consider line feeds a character.
6578 // Doesn't scan more than one line above to find a character.
6579 // Doesn't do anything on an empty line.
6580 // Doesn't do anything with non-empty selections.
6581 transposeChars: function (cm) { return runInOp(cm, function () {
6582 var ranges = cm.listSelections(), newSel = []
6583 for (var i = 0; i < ranges.length; i++) {
6584 if (!ranges[i].empty()) { continue }
6585 var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text
6586 if (line) {
6587 if (cur.ch == line.length) { cur = new Pos(cur.line, cur.ch - 1) }
6588 if (cur.ch > 0) {
6589 cur = new Pos(cur.line, cur.ch + 1)
6590 cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),
6591 Pos(cur.line, cur.ch - 2), cur, "+transpose")
6592 } else if (cur.line > cm.doc.first) {
6593 var prev = getLine(cm.doc, cur.line - 1).text
6594 if (prev) {
6595 cur = new Pos(cur.line, 1)
6596 cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() +
6597 prev.charAt(prev.length - 1),
6598 Pos(cur.line - 1, prev.length - 1), cur, "+transpose")
6599 }
6600 }
6601 }
6602 newSel.push(new Range(cur, cur))
6603 }
6604 cm.setSelections(newSel)
6605 }); },
6606 newlineAndIndent: function (cm) { return runInOp(cm, function () {
6607 var sels = cm.listSelections()
6608 for (var i = sels.length - 1; i >= 0; i--)
6609 { cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input") }
6610 sels = cm.listSelections()
6611 for (var i$1 = 0; i$1 < sels.length; i$1++)
6612 { cm.indentLine(sels[i$1].from().line, null, true) }
6613 ensureCursorVisible(cm)
6614 }); },
6615 openLine: function (cm) { return cm.replaceSelection("\n", "start"); },
6616 toggleOverwrite: function (cm) { return cm.toggleOverwrite(); }
6617}
6618
6619
6620function lineStart(cm, lineN) {
6621 var line = getLine(cm.doc, lineN)
6622 var visual = visualLine(line)
6623 if (visual != line) { lineN = lineNo(visual) }
6624 var order = getOrder(visual)
6625 var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual)
6626 return Pos(lineN, ch)
6627}
6628function lineEnd(cm, lineN) {
6629 var merged, line = getLine(cm.doc, lineN)
6630 while (merged = collapsedSpanAtEnd(line)) {
6631 line = merged.find(1, true).line
6632 lineN = null
6633 }
6634 var order = getOrder(line)
6635 var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line)
6636 return Pos(lineN == null ? lineNo(line) : lineN, ch)
6637}
6638function lineStartSmart(cm, pos) {
6639 var start = lineStart(cm, pos.line)
6640 var line = getLine(cm.doc, start.line)
6641 var order = getOrder(line)
6642 if (!order || order[0].level == 0) {
6643 var firstNonWS = Math.max(0, line.text.search(/\S/))
6644 var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch
6645 return Pos(start.line, inWS ? 0 : firstNonWS)
6646 }
6647 return start
6648}
6649
6650// Run a handler that was bound to a key.
6651function doHandleBinding(cm, bound, dropShift) {
6652 if (typeof bound == "string") {
6653 bound = commands[bound]
6654 if (!bound) { return false }
6655 }
6656 // Ensure previous input has been read, so that the handler sees a
6657 // consistent view of the document
6658 cm.display.input.ensurePolled()
6659 var prevShift = cm.display.shift, done = false
6660 try {
6661 if (cm.isReadOnly()) { cm.state.suppressEdits = true }
6662 if (dropShift) { cm.display.shift = false }
6663 done = bound(cm) != Pass
6664 } finally {
6665 cm.display.shift = prevShift
6666 cm.state.suppressEdits = false
6667 }
6668 return done
6669}
6670
6671function lookupKeyForEditor(cm, name, handle) {
6672 for (var i = 0; i < cm.state.keyMaps.length; i++) {
6673 var result = lookupKey(name, cm.state.keyMaps[i], handle, cm)
6674 if (result) { return result }
6675 }
6676 return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm))
6677 || lookupKey(name, cm.options.keyMap, handle, cm)
6678}
6679
6680var stopSeq = new Delayed
6681function dispatchKey(cm, name, e, handle) {
6682 var seq = cm.state.keySeq
6683 if (seq) {
6684 if (isModifierKey(name)) { return "handled" }
6685 stopSeq.set(50, function () {
6686 if (cm.state.keySeq == seq) {
6687 cm.state.keySeq = null
6688 cm.display.input.reset()
6689 }
6690 })
6691 name = seq + " " + name
6692 }
6693 var result = lookupKeyForEditor(cm, name, handle)
6694
6695 if (result == "multi")
6696 { cm.state.keySeq = name }
6697 if (result == "handled")
6698 { signalLater(cm, "keyHandled", cm, name, e) }
6699
6700 if (result == "handled" || result == "multi") {
6701 e_preventDefault(e)
6702 restartBlink(cm)
6703 }
6704
6705 if (seq && !result && /\'$/.test(name)) {
6706 e_preventDefault(e)
6707 return true
6708 }
6709 return !!result
6710}
6711
6712// Handle a key from the keydown event.
6713function handleKeyBinding(cm, e) {
6714 var name = keyName(e, true)
6715 if (!name) { return false }
6716
6717 if (e.shiftKey && !cm.state.keySeq) {
6718 // First try to resolve full name (including 'Shift-'). Failing
6719 // that, see if there is a cursor-motion command (starting with
6720 // 'go') bound to the keyname without 'Shift-'.
6721 return dispatchKey(cm, "Shift-" + name, e, function (b) { return doHandleBinding(cm, b, true); })
6722 || dispatchKey(cm, name, e, function (b) {
6723 if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
6724 { return doHandleBinding(cm, b) }
6725 })
6726 } else {
6727 return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b); })
6728 }
6729}
6730
6731// Handle a key from the keypress event
6732function handleCharBinding(cm, e, ch) {
6733 return dispatchKey(cm, "'" + ch + "'", e, function (b) { return doHandleBinding(cm, b, true); })
6734}
6735
6736var lastStoppedKey = null
6737function onKeyDown(e) {
6738 var cm = this
6739 cm.curOp.focus = activeElt()
6740 if (signalDOMEvent(cm, e)) { return }
6741 // IE does strange things with escape.
6742 if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false }
6743 var code = e.keyCode
6744 cm.display.shift = code == 16 || e.shiftKey
6745 var handled = handleKeyBinding(cm, e)
6746 if (presto) {
6747 lastStoppedKey = handled ? code : null
6748 // Opera has no cut event... we try to at least catch the key combo
6749 if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
6750 { cm.replaceSelection("", null, "cut") }
6751 }
6752
6753 // Turn mouse into crosshair when Alt is held on Mac.
6754 if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className))
6755 { showCrossHair(cm) }
6756}
6757
6758function showCrossHair(cm) {
6759 var lineDiv = cm.display.lineDiv
6760 addClass(lineDiv, "CodeMirror-crosshair")
6761
6762 function up(e) {
6763 if (e.keyCode == 18 || !e.altKey) {
6764 rmClass(lineDiv, "CodeMirror-crosshair")
6765 off(document, "keyup", up)
6766 off(document, "mouseover", up)
6767 }
6768 }
6769 on(document, "keyup", up)
6770 on(document, "mouseover", up)
6771}
6772
6773function onKeyUp(e) {
6774 if (e.keyCode == 16) { this.doc.sel.shift = false }
6775 signalDOMEvent(this, e)
6776}
6777
6778function onKeyPress(e) {
6779 var cm = this
6780 if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return }
6781 var keyCode = e.keyCode, charCode = e.charCode
6782 if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return}
6783 if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) { return }
6784 var ch = String.fromCharCode(charCode == null ? keyCode : charCode)
6785 // Some browsers fire keypress events for backspace
6786 if (ch == "\x08") { return }
6787 if (handleCharBinding(cm, e, ch)) { return }
6788 cm.display.input.onKeyPress(e)
6789}
6790
6791// A mouse down can be a single click, double click, triple click,
6792// start of selection drag, start of text drag, new cursor
6793// (ctrl-click), rectangle drag (alt-drag), or xwin
6794// middle-click-paste. Or it might be a click on something we should
6795// not interfere with, such as a scrollbar or widget.
6796function onMouseDown(e) {
6797 var cm = this, display = cm.display
6798 if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) { return }
6799 display.input.ensurePolled()
6800 display.shift = e.shiftKey
6801
6802 if (eventInWidget(display, e)) {
6803 if (!webkit) {
6804 // Briefly turn off draggability, to allow widgets to do
6805 // normal dragging things.
6806 display.scroller.draggable = false
6807 setTimeout(function () { return display.scroller.draggable = true; }, 100)
6808 }
6809 return
6810 }
6811 if (clickInGutter(cm, e)) { return }
6812 var start = posFromMouse(cm, e)
6813 window.focus()
6814
6815 switch (e_button(e)) {
6816 case 1:
6817 // #3261: make sure, that we're not starting a second selection
6818 if (cm.state.selectingText)
6819 { cm.state.selectingText(e) }
6820 else if (start)
6821 { leftButtonDown(cm, e, start) }
6822 else if (e_target(e) == display.scroller)
6823 { e_preventDefault(e) }
6824 break
6825 case 2:
6826 if (webkit) { cm.state.lastMiddleDown = +new Date }
6827 if (start) { extendSelection(cm.doc, start) }
6828 setTimeout(function () { return display.input.focus(); }, 20)
6829 e_preventDefault(e)
6830 break
6831 case 3:
6832 if (captureRightClick) { onContextMenu(cm, e) }
6833 else { delayBlurEvent(cm) }
6834 break
6835 }
6836}
6837
6838var lastClick;
6839var lastDoubleClick
6840function leftButtonDown(cm, e, start) {
6841 if (ie) { setTimeout(bind(ensureFocus, cm), 0) }
6842 else { cm.curOp.focus = activeElt() }
6843
6844 var now = +new Date, type
6845 if (lastDoubleClick && lastDoubleClick.time > now - 400 && cmp(lastDoubleClick.pos, start) == 0) {
6846 type = "triple"
6847 } else if (lastClick && lastClick.time > now - 400 && cmp(lastClick.pos, start) == 0) {
6848 type = "double"
6849 lastDoubleClick = {time: now, pos: start}
6850 } else {
6851 type = "single"
6852 lastClick = {time: now, pos: start}
6853 }
6854
6855 var sel = cm.doc.sel, modifier = mac ? e.metaKey : e.ctrlKey, contained
6856 if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() &&
6857 type == "single" && (contained = sel.contains(start)) > -1 &&
6858 (cmp((contained = sel.ranges[contained]).from(), start) < 0 || start.xRel > 0) &&
6859 (cmp(contained.to(), start) > 0 || start.xRel < 0))
6860 { leftButtonStartDrag(cm, e, start, modifier) }
6861 else
6862 { leftButtonSelect(cm, e, start, type, modifier) }
6863}
6864
6865// Start a text drag. When it ends, see if any dragging actually
6866// happen, and treat as a click if it didn't.
6867function leftButtonStartDrag(cm, e, start, modifier) {
6868 var display = cm.display, startTime = +new Date
6869 var dragEnd = operation(cm, function (e2) {
6870 if (webkit) { display.scroller.draggable = false }
6871 cm.state.draggingText = false
6872 off(document, "mouseup", dragEnd)
6873 off(display.scroller, "drop", dragEnd)
6874 if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
6875 e_preventDefault(e2)
6876 if (!modifier && +new Date - 200 < startTime)
6877 { extendSelection(cm.doc, start) }
6878 // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)
6879 if (webkit || ie && ie_version == 9)
6880 { setTimeout(function () {document.body.focus(); display.input.focus()}, 20) }
6881 else
6882 { display.input.focus() }
6883 }
6884 })
6885 // Let the drag handler handle this.
6886 if (webkit) { display.scroller.draggable = true }
6887 cm.state.draggingText = dragEnd
6888 dragEnd.copy = mac ? e.altKey : e.ctrlKey
6889 // IE's approach to draggable
6890 if (display.scroller.dragDrop) { display.scroller.dragDrop() }
6891 on(document, "mouseup", dragEnd)
6892 on(display.scroller, "drop", dragEnd)
6893}
6894
6895// Normal selection, as opposed to text dragging.
6896function leftButtonSelect(cm, e, start, type, addNew) {
6897 var display = cm.display, doc = cm.doc
6898 e_preventDefault(e)
6899
6900 var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges
6901 if (addNew && !e.shiftKey) {
6902 ourIndex = doc.sel.contains(start)
6903 if (ourIndex > -1)
6904 { ourRange = ranges[ourIndex] }
6905 else
6906 { ourRange = new Range(start, start) }
6907 } else {
6908 ourRange = doc.sel.primary()
6909 ourIndex = doc.sel.primIndex
6910 }
6911
6912 if (chromeOS ? e.shiftKey && e.metaKey : e.altKey) {
6913 type = "rect"
6914 if (!addNew) { ourRange = new Range(start, start) }
6915 start = posFromMouse(cm, e, true, true)
6916 ourIndex = -1
6917 } else if (type == "double") {
6918 var word = cm.findWordAt(start)
6919 if (cm.display.shift || doc.extend)
6920 { ourRange = extendRange(doc, ourRange, word.anchor, word.head) }
6921 else
6922 { ourRange = word }
6923 } else if (type == "triple") {
6924 var line = new Range(Pos(start.line, 0), clipPos(doc, Pos(start.line + 1, 0)))
6925 if (cm.display.shift || doc.extend)
6926 { ourRange = extendRange(doc, ourRange, line.anchor, line.head) }
6927 else
6928 { ourRange = line }
6929 } else {
6930 ourRange = extendRange(doc, ourRange, start)
6931 }
6932
6933 if (!addNew) {
6934 ourIndex = 0
6935 setSelection(doc, new Selection([ourRange], 0), sel_mouse)
6936 startSel = doc.sel
6937 } else if (ourIndex == -1) {
6938 ourIndex = ranges.length
6939 setSelection(doc, normalizeSelection(ranges.concat([ourRange]), ourIndex),
6940 {scroll: false, origin: "*mouse"})
6941 } else if (ranges.length > 1 && ranges[ourIndex].empty() && type == "single" && !e.shiftKey) {
6942 setSelection(doc, normalizeSelection(ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0),
6943 {scroll: false, origin: "*mouse"})
6944 startSel = doc.sel
6945 } else {
6946 replaceOneSelection(doc, ourIndex, ourRange, sel_mouse)
6947 }
6948
6949 var lastPos = start
6950 function extendTo(pos) {
6951 if (cmp(lastPos, pos) == 0) { return }
6952 lastPos = pos
6953
6954 if (type == "rect") {
6955 var ranges = [], tabSize = cm.options.tabSize
6956 var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize)
6957 var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize)
6958 var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol)
6959 for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line));
6960 line <= end; line++) {
6961 var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize)
6962 if (left == right)
6963 { ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))) }
6964 else if (text.length > leftPos)
6965 { ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))) }
6966 }
6967 if (!ranges.length) { ranges.push(new Range(start, start)) }
6968 setSelection(doc, normalizeSelection(startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex),
6969 {origin: "*mouse", scroll: false})
6970 cm.scrollIntoView(pos)
6971 } else {
6972 var oldRange = ourRange
6973 var anchor = oldRange.anchor, head = pos
6974 if (type != "single") {
6975 var range$$1
6976 if (type == "double")
6977 { range$$1 = cm.findWordAt(pos) }
6978 else
6979 { range$$1 = new Range(Pos(pos.line, 0), clipPos(doc, Pos(pos.line + 1, 0))) }
6980 if (cmp(range$$1.anchor, anchor) > 0) {
6981 head = range$$1.head
6982 anchor = minPos(oldRange.from(), range$$1.anchor)
6983 } else {
6984 head = range$$1.anchor
6985 anchor = maxPos(oldRange.to(), range$$1.head)
6986 }
6987 }
6988 var ranges$1 = startSel.ranges.slice(0)
6989 ranges$1[ourIndex] = new Range(clipPos(doc, anchor), head)
6990 setSelection(doc, normalizeSelection(ranges$1, ourIndex), sel_mouse)
6991 }
6992 }
6993
6994 var editorSize = display.wrapper.getBoundingClientRect()
6995 // Used to ensure timeout re-tries don't fire when another extend
6996 // happened in the meantime (clearTimeout isn't reliable -- at
6997 // least on Chrome, the timeouts still happen even when cleared,
6998 // if the clear happens after their scheduled firing time).
6999 var counter = 0
7000
7001 function extend(e) {
7002 var curCount = ++counter
7003 var cur = posFromMouse(cm, e, true, type == "rect")
7004 if (!cur) { return }
7005 if (cmp(cur, lastPos) != 0) {
7006 cm.curOp.focus = activeElt()
7007 extendTo(cur)
7008 var visible = visibleLines(display, doc)
7009 if (cur.line >= visible.to || cur.line < visible.from)
7010 { setTimeout(operation(cm, function () {if (counter == curCount) { extend(e) }}), 150) }
7011 } else {
7012 var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0
7013 if (outside) { setTimeout(operation(cm, function () {
7014 if (counter != curCount) { return }
7015 display.scroller.scrollTop += outside
7016 extend(e)
7017 }), 50) }
7018 }
7019 }
7020
7021 function done(e) {
7022 cm.state.selectingText = false
7023 counter = Infinity
7024 e_preventDefault(e)
7025 display.input.focus()
7026 off(document, "mousemove", move)
7027 off(document, "mouseup", up)
7028 doc.history.lastSelOrigin = null
7029 }
7030
7031 var move = operation(cm, function (e) {
7032 if (!e_button(e)) { done(e) }
7033 else { extend(e) }
7034 })
7035 var up = operation(cm, done)
7036 cm.state.selectingText = up
7037 on(document, "mousemove", move)
7038 on(document, "mouseup", up)
7039}
7040
7041
7042// Determines whether an event happened in the gutter, and fires the
7043// handlers for the corresponding event.
7044function gutterEvent(cm, e, type, prevent) {
7045 var mX, mY
7046 try { mX = e.clientX; mY = e.clientY }
7047 catch(e) { return false }
7048 if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false }
7049 if (prevent) { e_preventDefault(e) }
7050
7051 var display = cm.display
7052 var lineBox = display.lineDiv.getBoundingClientRect()
7053
7054 if (mY > lineBox.bottom || !hasHandler(cm, type)) { return e_defaultPrevented(e) }
7055 mY -= lineBox.top - display.viewOffset
7056
7057 for (var i = 0; i < cm.options.gutters.length; ++i) {
7058 var g = display.gutters.childNodes[i]
7059 if (g && g.getBoundingClientRect().right >= mX) {
7060 var line = lineAtHeight(cm.doc, mY)
7061 var gutter = cm.options.gutters[i]
7062 signal(cm, type, cm, line, gutter, e)
7063 return e_defaultPrevented(e)
7064 }
7065 }
7066}
7067
7068function clickInGutter(cm, e) {
7069 return gutterEvent(cm, e, "gutterClick", true)
7070}
7071
7072// CONTEXT MENU HANDLING
7073
7074// To make the context menu work, we need to briefly unhide the
7075// textarea (making it as unobtrusive as possible) to let the
7076// right-click take effect on it.
7077function onContextMenu(cm, e) {
7078 if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) { return }
7079 if (signalDOMEvent(cm, e, "contextmenu")) { return }
7080 cm.display.input.onContextMenu(e)
7081}
7082
7083function contextMenuInGutter(cm, e) {
7084 if (!hasHandler(cm, "gutterContextMenu")) { return false }
7085 return gutterEvent(cm, e, "gutterContextMenu", false)
7086}
7087
7088function themeChanged(cm) {
7089 cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
7090 cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-")
7091 clearCaches(cm)
7092}
7093
7094var Init = {toString: function(){return "CodeMirror.Init"}}
7095
7096var defaults = {}
7097var optionHandlers = {}
7098
7099function defineOptions(CodeMirror) {
7100 var optionHandlers = CodeMirror.optionHandlers
7101
7102 function option(name, deflt, handle, notOnInit) {
7103 CodeMirror.defaults[name] = deflt
7104 if (handle) { optionHandlers[name] =
7105 notOnInit ? function (cm, val, old) {if (old != Init) { handle(cm, val, old) }} : handle }
7106 }
7107
7108 CodeMirror.defineOption = option
7109
7110 // Passed to option handlers when there is no old value.
7111 CodeMirror.Init = Init
7112
7113 // These two are, on init, called from the constructor because they
7114 // have to be initialized before the editor can start at all.
7115 option("value", "", function (cm, val) { return cm.setValue(val); }, true)
7116 option("mode", null, function (cm, val) {
7117 cm.doc.modeOption = val
7118 loadMode(cm)
7119 }, true)
7120
7121 option("indentUnit", 2, loadMode, true)
7122 option("indentWithTabs", false)
7123 option("smartIndent", true)
7124 option("tabSize", 4, function (cm) {
7125 resetModeState(cm)
7126 clearCaches(cm)
7127 regChange(cm)
7128 }, true)
7129 option("lineSeparator", null, function (cm, val) {
7130 cm.doc.lineSep = val
7131 if (!val) { return }
7132 var newBreaks = [], lineNo = cm.doc.first
7133 cm.doc.iter(function (line) {
7134 for (var pos = 0;;) {
7135 var found = line.text.indexOf(val, pos)
7136 if (found == -1) { break }
7137 pos = found + val.length
7138 newBreaks.push(Pos(lineNo, found))
7139 }
7140 lineNo++
7141 })
7142 for (var i = newBreaks.length - 1; i >= 0; i--)
7143 { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)) }
7144 })
7145 option("specialChars", /[\u0000-\u001f\u007f\u00ad\u200b-\u200f\u2028\u2029\ufeff]/g, function (cm, val, old) {
7146 cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g")
7147 if (old != Init) { cm.refresh() }
7148 })
7149 option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function (cm) { return cm.refresh(); }, true)
7150 option("electricChars", true)
7151 option("inputStyle", mobile ? "contenteditable" : "textarea", function () {
7152 throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME
7153 }, true)
7154 option("spellcheck", false, function (cm, val) { return cm.getInputField().spellcheck = val; }, true)
7155 option("rtlMoveVisually", !windows)
7156 option("wholeLineUpdateBefore", true)
7157
7158 option("theme", "default", function (cm) {
7159 themeChanged(cm)
7160 guttersChanged(cm)
7161 }, true)
7162 option("keyMap", "default", function (cm, val, old) {
7163 var next = getKeyMap(val)
7164 var prev = old != Init && getKeyMap(old)
7165 if (prev && prev.detach) { prev.detach(cm, next) }
7166 if (next.attach) { next.attach(cm, prev || null) }
7167 })
7168 option("extraKeys", null)
7169
7170 option("lineWrapping", false, wrappingChanged, true)
7171 option("gutters", [], function (cm) {
7172 setGuttersForLineNumbers(cm.options)
7173 guttersChanged(cm)
7174 }, true)
7175 option("fixedGutter", true, function (cm, val) {
7176 cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0"
7177 cm.refresh()
7178 }, true)
7179 option("coverGutterNextToScrollbar", false, function (cm) { return updateScrollbars(cm); }, true)
7180 option("scrollbarStyle", "native", function (cm) {
7181 initScrollbars(cm)
7182 updateScrollbars(cm)
7183 cm.display.scrollbars.setScrollTop(cm.doc.scrollTop)
7184 cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft)
7185 }, true)
7186 option("lineNumbers", false, function (cm) {
7187 setGuttersForLineNumbers(cm.options)
7188 guttersChanged(cm)
7189 }, true)
7190 option("firstLineNumber", 1, guttersChanged, true)
7191 option("lineNumberFormatter", function (integer) { return integer; }, guttersChanged, true)
7192 option("showCursorWhenSelecting", false, updateSelection, true)
7193
7194 option("resetSelectionOnContextMenu", true)
7195 option("lineWiseCopyCut", true)
7196
7197 option("readOnly", false, function (cm, val) {
7198 if (val == "nocursor") {
7199 onBlur(cm)
7200 cm.display.input.blur()
7201 cm.display.disabled = true
7202 } else {
7203 cm.display.disabled = false
7204 }
7205 cm.display.input.readOnlyChanged(val)
7206 })
7207 option("disableInput", false, function (cm, val) {if (!val) { cm.display.input.reset() }}, true)
7208 option("dragDrop", true, dragDropChanged)
7209 option("allowDropFileTypes", null)
7210
7211 option("cursorBlinkRate", 530)
7212 option("cursorScrollMargin", 0)
7213 option("cursorHeight", 1, updateSelection, true)
7214 option("singleCursorHeightPerLine", true, updateSelection, true)
7215 option("workTime", 100)
7216 option("workDelay", 100)
7217 option("flattenSpans", true, resetModeState, true)
7218 option("addModeClass", false, resetModeState, true)
7219 option("pollInterval", 100)
7220 option("undoDepth", 200, function (cm, val) { return cm.doc.history.undoDepth = val; })
7221 option("historyEventDelay", 1250)
7222 option("viewportMargin", 10, function (cm) { return cm.refresh(); }, true)
7223 option("maxHighlightLength", 10000, resetModeState, true)
7224 option("moveInputWithCursor", true, function (cm, val) {
7225 if (!val) { cm.display.input.resetPosition() }
7226 })
7227
7228 option("tabindex", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || ""; })
7229 option("autofocus", null)
7230}
7231
7232function guttersChanged(cm) {
7233 updateGutters(cm)
7234 regChange(cm)
7235 setTimeout(function () { return alignHorizontally(cm); }, 20)
7236}
7237
7238function dragDropChanged(cm, value, old) {
7239 var wasOn = old && old != Init
7240 if (!value != !wasOn) {
7241 var funcs = cm.display.dragFunctions
7242 var toggle = value ? on : off
7243 toggle(cm.display.scroller, "dragstart", funcs.start)
7244 toggle(cm.display.scroller, "dragenter", funcs.enter)
7245 toggle(cm.display.scroller, "dragover", funcs.over)
7246 toggle(cm.display.scroller, "dragleave", funcs.leave)
7247 toggle(cm.display.scroller, "drop", funcs.drop)
7248 }
7249}
7250
7251function wrappingChanged(cm) {
7252 if (cm.options.lineWrapping) {
7253 addClass(cm.display.wrapper, "CodeMirror-wrap")
7254 cm.display.sizer.style.minWidth = ""
7255 cm.display.sizerWidth = null
7256 } else {
7257 rmClass(cm.display.wrapper, "CodeMirror-wrap")
7258 findMaxLine(cm)
7259 }
7260 estimateLineHeights(cm)
7261 regChange(cm)
7262 clearCaches(cm)
7263 setTimeout(function () { return updateScrollbars(cm); }, 100)
7264}
7265
7266// A CodeMirror instance represents an editor. This is the object
7267// that user code is usually dealing with.
7268
7269function CodeMirror$1(place, options) {
7270 var this$1 = this;
7271
7272 if (!(this instanceof CodeMirror$1)) { return new CodeMirror$1(place, options) }
7273
7274 this.options = options = options ? copyObj(options) : {}
7275 // Determine effective options based on given values and defaults.
7276 copyObj(defaults, options, false)
7277 setGuttersForLineNumbers(options)
7278
7279 var doc = options.value
7280 if (typeof doc == "string") { doc = new Doc(doc, options.mode, null, options.lineSeparator) }
7281 this.doc = doc
7282
7283 var input = new CodeMirror$1.inputStyles[options.inputStyle](this)
7284 var display = this.display = new Display(place, doc, input)
7285 display.wrapper.CodeMirror = this
7286 updateGutters(this)
7287 themeChanged(this)
7288 if (options.lineWrapping)
7289 { this.display.wrapper.className += " CodeMirror-wrap" }
7290 if (options.autofocus && !mobile) { display.input.focus() }
7291 initScrollbars(this)
7292
7293 this.state = {
7294 keyMaps: [], // stores maps added by addKeyMap
7295 overlays: [], // highlighting overlays, as added by addOverlay
7296 modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info
7297 overwrite: false,
7298 delayingBlurEvent: false,
7299 focused: false,
7300 suppressEdits: false, // used to disable editing during key handlers when in readOnly mode
7301 pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edits in input.poll
7302 selectingText: false,
7303 draggingText: false,
7304 highlight: new Delayed(), // stores highlight worker timeout
7305 keySeq: null, // Unfinished key sequence
7306 specialChars: null
7307 }
7308
7309 // Override magic textarea content restore that IE sometimes does
7310 // on our hidden textarea on reload
7311 if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20) }
7312
7313 registerEventHandlers(this)
7314 ensureGlobalHandlers()
7315
7316 startOperation(this)
7317 this.curOp.forceUpdate = true
7318 attachDoc(this, doc)
7319
7320 if ((options.autofocus && !mobile) || this.hasFocus())
7321 { setTimeout(bind(onFocus, this), 20) }
7322 else
7323 { onBlur(this) }
7324
7325 for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt))
7326 { optionHandlers[opt](this$1, options[opt], Init) } }
7327 maybeUpdateLineNumberWidth(this)
7328 if (options.finishInit) { options.finishInit(this) }
7329 for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this$1) }
7330 endOperation(this)
7331 // Suppress optimizelegibility in Webkit, since it breaks text
7332 // measuring on line wrapping boundaries.
7333 if (webkit && options.lineWrapping &&
7334 getComputedStyle(display.lineDiv).textRendering == "optimizelegibility")
7335 { display.lineDiv.style.textRendering = "auto" }
7336}
7337
7338// The default configuration options.
7339CodeMirror$1.defaults = defaults
7340// Functions to run when options are changed.
7341CodeMirror$1.optionHandlers = optionHandlers
7342
7343// Attach the necessary event handlers when initializing the editor
7344function registerEventHandlers(cm) {
7345 var d = cm.display
7346 on(d.scroller, "mousedown", operation(cm, onMouseDown))
7347 // Older IE's will not fire a second mousedown for a double click
7348 if (ie && ie_version < 11)
7349 { on(d.scroller, "dblclick", operation(cm, function (e) {
7350 if (signalDOMEvent(cm, e)) { return }
7351 var pos = posFromMouse(cm, e)
7352 if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) { return }
7353 e_preventDefault(e)
7354 var word = cm.findWordAt(pos)
7355 extendSelection(cm.doc, word.anchor, word.head)
7356 })) }
7357 else
7358 { on(d.scroller, "dblclick", function (e) { return signalDOMEvent(cm, e) || e_preventDefault(e); }) }
7359 // Some browsers fire contextmenu *after* opening the menu, at
7360 // which point we can't mess with it anymore. Context menu is
7361 // handled in onMouseDown for these browsers.
7362 if (!captureRightClick) { on(d.scroller, "contextmenu", function (e) { return onContextMenu(cm, e); }) }
7363
7364 // Used to suppress mouse event handling when a touch happens
7365 var touchFinished, prevTouch = {end: 0}
7366 function finishTouch() {
7367 if (d.activeTouch) {
7368 touchFinished = setTimeout(function () { return d.activeTouch = null; }, 1000)
7369 prevTouch = d.activeTouch
7370 prevTouch.end = +new Date
7371 }
7372 }
7373 function isMouseLikeTouchEvent(e) {
7374 if (e.touches.length != 1) { return false }
7375 var touch = e.touches[0]
7376 return touch.radiusX <= 1 && touch.radiusY <= 1
7377 }
7378 function farAway(touch, other) {
7379 if (other.left == null) { return true }
7380 var dx = other.left - touch.left, dy = other.top - touch.top
7381 return dx * dx + dy * dy > 20 * 20
7382 }
7383 on(d.scroller, "touchstart", function (e) {
7384 if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e)) {
7385 d.input.ensurePolled()
7386 clearTimeout(touchFinished)
7387 var now = +new Date
7388 d.activeTouch = {start: now, moved: false,
7389 prev: now - prevTouch.end <= 300 ? prevTouch : null}
7390 if (e.touches.length == 1) {
7391 d.activeTouch.left = e.touches[0].pageX
7392 d.activeTouch.top = e.touches[0].pageY
7393 }
7394 }
7395 })
7396 on(d.scroller, "touchmove", function () {
7397 if (d.activeTouch) { d.activeTouch.moved = true }
7398 })
7399 on(d.scroller, "touchend", function (e) {
7400 var touch = d.activeTouch
7401 if (touch && !eventInWidget(d, e) && touch.left != null &&
7402 !touch.moved && new Date - touch.start < 300) {
7403 var pos = cm.coordsChar(d.activeTouch, "page"), range
7404 if (!touch.prev || farAway(touch, touch.prev)) // Single tap
7405 { range = new Range(pos, pos) }
7406 else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap
7407 { range = cm.findWordAt(pos) }
7408 else // Triple tap
7409 { range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) }
7410 cm.setSelection(range.anchor, range.head)
7411 cm.focus()
7412 e_preventDefault(e)
7413 }
7414 finishTouch()
7415 })
7416 on(d.scroller, "touchcancel", finishTouch)
7417
7418 // Sync scrolling between fake scrollbars and real scrollable
7419 // area, ensure viewport is updated when scrolling.
7420 on(d.scroller, "scroll", function () {
7421 if (d.scroller.clientHeight) {
7422 setScrollTop(cm, d.scroller.scrollTop)
7423 setScrollLeft(cm, d.scroller.scrollLeft, true)
7424 signal(cm, "scroll", cm)
7425 }
7426 })
7427
7428 // Listen to wheel events in order to try and update the viewport on time.
7429 on(d.scroller, "mousewheel", function (e) { return onScrollWheel(cm, e); })
7430 on(d.scroller, "DOMMouseScroll", function (e) { return onScrollWheel(cm, e); })
7431
7432 // Prevent wrapper from ever scrolling
7433 on(d.wrapper, "scroll", function () { return d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; })
7434
7435 d.dragFunctions = {
7436 enter: function (e) {if (!signalDOMEvent(cm, e)) { e_stop(e) }},
7437 over: function (e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e) }},
7438 start: function (e) { return onDragStart(cm, e); },
7439 drop: operation(cm, onDrop),
7440 leave: function (e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm) }}
7441 }
7442
7443 var inp = d.input.getField()
7444 on(inp, "keyup", function (e) { return onKeyUp.call(cm, e); })
7445 on(inp, "keydown", operation(cm, onKeyDown))
7446 on(inp, "keypress", operation(cm, onKeyPress))
7447 on(inp, "focus", function (e) { return onFocus(cm, e); })
7448 on(inp, "blur", function (e) { return onBlur(cm, e); })
7449}
7450
7451var initHooks = []
7452CodeMirror$1.defineInitHook = function (f) { return initHooks.push(f); }
7453
7454// Indent the given line. The how parameter can be "smart",
7455// "add"/null, "subtract", or "prev". When aggressive is false
7456// (typically set to true for forced single-line indents), empty
7457// lines are not indented, and places where the mode returns Pass
7458// are left alone.
7459function indentLine(cm, n, how, aggressive) {
7460 var doc = cm.doc, state
7461 if (how == null) { how = "add" }
7462 if (how == "smart") {
7463 // Fall back to "prev" when the mode doesn't have an indentation
7464 // method.
7465 if (!doc.mode.indent) { how = "prev" }
7466 else { state = getStateBefore(cm, n) }
7467 }
7468
7469 var tabSize = cm.options.tabSize
7470 var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize)
7471 if (line.stateAfter) { line.stateAfter = null }
7472 var curSpaceString = line.text.match(/^\s*/)[0], indentation
7473 if (!aggressive && !/\S/.test(line.text)) {
7474 indentation = 0
7475 how = "not"
7476 } else if (how == "smart") {
7477 indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text)
7478 if (indentation == Pass || indentation > 150) {
7479 if (!aggressive) { return }
7480 how = "prev"
7481 }
7482 }
7483 if (how == "prev") {
7484 if (n > doc.first) { indentation = countColumn(getLine(doc, n-1).text, null, tabSize) }
7485 else { indentation = 0 }
7486 } else if (how == "add") {
7487 indentation = curSpace + cm.options.indentUnit
7488 } else if (how == "subtract") {
7489 indentation = curSpace - cm.options.indentUnit
7490 } else if (typeof how == "number") {
7491 indentation = curSpace + how
7492 }
7493 indentation = Math.max(0, indentation)
7494
7495 var indentString = "", pos = 0
7496 if (cm.options.indentWithTabs)
7497 { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t"} }
7498 if (pos < indentation) { indentString += spaceStr(indentation - pos) }
7499
7500 if (indentString != curSpaceString) {
7501 replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input")
7502 line.stateAfter = null
7503 return true
7504 } else {
7505 // Ensure that, if the cursor was in the whitespace at the start
7506 // of the line, it is moved to the end of that space.
7507 for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) {
7508 var range = doc.sel.ranges[i$1]
7509 if (range.head.line == n && range.head.ch < curSpaceString.length) {
7510 var pos$1 = Pos(n, curSpaceString.length)
7511 replaceOneSelection(doc, i$1, new Range(pos$1, pos$1))
7512 break
7513 }
7514 }
7515 }
7516}
7517
7518// This will be set to a {lineWise: bool, text: [string]} object, so
7519// that, when pasting, we know what kind of selections the copied
7520// text was made out of.
7521var lastCopied = null
7522
7523function setLastCopied(newLastCopied) {
7524 lastCopied = newLastCopied
7525}
7526
7527function applyTextInput(cm, inserted, deleted, sel, origin) {
7528 var doc = cm.doc
7529 cm.display.shift = false
7530 if (!sel) { sel = doc.sel }
7531
7532 var paste = cm.state.pasteIncoming || origin == "paste"
7533 var textLines = splitLinesAuto(inserted), multiPaste = null
7534 // When pasing N lines into N selections, insert one line per selection
7535 if (paste && sel.ranges.length > 1) {
7536 if (lastCopied && lastCopied.text.join("\n") == inserted) {
7537 if (sel.ranges.length % lastCopied.text.length == 0) {
7538 multiPaste = []
7539 for (var i = 0; i < lastCopied.text.length; i++)
7540 { multiPaste.push(doc.splitLines(lastCopied.text[i])) }
7541 }
7542 } else if (textLines.length == sel.ranges.length) {
7543 multiPaste = map(textLines, function (l) { return [l]; })
7544 }
7545 }
7546
7547 var updateInput
7548 // Normal behavior is to insert the new text into every selection
7549 for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) {
7550 var range$$1 = sel.ranges[i$1]
7551 var from = range$$1.from(), to = range$$1.to()
7552 if (range$$1.empty()) {
7553 if (deleted && deleted > 0) // Handle deletion
7554 { from = Pos(from.line, from.ch - deleted) }
7555 else if (cm.state.overwrite && !paste) // Handle overwrite
7556 { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)) }
7557 else if (lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == inserted)
7558 { from = to = Pos(from.line, 0) }
7559 }
7560 updateInput = cm.curOp.updateInput
7561 var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines,
7562 origin: origin || (paste ? "paste" : cm.state.cutIncoming ? "cut" : "+input")}
7563 makeChange(cm.doc, changeEvent)
7564 signalLater(cm, "inputRead", cm, changeEvent)
7565 }
7566 if (inserted && !paste)
7567 { triggerElectric(cm, inserted) }
7568
7569 ensureCursorVisible(cm)
7570 cm.curOp.updateInput = updateInput
7571 cm.curOp.typing = true
7572 cm.state.pasteIncoming = cm.state.cutIncoming = false
7573}
7574
7575function handlePaste(e, cm) {
7576 var pasted = e.clipboardData && e.clipboardData.getData("Text")
7577 if (pasted) {
7578 e.preventDefault()
7579 if (!cm.isReadOnly() && !cm.options.disableInput)
7580 { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, "paste"); }) }
7581 return true
7582 }
7583}
7584
7585function triggerElectric(cm, inserted) {
7586 // When an 'electric' character is inserted, immediately trigger a reindent
7587 if (!cm.options.electricChars || !cm.options.smartIndent) { return }
7588 var sel = cm.doc.sel
7589
7590 for (var i = sel.ranges.length - 1; i >= 0; i--) {
7591 var range$$1 = sel.ranges[i]
7592 if (range$$1.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range$$1.head.line)) { continue }
7593 var mode = cm.getModeAt(range$$1.head)
7594 var indented = false
7595 if (mode.electricChars) {
7596 for (var j = 0; j < mode.electricChars.length; j++)
7597 { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
7598 indented = indentLine(cm, range$$1.head.line, "smart")
7599 break
7600 } }
7601 } else if (mode.electricInput) {
7602 if (mode.electricInput.test(getLine(cm.doc, range$$1.head.line).text.slice(0, range$$1.head.ch)))
7603 { indented = indentLine(cm, range$$1.head.line, "smart") }
7604 }
7605 if (indented) { signalLater(cm, "electricInput", cm, range$$1.head.line) }
7606 }
7607}
7608
7609function copyableRanges(cm) {
7610 var text = [], ranges = []
7611 for (var i = 0; i < cm.doc.sel.ranges.length; i++) {
7612 var line = cm.doc.sel.ranges[i].head.line
7613 var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)}
7614 ranges.push(lineRange)
7615 text.push(cm.getRange(lineRange.anchor, lineRange.head))
7616 }
7617 return {text: text, ranges: ranges}
7618}
7619
7620function disableBrowserMagic(field, spellcheck) {
7621 field.setAttribute("autocorrect", "off")
7622 field.setAttribute("autocapitalize", "off")
7623 field.setAttribute("spellcheck", !!spellcheck)
7624}
7625
7626function hiddenTextarea() {
7627 var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none")
7628 var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;")
7629 // The textarea is kept positioned near the cursor to prevent the
7630 // fact that it'll be scrolled into view on input from scrolling
7631 // our fake cursor out of view. On webkit, when wrap=off, paste is
7632 // very slow. So make the area wide instead.
7633 if (webkit) { te.style.width = "1000px" }
7634 else { te.setAttribute("wrap", "off") }
7635 // If border: 0; -- iOS fails to open keyboard (issue #1287)
7636 if (ios) { te.style.border = "1px solid black" }
7637 disableBrowserMagic(te)
7638 return div
7639}
7640
7641// The publicly visible API. Note that methodOp(f) means
7642// 'wrap f in an operation, performed on its `this` parameter'.
7643
7644// This is not the complete set of editor methods. Most of the
7645// methods defined on the Doc type are also injected into
7646// CodeMirror.prototype, for backwards compatibility and
7647// convenience.
7648
7649var addEditorMethods = function(CodeMirror) {
7650 var optionHandlers = CodeMirror.optionHandlers
7651
7652 var helpers = CodeMirror.helpers = {}
7653
7654 CodeMirror.prototype = {
7655 constructor: CodeMirror,
7656 focus: function(){window.focus(); this.display.input.focus()},
7657
7658 setOption: function(option, value) {
7659 var options = this.options, old = options[option]
7660 if (options[option] == value && option != "mode") { return }
7661 options[option] = value
7662 if (optionHandlers.hasOwnProperty(option))
7663 { operation(this, optionHandlers[option])(this, value, old) }
7664 },
7665
7666 getOption: function(option) {return this.options[option]},
7667 getDoc: function() {return this.doc},
7668
7669 addKeyMap: function(map$$1, bottom) {
7670 this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map$$1))
7671 },
7672 removeKeyMap: function(map$$1) {
7673 var maps = this.state.keyMaps
7674 for (var i = 0; i < maps.length; ++i)
7675 { if (maps[i] == map$$1 || maps[i].name == map$$1) {
7676 maps.splice(i, 1)
7677 return true
7678 } }
7679 },
7680
7681 addOverlay: methodOp(function(spec, options) {
7682 var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec)
7683 if (mode.startState) { throw new Error("Overlays may not be stateful.") }
7684 insertSorted(this.state.overlays,
7685 {mode: mode, modeSpec: spec, opaque: options && options.opaque,
7686 priority: (options && options.priority) || 0},
7687 function (overlay) { return overlay.priority; })
7688 this.state.modeGen++
7689 regChange(this)
7690 }),
7691 removeOverlay: methodOp(function(spec) {
7692 var this$1 = this;
7693
7694 var overlays = this.state.overlays
7695 for (var i = 0; i < overlays.length; ++i) {
7696 var cur = overlays[i].modeSpec
7697 if (cur == spec || typeof spec == "string" && cur.name == spec) {
7698 overlays.splice(i, 1)
7699 this$1.state.modeGen++
7700 regChange(this$1)
7701 return
7702 }
7703 }
7704 }),
7705
7706 indentLine: methodOp(function(n, dir, aggressive) {
7707 if (typeof dir != "string" && typeof dir != "number") {
7708 if (dir == null) { dir = this.options.smartIndent ? "smart" : "prev" }
7709 else { dir = dir ? "add" : "subtract" }
7710 }
7711 if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive) }
7712 }),
7713 indentSelection: methodOp(function(how) {
7714 var this$1 = this;
7715
7716 var ranges = this.doc.sel.ranges, end = -1
7717 for (var i = 0; i < ranges.length; i++) {
7718 var range$$1 = ranges[i]
7719 if (!range$$1.empty()) {
7720 var from = range$$1.from(), to = range$$1.to()
7721 var start = Math.max(end, from.line)
7722 end = Math.min(this$1.lastLine(), to.line - (to.ch ? 0 : 1)) + 1
7723 for (var j = start; j < end; ++j)
7724 { indentLine(this$1, j, how) }
7725 var newRanges = this$1.doc.sel.ranges
7726 if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0)
7727 { replaceOneSelection(this$1.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll) }
7728 } else if (range$$1.head.line > end) {
7729 indentLine(this$1, range$$1.head.line, how, true)
7730 end = range$$1.head.line
7731 if (i == this$1.doc.sel.primIndex) { ensureCursorVisible(this$1) }
7732 }
7733 }
7734 }),
7735
7736 // Fetch the parser token for a given character. Useful for hacks
7737 // that want to inspect the mode state (say, for completion).
7738 getTokenAt: function(pos, precise) {
7739 return takeToken(this, pos, precise)
7740 },
7741
7742 getLineTokens: function(line, precise) {
7743 return takeToken(this, Pos(line), precise, true)
7744 },
7745
7746 getTokenTypeAt: function(pos) {
7747 pos = clipPos(this.doc, pos)
7748 var styles = getLineStyles(this, getLine(this.doc, pos.line))
7749 var before = 0, after = (styles.length - 1) / 2, ch = pos.ch
7750 var type
7751 if (ch == 0) { type = styles[2] }
7752 else { for (;;) {
7753 var mid = (before + after) >> 1
7754 if ((mid ? styles[mid * 2 - 1] : 0) >= ch) { after = mid }
7755 else if (styles[mid * 2 + 1] < ch) { before = mid + 1 }
7756 else { type = styles[mid * 2 + 2]; break }
7757 } }
7758 var cut = type ? type.indexOf("overlay ") : -1
7759 return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1)
7760 },
7761
7762 getModeAt: function(pos) {
7763 var mode = this.doc.mode
7764 if (!mode.innerMode) { return mode }
7765 return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode
7766 },
7767
7768 getHelper: function(pos, type) {
7769 return this.getHelpers(pos, type)[0]
7770 },
7771
7772 getHelpers: function(pos, type) {
7773 var this$1 = this;
7774
7775 var found = []
7776 if (!helpers.hasOwnProperty(type)) { return found }
7777 var help = helpers[type], mode = this.getModeAt(pos)
7778 if (typeof mode[type] == "string") {
7779 if (help[mode[type]]) { found.push(help[mode[type]]) }
7780 } else if (mode[type]) {
7781 for (var i = 0; i < mode[type].length; i++) {
7782 var val = help[mode[type][i]]
7783 if (val) { found.push(val) }
7784 }
7785 } else if (mode.helperType && help[mode.helperType]) {
7786 found.push(help[mode.helperType])
7787 } else if (help[mode.name]) {
7788 found.push(help[mode.name])
7789 }
7790 for (var i$1 = 0; i$1 < help._global.length; i$1++) {
7791 var cur = help._global[i$1]
7792 if (cur.pred(mode, this$1) && indexOf(found, cur.val) == -1)
7793 { found.push(cur.val) }
7794 }
7795 return found
7796 },
7797
7798 getStateAfter: function(line, precise) {
7799 var doc = this.doc
7800 line = clipLine(doc, line == null ? doc.first + doc.size - 1: line)
7801 return getStateBefore(this, line + 1, precise)
7802 },
7803
7804 cursorCoords: function(start, mode) {
7805 var pos, range$$1 = this.doc.sel.primary()
7806 if (start == null) { pos = range$$1.head }
7807 else if (typeof start == "object") { pos = clipPos(this.doc, start) }
7808 else { pos = start ? range$$1.from() : range$$1.to() }
7809 return cursorCoords(this, pos, mode || "page")
7810 },
7811
7812 charCoords: function(pos, mode) {
7813 return charCoords(this, clipPos(this.doc, pos), mode || "page")
7814 },
7815
7816 coordsChar: function(coords, mode) {
7817 coords = fromCoordSystem(this, coords, mode || "page")
7818 return coordsChar(this, coords.left, coords.top)
7819 },
7820
7821 lineAtHeight: function(height, mode) {
7822 height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top
7823 return lineAtHeight(this.doc, height + this.display.viewOffset)
7824 },
7825 heightAtLine: function(line, mode, includeWidgets) {
7826 var end = false, lineObj
7827 if (typeof line == "number") {
7828 var last = this.doc.first + this.doc.size - 1
7829 if (line < this.doc.first) { line = this.doc.first }
7830 else if (line > last) { line = last; end = true }
7831 lineObj = getLine(this.doc, line)
7832 } else {
7833 lineObj = line
7834 }
7835 return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page", includeWidgets).top +
7836 (end ? this.doc.height - heightAtLine(lineObj) : 0)
7837 },
7838
7839 defaultTextHeight: function() { return textHeight(this.display) },
7840 defaultCharWidth: function() { return charWidth(this.display) },
7841
7842 getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}},
7843
7844 addWidget: function(pos, node, scroll, vert, horiz) {
7845 var display = this.display
7846 pos = cursorCoords(this, clipPos(this.doc, pos))
7847 var top = pos.bottom, left = pos.left
7848 node.style.position = "absolute"
7849 node.setAttribute("cm-ignore-events", "true")
7850 this.display.input.setUneditable(node)
7851 display.sizer.appendChild(node)
7852 if (vert == "over") {
7853 top = pos.top
7854 } else if (vert == "above" || vert == "near") {
7855 var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
7856 hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth)
7857 // Default to positioning above (if specified and possible); otherwise default to positioning below
7858 if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
7859 { top = pos.top - node.offsetHeight }
7860 else if (pos.bottom + node.offsetHeight <= vspace)
7861 { top = pos.bottom }
7862 if (left + node.offsetWidth > hspace)
7863 { left = hspace - node.offsetWidth }
7864 }
7865 node.style.top = top + "px"
7866 node.style.left = node.style.right = ""
7867 if (horiz == "right") {
7868 left = display.sizer.clientWidth - node.offsetWidth
7869 node.style.right = "0px"
7870 } else {
7871 if (horiz == "left") { left = 0 }
7872 else if (horiz == "middle") { left = (display.sizer.clientWidth - node.offsetWidth) / 2 }
7873 node.style.left = left + "px"
7874 }
7875 if (scroll)
7876 { scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight) }
7877 },
7878
7879 triggerOnKeyDown: methodOp(onKeyDown),
7880 triggerOnKeyPress: methodOp(onKeyPress),
7881 triggerOnKeyUp: onKeyUp,
7882
7883 execCommand: function(cmd) {
7884 if (commands.hasOwnProperty(cmd))
7885 { return commands[cmd].call(null, this) }
7886 },
7887
7888 triggerElectric: methodOp(function(text) { triggerElectric(this, text) }),
7889
7890 findPosH: function(from, amount, unit, visually) {
7891 var this$1 = this;
7892
7893 var dir = 1
7894 if (amount < 0) { dir = -1; amount = -amount }
7895 var cur = clipPos(this.doc, from)
7896 for (var i = 0; i < amount; ++i) {
7897 cur = findPosH(this$1.doc, cur, dir, unit, visually)
7898 if (cur.hitSide) { break }
7899 }
7900 return cur
7901 },
7902
7903 moveH: methodOp(function(dir, unit) {
7904 var this$1 = this;
7905
7906 this.extendSelectionsBy(function (range$$1) {
7907 if (this$1.display.shift || this$1.doc.extend || range$$1.empty())
7908 { return findPosH(this$1.doc, range$$1.head, dir, unit, this$1.options.rtlMoveVisually) }
7909 else
7910 { return dir < 0 ? range$$1.from() : range$$1.to() }
7911 }, sel_move)
7912 }),
7913
7914 deleteH: methodOp(function(dir, unit) {
7915 var sel = this.doc.sel, doc = this.doc
7916 if (sel.somethingSelected())
7917 { doc.replaceSelection("", null, "+delete") }
7918 else
7919 { deleteNearSelection(this, function (range$$1) {
7920 var other = findPosH(doc, range$$1.head, dir, unit, false)
7921 return dir < 0 ? {from: other, to: range$$1.head} : {from: range$$1.head, to: other}
7922 }) }
7923 }),
7924
7925 findPosV: function(from, amount, unit, goalColumn) {
7926 var this$1 = this;
7927
7928 var dir = 1, x = goalColumn
7929 if (amount < 0) { dir = -1; amount = -amount }
7930 var cur = clipPos(this.doc, from)
7931 for (var i = 0; i < amount; ++i) {
7932 var coords = cursorCoords(this$1, cur, "div")
7933 if (x == null) { x = coords.left }
7934 else { coords.left = x }
7935 cur = findPosV(this$1, coords, dir, unit)
7936 if (cur.hitSide) { break }
7937 }
7938 return cur
7939 },
7940
7941 moveV: methodOp(function(dir, unit) {
7942 var this$1 = this;
7943
7944 var doc = this.doc, goals = []
7945 var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected()
7946 doc.extendSelectionsBy(function (range$$1) {
7947 if (collapse)
7948 { return dir < 0 ? range$$1.from() : range$$1.to() }
7949 var headPos = cursorCoords(this$1, range$$1.head, "div")
7950 if (range$$1.goalColumn != null) { headPos.left = range$$1.goalColumn }
7951 goals.push(headPos.left)
7952 var pos = findPosV(this$1, headPos, dir, unit)
7953 if (unit == "page" && range$$1 == doc.sel.primary())
7954 { addToScrollPos(this$1, null, charCoords(this$1, pos, "div").top - headPos.top) }
7955 return pos
7956 }, sel_move)
7957 if (goals.length) { for (var i = 0; i < doc.sel.ranges.length; i++)
7958 { doc.sel.ranges[i].goalColumn = goals[i] } }
7959 }),
7960
7961 // Find the word at the given position (as returned by coordsChar).
7962 findWordAt: function(pos) {
7963 var doc = this.doc, line = getLine(doc, pos.line).text
7964 var start = pos.ch, end = pos.ch
7965 if (line) {
7966 var helper = this.getHelper(pos, "wordChars")
7967 if ((pos.xRel < 0 || end == line.length) && start) { --start; } else { ++end }
7968 var startChar = line.charAt(start)
7969 var check = isWordChar(startChar, helper)
7970 ? function (ch) { return isWordChar(ch, helper); }
7971 : /\s/.test(startChar) ? function (ch) { return /\s/.test(ch); }
7972 : function (ch) { return (!/\s/.test(ch) && !isWordChar(ch)); }
7973 while (start > 0 && check(line.charAt(start - 1))) { --start }
7974 while (end < line.length && check(line.charAt(end))) { ++end }
7975 }
7976 return new Range(Pos(pos.line, start), Pos(pos.line, end))
7977 },
7978
7979 toggleOverwrite: function(value) {
7980 if (value != null && value == this.state.overwrite) { return }
7981 if (this.state.overwrite = !this.state.overwrite)
7982 { addClass(this.display.cursorDiv, "CodeMirror-overwrite") }
7983 else
7984 { rmClass(this.display.cursorDiv, "CodeMirror-overwrite") }
7985
7986 signal(this, "overwriteToggle", this, this.state.overwrite)
7987 },
7988 hasFocus: function() { return this.display.input.getField() == activeElt() },
7989 isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) },
7990
7991 scrollTo: methodOp(function(x, y) {
7992 if (x != null || y != null) { resolveScrollToPos(this) }
7993 if (x != null) { this.curOp.scrollLeft = x }
7994 if (y != null) { this.curOp.scrollTop = y }
7995 }),
7996 getScrollInfo: function() {
7997 var scroller = this.display.scroller
7998 return {left: scroller.scrollLeft, top: scroller.scrollTop,
7999 height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight,
8000 width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth,
8001 clientHeight: displayHeight(this), clientWidth: displayWidth(this)}
8002 },
8003
8004 scrollIntoView: methodOp(function(range$$1, margin) {
8005 if (range$$1 == null) {
8006 range$$1 = {from: this.doc.sel.primary().head, to: null}
8007 if (margin == null) { margin = this.options.cursorScrollMargin }
8008 } else if (typeof range$$1 == "number") {
8009 range$$1 = {from: Pos(range$$1, 0), to: null}
8010 } else if (range$$1.from == null) {
8011 range$$1 = {from: range$$1, to: null}
8012 }
8013 if (!range$$1.to) { range$$1.to = range$$1.from }
8014 range$$1.margin = margin || 0
8015
8016 if (range$$1.from.line != null) {
8017 resolveScrollToPos(this)
8018 this.curOp.scrollToPos = range$$1
8019 } else {
8020 var sPos = calculateScrollPos(this, Math.min(range$$1.from.left, range$$1.to.left),
8021 Math.min(range$$1.from.top, range$$1.to.top) - range$$1.margin,
8022 Math.max(range$$1.from.right, range$$1.to.right),
8023 Math.max(range$$1.from.bottom, range$$1.to.bottom) + range$$1.margin)
8024 this.scrollTo(sPos.scrollLeft, sPos.scrollTop)
8025 }
8026 }),
8027
8028 setSize: methodOp(function(width, height) {
8029 var this$1 = this;
8030
8031 var interpret = function (val) { return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; }
8032 if (width != null) { this.display.wrapper.style.width = interpret(width) }
8033 if (height != null) { this.display.wrapper.style.height = interpret(height) }
8034 if (this.options.lineWrapping) { clearLineMeasurementCache(this) }
8035 var lineNo$$1 = this.display.viewFrom
8036 this.doc.iter(lineNo$$1, this.display.viewTo, function (line) {
8037 if (line.widgets) { for (var i = 0; i < line.widgets.length; i++)
8038 { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo$$1, "widget"); break } } }
8039 ++lineNo$$1
8040 })
8041 this.curOp.forceUpdate = true
8042 signal(this, "refresh", this)
8043 }),
8044
8045 operation: function(f){return runInOp(this, f)},
8046
8047 refresh: methodOp(function() {
8048 var oldHeight = this.display.cachedTextHeight
8049 regChange(this)
8050 this.curOp.forceUpdate = true
8051 clearCaches(this)
8052 this.scrollTo(this.doc.scrollLeft, this.doc.scrollTop)
8053 updateGutterSpace(this)
8054 if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5)
8055 { estimateLineHeights(this) }
8056 signal(this, "refresh", this)
8057 }),
8058
8059 swapDoc: methodOp(function(doc) {
8060 var old = this.doc
8061 old.cm = null
8062 attachDoc(this, doc)
8063 clearCaches(this)
8064 this.display.input.reset()
8065 this.scrollTo(doc.scrollLeft, doc.scrollTop)
8066 this.curOp.forceScroll = true
8067 signalLater(this, "swapDoc", this, old)
8068 return old
8069 }),
8070
8071 getInputField: function(){return this.display.input.getField()},
8072 getWrapperElement: function(){return this.display.wrapper},
8073 getScrollerElement: function(){return this.display.scroller},
8074 getGutterElement: function(){return this.display.gutters}
8075 }
8076 eventMixin(CodeMirror)
8077
8078 CodeMirror.registerHelper = function(type, name, value) {
8079 if (!helpers.hasOwnProperty(type)) { helpers[type] = CodeMirror[type] = {_global: []} }
8080 helpers[type][name] = value
8081 }
8082 CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {
8083 CodeMirror.registerHelper(type, name, value)
8084 helpers[type]._global.push({pred: predicate, val: value})
8085 }
8086}
8087
8088// Used for horizontal relative motion. Dir is -1 or 1 (left or
8089// right), unit can be "char", "column" (like char, but doesn't
8090// cross line boundaries), "word" (across next word), or "group" (to
8091// the start of next group of word or non-word-non-whitespace
8092// chars). The visually param controls whether, in right-to-left
8093// text, direction 1 means to move towards the next index in the
8094// string, or towards the character to the right of the current
8095// position. The resulting position will have a hitSide=true
8096// property if it reached the end of the document.
8097function findPosH(doc, pos, dir, unit, visually) {
8098 var line = pos.line, ch = pos.ch, origDir = dir
8099 var lineObj = getLine(doc, line)
8100 function findNextLine() {
8101 var l = line + dir
8102 if (l < doc.first || l >= doc.first + doc.size) { return false }
8103 line = l
8104 return lineObj = getLine(doc, l)
8105 }
8106 function moveOnce(boundToLine) {
8107 var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, true)
8108 if (next == null) {
8109 if (!boundToLine && findNextLine()) {
8110 if (visually) { ch = (dir < 0 ? lineRight : lineLeft)(lineObj) }
8111 else { ch = dir < 0 ? lineObj.text.length : 0 }
8112 } else { return false }
8113 } else { ch = next }
8114 return true
8115 }
8116
8117 if (unit == "char") {
8118 moveOnce()
8119 } else if (unit == "column") {
8120 moveOnce(true)
8121 } else if (unit == "word" || unit == "group") {
8122 var sawType = null, group = unit == "group"
8123 var helper = doc.cm && doc.cm.getHelper(pos, "wordChars")
8124 for (var first = true;; first = false) {
8125 if (dir < 0 && !moveOnce(!first)) { break }
8126 var cur = lineObj.text.charAt(ch) || "\n"
8127 var type = isWordChar(cur, helper) ? "w"
8128 : group && cur == "\n" ? "n"
8129 : !group || /\s/.test(cur) ? null
8130 : "p"
8131 if (group && !first && !type) { type = "s" }
8132 if (sawType && sawType != type) {
8133 if (dir < 0) {dir = 1; moveOnce()}
8134 break
8135 }
8136
8137 if (type) { sawType = type }
8138 if (dir > 0 && !moveOnce(!first)) { break }
8139 }
8140 }
8141 var result = skipAtomic(doc, Pos(line, ch), pos, origDir, true)
8142 if (!cmp(pos, result)) { result.hitSide = true }
8143 return result
8144}
8145
8146// For relative vertical movement. Dir may be -1 or 1. Unit can be
8147// "page" or "line". The resulting position will have a hitSide=true
8148// property if it reached the end of the document.
8149function findPosV(cm, pos, dir, unit) {
8150 var doc = cm.doc, x = pos.left, y
8151 if (unit == "page") {
8152 var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight)
8153 var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3)
8154 y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount
8155
8156 } else if (unit == "line") {
8157 y = dir > 0 ? pos.bottom + 3 : pos.top - 3
8158 }
8159 var target
8160 for (;;) {
8161 target = coordsChar(cm, x, y)
8162 if (!target.outside) { break }
8163 if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break }
8164 y += dir * 5
8165 }
8166 return target
8167}
8168
8169// CONTENTEDITABLE INPUT STYLE
8170
8171function ContentEditableInput(cm) {
8172 this.cm = cm
8173 this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null
8174 this.polling = new Delayed()
8175 this.composing = null
8176 this.gracePeriod = false
8177 this.readDOMTimeout = null
8178}
8179
8180ContentEditableInput.prototype = copyObj({
8181 init: function(display) {
8182 var this$1 = this;
8183
8184 var input = this, cm = input.cm
8185 var div = input.div = display.lineDiv
8186 disableBrowserMagic(div, cm.options.spellcheck)
8187
8188 on(div, "paste", function (e) {
8189 if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
8190 // IE doesn't fire input events, so we schedule a read for the pasted content in this way
8191 if (ie_version <= 11) { setTimeout(operation(cm, function () {
8192 if (!input.pollContent()) { regChange(cm) }
8193 }), 20) }
8194 })
8195
8196 on(div, "compositionstart", function (e) {
8197 this$1.composing = {data: e.data}
8198 })
8199 on(div, "compositionupdate", function (e) {
8200 if (!this$1.composing) { this$1.composing = {data: e.data} }
8201 })
8202 on(div, "compositionend", function (e) {
8203 if (this$1.composing) {
8204 if (e.data != this$1.composing.data) { this$1.readFromDOMSoon() }
8205 this$1.composing = null
8206 }
8207 })
8208
8209 on(div, "touchstart", function () { return input.forceCompositionEnd(); })
8210
8211 on(div, "input", function () {
8212 if (!this$1.composing) { this$1.readFromDOMSoon() }
8213 })
8214
8215 function onCopyCut(e) {
8216 if (signalDOMEvent(cm, e)) { return }
8217 if (cm.somethingSelected()) {
8218 setLastCopied({lineWise: false, text: cm.getSelections()})
8219 if (e.type == "cut") { cm.replaceSelection("", null, "cut") }
8220 } else if (!cm.options.lineWiseCopyCut) {
8221 return
8222 } else {
8223 var ranges = copyableRanges(cm)
8224 setLastCopied({lineWise: true, text: ranges.text})
8225 if (e.type == "cut") {
8226 cm.operation(function () {
8227 cm.setSelections(ranges.ranges, 0, sel_dontScroll)
8228 cm.replaceSelection("", null, "cut")
8229 })
8230 }
8231 }
8232 if (e.clipboardData) {
8233 e.clipboardData.clearData()
8234 var content = lastCopied.text.join("\n")
8235 // iOS exposes the clipboard API, but seems to discard content inserted into it
8236 e.clipboardData.setData("Text", content)
8237 if (e.clipboardData.getData("Text") == content) {
8238 e.preventDefault()
8239 return
8240 }
8241 }
8242 // Old-fashioned briefly-focus-a-textarea hack
8243 var kludge = hiddenTextarea(), te = kludge.firstChild
8244 cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild)
8245 te.value = lastCopied.text.join("\n")
8246 var hadFocus = document.activeElement
8247 selectInput(te)
8248 setTimeout(function () {
8249 cm.display.lineSpace.removeChild(kludge)
8250 hadFocus.focus()
8251 if (hadFocus == div) { input.showPrimarySelection() }
8252 }, 50)
8253 }
8254 on(div, "copy", onCopyCut)
8255 on(div, "cut", onCopyCut)
8256 },
8257
8258 prepareSelection: function() {
8259 var result = prepareSelection(this.cm, false)
8260 result.focus = this.cm.state.focused
8261 return result
8262 },
8263
8264 showSelection: function(info, takeFocus) {
8265 if (!info || !this.cm.display.view.length) { return }
8266 if (info.focus || takeFocus) { this.showPrimarySelection() }
8267 this.showMultipleSelections(info)
8268 },
8269
8270 showPrimarySelection: function() {
8271 var sel = window.getSelection(), prim = this.cm.doc.sel.primary()
8272 var curAnchor = domToPos(this.cm, sel.anchorNode, sel.anchorOffset)
8273 var curFocus = domToPos(this.cm, sel.focusNode, sel.focusOffset)
8274 if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad &&
8275 cmp(minPos(curAnchor, curFocus), prim.from()) == 0 &&
8276 cmp(maxPos(curAnchor, curFocus), prim.to()) == 0)
8277 { return }
8278
8279 var start = posToDOM(this.cm, prim.from())
8280 var end = posToDOM(this.cm, prim.to())
8281 if (!start && !end) { return }
8282
8283 var view = this.cm.display.view
8284 var old = sel.rangeCount && sel.getRangeAt(0)
8285 if (!start) {
8286 start = {node: view[0].measure.map[2], offset: 0}
8287 } else if (!end) { // FIXME dangerously hacky
8288 var measure = view[view.length - 1].measure
8289 var map$$1 = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map
8290 end = {node: map$$1[map$$1.length - 1], offset: map$$1[map$$1.length - 2] - map$$1[map$$1.length - 3]}
8291 }
8292
8293 var rng
8294 try { rng = range(start.node, start.offset, end.offset, end.node) }
8295 catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible
8296 if (rng) {
8297 if (!gecko && this.cm.state.focused) {
8298 sel.collapse(start.node, start.offset)
8299 if (!rng.collapsed) {
8300 sel.removeAllRanges()
8301 sel.addRange(rng)
8302 }
8303 } else {
8304 sel.removeAllRanges()
8305 sel.addRange(rng)
8306 }
8307 if (old && sel.anchorNode == null) { sel.addRange(old) }
8308 else if (gecko) { this.startGracePeriod() }
8309 }
8310 this.rememberSelection()
8311 },
8312
8313 startGracePeriod: function() {
8314 var this$1 = this;
8315
8316 clearTimeout(this.gracePeriod)
8317 this.gracePeriod = setTimeout(function () {
8318 this$1.gracePeriod = false
8319 if (this$1.selectionChanged())
8320 { this$1.cm.operation(function () { return this$1.cm.curOp.selectionChanged = true; }) }
8321 }, 20)
8322 },
8323
8324 showMultipleSelections: function(info) {
8325 removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors)
8326 removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection)
8327 },
8328
8329 rememberSelection: function() {
8330 var sel = window.getSelection()
8331 this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset
8332 this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset
8333 },
8334
8335 selectionInEditor: function() {
8336 var sel = window.getSelection()
8337 if (!sel.rangeCount) { return false }
8338 var node = sel.getRangeAt(0).commonAncestorContainer
8339 return contains(this.div, node)
8340 },
8341
8342 focus: function() {
8343 if (this.cm.options.readOnly != "nocursor") {
8344 if (!this.selectionInEditor())
8345 { this.showSelection(this.prepareSelection(), true) }
8346 this.div.focus()
8347 }
8348 },
8349 blur: function() { this.div.blur() },
8350 getField: function() { return this.div },
8351
8352 supportsTouch: function() { return true },
8353
8354 receivedFocus: function() {
8355 var input = this
8356 if (this.selectionInEditor())
8357 { this.pollSelection() }
8358 else
8359 { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }) }
8360
8361 function poll() {
8362 if (input.cm.state.focused) {
8363 input.pollSelection()
8364 input.polling.set(input.cm.options.pollInterval, poll)
8365 }
8366 }
8367 this.polling.set(this.cm.options.pollInterval, poll)
8368 },
8369
8370 selectionChanged: function() {
8371 var sel = window.getSelection()
8372 return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||
8373 sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset
8374 },
8375
8376 pollSelection: function() {
8377 if (!this.composing && this.readDOMTimeout == null && !this.gracePeriod && this.selectionChanged()) {
8378 var sel = window.getSelection(), cm = this.cm
8379 this.rememberSelection()
8380 var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset)
8381 var head = domToPos(cm, sel.focusNode, sel.focusOffset)
8382 if (anchor && head) { runInOp(cm, function () {
8383 setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll)
8384 if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true }
8385 }) }
8386 }
8387 },
8388
8389 pollContent: function() {
8390 if (this.readDOMTimeout != null) {
8391 clearTimeout(this.readDOMTimeout)
8392 this.readDOMTimeout = null
8393 }
8394
8395 var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary()
8396 var from = sel.from(), to = sel.to()
8397 if (from.ch == 0 && from.line > cm.firstLine())
8398 { from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length) }
8399 if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine())
8400 { to = Pos(to.line + 1, 0) }
8401 if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return false }
8402
8403 var fromIndex, fromLine, fromNode
8404 if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) {
8405 fromLine = lineNo(display.view[0].line)
8406 fromNode = display.view[0].node
8407 } else {
8408 fromLine = lineNo(display.view[fromIndex].line)
8409 fromNode = display.view[fromIndex - 1].node.nextSibling
8410 }
8411 var toIndex = findViewIndex(cm, to.line)
8412 var toLine, toNode
8413 if (toIndex == display.view.length - 1) {
8414 toLine = display.viewTo - 1
8415 toNode = display.lineDiv.lastChild
8416 } else {
8417 toLine = lineNo(display.view[toIndex + 1].line) - 1
8418 toNode = display.view[toIndex + 1].node.previousSibling
8419 }
8420
8421 if (!fromNode) { return false }
8422 var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine))
8423 var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length))
8424 while (newText.length > 1 && oldText.length > 1) {
8425 if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine-- }
8426 else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++ }
8427 else { break }
8428 }
8429
8430 var cutFront = 0, cutEnd = 0
8431 var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length)
8432 while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront))
8433 { ++cutFront }
8434 var newBot = lst(newText), oldBot = lst(oldText)
8435 var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0),
8436 oldBot.length - (oldText.length == 1 ? cutFront : 0))
8437 while (cutEnd < maxCutEnd &&
8438 newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1))
8439 { ++cutEnd }
8440
8441 newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, "")
8442 newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, "")
8443
8444 var chFrom = Pos(fromLine, cutFront)
8445 var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0)
8446 if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {
8447 replaceRange(cm.doc, newText, chFrom, chTo, "+input")
8448 return true
8449 }
8450 },
8451
8452 ensurePolled: function() {
8453 this.forceCompositionEnd()
8454 },
8455 reset: function() {
8456 this.forceCompositionEnd()
8457 },
8458 forceCompositionEnd: function() {
8459 if (!this.composing) { return }
8460 this.composing = null
8461 if (!this.pollContent()) { regChange(this.cm) }
8462 this.div.blur()
8463 this.div.focus()
8464 },
8465 readFromDOMSoon: function() {
8466 var this$1 = this;
8467
8468 if (this.readDOMTimeout != null) { return }
8469 this.readDOMTimeout = setTimeout(function () {
8470 this$1.readDOMTimeout = null
8471 if (this$1.composing) { return }
8472 if (this$1.cm.isReadOnly() || !this$1.pollContent())
8473 { runInOp(this$1.cm, function () { return regChange(this$1.cm); }) }
8474 }, 80)
8475 },
8476
8477 setUneditable: function(node) {
8478 node.contentEditable = "false"
8479 },
8480
8481 onKeyPress: function(e) {
8482 e.preventDefault()
8483 if (!this.cm.isReadOnly())
8484 { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0) }
8485 },
8486
8487 readOnlyChanged: function(val) {
8488 this.div.contentEditable = String(val != "nocursor")
8489 },
8490
8491 onContextMenu: nothing,
8492 resetPosition: nothing,
8493
8494 needsContentAttribute: true
8495 }, ContentEditableInput.prototype)
8496
8497function posToDOM(cm, pos) {
8498 var view = findViewForLine(cm, pos.line)
8499 if (!view || view.hidden) { return null }
8500 var line = getLine(cm.doc, pos.line)
8501 var info = mapFromLineView(view, line, pos.line)
8502
8503 var order = getOrder(line), side = "left"
8504 if (order) {
8505 var partPos = getBidiPartAt(order, pos.ch)
8506 side = partPos % 2 ? "right" : "left"
8507 }
8508 var result = nodeAndOffsetInLineMap(info.map, pos.ch, side)
8509 result.offset = result.collapse == "right" ? result.end : result.start
8510 return result
8511}
8512
8513function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos }
8514
8515function domTextBetween(cm, from, to, fromLine, toLine) {
8516 var text = "", closing = false, lineSep = cm.doc.lineSeparator()
8517 function recognizeMarker(id) { return function (marker) { return marker.id == id; } }
8518 function walk(node) {
8519 if (node.nodeType == 1) {
8520 var cmText = node.getAttribute("cm-text")
8521 if (cmText != null) {
8522 if (cmText == "") { text += node.textContent.replace(/\u200b/g, "") }
8523 else { text += cmText }
8524 return
8525 }
8526 var markerID = node.getAttribute("cm-marker"), range$$1
8527 if (markerID) {
8528 var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID))
8529 if (found.length && (range$$1 = found[0].find()))
8530 { text += getBetween(cm.doc, range$$1.from, range$$1.to).join(lineSep) }
8531 return
8532 }
8533 if (node.getAttribute("contenteditable") == "false") { return }
8534 for (var i = 0; i < node.childNodes.length; i++)
8535 { walk(node.childNodes[i]) }
8536 if (/^(pre|div|p)$/i.test(node.nodeName))
8537 { closing = true }
8538 } else if (node.nodeType == 3) {
8539 var val = node.nodeValue
8540 if (!val) { return }
8541 if (closing) {
8542 text += lineSep
8543 closing = false
8544 }
8545 text += val
8546 }
8547 }
8548 for (;;) {
8549 walk(from)
8550 if (from == to) { break }
8551 from = from.nextSibling
8552 }
8553 return text
8554}
8555
8556function domToPos(cm, node, offset) {
8557 var lineNode
8558 if (node == cm.display.lineDiv) {
8559 lineNode = cm.display.lineDiv.childNodes[offset]
8560 if (!lineNode) { return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) }
8561 node = null; offset = 0
8562 } else {
8563 for (lineNode = node;; lineNode = lineNode.parentNode) {
8564 if (!lineNode || lineNode == cm.display.lineDiv) { return null }
8565 if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) { break }
8566 }
8567 }
8568 for (var i = 0; i < cm.display.view.length; i++) {
8569 var lineView = cm.display.view[i]
8570 if (lineView.node == lineNode)
8571 { return locateNodeInLineView(lineView, node, offset) }
8572 }
8573}
8574
8575function locateNodeInLineView(lineView, node, offset) {
8576 var wrapper = lineView.text.firstChild, bad = false
8577 if (!node || !contains(wrapper, node)) { return badPos(Pos(lineNo(lineView.line), 0), true) }
8578 if (node == wrapper) {
8579 bad = true
8580 node = wrapper.childNodes[offset]
8581 offset = 0
8582 if (!node) {
8583 var line = lineView.rest ? lst(lineView.rest) : lineView.line
8584 return badPos(Pos(lineNo(line), line.text.length), bad)
8585 }
8586 }
8587
8588 var textNode = node.nodeType == 3 ? node : null, topNode = node
8589 if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {
8590 textNode = node.firstChild
8591 if (offset) { offset = textNode.nodeValue.length }
8592 }
8593 while (topNode.parentNode != wrapper) { topNode = topNode.parentNode }
8594 var measure = lineView.measure, maps = measure.maps
8595
8596 function find(textNode, topNode, offset) {
8597 for (var i = -1; i < (maps ? maps.length : 0); i++) {
8598 var map$$1 = i < 0 ? measure.map : maps[i]
8599 for (var j = 0; j < map$$1.length; j += 3) {
8600 var curNode = map$$1[j + 2]
8601 if (curNode == textNode || curNode == topNode) {
8602 var line = lineNo(i < 0 ? lineView.line : lineView.rest[i])
8603 var ch = map$$1[j] + offset
8604 if (offset < 0 || curNode != textNode) { ch = map$$1[j + (offset ? 1 : 0)] }
8605 return Pos(line, ch)
8606 }
8607 }
8608 }
8609 }
8610 var found = find(textNode, topNode, offset)
8611 if (found) { return badPos(found, bad) }
8612
8613 // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems
8614 for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) {
8615 found = find(after, after.firstChild, 0)
8616 if (found)
8617 { return badPos(Pos(found.line, found.ch - dist), bad) }
8618 else
8619 { dist += after.textContent.length }
8620 }
8621 for (var before = topNode.previousSibling, dist$1 = offset; before; before = before.previousSibling) {
8622 found = find(before, before.firstChild, -1)
8623 if (found)
8624 { return badPos(Pos(found.line, found.ch + dist$1), bad) }
8625 else
8626 { dist$1 += before.textContent.length }
8627 }
8628}
8629
8630// TEXTAREA INPUT STYLE
8631
8632function TextareaInput(cm) {
8633 this.cm = cm
8634 // See input.poll and input.reset
8635 this.prevInput = ""
8636
8637 // Flag that indicates whether we expect input to appear real soon
8638 // now (after some event like 'keypress' or 'input') and are
8639 // polling intensively.
8640 this.pollingFast = false
8641 // Self-resetting timeout for the poller
8642 this.polling = new Delayed()
8643 // Tracks when input.reset has punted to just putting a short
8644 // string into the textarea instead of the full selection.
8645 this.inaccurateSelection = false
8646 // Used to work around IE issue with selection being forgotten when focus moves away from textarea
8647 this.hasSelection = false
8648 this.composing = null
8649}
8650
8651TextareaInput.prototype = copyObj({
8652 init: function(display) {
8653 var this$1 = this;
8654
8655 var input = this, cm = this.cm
8656
8657 // Wraps and hides input textarea
8658 var div = this.wrapper = hiddenTextarea()
8659 // The semihidden textarea that is focused when the editor is
8660 // focused, and receives input.
8661 var te = this.textarea = div.firstChild
8662 display.wrapper.insertBefore(div, display.wrapper.firstChild)
8663
8664 // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)
8665 if (ios) { te.style.width = "0px" }
8666
8667 on(te, "input", function () {
8668 if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = null }
8669 input.poll()
8670 })
8671
8672 on(te, "paste", function (e) {
8673 if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
8674
8675 cm.state.pasteIncoming = true
8676 input.fastPoll()
8677 })
8678
8679 function prepareCopyCut(e) {
8680 if (signalDOMEvent(cm, e)) { return }
8681 if (cm.somethingSelected()) {
8682 setLastCopied({lineWise: false, text: cm.getSelections()})
8683 if (input.inaccurateSelection) {
8684 input.prevInput = ""
8685 input.inaccurateSelection = false
8686 te.value = lastCopied.text.join("\n")
8687 selectInput(te)
8688 }
8689 } else if (!cm.options.lineWiseCopyCut) {
8690 return
8691 } else {
8692 var ranges = copyableRanges(cm)
8693 setLastCopied({lineWise: true, text: ranges.text})
8694 if (e.type == "cut") {
8695 cm.setSelections(ranges.ranges, null, sel_dontScroll)
8696 } else {
8697 input.prevInput = ""
8698 te.value = ranges.text.join("\n")
8699 selectInput(te)
8700 }
8701 }
8702 if (e.type == "cut") { cm.state.cutIncoming = true }
8703 }
8704 on(te, "cut", prepareCopyCut)
8705 on(te, "copy", prepareCopyCut)
8706
8707 on(display.scroller, "paste", function (e) {
8708 if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return }
8709 cm.state.pasteIncoming = true
8710 input.focus()
8711 })
8712
8713 // Prevent normal selection in the editor (we handle our own)
8714 on(display.lineSpace, "selectstart", function (e) {
8715 if (!eventInWidget(display, e)) { e_preventDefault(e) }
8716 })
8717
8718 on(te, "compositionstart", function () {
8719 var start = cm.getCursor("from")
8720 if (input.composing) { input.composing.range.clear() }
8721 input.composing = {
8722 start: start,
8723 range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"})
8724 }
8725 })
8726 on(te, "compositionend", function () {
8727 if (input.composing) {
8728 input.poll()
8729 input.composing.range.clear()
8730 input.composing = null
8731 }
8732 })
8733 },
8734
8735 prepareSelection: function() {
8736 // Redraw the selection and/or cursor
8737 var cm = this.cm, display = cm.display, doc = cm.doc
8738 var result = prepareSelection(cm)
8739
8740 // Move the hidden textarea near the cursor to prevent scrolling artifacts
8741 if (cm.options.moveInputWithCursor) {
8742 var headPos = cursorCoords(cm, doc.sel.primary().head, "div")
8743 var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect()
8744 result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
8745 headPos.top + lineOff.top - wrapOff.top))
8746 result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
8747 headPos.left + lineOff.left - wrapOff.left))
8748 }
8749
8750 return result
8751 },
8752
8753 showSelection: function(drawn) {
8754 var cm = this.cm, display = cm.display
8755 removeChildrenAndAdd(display.cursorDiv, drawn.cursors)
8756 removeChildrenAndAdd(display.selectionDiv, drawn.selection)
8757 if (drawn.teTop != null) {
8758 this.wrapper.style.top = drawn.teTop + "px"
8759 this.wrapper.style.left = drawn.teLeft + "px"
8760 }
8761 },
8762
8763 // Reset the input to correspond to the selection (or to be empty,
8764 // when not typing and nothing is selected)
8765 reset: function(typing) {
8766 if (this.contextMenuPending) { return }
8767 var minimal, selected, cm = this.cm, doc = cm.doc
8768 if (cm.somethingSelected()) {
8769 this.prevInput = ""
8770 var range$$1 = doc.sel.primary()
8771 minimal = hasCopyEvent &&
8772 (range$$1.to().line - range$$1.from().line > 100 || (selected = cm.getSelection()).length > 1000)
8773 var content = minimal ? "-" : selected || cm.getSelection()
8774 this.textarea.value = content
8775 if (cm.state.focused) { selectInput(this.textarea) }
8776 if (ie && ie_version >= 9) { this.hasSelection = content }
8777 } else if (!typing) {
8778 this.prevInput = this.textarea.value = ""
8779 if (ie && ie_version >= 9) { this.hasSelection = null }
8780 }
8781 this.inaccurateSelection = minimal
8782 },
8783
8784 getField: function() { return this.textarea },
8785
8786 supportsTouch: function() { return false },
8787
8788 focus: function() {
8789 if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) {
8790 try { this.textarea.focus() }
8791 catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM
8792 }
8793 },
8794
8795 blur: function() { this.textarea.blur() },
8796
8797 resetPosition: function() {
8798 this.wrapper.style.top = this.wrapper.style.left = 0
8799 },
8800
8801 receivedFocus: function() { this.slowPoll() },
8802
8803 // Poll for input changes, using the normal rate of polling. This
8804 // runs as long as the editor is focused.
8805 slowPoll: function() {
8806 var this$1 = this;
8807
8808 if (this.pollingFast) { return }
8809 this.polling.set(this.cm.options.pollInterval, function () {
8810 this$1.poll()
8811 if (this$1.cm.state.focused) { this$1.slowPoll() }
8812 })
8813 },
8814
8815 // When an event has just come in that is likely to add or change
8816 // something in the input textarea, we poll faster, to ensure that
8817 // the change appears on the screen quickly.
8818 fastPoll: function() {
8819 var missed = false, input = this
8820 input.pollingFast = true
8821 function p() {
8822 var changed = input.poll()
8823 if (!changed && !missed) {missed = true; input.polling.set(60, p)}
8824 else {input.pollingFast = false; input.slowPoll()}
8825 }
8826 input.polling.set(20, p)
8827 },
8828
8829 // Read input from the textarea, and update the document to match.
8830 // When something is selected, it is present in the textarea, and
8831 // selected (unless it is huge, in which case a placeholder is
8832 // used). When nothing is selected, the cursor sits after previously
8833 // seen text (can be empty), which is stored in prevInput (we must
8834 // not reset the textarea when typing, because that breaks IME).
8835 poll: function() {
8836 var this$1 = this;
8837
8838 var cm = this.cm, input = this.textarea, prevInput = this.prevInput
8839 // Since this is called a *lot*, try to bail out as cheaply as
8840 // possible when it is clear that nothing happened. hasSelection
8841 // will be the case when there is a lot of text in the textarea,
8842 // in which case reading its value would be expensive.
8843 if (this.contextMenuPending || !cm.state.focused ||
8844 (hasSelection(input) && !prevInput && !this.composing) ||
8845 cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq)
8846 { return false }
8847
8848 var text = input.value
8849 // If nothing changed, bail.
8850 if (text == prevInput && !cm.somethingSelected()) { return false }
8851 // Work around nonsensical selection resetting in IE9/10, and
8852 // inexplicable appearance of private area unicode characters on
8853 // some key combos in Mac (#2689).
8854 if (ie && ie_version >= 9 && this.hasSelection === text ||
8855 mac && /[\uf700-\uf7ff]/.test(text)) {
8856 cm.display.input.reset()
8857 return false
8858 }
8859
8860 if (cm.doc.sel == cm.display.selForContextMenu) {
8861 var first = text.charCodeAt(0)
8862 if (first == 0x200b && !prevInput) { prevInput = "\u200b" }
8863 if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") }
8864 }
8865 // Find the part of the input that is actually new
8866 var same = 0, l = Math.min(prevInput.length, text.length)
8867 while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same }
8868
8869 runInOp(cm, function () {
8870 applyTextInput(cm, text.slice(same), prevInput.length - same,
8871 null, this$1.composing ? "*compose" : null)
8872
8873 // Don't leave long text in the textarea, since it makes further polling slow
8874 if (text.length > 1000 || text.indexOf("\n") > -1) { input.value = this$1.prevInput = "" }
8875 else { this$1.prevInput = text }
8876
8877 if (this$1.composing) {
8878 this$1.composing.range.clear()
8879 this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor("to"),
8880 {className: "CodeMirror-composing"})
8881 }
8882 })
8883 return true
8884 },
8885
8886 ensurePolled: function() {
8887 if (this.pollingFast && this.poll()) { this.pollingFast = false }
8888 },
8889
8890 onKeyPress: function() {
8891 if (ie && ie_version >= 9) { this.hasSelection = null }
8892 this.fastPoll()
8893 },
8894
8895 onContextMenu: function(e) {
8896 var input = this, cm = input.cm, display = cm.display, te = input.textarea
8897 var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop
8898 if (!pos || presto) { return } // Opera is difficult.
8899
8900 // Reset the current text selection only if the click is done outside of the selection
8901 // and 'resetSelectionOnContextMenu' option is true.
8902 var reset = cm.options.resetSelectionOnContextMenu
8903 if (reset && cm.doc.sel.contains(pos) == -1)
8904 { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll) }
8905
8906 var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText
8907 input.wrapper.style.cssText = "position: absolute"
8908 var wrapperBox = input.wrapper.getBoundingClientRect()
8909 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);"
8910 var oldScrollY
8911 if (webkit) { oldScrollY = window.scrollY } // Work around Chrome issue (#2712)
8912 display.input.focus()
8913 if (webkit) { window.scrollTo(null, oldScrollY) }
8914 display.input.reset()
8915 // Adds "Select all" to context menu in FF
8916 if (!cm.somethingSelected()) { te.value = input.prevInput = " " }
8917 input.contextMenuPending = true
8918 display.selForContextMenu = cm.doc.sel
8919 clearTimeout(display.detectingSelectAll)
8920
8921 // Select-all will be greyed out if there's nothing to select, so
8922 // this adds a zero-width space so that we can later check whether
8923 // it got selected.
8924 function prepareSelectAllHack() {
8925 if (te.selectionStart != null) {
8926 var selected = cm.somethingSelected()
8927 var extval = "\u200b" + (selected ? te.value : "")
8928 te.value = "\u21da" // Used to catch context-menu undo
8929 te.value = extval
8930 input.prevInput = selected ? "" : "\u200b"
8931 te.selectionStart = 1; te.selectionEnd = extval.length
8932 // Re-set this, in case some other handler touched the
8933 // selection in the meantime.
8934 display.selForContextMenu = cm.doc.sel
8935 }
8936 }
8937 function rehide() {
8938 input.contextMenuPending = false
8939 input.wrapper.style.cssText = oldWrapperCSS
8940 te.style.cssText = oldCSS
8941 if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos) }
8942
8943 // Try to detect the user choosing select-all
8944 if (te.selectionStart != null) {
8945 if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack() }
8946 var i = 0, poll = function () {
8947 if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 &&
8948 te.selectionEnd > 0 && input.prevInput == "\u200b")
8949 { operation(cm, selectAll)(cm) }
8950 else if (i++ < 10) { display.detectingSelectAll = setTimeout(poll, 500) }
8951 else { display.input.reset() }
8952 }
8953 display.detectingSelectAll = setTimeout(poll, 200)
8954 }
8955 }
8956
8957 if (ie && ie_version >= 9) { prepareSelectAllHack() }
8958 if (captureRightClick) {
8959 e_stop(e)
8960 var mouseup = function () {
8961 off(window, "mouseup", mouseup)
8962 setTimeout(rehide, 20)
8963 }
8964 on(window, "mouseup", mouseup)
8965 } else {
8966 setTimeout(rehide, 50)
8967 }
8968 },
8969
8970 readOnlyChanged: function(val) {
8971 if (!val) { this.reset() }
8972 },
8973
8974 setUneditable: nothing,
8975
8976 needsContentAttribute: false
8977}, TextareaInput.prototype)
8978
8979function fromTextArea(textarea, options) {
8980 options = options ? copyObj(options) : {}
8981 options.value = textarea.value
8982 if (!options.tabindex && textarea.tabIndex)
8983 { options.tabindex = textarea.tabIndex }
8984 if (!options.placeholder && textarea.placeholder)
8985 { options.placeholder = textarea.placeholder }
8986 // Set autofocus to true if this textarea is focused, or if it has
8987 // autofocus and no other element is focused.
8988 if (options.autofocus == null) {
8989 var hasFocus = activeElt()
8990 options.autofocus = hasFocus == textarea ||
8991 textarea.getAttribute("autofocus") != null && hasFocus == document.body
8992 }
8993
8994 function save() {textarea.value = cm.getValue()}
8995
8996 var realSubmit
8997 if (textarea.form) {
8998 on(textarea.form, "submit", save)
8999 // Deplorable hack to make the submit method do the right thing.
9000 if (!options.leaveSubmitMethodAlone) {
9001 var form = textarea.form
9002 realSubmit = form.submit
9003 try {
9004 var wrappedSubmit = form.submit = function () {
9005 save()
9006 form.submit = realSubmit
9007 form.submit()
9008 form.submit = wrappedSubmit
9009 }
9010 } catch(e) {}
9011 }
9012 }
9013
9014 options.finishInit = function (cm) {
9015 cm.save = save
9016 cm.getTextArea = function () { return textarea; }
9017 cm.toTextArea = function () {
9018 cm.toTextArea = isNaN // Prevent this from being ran twice
9019 save()
9020 textarea.parentNode.removeChild(cm.getWrapperElement())
9021 textarea.style.display = ""
9022 if (textarea.form) {
9023 off(textarea.form, "submit", save)
9024 if (typeof textarea.form.submit == "function")
9025 { textarea.form.submit = realSubmit }
9026 }
9027 }
9028 }
9029
9030 textarea.style.display = "none"
9031 var cm = CodeMirror$1(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); },
9032 options)
9033 return cm
9034}
9035
9036function addLegacyProps(CodeMirror) {
9037 CodeMirror.off = off
9038 CodeMirror.on = on
9039 CodeMirror.wheelEventPixels = wheelEventPixels
9040 CodeMirror.Doc = Doc
9041 CodeMirror.splitLines = splitLinesAuto
9042 CodeMirror.countColumn = countColumn
9043 CodeMirror.findColumn = findColumn
9044 CodeMirror.isWordChar = isWordCharBasic
9045 CodeMirror.Pass = Pass
9046 CodeMirror.signal = signal
9047 CodeMirror.Line = Line
9048 CodeMirror.changeEnd = changeEnd
9049 CodeMirror.scrollbarModel = scrollbarModel
9050 CodeMirror.Pos = Pos
9051 CodeMirror.cmpPos = cmp
9052 CodeMirror.modes = modes
9053 CodeMirror.mimeModes = mimeModes
9054 CodeMirror.resolveMode = resolveMode
9055 CodeMirror.getMode = getMode
9056 CodeMirror.modeExtensions = modeExtensions
9057 CodeMirror.extendMode = extendMode
9058 CodeMirror.copyState = copyState
9059 CodeMirror.startState = startState
9060 CodeMirror.innerMode = innerMode
9061 CodeMirror.commands = commands
9062 CodeMirror.keyMap = keyMap
9063 CodeMirror.keyName = keyName
9064 CodeMirror.isModifierKey = isModifierKey
9065 CodeMirror.lookupKey = lookupKey
9066 CodeMirror.normalizeKeyMap = normalizeKeyMap
9067 CodeMirror.StringStream = StringStream
9068 CodeMirror.SharedTextMarker = SharedTextMarker
9069 CodeMirror.TextMarker = TextMarker
9070 CodeMirror.LineWidget = LineWidget
9071 CodeMirror.e_preventDefault = e_preventDefault
9072 CodeMirror.e_stopPropagation = e_stopPropagation
9073 CodeMirror.e_stop = e_stop
9074 CodeMirror.addClass = addClass
9075 CodeMirror.contains = contains
9076 CodeMirror.rmClass = rmClass
9077 CodeMirror.keyNames = keyNames
9078}
9079
9080// EDITOR CONSTRUCTOR
9081
9082defineOptions(CodeMirror$1)
9083
9084addEditorMethods(CodeMirror$1)
9085
9086// Set up methods on CodeMirror's prototype to redirect to the editor's document.
9087var dontDelegate = "iter insert remove copy getEditor constructor".split(" ")
9088for (var prop in Doc.prototype) { if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
9089 { CodeMirror$1.prototype[prop] = (function(method) {
9090 return function() {return method.apply(this.doc, arguments)}
9091 })(Doc.prototype[prop]) } }
9092
9093eventMixin(Doc)
9094
9095// INPUT HANDLING
9096
9097CodeMirror$1.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput}
9098
9099// MODE DEFINITION AND QUERYING
9100
9101// Extra arguments are stored as the mode's dependencies, which is
9102// used by (legacy) mechanisms like loadmode.js to automatically
9103// load a mode. (Preferred mechanism is the require/define calls.)
9104CodeMirror$1.defineMode = function(name/*, mode, …*/) {
9105 if (!CodeMirror$1.defaults.mode && name != "null") { CodeMirror$1.defaults.mode = name }
9106 defineMode.apply(this, arguments)
9107}
9108
9109CodeMirror$1.defineMIME = defineMIME
9110
9111// Minimal default mode.
9112CodeMirror$1.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); })
9113CodeMirror$1.defineMIME("text/plain", "null")
9114
9115// EXTENSIONS
9116
9117CodeMirror$1.defineExtension = function (name, func) {
9118 CodeMirror$1.prototype[name] = func
9119}
9120CodeMirror$1.defineDocExtension = function (name, func) {
9121 Doc.prototype[name] = func
9122}
9123
9124CodeMirror$1.fromTextArea = fromTextArea
9125
9126addLegacyProps(CodeMirror$1)
9127
9128CodeMirror$1.version = "5.21.0"
9129
9130return CodeMirror$1;
9131
9132})));
9133
9134
9135
9136
9137
9138
9139
9140
9141
9142
9143
9144
9145
9146
9147
9148
9149
9150
9151
9152
9153
9154
9155
9156
9157
9158
9159
9160// CodeMirror, copyright (c) by Marijn Haverbeke and others
9161// Distributed under an MIT license: http://codemirror.net/LICENSE
9162
9163(function(mod) {
9164 if (typeof exports == "object" && typeof module == "object") // CommonJS
9165 mod(require("../../lib/codemirror"));
9166 else if (typeof define == "function" && define.amd) // AMD
9167 define(["../../lib/codemirror"], mod);
9168 else // Plain browser env
9169 mod(CodeMirror);
9170})(function(CodeMirror) {
9171"use strict";
9172
9173CodeMirror.defineMode("css", function(config, parserConfig) {
9174 var inline = parserConfig.inline
9175 if (!parserConfig.propertyKeywords) parserConfig = CodeMirror.resolveMode("text/css");
9176
9177 var indentUnit = config.indentUnit,
9178 tokenHooks = parserConfig.tokenHooks,
9179 documentTypes = parserConfig.documentTypes || {},
9180 mediaTypes = parserConfig.mediaTypes || {},
9181 mediaFeatures = parserConfig.mediaFeatures || {},
9182 mediaValueKeywords = parserConfig.mediaValueKeywords || {},
9183 propertyKeywords = parserConfig.propertyKeywords || {},
9184 nonStandardPropertyKeywords = parserConfig.nonStandardPropertyKeywords || {},
9185 fontProperties = parserConfig.fontProperties || {},
9186 counterDescriptors = parserConfig.counterDescriptors || {},
9187 colorKeywords = parserConfig.colorKeywords || {},
9188 valueKeywords = parserConfig.valueKeywords || {},
9189 allowNested = parserConfig.allowNested,
9190 supportsAtComponent = parserConfig.supportsAtComponent === true;
9191
9192 var type, override;
9193 function ret(style, tp) { type = tp; return style; }
9194
9195 // Tokenizers
9196
9197 function tokenBase(stream, state) {
9198 var ch = stream.next();
9199 if (tokenHooks[ch]) {
9200 var result = tokenHooks[ch](stream, state);
9201 if (result !== false) return result;
9202 }
9203 if (ch == "@") {
9204 stream.eatWhile(/[\w\\\-]/);
9205 return ret("def", stream.current());
9206 } else if (ch == "=" || (ch == "~" || ch == "|") && stream.eat("=")) {
9207 return ret(null, "compare");
9208 } else if (ch == "\"" || ch == "'") {
9209 state.tokenize = tokenString(ch);
9210 return state.tokenize(stream, state);
9211 } else if (ch == "#") {
9212 stream.eatWhile(/[\w\\\-]/);
9213 return ret("atom", "hash");
9214 } else if (ch == "!") {
9215 stream.match(/^\s*\w*/);
9216 return ret("keyword", "important");
9217 } else if (/\d/.test(ch) || ch == "." && stream.eat(/\d/)) {
9218 stream.eatWhile(/[\w.%]/);
9219 return ret("number", "unit");
9220 } else if (ch === "-") {
9221 if (/[\d.]/.test(stream.peek())) {
9222 stream.eatWhile(/[\w.%]/);
9223 return ret("number", "unit");
9224 } else if (stream.match(/^-[\w\\\-]+/)) {
9225 stream.eatWhile(/[\w\\\-]/);
9226 if (stream.match(/^\s*:/, false))
9227 return ret("variable-2", "variable-definition");
9228 return ret("variable-2", "variable");
9229 } else if (stream.match(/^\w+-/)) {
9230 return ret("meta", "meta");
9231 }
9232 } else if (/[,+>*\/]/.test(ch)) {
9233 return ret(null, "select-op");
9234 } else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {
9235 return ret("qualifier", "qualifier");
9236 } else if (/[:;{}\[\]\(\)]/.test(ch)) {
9237 return ret(null, ch);
9238 } else if ((ch == "u" && stream.match(/rl(-prefix)?\(/)) ||
9239 (ch == "d" && stream.match("omain(")) ||
9240 (ch == "r" && stream.match("egexp("))) {
9241 stream.backUp(1);
9242 state.tokenize = tokenParenthesized;
9243 return ret("property", "word");
9244 } else if (/[\w\\\-]/.test(ch)) {
9245 stream.eatWhile(/[\w\\\-]/);
9246 return ret("property", "word");
9247 } else {
9248 return ret(null, null);
9249 }
9250 }
9251
9252 function tokenString(quote) {
9253 return function(stream, state) {
9254 var escaped = false, ch;
9255 while ((ch = stream.next()) != null) {
9256 if (ch == quote && !escaped) {
9257 if (quote == ")") stream.backUp(1);
9258 break;
9259 }
9260 escaped = !escaped && ch == "\\";
9261 }
9262 if (ch == quote || !escaped && quote != ")") state.tokenize = null;
9263 return ret("string", "string");
9264 };
9265 }
9266
9267 function tokenParenthesized(stream, state) {
9268 stream.next(); // Must be '('
9269 if (!stream.match(/\s*[\"\')]/, false))
9270 state.tokenize = tokenString(")");
9271 else
9272 state.tokenize = null;
9273 return ret(null, "(");
9274 }
9275
9276 // Context management
9277
9278 function Context(type, indent, prev) {
9279 this.type = type;
9280 this.indent = indent;
9281 this.prev = prev;
9282 }
9283
9284 function pushContext(state, stream, type, indent) {
9285 state.context = new Context(type, stream.indentation() + (indent === false ? 0 : indentUnit), state.context);
9286 return type;
9287 }
9288
9289 function popContext(state) {
9290 if (state.context.prev)
9291 state.context = state.context.prev;
9292 return state.context.type;
9293 }
9294
9295 function pass(type, stream, state) {
9296 return states[state.context.type](type, stream, state);
9297 }
9298 function popAndPass(type, stream, state, n) {
9299 for (var i = n || 1; i > 0; i--)
9300 state.context = state.context.prev;
9301 return pass(type, stream, state);
9302 }
9303
9304 // Parser
9305
9306 function wordAsValue(stream) {
9307 var word = stream.current().toLowerCase();
9308 if (valueKeywords.hasOwnProperty(word))
9309 override = "atom";
9310 else if (colorKeywords.hasOwnProperty(word))
9311 override = "keyword";
9312 else
9313 override = "variable";
9314 }
9315
9316 var states = {};
9317
9318 states.top = function(type, stream, state) {
9319 if (type == "{") {
9320 return pushContext(state, stream, "block");
9321 } else if (type == "}" && state.context.prev) {
9322 return popContext(state);
9323 } else if (supportsAtComponent && /@component/.test(type)) {
9324 return pushContext(state, stream, "atComponentBlock");
9325 } else if (/^@(-moz-)?document$/.test(type)) {
9326 return pushContext(state, stream, "documentTypes");
9327 } else if (/^@(media|supports|(-moz-)?document|import)$/.test(type)) {
9328 return pushContext(state, stream, "atBlock");
9329 } else if (/^@(font-face|counter-style)/.test(type)) {
9330 state.stateArg = type;
9331 return "restricted_atBlock_before";
9332 } else if (/^@(-(moz|ms|o|webkit)-)?keyframes$/.test(type)) {
9333 return "keyframes";
9334 } else if (type && type.charAt(0) == "@") {
9335 return pushContext(state, stream, "at");
9336 } else if (type == "hash") {
9337 override = "builtin";
9338 } else if (type == "word") {
9339 override = "tag";
9340 } else if (type == "variable-definition") {
9341 return "maybeprop";
9342 } else if (type == "interpolation") {
9343 return pushContext(state, stream, "interpolation");
9344 } else if (type == ":") {
9345 return "pseudo";
9346 } else if (allowNested && type == "(") {
9347 return pushContext(state, stream, "parens");
9348 }
9349 return state.context.type;
9350 };
9351
9352 states.block = function(type, stream, state) {
9353 if (type == "word") {
9354 var word = stream.current().toLowerCase();
9355 if (propertyKeywords.hasOwnProperty(word)) {
9356 override = "property";
9357 return "maybeprop";
9358 } else if (nonStandardPropertyKeywords.hasOwnProperty(word)) {
9359 override = "string-2";
9360 return "maybeprop";
9361 } else if (allowNested) {
9362 override = stream.match(/^\s*:(?:\s|$)/, false) ? "property" : "tag";
9363 return "block";
9364 } else {
9365 override += " error";
9366 return "maybeprop";
9367 }
9368 } else if (type == "meta") {
9369 return "block";
9370 } else if (!allowNested && (type == "hash" || type == "qualifier")) {
9371 override = "error";
9372 return "block";
9373 } else {
9374 return states.top(type, stream, state);
9375 }
9376 };
9377
9378 states.maybeprop = function(type, stream, state) {
9379 if (type == ":") return pushContext(state, stream, "prop");
9380 return pass(type, stream, state);
9381 };
9382
9383 states.prop = function(type, stream, state) {
9384 if (type == ";") return popContext(state);
9385 if (type == "{" && allowNested) return pushContext(state, stream, "propBlock");
9386 if (type == "}" || type == "{") return popAndPass(type, stream, state);
9387 if (type == "(") return pushContext(state, stream, "parens");
9388
9389 if (type == "hash" && !/^#([0-9a-fA-f]{3,4}|[0-9a-fA-f]{6}|[0-9a-fA-f]{8})$/.test(stream.current())) {
9390 override += " error";
9391 } else if (type == "word") {
9392 wordAsValue(stream);
9393 } else if (type == "interpolation") {
9394 return pushContext(state, stream, "interpolation");
9395 }
9396 return "prop";
9397 };
9398
9399 states.propBlock = function(type, _stream, state) {
9400 if (type == "}") return popContext(state);
9401 if (type == "word") { override = "property"; return "maybeprop"; }
9402 return state.context.type;
9403 };
9404
9405 states.parens = function(type, stream, state) {
9406 if (type == "{" || type == "}") return popAndPass(type, stream, state);
9407 if (type == ")") return popContext(state);
9408 if (type == "(") return pushContext(state, stream, "parens");
9409 if (type == "interpolation") return pushContext(state, stream, "interpolation");
9410 if (type == "word") wordAsValue(stream);
9411 return "parens";
9412 };
9413
9414 states.pseudo = function(type, stream, state) {
9415 if (type == "word") {
9416 override = "variable-3";
9417 return state.context.type;
9418 }
9419 return pass(type, stream, state);
9420 };
9421
9422 states.documentTypes = function(type, stream, state) {
9423 if (type == "word" && documentTypes.hasOwnProperty(stream.current())) {
9424 override = "tag";
9425 return state.context.type;
9426 } else {
9427 return states.atBlock(type, stream, state);
9428 }
9429 };
9430
9431 states.atBlock = function(type, stream, state) {
9432 if (type == "(") return pushContext(state, stream, "atBlock_parens");
9433 if (type == "}" || type == ";") return popAndPass(type, stream, state);
9434 if (type == "{") return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top");
9435
9436 if (type == "interpolation") return pushContext(state, stream, "interpolation");
9437
9438 if (type == "word") {
9439 var word = stream.current().toLowerCase();
9440 if (word == "only" || word == "not" || word == "and" || word == "or")
9441 override = "keyword";
9442 else if (mediaTypes.hasOwnProperty(word))
9443 override = "attribute";
9444 else if (mediaFeatures.hasOwnProperty(word))
9445 override = "property";
9446 else if (mediaValueKeywords.hasOwnProperty(word))
9447 override = "keyword";
9448 else if (propertyKeywords.hasOwnProperty(word))
9449 override = "property";
9450 else if (nonStandardPropertyKeywords.hasOwnProperty(word))
9451 override = "string-2";
9452 else if (valueKeywords.hasOwnProperty(word))
9453 override = "atom";
9454 else if (colorKeywords.hasOwnProperty(word))
9455 override = "keyword";
9456 else
9457 override = "error";
9458 }
9459 return state.context.type;
9460 };
9461
9462 states.atComponentBlock = function(type, stream, state) {
9463 if (type == "}")
9464 return popAndPass(type, stream, state);
9465 if (type == "{")
9466 return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top", false);
9467 if (type == "word")
9468 override = "error";
9469 return state.context.type;
9470 };
9471
9472 states.atBlock_parens = function(type, stream, state) {
9473 if (type == ")") return popContext(state);
9474 if (type == "{" || type == "}") return popAndPass(type, stream, state, 2);
9475 return states.atBlock(type, stream, state);
9476 };
9477
9478 states.restricted_atBlock_before = function(type, stream, state) {
9479 if (type == "{")
9480 return pushContext(state, stream, "restricted_atBlock");
9481 if (type == "word" && state.stateArg == "@counter-style") {
9482 override = "variable";
9483 return "restricted_atBlock_before";
9484 }
9485 return pass(type, stream, state);
9486 };
9487
9488 states.restricted_atBlock = function(type, stream, state) {
9489 if (type == "}") {
9490 state.stateArg = null;
9491 return popContext(state);
9492 }
9493 if (type == "word") {
9494 if ((state.stateArg == "@font-face" && !fontProperties.hasOwnProperty(stream.current().toLowerCase())) ||
9495 (state.stateArg == "@counter-style" && !counterDescriptors.hasOwnProperty(stream.current().toLowerCase())))
9496 override = "error";
9497 else
9498 override = "property";
9499 return "maybeprop";
9500 }
9501 return "restricted_atBlock";
9502 };
9503
9504 states.keyframes = function(type, stream, state) {
9505 if (type == "word") { override = "variable"; return "keyframes"; }
9506 if (type == "{") return pushContext(state, stream, "top");
9507 return pass(type, stream, state);
9508 };
9509
9510 states.at = function(type, stream, state) {
9511 if (type == ";") return popContext(state);
9512 if (type == "{" || type == "}") return popAndPass(type, stream, state);
9513 if (type == "word") override = "tag";
9514 else if (type == "hash") override = "builtin";
9515 return "at";
9516 };
9517
9518 states.interpolation = function(type, stream, state) {
9519 if (type == "}") return popContext(state);
9520 if (type == "{" || type == ";") return popAndPass(type, stream, state);
9521 if (type == "word") override = "variable";
9522 else if (type != "variable" && type != "(" && type != ")") override = "error";
9523 return "interpolation";
9524 };
9525
9526 return {
9527 startState: function(base) {
9528 return {tokenize: null,
9529 state: inline ? "block" : "top",
9530 stateArg: null,
9531 context: new Context(inline ? "block" : "top", base || 0, null)};
9532 },
9533
9534 token: function(stream, state) {
9535 if (!state.tokenize && stream.eatSpace()) return null;
9536 var style = (state.tokenize || tokenBase)(stream, state);
9537 if (style && typeof style == "object") {
9538 type = style[1];
9539 style = style[0];
9540 }
9541 override = style;
9542 state.state = states[state.state](type, stream, state);
9543 return override;
9544 },
9545
9546 indent: function(state, textAfter) {
9547 var cx = state.context, ch = textAfter && textAfter.charAt(0);
9548 var indent = cx.indent;
9549 if (cx.type == "prop" && (ch == "}" || ch == ")")) cx = cx.prev;
9550 if (cx.prev) {
9551 if (ch == "}" && (cx.type == "block" || cx.type == "top" ||
9552 cx.type == "interpolation" || cx.type == "restricted_atBlock")) {
9553 // Resume indentation from parent context.
9554 cx = cx.prev;
9555 indent = cx.indent;
9556 } else if (ch == ")" && (cx.type == "parens" || cx.type == "atBlock_parens") ||
9557 ch == "{" && (cx.type == "at" || cx.type == "atBlock")) {
9558 // Dedent relative to current context.
9559 indent = Math.max(0, cx.indent - indentUnit);
9560 cx = cx.prev;
9561 }
9562 }
9563 return indent;
9564 },
9565
9566 electricChars: "}",
9567 blockCommentStart: "/*",
9568 blockCommentEnd: "*/",
9569 fold: "brace"
9570 };
9571});
9572
9573 function keySet(array) {
9574 var keys = {};
9575 for (var i = 0; i < array.length; ++i) {
9576 keys[array[i].toLowerCase()] = true;
9577 }
9578 return keys;
9579 }
9580
9581 var documentTypes_ = [
9582 "domain", "regexp", "url", "url-prefix"
9583 ], documentTypes = keySet(documentTypes_);
9584
9585 var mediaTypes_ = [
9586 "all", "aural", "braille", "handheld", "print", "projection", "screen",
9587 "tty", "tv", "embossed"
9588 ], mediaTypes = keySet(mediaTypes_);
9589
9590 var mediaFeatures_ = [
9591 "width", "min-width", "max-width", "height", "min-height", "max-height",
9592 "device-width", "min-device-width", "max-device-width", "device-height",
9593 "min-device-height", "max-device-height", "aspect-ratio",
9594 "min-aspect-ratio", "max-aspect-ratio", "device-aspect-ratio",
9595 "min-device-aspect-ratio", "max-device-aspect-ratio", "color", "min-color",
9596 "max-color", "color-index", "min-color-index", "max-color-index",
9597 "monochrome", "min-monochrome", "max-monochrome", "resolution",
9598 "min-resolution", "max-resolution", "scan", "grid", "orientation",
9599 "device-pixel-ratio", "min-device-pixel-ratio", "max-device-pixel-ratio",
9600 "pointer", "any-pointer", "hover", "any-hover"
9601 ], mediaFeatures = keySet(mediaFeatures_);
9602
9603 var mediaValueKeywords_ = [
9604 "landscape", "portrait", "none", "coarse", "fine", "on-demand", "hover",
9605 "interlace", "progressive"
9606 ], mediaValueKeywords = keySet(mediaValueKeywords_);
9607
9608 var propertyKeywords_ = [
9609 "align-content", "align-items", "align-self", "alignment-adjust",
9610 "alignment-baseline", "anchor-point", "animation", "animation-delay",
9611 "animation-direction", "animation-duration", "animation-fill-mode",
9612 "animation-iteration-count", "animation-name", "animation-play-state",
9613 "animation-timing-function", "appearance", "azimuth", "backface-visibility",
9614 "background", "background-attachment", "background-blend-mode", "background-clip",
9615 "background-color", "background-image", "background-origin", "background-position",
9616 "background-repeat", "background-size", "baseline-shift", "binding",
9617 "bleed", "bookmark-label", "bookmark-level", "bookmark-state",
9618 "bookmark-target", "border", "border-bottom", "border-bottom-color",
9619 "border-bottom-left-radius", "border-bottom-right-radius",
9620 "border-bottom-style", "border-bottom-width", "border-collapse",
9621 "border-color", "border-image", "border-image-outset",
9622 "border-image-repeat", "border-image-slice", "border-image-source",
9623 "border-image-width", "border-left", "border-left-color",
9624 "border-left-style", "border-left-width", "border-radius", "border-right",
9625 "border-right-color", "border-right-style", "border-right-width",
9626 "border-spacing", "border-style", "border-top", "border-top-color",
9627 "border-top-left-radius", "border-top-right-radius", "border-top-style",
9628 "border-top-width", "border-width", "bottom", "box-decoration-break",
9629 "box-shadow", "box-sizing", "break-after", "break-before", "break-inside",
9630 "caption-side", "clear", "clip", "color", "color-profile", "column-count",
9631 "column-fill", "column-gap", "column-rule", "column-rule-color",
9632 "column-rule-style", "column-rule-width", "column-span", "column-width",
9633 "columns", "content", "counter-increment", "counter-reset", "crop", "cue",
9634 "cue-after", "cue-before", "cursor", "direction", "display",
9635 "dominant-baseline", "drop-initial-after-adjust",
9636 "drop-initial-after-align", "drop-initial-before-adjust",
9637 "drop-initial-before-align", "drop-initial-size", "drop-initial-value",
9638 "elevation", "empty-cells", "fit", "fit-position", "flex", "flex-basis",
9639 "flex-direction", "flex-flow", "flex-grow", "flex-shrink", "flex-wrap",
9640 "float", "float-offset", "flow-from", "flow-into", "font", "font-feature-settings",
9641 "font-family", "font-kerning", "font-language-override", "font-size", "font-size-adjust",
9642 "font-stretch", "font-style", "font-synthesis", "font-variant",
9643 "font-variant-alternates", "font-variant-caps", "font-variant-east-asian",
9644 "font-variant-ligatures", "font-variant-numeric", "font-variant-position",
9645 "font-weight", "grid", "grid-area", "grid-auto-columns", "grid-auto-flow",
9646 "grid-auto-rows", "grid-column", "grid-column-end", "grid-column-gap",
9647 "grid-column-start", "grid-gap", "grid-row", "grid-row-end", "grid-row-gap",
9648 "grid-row-start", "grid-template", "grid-template-areas", "grid-template-columns",
9649 "grid-template-rows", "hanging-punctuation", "height", "hyphens",
9650 "icon", "image-orientation", "image-rendering", "image-resolution",
9651 "inline-box-align", "justify-content", "left", "letter-spacing",
9652 "line-break", "line-height", "line-stacking", "line-stacking-ruby",
9653 "line-stacking-shift", "line-stacking-strategy", "list-style",
9654 "list-style-image", "list-style-position", "list-style-type", "margin",
9655 "margin-bottom", "margin-left", "margin-right", "margin-top",
9656 "marks", "marquee-direction", "marquee-loop",
9657 "marquee-play-count", "marquee-speed", "marquee-style", "max-height",
9658 "max-width", "min-height", "min-width", "move-to", "nav-down", "nav-index",
9659 "nav-left", "nav-right", "nav-up", "object-fit", "object-position",
9660 "opacity", "order", "orphans", "outline",
9661 "outline-color", "outline-offset", "outline-style", "outline-width",
9662 "overflow", "overflow-style", "overflow-wrap", "overflow-x", "overflow-y",
9663 "padding", "padding-bottom", "padding-left", "padding-right", "padding-top",
9664 "page", "page-break-after", "page-break-before", "page-break-inside",
9665 "page-policy", "pause", "pause-after", "pause-before", "perspective",
9666 "perspective-origin", "pitch", "pitch-range", "play-during", "position",
9667 "presentation-level", "punctuation-trim", "quotes", "region-break-after",
9668 "region-break-before", "region-break-inside", "region-fragment",
9669 "rendering-intent", "resize", "rest", "rest-after", "rest-before", "richness",
9670 "right", "rotation", "rotation-point", "ruby-align", "ruby-overhang",
9671 "ruby-position", "ruby-span", "shape-image-threshold", "shape-inside", "shape-margin",
9672 "shape-outside", "size", "speak", "speak-as", "speak-header",
9673 "speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set",
9674 "tab-size", "table-layout", "target", "target-name", "target-new",
9675 "target-position", "text-align", "text-align-last", "text-decoration",
9676 "text-decoration-color", "text-decoration-line", "text-decoration-skip",
9677 "text-decoration-style", "text-emphasis", "text-emphasis-color",
9678 "text-emphasis-position", "text-emphasis-style", "text-height",
9679 "text-indent", "text-justify", "text-outline", "text-overflow", "text-shadow",
9680 "text-size-adjust", "text-space-collapse", "text-transform", "text-underline-position",
9681 "text-wrap", "top", "transform", "transform-origin", "transform-style",
9682 "transition", "transition-delay", "transition-duration",
9683 "transition-property", "transition-timing-function", "unicode-bidi",
9684 "user-select", "vertical-align", "visibility", "voice-balance", "voice-duration",
9685 "voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress",
9686 "voice-volume", "volume", "white-space", "widows", "width", "word-break",
9687 "word-spacing", "word-wrap", "z-index",
9688 // SVG-specific
9689 "clip-path", "clip-rule", "mask", "enable-background", "filter", "flood-color",
9690 "flood-opacity", "lighting-color", "stop-color", "stop-opacity", "pointer-events",
9691 "color-interpolation", "color-interpolation-filters",
9692 "color-rendering", "fill", "fill-opacity", "fill-rule", "image-rendering",
9693 "marker", "marker-end", "marker-mid", "marker-start", "shape-rendering", "stroke",
9694 "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin",
9695 "stroke-miterlimit", "stroke-opacity", "stroke-width", "text-rendering",
9696 "baseline-shift", "dominant-baseline", "glyph-orientation-horizontal",
9697 "glyph-orientation-vertical", "text-anchor", "writing-mode"
9698 ], propertyKeywords = keySet(propertyKeywords_);
9699
9700 var nonStandardPropertyKeywords_ = [
9701 "scrollbar-arrow-color", "scrollbar-base-color", "scrollbar-dark-shadow-color",
9702 "scrollbar-face-color", "scrollbar-highlight-color", "scrollbar-shadow-color",
9703 "scrollbar-3d-light-color", "scrollbar-track-color", "shape-inside",
9704 "searchfield-cancel-button", "searchfield-decoration", "searchfield-results-button",
9705 "searchfield-results-decoration", "zoom"
9706 ], nonStandardPropertyKeywords = keySet(nonStandardPropertyKeywords_);
9707
9708 var fontProperties_ = [
9709 "font-family", "src", "unicode-range", "font-variant", "font-feature-settings",
9710 "font-stretch", "font-weight", "font-style"
9711 ], fontProperties = keySet(fontProperties_);
9712
9713 var counterDescriptors_ = [
9714 "additive-symbols", "fallback", "negative", "pad", "prefix", "range",
9715 "speak-as", "suffix", "symbols", "system"
9716 ], counterDescriptors = keySet(counterDescriptors_);
9717
9718 var colorKeywords_ = [
9719 "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige",
9720 "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown",
9721 "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue",
9722 "cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod",
9723 "darkgray", "darkgreen", "darkkhaki", "darkmagenta", "darkolivegreen",
9724 "darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen",
9725 "darkslateblue", "darkslategray", "darkturquoise", "darkviolet",
9726 "deeppink", "deepskyblue", "dimgray", "dodgerblue", "firebrick",
9727 "floralwhite", "forestgreen", "fuchsia", "gainsboro", "ghostwhite",
9728 "gold", "goldenrod", "gray", "grey", "green", "greenyellow", "honeydew",
9729 "hotpink", "indianred", "indigo", "ivory", "khaki", "lavender",
9730 "lavenderblush", "lawngreen", "lemonchiffon", "lightblue", "lightcoral",
9731 "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightpink",
9732 "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray",
9733 "lightsteelblue", "lightyellow", "lime", "limegreen", "linen", "magenta",
9734 "maroon", "mediumaquamarine", "mediumblue", "mediumorchid", "mediumpurple",
9735 "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise",
9736 "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin",
9737 "navajowhite", "navy", "oldlace", "olive", "olivedrab", "orange", "orangered",
9738 "orchid", "palegoldenrod", "palegreen", "paleturquoise", "palevioletred",
9739 "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue",
9740 "purple", "rebeccapurple", "red", "rosybrown", "royalblue", "saddlebrown",
9741 "salmon", "sandybrown", "seagreen", "seashell", "sienna", "silver", "skyblue",
9742 "slateblue", "slategray", "snow", "springgreen", "steelblue", "tan",
9743 "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white",
9744 "whitesmoke", "yellow", "yellowgreen"
9745 ], colorKeywords = keySet(colorKeywords_);
9746
9747 var valueKeywords_ = [
9748 "above", "absolute", "activeborder", "additive", "activecaption", "afar",
9749 "after-white-space", "ahead", "alias", "all", "all-scroll", "alphabetic", "alternate",
9750 "always", "amharic", "amharic-abegede", "antialiased", "appworkspace",
9751 "arabic-indic", "armenian", "asterisks", "attr", "auto", "avoid", "avoid-column", "avoid-page",
9752 "avoid-region", "background", "backwards", "baseline", "below", "bidi-override", "binary",
9753 "bengali", "blink", "block", "block-axis", "bold", "bolder", "border", "border-box",
9754 "both", "bottom", "break", "break-all", "break-word", "bullets", "button", "button-bevel",
9755 "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "calc", "cambodian",
9756 "capitalize", "caps-lock-indicator", "caption", "captiontext", "caret",
9757 "cell", "center", "checkbox", "circle", "cjk-decimal", "cjk-earthly-branch",
9758 "cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote",
9759 "col-resize", "collapse", "color", "color-burn", "color-dodge", "column", "column-reverse",
9760 "compact", "condensed", "contain", "content",
9761 "content-box", "context-menu", "continuous", "copy", "counter", "counters", "cover", "crop",
9762 "cross", "crosshair", "currentcolor", "cursive", "cyclic", "darken", "dashed", "decimal",
9763 "decimal-leading-zero", "default", "default-button", "dense", "destination-atop",
9764 "destination-in", "destination-out", "destination-over", "devanagari", "difference",
9765 "disc", "discard", "disclosure-closed", "disclosure-open", "document",
9766 "dot-dash", "dot-dot-dash",
9767 "dotted", "double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out",
9768 "element", "ellipse", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede",
9769 "ethiopic-abegede-am-et", "ethiopic-abegede-gez", "ethiopic-abegede-ti-er",
9770 "ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er",
9771 "ethiopic-halehame-aa-et", "ethiopic-halehame-am-et",
9772 "ethiopic-halehame-gez", "ethiopic-halehame-om-et",
9773 "ethiopic-halehame-sid-et", "ethiopic-halehame-so-et",
9774 "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et", "ethiopic-halehame-tig",
9775 "ethiopic-numeric", "ew-resize", "exclusion", "expanded", "extends", "extra-condensed",
9776 "extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "flex", "flex-end", "flex-start", "footnotes",
9777 "forwards", "from", "geometricPrecision", "georgian", "graytext", "grid", "groove",
9778 "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hard-light", "hebrew",
9779 "help", "hidden", "hide", "higher", "highlight", "highlighttext",
9780 "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "hue", "icon", "ignore",
9781 "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite",
9782 "infobackground", "infotext", "inherit", "initial", "inline", "inline-axis",
9783 "inline-block", "inline-flex", "inline-grid", "inline-table", "inset", "inside", "intrinsic", "invert",
9784 "italic", "japanese-formal", "japanese-informal", "justify", "kannada",
9785 "katakana", "katakana-iroha", "keep-all", "khmer",
9786 "korean-hangul-formal", "korean-hanja-formal", "korean-hanja-informal",
9787 "landscape", "lao", "large", "larger", "left", "level", "lighter", "lighten",
9788 "line-through", "linear", "linear-gradient", "lines", "list-item", "listbox", "listitem",
9789 "local", "logical", "loud", "lower", "lower-alpha", "lower-armenian",
9790 "lower-greek", "lower-hexadecimal", "lower-latin", "lower-norwegian",
9791 "lower-roman", "lowercase", "ltr", "luminosity", "malayalam", "match", "matrix", "matrix3d",
9792 "media-controls-background", "media-current-time-display",
9793 "media-fullscreen-button", "media-mute-button", "media-play-button",
9794 "media-return-to-realtime-button", "media-rewind-button",
9795 "media-seek-back-button", "media-seek-forward-button", "media-slider",
9796 "media-sliderthumb", "media-time-remaining-display", "media-volume-slider",
9797 "media-volume-slider-container", "media-volume-sliderthumb", "medium",
9798 "menu", "menulist", "menulist-button", "menulist-text",
9799 "menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic",
9800 "mix", "mongolian", "monospace", "move", "multiple", "multiply", "myanmar", "n-resize",
9801 "narrower", "ne-resize", "nesw-resize", "no-close-quote", "no-drop",
9802 "no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap",
9803 "ns-resize", "numbers", "numeric", "nw-resize", "nwse-resize", "oblique", "octal", "open-quote",
9804 "optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset",
9805 "outside", "outside-shape", "overlay", "overline", "padding", "padding-box",
9806 "painted", "page", "paused", "persian", "perspective", "plus-darker", "plus-lighter",
9807 "pointer", "polygon", "portrait", "pre", "pre-line", "pre-wrap", "preserve-3d",
9808 "progress", "push-button", "radial-gradient", "radio", "read-only",
9809 "read-write", "read-write-plaintext-only", "rectangle", "region",
9810 "relative", "repeat", "repeating-linear-gradient",
9811 "repeating-radial-gradient", "repeat-x", "repeat-y", "reset", "reverse",
9812 "rgb", "rgba", "ridge", "right", "rotate", "rotate3d", "rotateX", "rotateY",
9813 "rotateZ", "round", "row", "row-resize", "row-reverse", "rtl", "run-in", "running",
9814 "s-resize", "sans-serif", "saturation", "scale", "scale3d", "scaleX", "scaleY", "scaleZ", "screen",
9815 "scroll", "scrollbar", "se-resize", "searchfield",
9816 "searchfield-cancel-button", "searchfield-decoration",
9817 "searchfield-results-button", "searchfield-results-decoration",
9818 "semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama",
9819 "simp-chinese-formal", "simp-chinese-informal", "single",
9820 "skew", "skewX", "skewY", "skip-white-space", "slide", "slider-horizontal",
9821 "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow",
9822 "small", "small-caps", "small-caption", "smaller", "soft-light", "solid", "somali",
9823 "source-atop", "source-in", "source-out", "source-over", "space", "space-around", "space-between", "spell-out", "square",
9824 "square-button", "start", "static", "status-bar", "stretch", "stroke", "sub",
9825 "subpixel-antialiased", "super", "sw-resize", "symbolic", "symbols", "table",
9826 "table-caption", "table-cell", "table-column", "table-column-group",
9827 "table-footer-group", "table-header-group", "table-row", "table-row-group",
9828 "tamil",
9829 "telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai",
9830 "thick", "thin", "threeddarkshadow", "threedface", "threedhighlight",
9831 "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er",
9832 "tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top",
9833 "trad-chinese-formal", "trad-chinese-informal",
9834 "translate", "translate3d", "translateX", "translateY", "translateZ",
9835 "transparent", "ultra-condensed", "ultra-expanded", "underline", "up",
9836 "upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal",
9837 "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url",
9838 "var", "vertical", "vertical-text", "visible", "visibleFill", "visiblePainted",
9839 "visibleStroke", "visual", "w-resize", "wait", "wave", "wider",
9840 "window", "windowframe", "windowtext", "words", "wrap", "wrap-reverse", "x-large", "x-small", "xor",
9841 "xx-large", "xx-small"
9842 ], valueKeywords = keySet(valueKeywords_);
9843
9844 var allWords = documentTypes_.concat(mediaTypes_).concat(mediaFeatures_).concat(mediaValueKeywords_)
9845 .concat(propertyKeywords_).concat(nonStandardPropertyKeywords_).concat(colorKeywords_)
9846 .concat(valueKeywords_);
9847 CodeMirror.registerHelper("hintWords", "css", allWords);
9848
9849 function tokenCComment(stream, state) {
9850 var maybeEnd = false, ch;
9851 while ((ch = stream.next()) != null) {
9852 if (maybeEnd && ch == "/") {
9853 state.tokenize = null;
9854 break;
9855 }
9856 maybeEnd = (ch == "*");
9857 }
9858 return ["comment", "comment"];
9859 }
9860
9861 CodeMirror.defineMIME("text/css", {
9862 documentTypes: documentTypes,
9863 mediaTypes: mediaTypes,
9864 mediaFeatures: mediaFeatures,
9865 mediaValueKeywords: mediaValueKeywords,
9866 propertyKeywords: propertyKeywords,
9867 nonStandardPropertyKeywords: nonStandardPropertyKeywords,
9868 fontProperties: fontProperties,
9869 counterDescriptors: counterDescriptors,
9870 colorKeywords: colorKeywords,
9871 valueKeywords: valueKeywords,
9872 tokenHooks: {
9873 "/": function(stream, state) {
9874 if (!stream.eat("*")) return false;
9875 state.tokenize = tokenCComment;
9876 return tokenCComment(stream, state);
9877 }
9878 },
9879 name: "css"
9880 });
9881
9882 CodeMirror.defineMIME("text/x-scss", {
9883 mediaTypes: mediaTypes,
9884 mediaFeatures: mediaFeatures,
9885 mediaValueKeywords: mediaValueKeywords,
9886 propertyKeywords: propertyKeywords,
9887 nonStandardPropertyKeywords: nonStandardPropertyKeywords,
9888 colorKeywords: colorKeywords,
9889 valueKeywords: valueKeywords,
9890 fontProperties: fontProperties,
9891 allowNested: true,
9892 tokenHooks: {
9893 "/": function(stream, state) {
9894 if (stream.eat("/")) {
9895 stream.skipToEnd();
9896 return ["comment", "comment"];
9897 } else if (stream.eat("*")) {
9898 state.tokenize = tokenCComment;
9899 return tokenCComment(stream, state);
9900 } else {
9901 return ["operator", "operator"];
9902 }
9903 },
9904 ":": function(stream) {
9905 if (stream.match(/\s*\{/))
9906 return [null, "{"];
9907 return false;
9908 },
9909 "$": function(stream) {
9910 stream.match(/^[\w-]+/);
9911 if (stream.match(/^\s*:/, false))
9912 return ["variable-2", "variable-definition"];
9913 return ["variable-2", "variable"];
9914 },
9915 "#": function(stream) {
9916 if (!stream.eat("{")) return false;
9917 return [null, "interpolation"];
9918 }
9919 },
9920 name: "css",
9921 helperType: "scss"
9922 });
9923
9924 CodeMirror.defineMIME("text/x-less", {
9925 mediaTypes: mediaTypes,
9926 mediaFeatures: mediaFeatures,
9927 mediaValueKeywords: mediaValueKeywords,
9928 propertyKeywords: propertyKeywords,
9929 nonStandardPropertyKeywords: nonStandardPropertyKeywords,
9930 colorKeywords: colorKeywords,
9931 valueKeywords: valueKeywords,
9932 fontProperties: fontProperties,
9933 allowNested: true,
9934 tokenHooks: {
9935 "/": function(stream, state) {
9936 if (stream.eat("/")) {
9937 stream.skipToEnd();
9938 return ["comment", "comment"];
9939 } else if (stream.eat("*")) {
9940 state.tokenize = tokenCComment;
9941 return tokenCComment(stream, state);
9942 } else {
9943 return ["operator", "operator"];
9944 }
9945 },
9946 "@": function(stream) {
9947 if (stream.eat("{")) return [null, "interpolation"];
9948 if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/, false)) return false;
9949 stream.eatWhile(/[\w\\\-]/);
9950 if (stream.match(/^\s*:/, false))
9951 return ["variable-2", "variable-definition"];
9952 return ["variable-2", "variable"];
9953 },
9954 "&": function() {
9955 return ["atom", "atom"];
9956 }
9957 },
9958 name: "css",
9959 helperType: "less"
9960 });
9961
9962 CodeMirror.defineMIME("text/x-gss", {
9963 documentTypes: documentTypes,
9964 mediaTypes: mediaTypes,
9965 mediaFeatures: mediaFeatures,
9966 propertyKeywords: propertyKeywords,
9967 nonStandardPropertyKeywords: nonStandardPropertyKeywords,
9968 fontProperties: fontProperties,
9969 counterDescriptors: counterDescriptors,
9970 colorKeywords: colorKeywords,
9971 valueKeywords: valueKeywords,
9972 supportsAtComponent: true,
9973 tokenHooks: {
9974 "/": function(stream, state) {
9975 if (!stream.eat("*")) return false;
9976 state.tokenize = tokenCComment;
9977 return tokenCComment(stream, state);
9978 }
9979 },
9980 name: "css",
9981 helperType: "gss"
9982 });
9983
9984});
9985
9986
9987
9988
9989
9990
9991//XML:
9992
9993
9994// CodeMirror, copyright (c) by Marijn Haverbeke and others
9995// Distributed under an MIT license: http://codemirror.net/LICENSE
9996
9997(function(mod) {
9998 if (typeof exports == "object" && typeof module == "object") // CommonJS
9999 mod(require("../../lib/codemirror"));
10000 else if (typeof define == "function" && define.amd) // AMD
10001 define(["../../lib/codemirror"], mod);
10002 else // Plain browser env
10003 mod(CodeMirror);
10004})(function(CodeMirror) {
10005"use strict";
10006
10007var htmlConfig = {
10008 autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true,
10009 'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true,
10010 'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true,
10011 'track': true, 'wbr': true, 'menuitem': true},
10012 implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true,
10013 'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true,
10014 'th': true, 'tr': true},
10015 contextGrabbers: {
10016 'dd': {'dd': true, 'dt': true},
10017 'dt': {'dd': true, 'dt': true},
10018 'li': {'li': true},
10019 'option': {'option': true, 'optgroup': true},
10020 'optgroup': {'optgroup': true},
10021 'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true,
10022 'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true,
10023 'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true,
10024 'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true,
10025 'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true},
10026 'rp': {'rp': true, 'rt': true},
10027 'rt': {'rp': true, 'rt': true},
10028 'tbody': {'tbody': true, 'tfoot': true},
10029 'td': {'td': true, 'th': true},
10030 'tfoot': {'tbody': true},
10031 'th': {'td': true, 'th': true},
10032 'thead': {'tbody': true, 'tfoot': true},
10033 'tr': {'tr': true}
10034 },
10035 doNotIndent: {"pre": true},
10036 allowUnquoted: true,
10037 allowMissing: true,
10038 caseFold: true
10039}
10040
10041var xmlConfig = {
10042 autoSelfClosers: {},
10043 implicitlyClosed: {},
10044 contextGrabbers: {},
10045 doNotIndent: {},
10046 allowUnquoted: false,
10047 allowMissing: false,
10048 caseFold: false
10049}
10050
10051CodeMirror.defineMode("xml", function(editorConf, config_) {
10052 var indentUnit = editorConf.indentUnit
10053 var config = {}
10054 var defaults = config_.htmlMode ? htmlConfig : xmlConfig
10055 for (var prop in defaults) config[prop] = defaults[prop]
10056 for (var prop in config_) config[prop] = config_[prop]
10057
10058 // Return variables for tokenizers
10059 var type, setStyle;
10060
10061 function inText(stream, state) {
10062 function chain(parser) {
10063 state.tokenize = parser;
10064 return parser(stream, state);
10065 }
10066
10067 var ch = stream.next();
10068 if (ch == "<") {
10069 if (stream.eat("!")) {
10070 if (stream.eat("[")) {
10071 if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));
10072 else return null;
10073 } else if (stream.match("--")) {
10074 return chain(inBlock("comment", "-->"));
10075 } else if (stream.match("DOCTYPE", true, true)) {
10076 stream.eatWhile(/[\w\._\-]/);
10077 return chain(doctype(1));
10078 } else {
10079 return null;
10080 }
10081 } else if (stream.eat("?")) {
10082 stream.eatWhile(/[\w\._\-]/);
10083 state.tokenize = inBlock("meta", "?>");
10084 return "meta";
10085 } else {
10086 type = stream.eat("/") ? "closeTag" : "openTag";
10087 state.tokenize = inTag;
10088 return "tag bracket";
10089 }
10090 } else if (ch == "&") {
10091 var ok;
10092 if (stream.eat("#")) {
10093 if (stream.eat("x")) {
10094 ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";");
10095 } else {
10096 ok = stream.eatWhile(/[\d]/) && stream.eat(";");
10097 }
10098 } else {
10099 ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";");
10100 }
10101 return ok ? "atom" : "error";
10102 } else {
10103 stream.eatWhile(/[^&<]/);
10104 return null;
10105 }
10106 }
10107 inText.isInText = true;
10108
10109 function inTag(stream, state) {
10110 var ch = stream.next();
10111 if (ch == ">" || (ch == "/" && stream.eat(">"))) {
10112 state.tokenize = inText;
10113 type = ch == ">" ? "endTag" : "selfcloseTag";
10114 return "tag bracket";
10115 } else if (ch == "=") {
10116 type = "equals";
10117 return null;
10118 } else if (ch == "<") {
10119 state.tokenize = inText;
10120 state.state = baseState;
10121 state.tagName = state.tagStart = null;
10122 var next = state.tokenize(stream, state);
10123 return next ? next + " tag error" : "tag error";
10124 } else if (/[\'\"]/.test(ch)) {
10125 state.tokenize = inAttribute(ch);
10126 state.stringStartCol = stream.column();
10127 return state.tokenize(stream, state);
10128 } else {
10129 stream.match(/^[^\s\u00a0=<>\"\']*[^\s\u00a0=<>\"\'\/]/);
10130 return "word";
10131 }
10132 }
10133
10134 function inAttribute(quote) {
10135 var closure = function(stream, state) {
10136 while (!stream.eol()) {
10137 if (stream.next() == quote) {
10138 state.tokenize = inTag;
10139 break;
10140 }
10141 }
10142 return "string";
10143 };
10144 closure.isInAttribute = true;
10145 return closure;
10146 }
10147
10148 function inBlock(style, terminator) {
10149 return function(stream, state) {
10150 while (!stream.eol()) {
10151 if (stream.match(terminator)) {
10152 state.tokenize = inText;
10153 break;
10154 }
10155 stream.next();
10156 }
10157 return style;
10158 };
10159 }
10160 function doctype(depth) {
10161 return function(stream, state) {
10162 var ch;
10163 while ((ch = stream.next()) != null) {
10164 if (ch == "<") {
10165 state.tokenize = doctype(depth + 1);
10166 return state.tokenize(stream, state);
10167 } else if (ch == ">") {
10168 if (depth == 1) {
10169 state.tokenize = inText;
10170 break;
10171 } else {
10172 state.tokenize = doctype(depth - 1);
10173 return state.tokenize(stream, state);
10174 }
10175 }
10176 }
10177 return "meta";
10178 };
10179 }
10180
10181 function Context(state, tagName, startOfLine) {
10182 this.prev = state.context;
10183 this.tagName = tagName;
10184 this.indent = state.indented;
10185 this.startOfLine = startOfLine;
10186 if (config.doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.noIndent))
10187 this.noIndent = true;
10188 }
10189 function popContext(state) {
10190 if (state.context) state.context = state.context.prev;
10191 }
10192 function maybePopContext(state, nextTagName) {
10193 var parentTagName;
10194 while (true) {
10195 if (!state.context) {
10196 return;
10197 }
10198 parentTagName = state.context.tagName;
10199 if (!config.contextGrabbers.hasOwnProperty(parentTagName) ||
10200 !config.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
10201 return;
10202 }
10203 popContext(state);
10204 }
10205 }
10206
10207 function baseState(type, stream, state) {
10208 if (type == "openTag") {
10209 state.tagStart = stream.column();
10210 return tagNameState;
10211 } else if (type == "closeTag") {
10212 return closeTagNameState;
10213 } else {
10214 return baseState;
10215 }
10216 }
10217 function tagNameState(type, stream, state) {
10218 if (type == "word") {
10219 state.tagName = stream.current();
10220 setStyle = "tag";
10221 return attrState;
10222 } else {
10223 setStyle = "error";
10224 return tagNameState;
10225 }
10226 }
10227 function closeTagNameState(type, stream, state) {
10228 if (type == "word") {
10229 var tagName = stream.current();
10230 if (state.context && state.context.tagName != tagName &&
10231 config.implicitlyClosed.hasOwnProperty(state.context.tagName))
10232 popContext(state);
10233 if ((state.context && state.context.tagName == tagName) || config.matchClosing === false) {
10234 setStyle = "tag";
10235 return closeState;
10236 } else {
10237 setStyle = "tag error";
10238 return closeStateErr;
10239 }
10240 } else {
10241 setStyle = "error";
10242 return closeStateErr;
10243 }
10244 }
10245
10246 function closeState(type, _stream, state) {
10247 if (type != "endTag") {
10248 setStyle = "error";
10249 return closeState;
10250 }
10251 popContext(state);
10252 return baseState;
10253 }
10254 function closeStateErr(type, stream, state) {
10255 setStyle = "error";
10256 return closeState(type, stream, state);
10257 }
10258
10259 function attrState(type, _stream, state) {
10260 if (type == "word") {
10261 setStyle = "attribute";
10262 return attrEqState;
10263 } else if (type == "endTag" || type == "selfcloseTag") {
10264 var tagName = state.tagName, tagStart = state.tagStart;
10265 state.tagName = state.tagStart = null;
10266 if (type == "selfcloseTag" ||
10267 config.autoSelfClosers.hasOwnProperty(tagName)) {
10268 maybePopContext(state, tagName);
10269 } else {
10270 maybePopContext(state, tagName);
10271 state.context = new Context(state, tagName, tagStart == state.indented);
10272 }
10273 return baseState;
10274 }
10275 setStyle = "error";
10276 return attrState;
10277 }
10278 function attrEqState(type, stream, state) {
10279 if (type == "equals") return attrValueState;
10280 if (!config.allowMissing) setStyle = "error";
10281 return attrState(type, stream, state);
10282 }
10283 function attrValueState(type, stream, state) {
10284 if (type == "string") return attrContinuedState;
10285 if (type == "word" && config.allowUnquoted) {setStyle = "string"; return attrState;}
10286 setStyle = "error";
10287 return attrState(type, stream, state);
10288 }
10289 function attrContinuedState(type, stream, state) {
10290 if (type == "string") return attrContinuedState;
10291 return attrState(type, stream, state);
10292 }
10293
10294 return {
10295 startState: function(baseIndent) {
10296 var state = {tokenize: inText,
10297 state: baseState,
10298 indented: baseIndent || 0,
10299 tagName: null, tagStart: null,
10300 context: null}
10301 if (baseIndent != null) state.baseIndent = baseIndent
10302 return state
10303 },
10304
10305 token: function(stream, state) {
10306 if (!state.tagName && stream.sol())
10307 state.indented = stream.indentation();
10308
10309 if (stream.eatSpace()) return null;
10310 type = null;
10311 var style = state.tokenize(stream, state);
10312 if ((style || type) && style != "comment") {
10313 setStyle = null;
10314 state.state = state.state(type || style, stream, state);
10315 if (setStyle)
10316 style = setStyle == "error" ? style + " error" : setStyle;
10317 }
10318 return style;
10319 },
10320
10321 indent: function(state, textAfter, fullLine) {
10322 var context = state.context;
10323 // Indent multi-line strings (e.g. css).
10324 if (state.tokenize.isInAttribute) {
10325 if (state.tagStart == state.indented)
10326 return state.stringStartCol + 1;
10327 else
10328 return state.indented + indentUnit;
10329 }
10330 if (context && context.noIndent) return CodeMirror.Pass;
10331 if (state.tokenize != inTag && state.tokenize != inText)
10332 return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
10333 // Indent the starts of attribute names.
10334 if (state.tagName) {
10335 if (config.multilineTagIndentPastTag !== false)
10336 return state.tagStart + state.tagName.length + 2;
10337 else
10338 return state.tagStart + indentUnit * (config.multilineTagIndentFactor || 1);
10339 }
10340 if (config.alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
10341 var tagAfter = textAfter && /^<(\/)?([\w_:\.-]*)/.exec(textAfter);
10342 if (tagAfter && tagAfter[1]) { // Closing tag spotted
10343 while (context) {
10344 if (context.tagName == tagAfter[2]) {
10345 context = context.prev;
10346 break;
10347 } else if (config.implicitlyClosed.hasOwnProperty(context.tagName)) {
10348 context = context.prev;
10349 } else {
10350 break;
10351 }
10352 }
10353 } else if (tagAfter) { // Opening tag spotted
10354 while (context) {
10355 var grabbers = config.contextGrabbers[context.tagName];
10356 if (grabbers && grabbers.hasOwnProperty(tagAfter[2]))
10357 context = context.prev;
10358 else
10359 break;
10360 }
10361 }
10362 while (context && context.prev && !context.startOfLine)
10363 context = context.prev;
10364 if (context) return context.indent + indentUnit;
10365 else return state.baseIndent || 0;
10366 },
10367
10368 electricInput: /<\/[\s\w:]+>$/,
10369 blockCommentStart: "<!--",
10370 blockCommentEnd: "-->",
10371
10372 configuration: config.htmlMode ? "html" : "xml",
10373 helperType: config.htmlMode ? "html" : "xml",
10374
10375 skipAttribute: function(state) {
10376 if (state.state == attrValueState)
10377 state.state = attrState
10378 }
10379 };
10380});
10381
10382CodeMirror.defineMIME("text/xml", "xml");
10383CodeMirror.defineMIME("application/xml", "xml");
10384if (!CodeMirror.mimeModes.hasOwnProperty("text/html"))
10385 CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
10386
10387});
10388
10389
10390
10391
10392
10393
10394
10395
10396
10397//JAVASCRIPT:
10398
10399// CodeMirror, copyright (c) by Marijn Haverbeke and others
10400// Distributed under an MIT license: http://codemirror.net/LICENSE
10401
10402(function(mod) {
10403 if (typeof exports == "object" && typeof module == "object") // CommonJS
10404 mod(require("../../lib/codemirror"));
10405 else if (typeof define == "function" && define.amd) // AMD
10406 define(["../../lib/codemirror"], mod);
10407 else // Plain browser env
10408 mod(CodeMirror);
10409})(function(CodeMirror) {
10410"use strict";
10411
10412function expressionAllowed(stream, state, backUp) {
10413 return /^(?:operator|sof|keyword c|case|new|[\[{}\(,;:]|=>)$/.test(state.lastType) ||
10414 (state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0))))
10415}
10416
10417CodeMirror.defineMode("javascript", function(config, parserConfig) {
10418 var indentUnit = config.indentUnit;
10419 var statementIndent = parserConfig.statementIndent;
10420 var jsonldMode = parserConfig.jsonld;
10421 var jsonMode = parserConfig.json || jsonldMode;
10422 var isTS = parserConfig.typescript;
10423 var wordRE = parserConfig.wordCharacters || /[\w$\xa1-\uffff]/;
10424
10425 // Tokenizer
10426
10427 var keywords = function(){
10428 function kw(type) {return {type: type, style: "keyword"};}
10429 var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c");
10430 var operator = kw("operator"), atom = {type: "atom", style: "atom"};
10431
10432 var jsKeywords = {
10433 "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
10434 "return": C, "break": C, "continue": C, "new": kw("new"), "delete": C, "throw": C, "debugger": C,
10435 "var": kw("var"), "const": kw("var"), "let": kw("var"),
10436 "function": kw("function"), "catch": kw("catch"),
10437 "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
10438 "in": operator, "typeof": operator, "instanceof": operator,
10439 "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom,
10440 "this": kw("this"), "class": kw("class"), "super": kw("atom"),
10441 "yield": C, "export": kw("export"), "import": kw("import"), "extends": C,
10442 "await": C, "async": kw("async"), "event": kw("event")
10443 };
10444
10445 // Extend the 'normal' keywords with the TypeScript language extensions
10446 if (isTS) {
10447 var type = {type: "variable", style: "variable-3"};
10448 var tsKeywords = {
10449 // object-like things
10450 "interface": kw("class"),
10451 "implements": C,
10452 "namespace": C,
10453 "module": kw("module"),
10454 "enum": kw("module"),
10455 "type": kw("type"),
10456
10457 // scope modifiers
10458 "public": kw("modifier"),
10459 "private": kw("modifier"),
10460 "protected": kw("modifier"),
10461 "abstract": kw("modifier"),
10462
10463 // operators
10464 "as": operator,
10465
10466 // types
10467 "string": type, "number": type, "boolean": type, "any": type
10468 };
10469
10470 for (var attr in tsKeywords) {
10471 jsKeywords[attr] = tsKeywords[attr];
10472 }
10473 }
10474
10475 return jsKeywords;
10476 }();
10477
10478 var isOperatorChar = /[+\-*&%=<>!?|~^]/;
10479 var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/;
10480
10481 function readRegexp(stream) {
10482 var escaped = false, next, inSet = false;
10483 while ((next = stream.next()) != null) {
10484 if (!escaped) {
10485 if (next == "/" && !inSet) return;
10486 if (next == "[") inSet = true;
10487 else if (inSet && next == "]") inSet = false;
10488 }
10489 escaped = !escaped && next == "\\";
10490 }
10491 }
10492
10493 // Used as scratch variables to communicate multiple values without
10494 // consing up tons of objects.
10495 var type, content;
10496 function ret(tp, style, cont) {
10497 type = tp; content = cont;
10498 return style;
10499 }
10500 function tokenBase(stream, state) {
10501 var ch = stream.next();
10502 if (ch == '"' || ch == "'") {
10503 state.tokenize = tokenString(ch);
10504 return state.tokenize(stream, state);
10505 } else if (ch == "." && stream.match(/^\d+(?:[eE][+\-]?\d+)?/)) {
10506 return ret("number", "number");
10507 } else if (ch == "." && stream.match("..")) {
10508 return ret("spread", "meta");
10509 } else if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
10510 return ret(ch);
10511 } else if (ch == "=" && stream.eat(">")) {
10512 return ret("=>", "operator");
10513 } else if (ch == "0" && stream.eat(/x/i)) {
10514 stream.eatWhile(/[\da-f]/i);
10515 return ret("number", "number");
10516 } else if (ch == "0" && stream.eat(/o/i)) {
10517 stream.eatWhile(/[0-7]/i);
10518 return ret("number", "number");
10519 } else if (ch == "0" && stream.eat(/b/i)) {
10520 stream.eatWhile(/[01]/i);
10521 return ret("number", "number");
10522 } else if (/\d/.test(ch)) {
10523 stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);
10524 return ret("number", "number");
10525 } else if (ch == "/") {
10526 if (stream.eat("*")) {
10527 state.tokenize = tokenComment;
10528 return tokenComment(stream, state);
10529 } else if (stream.eat("/")) {
10530 stream.skipToEnd();
10531 return ret("comment", "comment");
10532 } else if (expressionAllowed(stream, state, 1)) {
10533 readRegexp(stream);
10534 stream.match(/^\b(([gimyu])(?![gimyu]*\2))+\b/);
10535 return ret("regexp", "string-2");
10536 } else {
10537 stream.eatWhile(isOperatorChar);
10538 return ret("operator", "operator", stream.current());
10539 }
10540 } else if (ch == "`") {
10541 state.tokenize = tokenQuasi;
10542 return tokenQuasi(stream, state);
10543 } else if (ch == "#") {
10544 stream.skipToEnd();
10545 return ret("error", "error");
10546 } else if (isOperatorChar.test(ch)) {
10547 stream.eatWhile(isOperatorChar);
10548 return ret("operator", "operator", stream.current());
10549 } else if (wordRE.test(ch)) {
10550 stream.eatWhile(wordRE);
10551 var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
10552 return (known && state.lastType != ".") ? ret(known.type, known.style, word) :
10553 ret("variable", "variable", word);
10554 }
10555 }
10556
10557 function tokenString(quote) {
10558 return function(stream, state) {
10559 var escaped = false, next;
10560 if (jsonldMode && stream.peek() == "@" && stream.match(isJsonldKeyword)){
10561 state.tokenize = tokenBase;
10562 return ret("jsonld-keyword", "meta");
10563 }
10564 while ((next = stream.next()) != null) {
10565 if (next == quote && !escaped) break;
10566 escaped = !escaped && next == "\\";
10567 }
10568 if (!escaped) state.tokenize = tokenBase;
10569 return ret("string", "string");
10570 };
10571 }
10572
10573 function tokenComment(stream, state) {
10574 var maybeEnd = false, ch;
10575 while (ch = stream.next()) {
10576 if (ch == "/" && maybeEnd) {
10577 state.tokenize = tokenBase;
10578 break;
10579 }
10580 maybeEnd = (ch == "*");
10581 }
10582 return ret("comment", "comment");
10583 }
10584
10585 function tokenQuasi(stream, state) {
10586 var escaped = false, next;
10587 while ((next = stream.next()) != null) {
10588 if (!escaped && (next == "`" || next == "$" && stream.eat("{"))) {
10589 state.tokenize = tokenBase;
10590 break;
10591 }
10592 escaped = !escaped && next == "\\";
10593 }
10594 return ret("quasi", "string-2", stream.current());
10595 }
10596
10597 var brackets = "([{}])";
10598 // This is a crude lookahead trick to try and notice that we're
10599 // parsing the argument patterns for a fat-arrow function before we
10600 // actually hit the arrow token. It only works if the arrow is on
10601 // the same line as the arguments and there's no strange noise
10602 // (comments) in between. Fallback is to only notice when we hit the
10603 // arrow, and not declare the arguments as locals for the arrow
10604 // body.
10605 function findFatArrow(stream, state) {
10606 if (state.fatArrowAt) state.fatArrowAt = null;
10607 var arrow = stream.string.indexOf("=>", stream.start);
10608 if (arrow < 0) return;
10609
10610 if (isTS) { // Try to skip TypeScript return type declarations after the arguments
10611 var m = /:\s*(?:\w+(?:<[^>]*>|\[\])?|\{[^}]*\})\s*$/.exec(stream.string.slice(stream.start, arrow))
10612 if (m) arrow = m.index
10613 }
10614
10615 var depth = 0, sawSomething = false;
10616 for (var pos = arrow - 1; pos >= 0; --pos) {
10617 var ch = stream.string.charAt(pos);
10618 var bracket = brackets.indexOf(ch);
10619 if (bracket >= 0 && bracket < 3) {
10620 if (!depth) { ++pos; break; }
10621 if (--depth == 0) { if (ch == "(") sawSomething = true; break; }
10622 } else if (bracket >= 3 && bracket < 6) {
10623 ++depth;
10624 } else if (wordRE.test(ch)) {
10625 sawSomething = true;
10626 } else if (/["'\/]/.test(ch)) {
10627 return;
10628 } else if (sawSomething && !depth) {
10629 ++pos;
10630 break;
10631 }
10632 }
10633 if (sawSomething && !depth) state.fatArrowAt = pos;
10634 }
10635
10636 // Parser
10637
10638 var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true, "jsonld-keyword": true};
10639
10640 function JSLexical(indented, column, type, align, prev, info) {
10641 this.indented = indented;
10642 this.column = column;
10643 this.type = type;
10644 this.prev = prev;
10645 this.info = info;
10646 if (align != null) this.align = align;
10647 }
10648
10649 function inScope(state, varname) {
10650 for (var v = state.localVars; v; v = v.next)
10651 if (v.name == varname) return true;
10652 for (var cx = state.context; cx; cx = cx.prev) {
10653 for (var v = cx.vars; v; v = v.next)
10654 if (v.name == varname) return true;
10655 }
10656 }
10657
10658 function parseJS(state, style, type, content, stream) {
10659 var cc = state.cc;
10660 // Communicate our context to the combinators.
10661 // (Less wasteful than consing up a hundred closures on every call.)
10662 cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; cx.style = style;
10663
10664 if (!state.lexical.hasOwnProperty("align"))
10665 state.lexical.align = true;
10666
10667 while(true) {
10668 var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;
10669 if (combinator(type, content)) {
10670 while(cc.length && cc[cc.length - 1].lex)
10671 cc.pop()();
10672 if (cx.marked) return cx.marked;
10673 if (type == "variable" && inScope(state, content)) return "variable-2";
10674 return style;
10675 }
10676 }
10677 }
10678
10679 // Combinator utils
10680
10681 var cx = {state: null, column: null, marked: null, cc: null};
10682 function pass() {
10683 for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
10684 }
10685 function cont() {
10686 pass.apply(null, arguments);
10687 return true;
10688 }
10689 function register(varname) {
10690 function inList(list) {
10691 for (var v = list; v; v = v.next)
10692 if (v.name == varname) return true;
10693 return false;
10694 }
10695 var state = cx.state;
10696 cx.marked = "def";
10697 if (state.context) {
10698 if (inList(state.localVars)) return;
10699 state.localVars = {name: varname, next: state.localVars};
10700 } else {
10701 if (inList(state.globalVars)) return;
10702 if (parserConfig.globalVars)
10703 state.globalVars = {name: varname, next: state.globalVars};
10704 }
10705 }
10706
10707 // Combinators
10708
10709 var defaultVars = {name: "this", next: {name: "arguments"}};
10710 function pushcontext() {
10711 cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
10712 cx.state.localVars = defaultVars;
10713 }
10714 function popcontext() {
10715 cx.state.localVars = cx.state.context.vars;
10716 cx.state.context = cx.state.context.prev;
10717 }
10718 function pushlex(type, info) {
10719 var result = function() {
10720 var state = cx.state, indent = state.indented;
10721 if (state.lexical.type == "stat") indent = state.lexical.indented;
10722 else for (var outer = state.lexical; outer && outer.type == ")" && outer.align; outer = outer.prev)
10723 indent = outer.indented;
10724 state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info);
10725 };
10726 result.lex = true;
10727 return result;
10728 }
10729 function poplex() {
10730 var state = cx.state;
10731 if (state.lexical.prev) {
10732 if (state.lexical.type == ")")
10733 state.indented = state.lexical.indented;
10734 state.lexical = state.lexical.prev;
10735 }
10736 }
10737 poplex.lex = true;
10738
10739 function expect(wanted) {
10740 function exp(type) {
10741 if (type == wanted) return cont();
10742 else if (wanted == ";") return pass();
10743 else return cont(exp);
10744 };
10745 return exp;
10746 }
10747
10748 function statement(type, value) {
10749 if (type == "var") return cont(pushlex("vardef", value.length), vardef, expect(";"), poplex);
10750 if (type == "keyword a") return cont(pushlex("form"), parenExpr, statement, poplex);
10751 if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
10752 if (type == "{") return cont(pushlex("}"), block, poplex);
10753 if (type == ";") return cont();
10754 if (type == "if") {
10755 if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex)
10756 cx.state.cc.pop()();
10757 return cont(pushlex("form"), parenExpr, statement, poplex, maybeelse);
10758 }
10759 if (type == "function") return cont(functiondef);
10760 if (type == "for") return cont(pushlex("form"), forspec, statement, poplex);
10761 if (type == "variable") return cont(pushlex("stat"), maybelabel);
10762 if (type == "switch") return cont(pushlex("form"), parenExpr, pushlex("}", "switch"), expect("{"),
10763 block, poplex, poplex);
10764 if (type == "case") return cont(expression, expect(":"));
10765 if (type == "default") return cont(expect(":"));
10766 if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
10767 statement, poplex, popcontext);
10768 if (type == "class") return cont(pushlex("form"), className, poplex);
10769 if (type == "export") return cont(pushlex("stat"), afterExport, poplex);
10770 if (type == "import") return cont(pushlex("stat"), afterImport, poplex);
10771 if (type == "module") return cont(pushlex("form"), pattern, pushlex("}"), expect("{"), block, poplex, poplex)
10772 if (type == "type") return cont(typeexpr, expect("operator"), typeexpr, expect(";"));
10773 if (type == "async") return cont(statement)
10774 return pass(pushlex("stat"), expression, expect(";"), poplex);
10775 }
10776 function expression(type) {
10777 return expressionInner(type, false);
10778 }
10779 function expressionNoComma(type) {
10780 return expressionInner(type, true);
10781 }
10782 function parenExpr(type) {
10783 if (type != "(") return pass()
10784 return cont(pushlex(")"), expression, expect(")"), poplex)
10785 }
10786 function expressionInner(type, noComma) {
10787 if (cx.state.fatArrowAt == cx.stream.start) {
10788 var body = noComma ? arrowBodyNoComma : arrowBody;
10789 if (type == "(") return cont(pushcontext, pushlex(")"), commasep(pattern, ")"), poplex, expect("=>"), body, popcontext);
10790 else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext);
10791 }
10792
10793 var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
10794 if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
10795 if (type == "function") return cont(functiondef, maybeop);
10796 if (type == "class") return cont(pushlex("form"), classExpression, poplex);
10797 if (type == "keyword c" || type == "async") return cont(noComma ? maybeexpressionNoComma : maybeexpression);
10798 if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop);
10799 if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression);
10800 if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop);
10801 if (type == "{") return contCommasep(objprop, "}", null, maybeop);
10802 if (type == "quasi") return pass(quasi, maybeop);
10803 if (type == "new") return cont(maybeTarget(noComma));
10804 return cont();
10805 }
10806 function maybeexpression(type) {
10807 if (type.match(/[;\}\)\],]/)) return pass();
10808 return pass(expression);
10809 }
10810 function maybeexpressionNoComma(type) {
10811 if (type.match(/[;\}\)\],]/)) return pass();
10812 return pass(expressionNoComma);
10813 }
10814
10815 function maybeoperatorComma(type, value) {
10816 if (type == ",") return cont(expression);
10817 return maybeoperatorNoComma(type, value, false);
10818 }
10819 function maybeoperatorNoComma(type, value, noComma) {
10820 var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma;
10821 var expr = noComma == false ? expression : expressionNoComma;
10822 if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext);
10823 if (type == "operator") {
10824 if (/\+\+|--/.test(value)) return cont(me);
10825 if (value == "?") return cont(expression, expect(":"), expr);
10826 return cont(expr);
10827 }
10828 if (type == "quasi") { return pass(quasi, me); }
10829 if (type == ";") return;
10830 if (type == "(") return contCommasep(expressionNoComma, ")", "call", me);
10831 if (type == ".") return cont(property, me);
10832 if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me);
10833 }
10834 function quasi(type, value) {
10835 if (type != "quasi") return pass();
10836 if (value.slice(value.length - 2) != "${") return cont(quasi);
10837 return cont(expression, continueQuasi);
10838 }
10839 function continueQuasi(type) {
10840 if (type == "}") {
10841 cx.marked = "string-2";
10842 cx.state.tokenize = tokenQuasi;
10843 return cont(quasi);
10844 }
10845 }
10846 function arrowBody(type) {
10847 findFatArrow(cx.stream, cx.state);
10848 return pass(type == "{" ? statement : expression);
10849 }
10850 function arrowBodyNoComma(type) {
10851 findFatArrow(cx.stream, cx.state);
10852 return pass(type == "{" ? statement : expressionNoComma);
10853 }
10854 function maybeTarget(noComma) {
10855 return function(type) {
10856 if (type == ".") return cont(noComma ? targetNoComma : target);
10857 else return pass(noComma ? expressionNoComma : expression);
10858 };
10859 }
10860 function target(_, value) {
10861 if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorComma); }
10862 }
10863 function targetNoComma(_, value) {
10864 if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorNoComma); }
10865 }
10866 function maybelabel(type) {
10867 if (type == ":") return cont(poplex, statement);
10868 return pass(maybeoperatorComma, expect(";"), poplex);
10869 }
10870 function property(type) {
10871 if (type == "variable") {cx.marked = "property"; return cont();}
10872 }
10873 function objprop(type, value) {
10874 if (type == "async") {
10875 cx.marked = "property";
10876 return cont(objprop);
10877 } else if (type == "variable" || cx.style == "keyword") {
10878 cx.marked = "property";
10879 if (value == "get" || value == "set") return cont(getterSetter);
10880 return cont(afterprop);
10881 } else if (type == "number" || type == "string") {
10882 cx.marked = jsonldMode ? "property" : (cx.style + " property");
10883 return cont(afterprop);
10884 } else if (type == "jsonld-keyword") {
10885 return cont(afterprop);
10886 } else if (type == "modifier") {
10887 return cont(objprop)
10888 } else if (type == "[") {
10889 return cont(expression, expect("]"), afterprop);
10890 } else if (type == "spread") {
10891 return cont(expression);
10892 } else if (type == ":") {
10893 return pass(afterprop)
10894 }
10895 }
10896 function getterSetter(type) {
10897 if (type != "variable") return pass(afterprop);
10898 cx.marked = "property";
10899 return cont(functiondef);
10900 }
10901 function afterprop(type) {
10902 if (type == ":") return cont(expressionNoComma);
10903 if (type == "(") return pass(functiondef);
10904 }
10905 function commasep(what, end) {
10906 function proceed(type, value) {
10907 if (type == ",") {
10908 var lex = cx.state.lexical;
10909 if (lex.info == "call") lex.pos = (lex.pos || 0) + 1;
10910 return cont(function(type, value) {
10911 if (type == end || value == end) return pass()
10912 return pass(what)
10913 }, proceed);
10914 }
10915 if (type == end || value == end) return cont();
10916 return cont(expect(end));
10917 }
10918 return function(type, value) {
10919 if (type == end || value == end) return cont();
10920 return pass(what, proceed);
10921 };
10922 }
10923 function contCommasep(what, end, info) {
10924 for (var i = 3; i < arguments.length; i++)
10925 cx.cc.push(arguments[i]);
10926 return cont(pushlex(end, info), commasep(what, end), poplex);
10927 }
10928 function block(type) {
10929 if (type == "}") return cont();
10930 return pass(statement, block);
10931 }
10932 function maybetype(type, value) {
10933 if (isTS) {
10934 if (type == ":") return cont(typeexpr);
10935 if (value == "?") return cont(maybetype);
10936 }
10937 }
10938 function typeexpr(type) {
10939 if (type == "variable") {cx.marked = "variable-3"; return cont(afterType);}
10940 if (type == "{") return cont(commasep(typeprop, "}"))
10941 if (type == "(") return cont(commasep(typearg, ")"), maybeReturnType)
10942 }
10943 function maybeReturnType(type) {
10944 if (type == "=>") return cont(typeexpr)
10945 }
10946 function typeprop(type) {
10947 if (type == "variable" || cx.style == "keyword") {
10948 cx.marked = "property"
10949 return cont(typeprop)
10950 } else if (type == ":") {
10951 return cont(typeexpr)
10952 }
10953 }
10954 function typearg(type) {
10955 if (type == "variable") return cont(typearg)
10956 else if (type == ":") return cont(typeexpr)
10957 }
10958 function afterType(type, value) {
10959 if (value == "<") return cont(commasep(typeexpr, ">"), afterType)
10960 if (type == "[") return cont(expect("]"), afterType)
10961 }
10962 function vardef() {
10963 return pass(pattern, maybetype, maybeAssign, vardefCont);
10964 }
10965 function pattern(type, value) {
10966 if (type == "modifier") return cont(pattern)
10967 if (type == "variable") { register(value); return cont(); }
10968 if (type == "spread") return cont(pattern);
10969 if (type == "[") return contCommasep(pattern, "]");
10970 if (type == "{") return contCommasep(proppattern, "}");
10971 }
10972 function proppattern(type, value) {
10973 if (type == "variable" && !cx.stream.match(/^\s*:/, false)) {
10974 register(value);
10975 return cont(maybeAssign);
10976 }
10977 if (type == "variable") cx.marked = "property";
10978 if (type == "spread") return cont(pattern);
10979 if (type == "}") return pass();
10980 return cont(expect(":"), pattern, maybeAssign);
10981 }
10982 function maybeAssign(_type, value) {
10983 if (value == "=") return cont(expressionNoComma);
10984 }
10985 function vardefCont(type) {
10986 if (type == ",") return cont(vardef);
10987 }
10988 function maybeelse(type, value) {
10989 if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex);
10990 }
10991 function forspec(type) {
10992 if (type == "(") return cont(pushlex(")"), forspec1, expect(")"), poplex);
10993 }
10994 function forspec1(type) {
10995 if (type == "var") return cont(vardef, expect(";"), forspec2);
10996 if (type == ";") return cont(forspec2);
10997 if (type == "variable") return cont(formaybeinof);
10998 return pass(expression, expect(";"), forspec2);
10999 }
11000 function formaybeinof(_type, value) {
11001 if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); }
11002 return cont(maybeoperatorComma, forspec2);
11003 }
11004 function forspec2(type, value) {
11005 if (type == ";") return cont(forspec3);
11006 if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); }
11007 return pass(expression, expect(";"), forspec3);
11008 }
11009 function forspec3(type) {
11010 if (type != ")") cont(expression);
11011 }
11012 function functiondef(type, value) {
11013 if (value == "*") {cx.marked = "keyword"; return cont(functiondef);}
11014 if (type == "variable") {register(value); return cont(functiondef);}
11015 if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, maybetype, statement, popcontext);
11016 }
11017 function funarg(type) {
11018 if (type == "spread") return cont(funarg);
11019 return pass(pattern, maybetype, maybeAssign);
11020 }
11021 function classExpression(type, value) {
11022 // Class expressions may have an optional name.
11023 if (type == "variable") return className(type, value);
11024 return classNameAfter(type, value);
11025 }
11026 function className(type, value) {
11027 if (type == "variable") {register(value); return cont(classNameAfter);}
11028 }
11029 function classNameAfter(type, value) {
11030 if (value == "extends" || value == "implements") return cont(isTS ? typeexpr : expression, classNameAfter);
11031 if (type == "{") return cont(pushlex("}"), classBody, poplex);
11032 }
11033 function classBody(type, value) {
11034 if (type == "variable" || cx.style == "keyword") {
11035 if ((value == "static" || value == "get" || value == "set" ||
11036 (isTS && (value == "public" || value == "private" || value == "protected" || value == "readonly" || value == "abstract"))) &&
11037 cx.stream.match(/^\s+[\w$\xa1-\uffff]/, false)) {
11038 cx.marked = "keyword";
11039 return cont(classBody);
11040 }
11041 cx.marked = "property";
11042 return cont(isTS ? classfield : functiondef, classBody);
11043 }
11044 if (value == "*") {
11045 cx.marked = "keyword";
11046 return cont(classBody);
11047 }
11048 if (type == ";") return cont(classBody);
11049 if (type == "}") return cont();
11050 }
11051 function classfield(type, value) {
11052 if (value == "?") return cont(classfield)
11053 if (type == ":") return cont(typeexpr, maybeAssign)
11054 return pass(functiondef)
11055 }
11056 function afterExport(_type, value) {
11057 if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); }
11058 if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); }
11059 return pass(statement);
11060 }
11061 function afterImport(type) {
11062 if (type == "string") return cont();
11063 return pass(importSpec, maybeFrom);
11064 }
11065 function importSpec(type, value) {
11066 if (type == "{") return contCommasep(importSpec, "}");
11067 if (type == "variable") register(value);
11068 if (value == "*") cx.marked = "keyword";
11069 return cont(maybeAs);
11070 }
11071 function maybeAs(_type, value) {
11072 if (value == "as") { cx.marked = "keyword"; return cont(importSpec); }
11073 }
11074 function maybeFrom(_type, value) {
11075 if (value == "from") { cx.marked = "keyword"; return cont(expression); }
11076 }
11077 function arrayLiteral(type) {
11078 if (type == "]") return cont();
11079 return pass(commasep(expressionNoComma, "]"));
11080 }
11081
11082 function isContinuedStatement(state, textAfter) {
11083 return state.lastType == "operator" || state.lastType == "," ||
11084 isOperatorChar.test(textAfter.charAt(0)) ||
11085 /[,.]/.test(textAfter.charAt(0));
11086 }
11087
11088 // Interface
11089
11090 return {
11091 startState: function(basecolumn) {
11092 var state = {
11093 tokenize: tokenBase,
11094 lastType: "sof",
11095 cc: [],
11096 lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
11097 localVars: parserConfig.localVars,
11098 context: parserConfig.localVars && {vars: parserConfig.localVars},
11099 indented: basecolumn || 0
11100 };
11101 if (parserConfig.globalVars && typeof parserConfig.globalVars == "object")
11102 state.globalVars = parserConfig.globalVars;
11103 return state;
11104 },
11105
11106 token: function(stream, state) {
11107 if (stream.sol()) {
11108 if (!state.lexical.hasOwnProperty("align"))
11109 state.lexical.align = false;
11110 state.indented = stream.indentation();
11111 findFatArrow(stream, state);
11112 }
11113 if (state.tokenize != tokenComment && stream.eatSpace()) return null;
11114 var style = state.tokenize(stream, state);
11115 if (type == "comment") return style;
11116 state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type;
11117 return parseJS(state, style, type, content, stream);
11118 },
11119
11120 indent: function(state, textAfter) {
11121 if (state.tokenize == tokenComment) return CodeMirror.Pass;
11122 if (state.tokenize != tokenBase) return 0;
11123 var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical, top
11124 // Kludge to prevent 'maybelse' from blocking lexical scope pops
11125 if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) {
11126 var c = state.cc[i];
11127 if (c == poplex) lexical = lexical.prev;
11128 else if (c != maybeelse) break;
11129 }
11130 while ((lexical.type == "stat" || lexical.type == "form") &&
11131 (firstChar == "}" || ((top = state.cc[state.cc.length - 1]) &&
11132 (top == maybeoperatorComma || top == maybeoperatorNoComma) &&
11133 !/^[,\.=+\-*:?[\(]/.test(textAfter))))
11134 lexical = lexical.prev;
11135 if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat")
11136 lexical = lexical.prev;
11137 var type = lexical.type, closing = firstChar == type;
11138
11139 if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info + 1 : 0);
11140 else if (type == "form" && firstChar == "{") return lexical.indented;
11141 else if (type == "form") return lexical.indented + indentUnit;
11142 else if (type == "stat")
11143 return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0);
11144 else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false)
11145 return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
11146 else if (lexical.align) return lexical.column + (closing ? 0 : 1);
11147 else return lexical.indented + (closing ? 0 : indentUnit);
11148 },
11149
11150 electricInput: /^\s*(?:case .*?:|default:|\{|\})$/,
11151 blockCommentStart: jsonMode ? null : "/*",
11152 blockCommentEnd: jsonMode ? null : "*/",
11153 lineComment: jsonMode ? null : "//",
11154 fold: "brace",
11155 closeBrackets: "()[]{}''\"\"``",
11156
11157 helperType: jsonMode ? "json" : "javascript",
11158 jsonldMode: jsonldMode,
11159 jsonMode: jsonMode,
11160
11161 expressionAllowed: expressionAllowed,
11162 skipExpression: function(state) {
11163 var top = state.cc[state.cc.length - 1]
11164 if (top == expression || top == expressionNoComma) state.cc.pop()
11165 }
11166 };
11167});
11168
11169CodeMirror.registerHelper("wordChars", "javascript", /[\w$]/);
11170
11171CodeMirror.defineMIME("text/javascript", "javascript");
11172CodeMirror.defineMIME("text/ecmascript", "javascript");
11173CodeMirror.defineMIME("application/javascript", "javascript");
11174CodeMirror.defineMIME("application/x-javascript", "javascript");
11175CodeMirror.defineMIME("application/ecmascript", "javascript");
11176CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
11177CodeMirror.defineMIME("application/x-json", {name: "javascript", json: true});
11178CodeMirror.defineMIME("application/ld+json", {name: "javascript", jsonld: true});
11179CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true });
11180CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true });
11181
11182});
11183
11184
11185
11186
11187
11188//HTMLMIXED:
11189
11190
11191
11192// CodeMirror, copyright (c) by Marijn Haverbeke and others
11193// Distributed under an MIT license: http://codemirror.net/LICENSE
11194
11195(function(mod) {
11196 if (typeof exports == "object" && typeof module == "object") // CommonJS
11197 mod(require("../../lib/codemirror"), require("../xml/xml"), require("../javascript/javascript"), require("../css/css"));
11198 else if (typeof define == "function" && define.amd) // AMD
11199 define(["../../lib/codemirror", "../xml/xml", "../javascript/javascript", "../css/css"], mod);
11200 else // Plain browser env
11201 mod(CodeMirror);
11202})(function(CodeMirror) {
11203 "use strict";
11204
11205 var defaultTags = {
11206 script: [
11207 ["lang", /(javascript|babel)/i, "javascript"],
11208 ["type", /^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^$/i, "javascript"],
11209 ["type", /./, "text/plain"],
11210 [null, null, "javascript"]
11211 ],
11212 style: [
11213 ["lang", /^css$/i, "css"],
11214 ["type", /^(text\/)?(x-)?(stylesheet|css)$/i, "css"],
11215 ["type", /./, "text/plain"],
11216 [null, null, "css"]
11217 ]
11218 };
11219
11220 function maybeBackup(stream, pat, style) {
11221 var cur = stream.current(), close = cur.search(pat);
11222 if (close > -1) {
11223 stream.backUp(cur.length - close);
11224 } else if (cur.match(/<\/?$/)) {
11225 stream.backUp(cur.length);
11226 if (!stream.match(pat, false)) stream.match(cur);
11227 }
11228 return style;
11229 }
11230
11231 var attrRegexpCache = {};
11232 function getAttrRegexp(attr) {
11233 var regexp = attrRegexpCache[attr];
11234 if (regexp) return regexp;
11235 return attrRegexpCache[attr] = new RegExp("\\s+" + attr + "\\s*=\\s*('|\")?([^'\"]+)('|\")?\\s*");
11236 }
11237
11238 function getAttrValue(text, attr) {
11239 var match = text.match(getAttrRegexp(attr))
11240 return match ? /^\s*(.*?)\s*$/.exec(match[2])[1] : ""
11241 }
11242
11243 function getTagRegexp(tagName, anchored) {
11244 return new RegExp((anchored ? "^" : "") + "<\/\s*" + tagName + "\s*>", "i");
11245 }
11246
11247 function addTags(from, to) {
11248 for (var tag in from) {
11249 var dest = to[tag] || (to[tag] = []);
11250 var source = from[tag];
11251 for (var i = source.length - 1; i >= 0; i--)
11252 dest.unshift(source[i])
11253 }
11254 }
11255
11256 function findMatchingMode(tagInfo, tagText) {
11257 for (var i = 0; i < tagInfo.length; i++) {
11258 var spec = tagInfo[i];
11259 if (!spec[0] || spec[1].test(getAttrValue(tagText, spec[0]))) return spec[2];
11260 }
11261 }
11262
11263 CodeMirror.defineMode("htmlmixed", function (config, parserConfig) {
11264 var htmlMode = CodeMirror.getMode(config, {
11265 name: "xml",
11266 htmlMode: true,
11267 multilineTagIndentFactor: parserConfig.multilineTagIndentFactor,
11268 multilineTagIndentPastTag: parserConfig.multilineTagIndentPastTag
11269 });
11270
11271 var tags = {};
11272 var configTags = parserConfig && parserConfig.tags, configScript = parserConfig && parserConfig.scriptTypes;
11273 addTags(defaultTags, tags);
11274 if (configTags) addTags(configTags, tags);
11275 if (configScript) for (var i = configScript.length - 1; i >= 0; i--)
11276 tags.script.unshift(["type", configScript[i].matches, configScript[i].mode])
11277
11278 function html(stream, state) {
11279 var style = htmlMode.token(stream, state.htmlState), tag = /\btag\b/.test(style), tagName
11280 if (tag && !/[<>\s\/]/.test(stream.current()) &&
11281 (tagName = state.htmlState.tagName && state.htmlState.tagName.toLowerCase()) &&
11282 tags.hasOwnProperty(tagName)) {
11283 state.inTag = tagName + " "
11284 } else if (state.inTag && tag && />$/.test(stream.current())) {
11285 var inTag = /^([\S]+) (.*)/.exec(state.inTag)
11286 state.inTag = null
11287 var modeSpec = stream.current() == ">" && findMatchingMode(tags[inTag[1]], inTag[2])
11288 var mode = CodeMirror.getMode(config, modeSpec)
11289 var endTagA = getTagRegexp(inTag[1], true), endTag = getTagRegexp(inTag[1], false);
11290 state.token = function (stream, state) {
11291 if (stream.match(endTagA, false)) {
11292 state.token = html;
11293 state.localState = state.localMode = null;
11294 return null;
11295 }
11296 return maybeBackup(stream, endTag, state.localMode.token(stream, state.localState));
11297 };
11298 state.localMode = mode;
11299 state.localState = CodeMirror.startState(mode, htmlMode.indent(state.htmlState, ""));
11300 } else if (state.inTag) {
11301 state.inTag += stream.current()
11302 if (stream.eol()) state.inTag += " "
11303 }
11304 return style;
11305 };
11306
11307 return {
11308 startState: function () {
11309 var state = CodeMirror.startState(htmlMode);
11310 return {token: html, inTag: null, localMode: null, localState: null, htmlState: state};
11311 },
11312
11313 copyState: function (state) {
11314 var local;
11315 if (state.localState) {
11316 local = CodeMirror.copyState(state.localMode, state.localState);
11317 }
11318 return {token: state.token, inTag: state.inTag,
11319 localMode: state.localMode, localState: local,
11320 htmlState: CodeMirror.copyState(htmlMode, state.htmlState)};
11321 },
11322
11323 token: function (stream, state) {
11324 return state.token(stream, state);
11325 },
11326
11327 indent: function (state, textAfter) {
11328 if (!state.localMode || /^\s*<\//.test(textAfter))
11329 return htmlMode.indent(state.htmlState, textAfter);
11330 else if (state.localMode.indent)
11331 return state.localMode.indent(state.localState, textAfter);
11332 else
11333 return CodeMirror.Pass;
11334 },
11335
11336 innerMode: function (state) {
11337 return {state: state.localState || state.htmlState, mode: state.localMode || htmlMode};
11338 }
11339 };
11340 }, "xml", "javascript", "css");
11341
11342 CodeMirror.defineMIME("text/html", "htmlmixed");
11343});