Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(14)

Side by Side Diff: third_party/WebKit/Source/devtools/front_end/cm/codemirror.js

Issue 2772343006: DevTools: Roll CodeMirror to 5.25.1 (Closed)
Patch Set: stray space Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others 1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE 2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3 3
4 // This is CodeMirror (http://codemirror.net), a code editor 4 // This is CodeMirror (http://codemirror.net), a code editor
5 // implemented in JavaScript on top of the browser's DOM. 5 // implemented in JavaScript on top of the browser's DOM.
6 // 6 //
7 // You can find some technical background for some of the code below 7 // You can find some technical background for some of the code below
8 // at http://marijnhaverbeke.nl/blog/#cm-internals . 8 // at http://marijnhaverbeke.nl/blog/#cm-internals .
9 9
10 (function(mod) { 10 (function (global, factory) {
11 if (typeof exports == "object" && typeof module == "object") // CommonJS 11 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
12 module.exports = mod(); 12 typeof define === 'function' && define.amd ? define(factory) :
13 else if (typeof define == "function" && define.amd) // AMD 13 (global.CodeMirror = factory());
14 return define([], mod); 14 }(this, (function () { 'use strict';
15 else // Plain browser env 15
16 (this || window).CodeMirror = mod(); 16 // Kludges for bugs and behavior differences that can't be feature
17 })(function() { 17 // detected are enabled based on userAgent etc sniffing.
18 "use strict"; 18 var userAgent = navigator.userAgent
19 19 var platform = navigator.platform
20 // BROWSER SNIFFING 20
21 21 var gecko = /gecko\/\d/i.test(userAgent)
22 // Kludges for bugs and behavior differences that can't be feature 22 var ie_upto10 = /MSIE \d/.test(userAgent)
23 // detected are enabled based on userAgent etc sniffing. 23 var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent)
24 var userAgent = navigator.userAgent; 24 var edge = /Edge\/(\d+)/.exec(userAgent)
25 var platform = navigator.platform; 25 var ie = ie_upto10 || ie_11up || edge
26 26 var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11 up)[1])
27 var gecko = /gecko\/\d/i.test(userAgent); 27 var webkit = !edge && /WebKit\//.test(userAgent)
28 var ie_upto10 = /MSIE \d/.test(userAgent); 28 var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent)
29 var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent); 29 var chrome = !edge && /Chrome\//.test(userAgent)
30 var ie = ie_upto10 || ie_11up; 30 var presto = /Opera\//.test(userAgent)
31 var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : ie_11up[1]); 31 var safari = /Apple Computer/.test(navigator.vendor)
32 var webkit = /WebKit\//.test(userAgent); 32 var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent)
33 var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent); 33 var phantom = /PhantomJS/.test(userAgent)
34 var chrome = /Chrome\//.test(userAgent); 34
35 var presto = /Opera\//.test(userAgent); 35 var ios = !edge && /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent )
36 var safari = /Apple Computer/.test(navigator.vendor); 36 var android = /Android/.test(userAgent)
37 var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent); 37 // This is woefully incomplete. Suggestions for alternative methods welcome.
38 var phantom = /PhantomJS/.test(userAgent); 38 var mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/ i.test(userAgent)
39 39 var mac = ios || /Mac/.test(platform)
40 var ios = /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent); 40 var chromeOS = /\bCrOS\b/.test(userAgent)
41 // This is woefully incomplete. Suggestions for alternative methods welcome. 41 var windows = /win/i.test(platform)
42 var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i .test(userAgent); 42
43 var mac = ios || /Mac/.test(platform); 43 var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/)
44 var chromeOS = /\bCrOS\b/.test(userAgent); 44 if (presto_version) { presto_version = Number(presto_version[1]) }
45 var windows = /win/i.test(platform); 45 if (presto_version && presto_version >= 15) { presto = false; webkit = true }
46 46 // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
47 var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/); 47 var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || prest o_version < 12.11))
48 if (presto_version) presto_version = Number(presto_version[1]); 48 var captureRightClick = gecko || (ie && ie_version >= 9)
49 if (presto_version && presto_version >= 15) { presto = false; webkit = true; } 49
50 // Some browsers use the wrong event properties to signal cmd/ctrl on OS X 50 function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") }
51 var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || pre sto_version < 12.11)); 51
52 var captureRightClick = gecko || (ie && ie_version >= 9); 52 var rmClass = function(node, cls) {
53 53 var current = node.className
54 // Optimize some code when these features are not used. 54 var match = classTest(cls).exec(current)
55 var sawReadOnlySpans = false, sawCollapsedSpans = false; 55 if (match) {
56 56 var after = current.slice(match.index + match[0].length)
57 // EDITOR CONSTRUCTOR 57 node.className = current.slice(0, match.index) + (after ? match[1] + after : "")
58 58 }
59 // A CodeMirror instance represents an editor. This is the object 59 }
60 // that user code is usually dealing with. 60
61 61 function removeChildren(e) {
62 function CodeMirror(place, options) { 62 for (var count = e.childNodes.length; count > 0; --count)
63 if (!(this instanceof CodeMirror)) return new CodeMirror(place, options); 63 { e.removeChild(e.firstChild) }
64 64 return e
65 this.options = options = options ? copyObj(options) : {}; 65 }
66 // Determine effective options based on given values and defaults. 66
67 copyObj(defaults, options, false); 67 function removeChildrenAndAdd(parent, e) {
68 setGuttersForLineNumbers(options); 68 return removeChildren(parent).appendChild(e)
69 69 }
70 var doc = options.value; 70
71 if (typeof doc == "string") doc = new Doc(doc, options.mode, null, options.l ineSeparator); 71 function elt(tag, content, className, style) {
72 this.doc = doc; 72 var e = document.createElement(tag)
73 73 if (className) { e.className = className }
74 var input = new CodeMirror.inputStyles[options.inputStyle](this); 74 if (style) { e.style.cssText = style }
75 var display = this.display = new Display(place, doc, input); 75 if (typeof content == "string") { e.appendChild(document.createTextNode(conten t)) }
76 display.wrapper.CodeMirror = this; 76 else if (content) { for (var i = 0; i < content.length; ++i) { e.appendChild(c ontent[i]) } }
77 updateGutters(this); 77 return e
78 themeChanged(this); 78 }
79 if (options.lineWrapping) 79 // wrapper for elt, which removes the elt from the accessibility tree
80 this.display.wrapper.className += " CodeMirror-wrap"; 80 function eltP(tag, content, className, style) {
81 if (options.autofocus && !mobile) display.input.focus(); 81 var e = elt(tag, content, className, style)
82 initScrollbars(this); 82 e.setAttribute("role", "presentation")
83 83 return e
84 this.state = { 84 }
85 keyMaps: [], // stores maps added by addKeyMap 85
86 overlays: [], // highlighting overlays, as added by addOverlay 86 var range
87 modeGen: 0, // bumped when mode/overlay changes, used to invalidate high lighting info 87 if (document.createRange) { range = function(node, start, end, endNode) {
88 overwrite: false, 88 var r = document.createRange()
89 delayingBlurEvent: false, 89 r.setEnd(endNode || node, end)
90 focused: false, 90 r.setStart(node, start)
91 suppressEdits: false, // used to disable editing during key handlers when in readOnly mode 91 return r
92 pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edit s in input.poll 92 } }
93 selectingText: false, 93 else { range = function(node, start, end) {
94 draggingText: false, 94 var r = document.body.createTextRange()
95 highlight: new Delayed(), // stores highlight worker timeout 95 try { r.moveToElementText(node.parentNode) }
96 keySeq: null, // Unfinished key sequence 96 catch(e) { return r }
97 specialChars: null 97 r.collapse(true)
98 }; 98 r.moveEnd("character", end)
99 99 r.moveStart("character", start)
100 var cm = this; 100 return r
101 101 } }
102 // Override magic textarea content restore that IE sometimes does 102
103 // on our hidden textarea on reload 103 function contains(parent, child) {
104 if (ie && ie_version < 11) setTimeout(function() { cm.display.input.reset(tr ue); }, 20); 104 if (child.nodeType == 3) // Android browser always returns false when child is a textnode
105 105 { child = child.parentNode }
106 registerEventHandlers(this); 106 if (parent.contains)
107 ensureGlobalHandlers(); 107 { return parent.contains(child) }
108 108 do {
109 startOperation(this); 109 if (child.nodeType == 11) { child = child.host }
110 this.curOp.forceUpdate = true; 110 if (child == parent) { return true }
111 attachDoc(this, doc); 111 } while (child = child.parentNode)
112 112 }
113 if ((options.autofocus && !mobile) || cm.hasFocus()) 113
114 setTimeout(bind(onFocus, this), 20); 114 function activeElt() {
115 else 115 // IE and Edge may throw an "Unspecified Error" when accessing document.active Element.
116 onBlur(this); 116 // IE < 10 will throw when accessed while the page is loading or in an iframe.
117 117 // IE > 9 and Edge will throw when accessed in an iframe if document.body is u navailable.
118 for (var opt in optionHandlers) if (optionHandlers.hasOwnProperty(opt)) 118 var activeElement
119 optionHandlers[opt](this, options[opt], Init); 119 try {
120 maybeUpdateLineNumberWidth(this); 120 activeElement = document.activeElement
121 if (options.finishInit) options.finishInit(this); 121 } catch(e) {
122 for (var i = 0; i < initHooks.length; ++i) initHooks[i](this); 122 activeElement = document.body || null
123 endOperation(this); 123 }
124 // Suppress optimizelegibility in Webkit, since it breaks text 124 while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.a ctiveElement)
125 // measuring on line wrapping boundaries. 125 { activeElement = activeElement.shadowRoot.activeElement }
126 if (webkit && options.lineWrapping && 126 return activeElement
127 getComputedStyle(display.lineDiv).textRendering == "optimizelegibility") 127 }
128 display.lineDiv.style.textRendering = "auto"; 128
129 } 129 function addClass(node, cls) {
130 130 var current = node.className
131 // DISPLAY CONSTRUCTOR 131 if (!classTest(cls).test(current)) { node.className += (current ? " " : "") + cls }
132 132 }
133 // The display handles the DOM integration, both for input reading 133 function joinClasses(a, b) {
134 // and content drawing. It holds references to DOM nodes and 134 var as = a.split(" ")
135 // display-related state. 135 for (var i = 0; i < as.length; i++)
136 136 { if (as[i] && !classTest(as[i]).test(b)) { b += " " + as[i] } }
137 function Display(place, doc, input) { 137 return b
138 var d = this; 138 }
139 this.input = input; 139
140 140 var selectInput = function(node) { node.select() }
141 // Covers bottom-right square when both scrollbars are present. 141 if (ios) // Mobile Safari apparently has a bug where select() is broken.
142 d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler"); 142 { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length } }
143 d.scrollbarFiller.setAttribute("cm-not-content", "true"); 143 else if (ie) // Suppress mysterious IE10 errors
144 // Covers bottom of gutter when coverGutterNextToScrollbar is on 144 { selectInput = function(node) { try { node.select() } catch(_e) {} } }
145 // and h scrollbar is present. 145
146 d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler"); 146 function bind(f) {
147 d.gutterFiller.setAttribute("cm-not-content", "true"); 147 var args = Array.prototype.slice.call(arguments, 1)
148 // Will contain the actual code, positioned to cover the viewport. 148 return function(){return f.apply(null, args)}
149 d.lineDiv = elt("div", null, "CodeMirror-code"); 149 }
150 // Elements are added to these to represent selection and cursors. 150
151 d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1"); 151 function copyObj(obj, target, overwrite) {
152 d.cursorDiv = elt("div", null, "CodeMirror-cursors"); 152 if (!target) { target = {} }
153 // A visibility: hidden element used to find the size of things. 153 for (var prop in obj)
154 d.measure = elt("div", null, "CodeMirror-measure"); 154 { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProp erty(prop)))
155 // When lines outside of the viewport are measured, they are drawn in this. 155 { target[prop] = obj[prop] } }
156 d.lineMeasure = elt("div", null, "CodeMirror-measure"); 156 return target
157 // Wraps everything that needs to exist inside the vertically-padded coordin ate system 157 }
158 d.lineSpace = elt("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursor Div, d.lineDiv], 158
159 null, "position: relative; outline: none"); 159 // Counts the column offset in a string, taking tabs into account.
160 // Moved around its parent to cover visible view. 160 // Used mostly to find indentation.
161 d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null, "position: relative"); 161 function countColumn(string, end, tabSize, startIndex, startValue) {
162 // Set to the height of the document, allowing scrolling. 162 if (end == null) {
163 d.sizer = elt("div", [d.mover], "CodeMirror-sizer"); 163 end = string.search(/[^\s\u00a0]/)
164 d.sizerWidth = null; 164 if (end == -1) { end = string.length }
165 // Behavior of elts with overflow: auto and padding is 165 }
166 // inconsistent across browsers. This is used to ensure the 166 for (var i = startIndex || 0, n = startValue || 0;;) {
167 // scrollable area is big enough. 167 var nextTab = string.indexOf("\t", i)
168 d.heightForcer = elt("div", null, null, "position: absolute; height: " + scr ollerGap + "px; width: 1px;"); 168 if (nextTab < 0 || nextTab >= end)
169 // Will contain the gutters, if any. 169 { return n + (end - i) }
170 d.gutters = elt("div", null, "CodeMirror-gutters"); 170 n += nextTab - i
171 d.lineGutter = null; 171 n += tabSize - (n % tabSize)
172 // Actual scrollable element. 172 i = nextTab + 1
173 d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-sc roll"); 173 }
174 d.scroller.setAttribute("tabIndex", "-1"); 174 }
175 // The element in which the editor lives. 175
176 d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "Cod eMirror"); 176 var Delayed = function Delayed() {this.id = null};
177 177 Delayed.prototype.set = function set (ms, f) {
178 // Work around IE7 z-index bug (not perfect, hence IE7 not really being supp orted) 178 clearTimeout(this.id)
179 if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.pa ddingRight = 0; } 179 this.id = setTimeout(f, ms)
180 if (!webkit && !(gecko && mobile)) d.scroller.draggable = true; 180 };
181 181
182 if (place) { 182 function indexOf(array, elt) {
183 if (place.appendChild) place.appendChild(d.wrapper); 183 for (var i = 0; i < array.length; ++i)
184 else place(d.wrapper); 184 { if (array[i] == elt) { return i } }
185 } 185 return -1
186 186 }
187 // Current rendered range (may be bigger than the view window). 187
188 d.viewFrom = d.viewTo = doc.first; 188 // Number of pixels added to scroller and sizer to hide scrollbar
189 d.reportedViewFrom = d.reportedViewTo = doc.first; 189 var scrollerGap = 30
190 // Information about the rendered lines. 190
191 d.view = []; 191 // Returned or thrown by various protocols to signal 'I'm not
192 d.renderedView = null; 192 // handling this'.
193 // Holds info about a single rendered line when it was rendered 193 var Pass = {toString: function(){return "CodeMirror.Pass"}}
194 // for measurement, while not in view. 194
195 d.externalMeasured = null; 195 // Reused option objects for setSelection & friends
196 // Empty space (in pixels) above the view 196 var sel_dontScroll = {scroll: false};
197 d.viewOffset = 0; 197 var sel_mouse = {origin: "*mouse"};
198 d.lastWrapHeight = d.lastWrapWidth = 0; 198 var sel_move = {origin: "+move"};
199 d.updateLineNumbers = null; 199 // The inverse of countColumn -- find the offset that corresponds to
200 200 // a particular column.
201 d.nativeBarWidth = d.barHeight = d.barWidth = 0; 201 function findColumn(string, goal, tabSize) {
202 d.scrollbarsClipped = false; 202 for (var pos = 0, col = 0;;) {
203 203 var nextTab = string.indexOf("\t", pos)
204 // Used to only resize the line number gutter when necessary (when 204 if (nextTab == -1) { nextTab = string.length }
205 // the amount of lines crosses a boundary that makes its width change) 205 var skipped = nextTab - pos
206 d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null; 206 if (nextTab == string.length || col + skipped >= goal)
207 // Set to true when a non-horizontal-scrolling line widget is 207 { return pos + Math.min(skipped, goal - col) }
208 // added. As an optimization, line widget aligning is skipped when 208 col += nextTab - pos
209 // this is false. 209 col += tabSize - (col % tabSize)
210 d.alignWidgets = false; 210 pos = nextTab + 1
211 211 if (col >= goal) { return pos }
212 d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null; 212 }
213 213 }
214 // Tracks the maximum line length so that the horizontal scrollbar 214
215 // can be kept static when scrolling. 215 var spaceStrs = [""]
216 d.maxLine = null; 216 function spaceStr(n) {
217 d.maxLineLength = 0; 217 while (spaceStrs.length <= n)
218 d.maxLineChanged = false; 218 { spaceStrs.push(lst(spaceStrs) + " ") }
219 219 return spaceStrs[n]
220 // Used for measuring wheel scrolling granularity 220 }
221 d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null; 221
222 222 function lst(arr) { return arr[arr.length-1] }
223 // True when shift is held down. 223
224 d.shift = false; 224 function map(array, f) {
225 225 var out = []
226 // Used to track whether anything happened since the context menu 226 for (var i = 0; i < array.length; i++) { out[i] = f(array[i], i) }
227 // was opened. 227 return out
228 d.selForContextMenu = null; 228 }
229 229
230 d.activeTouch = null; 230 function insertSorted(array, value, score) {
231 231 var pos = 0, priority = score(value)
232 input.init(d); 232 while (pos < array.length && score(array[pos]) <= priority) { pos++ }
233 } 233 array.splice(pos, 0, value)
234 234 }
235 // STATE UPDATES 235
236 236 function nothing() {}
237 // Used to get the editor into a consistent state again when options change. 237
238 238 function createObj(base, props) {
239 function loadMode(cm) { 239 var inst
240 cm.doc.mode = CodeMirror.getMode(cm.options, cm.doc.modeOption); 240 if (Object.create) {
241 resetModeState(cm); 241 inst = Object.create(base)
242 } 242 } else {
243 243 nothing.prototype = base
244 function resetModeState(cm) { 244 inst = new nothing()
245 cm.doc.iter(function(line) { 245 }
246 if (line.stateAfter) line.stateAfter = null; 246 if (props) { copyObj(props, inst) }
247 if (line.styles) line.styles = null; 247 return inst
248 }); 248 }
249 cm.doc.frontier = cm.doc.first; 249
250 startWorker(cm, 100); 250 var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040- \u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/
251 cm.state.modeGen++; 251 function isWordCharBasic(ch) {
252 if (cm.curOp) regChange(cm); 252 return /\w/.test(ch) || ch > "\x80" &&
253 } 253 (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch) )
254 254 }
255 function wrappingChanged(cm) { 255 function isWordChar(ch, helper) {
256 if (cm.options.lineWrapping) { 256 if (!helper) { return isWordCharBasic(ch) }
257 addClass(cm.display.wrapper, "CodeMirror-wrap"); 257 if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) { return true }
258 cm.display.sizer.style.minWidth = ""; 258 return helper.test(ch)
259 cm.display.sizerWidth = null; 259 }
260
261 function isEmpty(obj) {
262 for (var n in obj) { if (obj.hasOwnProperty(n) && obj[n]) { return false } }
263 return true
264 }
265
266 // Extending unicode characters. A series of a non-extending char +
267 // any number of extending chars is treated as a single unit as far
268 // as editing and measuring is concerned. This is not fully correct,
269 // since some scripts/fonts/browsers also treat other configurations
270 // of code points as a group.
271 var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2 \u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06 e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\ u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u09 51-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a 01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a8 1\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\u0f8 0-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u 1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u 108d\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\u1a 7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u 1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8 \u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2 dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua 80b\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]/
272 function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars. test(ch) }
273
274 // Returns a number from the range [`0`; `str.length`] unless `pos` is outside t hat range.
275 function skipExtendingChars(str, pos, dir) {
276 while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(po s))) { pos += dir }
277 return pos
278 }
279
280 // Returns the value from the range [`from`; `to`] that satisfies
281 // `pred` and is closest to `from`. Assumes that at least `to` satisfies `pred`.
282 function findFirst(pred, from, to) {
283 for (;;) {
284 if (Math.abs(from - to) <= 1) { return pred(from) ? from : to }
285 var mid = Math.floor((from + to) / 2)
286 if (pred(mid)) { to = mid }
287 else { from = mid }
288 }
289 }
290
291 // The display handles the DOM integration, both for input reading
292 // and content drawing. It holds references to DOM nodes and
293 // display-related state.
294
295 function Display(place, doc, input) {
296 var d = this
297 this.input = input
298
299 // Covers bottom-right square when both scrollbars are present.
300 d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler")
301 d.scrollbarFiller.setAttribute("cm-not-content", "true")
302 // Covers bottom of gutter when coverGutterNextToScrollbar is on
303 // and h scrollbar is present.
304 d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler")
305 d.gutterFiller.setAttribute("cm-not-content", "true")
306 // Will contain the actual code, positioned to cover the viewport.
307 d.lineDiv = eltP("div", null, "CodeMirror-code")
308 // Elements are added to these to represent selection and cursors.
309 d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1")
310 d.cursorDiv = elt("div", null, "CodeMirror-cursors")
311 // A visibility: hidden element used to find the size of things.
312 d.measure = elt("div", null, "CodeMirror-measure")
313 // When lines outside of the viewport are measured, they are drawn in this.
314 d.lineMeasure = elt("div", null, "CodeMirror-measure")
315 // Wraps everything that needs to exist inside the vertically-padded coordinat e system
316 d.lineSpace = eltP("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorD iv, d.lineDiv],
317 null, "position: relative; outline: none")
318 var lines = eltP("div", [d.lineSpace], "CodeMirror-lines")
319 // Moved around its parent to cover visible view.
320 d.mover = elt("div", [lines], null, "position: relative")
321 // Set to the height of the document, allowing scrolling.
322 d.sizer = elt("div", [d.mover], "CodeMirror-sizer")
323 d.sizerWidth = null
324 // Behavior of elts with overflow: auto and padding is
325 // inconsistent across browsers. This is used to ensure the
326 // scrollable area is big enough.
327 d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrol lerGap + "px; width: 1px;")
328 // Will contain the gutters, if any.
329 d.gutters = elt("div", null, "CodeMirror-gutters")
330 d.lineGutter = null
331 // Actual scrollable element.
332 d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scro ll")
333 d.scroller.setAttribute("tabIndex", "-1")
334 // The element in which the editor lives.
335 d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeM irror")
336
337 // Work around IE7 z-index bug (not perfect, hence IE7 not really being suppor ted)
338 if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.padd ingRight = 0 }
339 if (!webkit && !(gecko && mobile)) { d.scroller.draggable = true }
340
341 if (place) {
342 if (place.appendChild) { place.appendChild(d.wrapper) }
343 else { place(d.wrapper) }
344 }
345
346 // Current rendered range (may be bigger than the view window).
347 d.viewFrom = d.viewTo = doc.first
348 d.reportedViewFrom = d.reportedViewTo = doc.first
349 // Information about the rendered lines.
350 d.view = []
351 d.renderedView = null
352 // Holds info about a single rendered line when it was rendered
353 // for measurement, while not in view.
354 d.externalMeasured = null
355 // Empty space (in pixels) above the view
356 d.viewOffset = 0
357 d.lastWrapHeight = d.lastWrapWidth = 0
358 d.updateLineNumbers = null
359
360 d.nativeBarWidth = d.barHeight = d.barWidth = 0
361 d.scrollbarsClipped = false
362
363 // Used to only resize the line number gutter when necessary (when
364 // the amount of lines crosses a boundary that makes its width change)
365 d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null
366 // Set to true when a non-horizontal-scrolling line widget is
367 // added. As an optimization, line widget aligning is skipped when
368 // this is false.
369 d.alignWidgets = false
370
371 d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null
372
373 // Tracks the maximum line length so that the horizontal scrollbar
374 // can be kept static when scrolling.
375 d.maxLine = null
376 d.maxLineLength = 0
377 d.maxLineChanged = false
378
379 // Used for measuring wheel scrolling granularity
380 d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null
381
382 // True when shift is held down.
383 d.shift = false
384
385 // Used to track whether anything happened since the context menu
386 // was opened.
387 d.selForContextMenu = null
388
389 d.activeTouch = null
390
391 input.init(d)
392 }
393
394 // Find the line object corresponding to the given line number.
395 function getLine(doc, n) {
396 n -= doc.first
397 if (n < 0 || n >= doc.size) { throw new Error("There is no line " + (n + doc.f irst) + " in the document.") }
398 var chunk = doc
399 while (!chunk.lines) {
400 for (var i = 0;; ++i) {
401 var child = chunk.children[i], sz = child.chunkSize()
402 if (n < sz) { chunk = child; break }
403 n -= sz
404 }
405 }
406 return chunk.lines[n]
407 }
408
409 // Get the part of a document between two positions, as an array of
410 // strings.
411 function getBetween(doc, start, end) {
412 var out = [], n = start.line
413 doc.iter(start.line, end.line + 1, function (line) {
414 var text = line.text
415 if (n == end.line) { text = text.slice(0, end.ch) }
416 if (n == start.line) { text = text.slice(start.ch) }
417 out.push(text)
418 ++n
419 })
420 return out
421 }
422 // Get the lines between from and to, as array of strings.
423 function getLines(doc, from, to) {
424 var out = []
425 doc.iter(from, to, function (line) { out.push(line.text) }) // iter aborts whe n callback returns truthy value
426 return out
427 }
428
429 // Update the height of a line, propagating the height change
430 // upwards to parent nodes.
431 function updateLineHeight(line, height) {
432 var diff = height - line.height
433 if (diff) { for (var n = line; n; n = n.parent) { n.height += diff } }
434 }
435
436 // Given a line object, find its line number by walking up through
437 // its parent links.
438 function lineNo(line) {
439 if (line.parent == null) { return null }
440 var cur = line.parent, no = indexOf(cur.lines, line)
441 for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
442 for (var i = 0;; ++i) {
443 if (chunk.children[i] == cur) { break }
444 no += chunk.children[i].chunkSize()
445 }
446 }
447 return no + cur.first
448 }
449
450 // Find the line at the given vertical position, using the height
451 // information in the document tree.
452 function lineAtHeight(chunk, h) {
453 var n = chunk.first
454 outer: do {
455 for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) {
456 var child = chunk.children[i$1], ch = child.height
457 if (h < ch) { chunk = child; continue outer }
458 h -= ch
459 n += child.chunkSize()
460 }
461 return n
462 } while (!chunk.lines)
463 var i = 0
464 for (; i < chunk.lines.length; ++i) {
465 var line = chunk.lines[i], lh = line.height
466 if (h < lh) { break }
467 h -= lh
468 }
469 return n + i
470 }
471
472 function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size}
473
474 function lineNumberFor(options, i) {
475 return String(options.lineNumberFormatter(i + options.firstLineNumber))
476 }
477
478 // A Pos instance represents a position within the text.
479 function Pos(line, ch, sticky) {
480 if ( sticky === void 0 ) sticky = null;
481
482 if (!(this instanceof Pos)) { return new Pos(line, ch, sticky) }
483 this.line = line
484 this.ch = ch
485 this.sticky = sticky
486 }
487
488 // Compare two positions, return 0 if they are the same, a negative
489 // number when a is less, and a positive number otherwise.
490 function cmp(a, b) { return a.line - b.line || a.ch - b.ch }
491
492 function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 }
493
494 function copyPos(x) {return Pos(x.line, x.ch)}
495 function maxPos(a, b) { return cmp(a, b) < 0 ? b : a }
496 function minPos(a, b) { return cmp(a, b) < 0 ? a : b }
497
498 // Most of the external API clips given positions to make sure they
499 // actually exist within the document.
500 function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + do c.size - 1))}
501 function clipPos(doc, pos) {
502 if (pos.line < doc.first) { return Pos(doc.first, 0) }
503 var last = doc.first + doc.size - 1
504 if (pos.line > last) { return Pos(last, getLine(doc, last).text.length) }
505 return clipToLen(pos, getLine(doc, pos.line).text.length)
506 }
507 function clipToLen(pos, linelen) {
508 var ch = pos.ch
509 if (ch == null || ch > linelen) { return Pos(pos.line, linelen) }
510 else if (ch < 0) { return Pos(pos.line, 0) }
511 else { return pos }
512 }
513 function clipPosArray(doc, array) {
514 var out = []
515 for (var i = 0; i < array.length; i++) { out[i] = clipPos(doc, array[i]) }
516 return out
517 }
518
519 // Optimize some code when these features are not used.
520 var sawReadOnlySpans = false;
521 var sawCollapsedSpans = false;
522 function seeReadOnlySpans() {
523 sawReadOnlySpans = true
524 }
525
526 function seeCollapsedSpans() {
527 sawCollapsedSpans = true
528 }
529
530 // TEXTMARKER SPANS
531
532 function MarkedSpan(marker, from, to) {
533 this.marker = marker
534 this.from = from; this.to = to
535 }
536
537 // Search an array of spans for a span matching the given marker.
538 function getMarkedSpanFor(spans, marker) {
539 if (spans) { for (var i = 0; i < spans.length; ++i) {
540 var span = spans[i]
541 if (span.marker == marker) { return span }
542 } }
543 }
544 // Remove a span from an array, returning undefined if no spans are
545 // left (we don't store arrays for lines without spans).
546 function removeMarkedSpan(spans, span) {
547 var r
548 for (var i = 0; i < spans.length; ++i)
549 { if (spans[i] != span) { (r || (r = [])).push(spans[i]) } }
550 return r
551 }
552 // Add a span to a line.
553 function addMarkedSpan(line, span) {
554 line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span]
555 span.marker.attachLine(line)
556 }
557
558 // Used for the algorithm that adjusts markers for a change in the
559 // document. These functions cut an array of spans at a given
560 // character position, returning an array of remaining chunks (or
561 // undefined if nothing remains).
562 function markedSpansBefore(old, startCh, isInsert) {
563 var nw
564 if (old) { for (var i = 0; i < old.length; ++i) {
565 var span = old[i], marker = span.marker
566 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh)
567 if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!i sInsert || !span.marker.insertLeft)) {
568 var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= sta rtCh : span.to > startCh)
569 ;(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to))
570 }
571 } }
572 return nw
573 }
574 function markedSpansAfter(old, endCh, isInsert) {
575 var nw
576 if (old) { for (var i = 0; i < old.length; ++i) {
577 var span = old[i], marker = span.marker
578 var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh)
579 if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInse rt || span.marker.insertLeft)) {
580 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh)
581 ;(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span. from - endCh,
582 span.to == null ? null : span.to - e ndCh))
583 }
584 } }
585 return nw
586 }
587
588 // Given a change object, compute the new set of marker spans that
589 // cover the line in which the change took place. Removes spans
590 // entirely within the change, reconnects spans belonging to the
591 // same marker that appear on both sides of the change, and cuts off
592 // spans partially within the change. Returns an array of span
593 // arrays with one element for each line in (after) the change.
594 function stretchSpansOverChange(doc, change) {
595 if (change.full) { return null }
596 var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line) .markedSpans
597 var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).mark edSpans
598 if (!oldFirst && !oldLast) { return null }
599
600 var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from , change.to) == 0
601 // Get the spans that 'stick out' on both sides
602 var first = markedSpansBefore(oldFirst, startCh, isInsert)
603 var last = markedSpansAfter(oldLast, endCh, isInsert)
604
605 // Next, merge those two ends
606 var sameLine = change.text.length == 1, offset = lst(change.text).length + (sa meLine ? startCh : 0)
607 if (first) {
608 // Fix up .to properties of first
609 for (var i = 0; i < first.length; ++i) {
610 var span = first[i]
611 if (span.to == null) {
612 var found = getMarkedSpanFor(last, span.marker)
613 if (!found) { span.to = startCh }
614 else if (sameLine) { span.to = found.to == null ? null : found.to + offs et }
615 }
616 }
617 }
618 if (last) {
619 // Fix up .from in last (or move them into first in case of sameLine)
620 for (var i$1 = 0; i$1 < last.length; ++i$1) {
621 var span$1 = last[i$1]
622 if (span$1.to != null) { span$1.to += offset }
623 if (span$1.from == null) {
624 var found$1 = getMarkedSpanFor(first, span$1.marker)
625 if (!found$1) {
626 span$1.from = offset
627 if (sameLine) { (first || (first = [])).push(span$1) }
628 }
629 } else {
630 span$1.from += offset
631 if (sameLine) { (first || (first = [])).push(span$1) }
632 }
633 }
634 }
635 // Make sure we didn't create any zero-length spans
636 if (first) { first = clearEmptySpans(first) }
637 if (last && last != first) { last = clearEmptySpans(last) }
638
639 var newMarkers = [first]
640 if (!sameLine) {
641 // Fill gap with whole-line-spans
642 var gap = change.text.length - 2, gapMarkers
643 if (gap > 0 && first)
644 { for (var i$2 = 0; i$2 < first.length; ++i$2)
645 { if (first[i$2].to == null)
646 { (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i$2].mar ker, null, null)) } } }
647 for (var i$3 = 0; i$3 < gap; ++i$3)
648 { newMarkers.push(gapMarkers) }
649 newMarkers.push(last)
650 }
651 return newMarkers
652 }
653
654 // Remove spans that are empty and don't have a clearWhenEmpty
655 // option of false.
656 function clearEmptySpans(spans) {
657 for (var i = 0; i < spans.length; ++i) {
658 var span = spans[i]
659 if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)
660 { spans.splice(i--, 1) }
661 }
662 if (!spans.length) { return null }
663 return spans
664 }
665
666 // Used to 'clip' out readOnly ranges when making a change.
667 function removeReadOnlyRanges(doc, from, to) {
668 var markers = null
669 doc.iter(from.line, to.line + 1, function (line) {
670 if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
671 var mark = line.markedSpans[i].marker
672 if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
673 { (markers || (markers = [])).push(mark) }
674 } }
675 })
676 if (!markers) { return null }
677 var parts = [{from: from, to: to}]
678 for (var i = 0; i < markers.length; ++i) {
679 var mk = markers[i], m = mk.find(0)
680 for (var j = 0; j < parts.length; ++j) {
681 var p = parts[j]
682 if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) { continue }
683 var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to)
684 if (dfrom < 0 || !mk.inclusiveLeft && !dfrom)
685 { newParts.push({from: p.from, to: m.from}) }
686 if (dto > 0 || !mk.inclusiveRight && !dto)
687 { newParts.push({from: m.to, to: p.to}) }
688 parts.splice.apply(parts, newParts)
689 j += newParts.length - 3
690 }
691 }
692 return parts
693 }
694
695 // Connect or disconnect spans from a line.
696 function detachMarkedSpans(line) {
697 var spans = line.markedSpans
698 if (!spans) { return }
699 for (var i = 0; i < spans.length; ++i)
700 { spans[i].marker.detachLine(line) }
701 line.markedSpans = null
702 }
703 function attachMarkedSpans(line, spans) {
704 if (!spans) { return }
705 for (var i = 0; i < spans.length; ++i)
706 { spans[i].marker.attachLine(line) }
707 line.markedSpans = spans
708 }
709
710 // Helpers used when computing which overlapping collapsed span
711 // counts as the larger one.
712 function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 }
713 function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 }
714
715 // Returns a number indicating which of two overlapping collapsed
716 // spans is larger (and thus includes the other). Falls back to
717 // comparing ids when the spans cover exactly the same range.
718 function compareCollapsedMarkers(a, b) {
719 var lenDiff = a.lines.length - b.lines.length
720 if (lenDiff != 0) { return lenDiff }
721 var aPos = a.find(), bPos = b.find()
722 var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b)
723 if (fromCmp) { return -fromCmp }
724 var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b)
725 if (toCmp) { return toCmp }
726 return b.id - a.id
727 }
728
729 // Find out whether a line ends or starts in a collapsed span. If
730 // so, return the marker for that span.
731 function collapsedSpanAtSide(line, start) {
732 var sps = sawCollapsedSpans && line.markedSpans, found
733 if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
734 sp = sps[i]
735 if (sp.marker.collapsed && (start ? sp.from : sp.to) == null &&
736 (!found || compareCollapsedMarkers(found, sp.marker) < 0))
737 { found = sp.marker }
738 } }
739 return found
740 }
741 function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) }
742 function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) }
743
744 // Test whether there exists a collapsed span that partially
745 // overlaps (covers the start or end, but not both) of a new span.
746 // Such overlap is not allowed.
747 function conflictingCollapsedRange(doc, lineNo, from, to, marker) {
748 var line = getLine(doc, lineNo)
749 var sps = sawCollapsedSpans && line.markedSpans
750 if (sps) { for (var i = 0; i < sps.length; ++i) {
751 var sp = sps[i]
752 if (!sp.marker.collapsed) { continue }
753 var found = sp.marker.find(0)
754 var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(mark er)
755 var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker)
756 if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue }
757 if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp( found.to, from) >= 0 : cmp(found.to, from) > 0) ||
758 fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp( found.from, to) <= 0 : cmp(found.from, to) < 0))
759 { return true }
760 } }
761 }
762
763 // A visual line is a line as drawn on the screen. Folding, for
764 // example, can cause multiple logical lines to appear on the same
765 // visual line. This finds the start of the visual line that the
766 // given line is part of (usually that is the line itself).
767 function visualLine(line) {
768 var merged
769 while (merged = collapsedSpanAtStart(line))
770 { line = merged.find(-1, true).line }
771 return line
772 }
773
774 function visualLineEnd(line) {
775 var merged
776 while (merged = collapsedSpanAtEnd(line))
777 { line = merged.find(1, true).line }
778 return line
779 }
780
781 // Returns an array of logical lines that continue the visual line
782 // started by the argument, or undefined if there are no such lines.
783 function visualLineContinued(line) {
784 var merged, lines
785 while (merged = collapsedSpanAtEnd(line)) {
786 line = merged.find(1, true).line
787 ;(lines || (lines = [])).push(line)
788 }
789 return lines
790 }
791
792 // Get the line number of the start of the visual line that the
793 // given line number is part of.
794 function visualLineNo(doc, lineN) {
795 var line = getLine(doc, lineN), vis = visualLine(line)
796 if (line == vis) { return lineN }
797 return lineNo(vis)
798 }
799
800 // Get the line number of the start of the next visual line after
801 // the given line.
802 function visualLineEndNo(doc, lineN) {
803 if (lineN > doc.lastLine()) { return lineN }
804 var line = getLine(doc, lineN), merged
805 if (!lineIsHidden(doc, line)) { return lineN }
806 while (merged = collapsedSpanAtEnd(line))
807 { line = merged.find(1, true).line }
808 return lineNo(line) + 1
809 }
810
811 // Compute whether a line is hidden. Lines count as hidden when they
812 // are part of a visual line that starts with another line, or when
813 // they are entirely covered by collapsed, non-widget span.
814 function lineIsHidden(doc, line) {
815 var sps = sawCollapsedSpans && line.markedSpans
816 if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
817 sp = sps[i]
818 if (!sp.marker.collapsed) { continue }
819 if (sp.from == null) { return true }
820 if (sp.marker.widgetNode) { continue }
821 if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
822 { return true }
823 } }
824 }
825 function lineIsHiddenInner(doc, line, span) {
826 if (span.to == null) {
827 var end = span.marker.find(1, true)
828 return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpan s, span.marker))
829 }
830 if (span.marker.inclusiveRight && span.to == line.text.length)
831 { return true }
832 for (var sp = (void 0), i = 0; i < line.markedSpans.length; ++i) {
833 sp = line.markedSpans[i]
834 if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to &&
835 (sp.to == null || sp.to != span.from) &&
836 (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
837 lineIsHiddenInner(doc, line, sp)) { return true }
838 }
839 }
840
841 // Find the height above the given line.
842 function heightAtLine(lineObj) {
843 lineObj = visualLine(lineObj)
844
845 var h = 0, chunk = lineObj.parent
846 for (var i = 0; i < chunk.lines.length; ++i) {
847 var line = chunk.lines[i]
848 if (line == lineObj) { break }
849 else { h += line.height }
850 }
851 for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
852 for (var i$1 = 0; i$1 < p.children.length; ++i$1) {
853 var cur = p.children[i$1]
854 if (cur == chunk) { break }
855 else { h += cur.height }
856 }
857 }
858 return h
859 }
860
861 // Compute the character length of a line, taking into account
862 // collapsed ranges (see markText) that might hide parts, and join
863 // other lines onto it.
864 function lineLength(line) {
865 if (line.height == 0) { return 0 }
866 var len = line.text.length, merged, cur = line
867 while (merged = collapsedSpanAtStart(cur)) {
868 var found = merged.find(0, true)
869 cur = found.from.line
870 len += found.from.ch - found.to.ch
871 }
872 cur = line
873 while (merged = collapsedSpanAtEnd(cur)) {
874 var found$1 = merged.find(0, true)
875 len -= cur.text.length - found$1.from.ch
876 cur = found$1.to.line
877 len += cur.text.length - found$1.to.ch
878 }
879 return len
880 }
881
882 // Find the longest line in the document.
883 function findMaxLine(cm) {
884 var d = cm.display, doc = cm.doc
885 d.maxLine = getLine(doc, doc.first)
886 d.maxLineLength = lineLength(d.maxLine)
887 d.maxLineChanged = true
888 doc.iter(function (line) {
889 var len = lineLength(line)
890 if (len > d.maxLineLength) {
891 d.maxLineLength = len
892 d.maxLine = line
893 }
894 })
895 }
896
897 // BIDI HELPERS
898
899 function iterateBidiSections(order, from, to, f) {
900 if (!order) { return f(from, to, "ltr") }
901 var found = false
902 for (var i = 0; i < order.length; ++i) {
903 var part = order[i]
904 if (part.from < to && part.to > from || from == to && part.to == from) {
905 f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl " : "ltr")
906 found = true
907 }
908 }
909 if (!found) { f(from, to, "ltr") }
910 }
911
912 var bidiOther = null
913 function getBidiPartAt(order, ch, sticky) {
914 var found
915 bidiOther = null
916 for (var i = 0; i < order.length; ++i) {
917 var cur = order[i]
918 if (cur.from < ch && cur.to > ch) { return i }
919 if (cur.to == ch) {
920 if (cur.from != cur.to && sticky == "before") { found = i }
921 else { bidiOther = i }
922 }
923 if (cur.from == ch) {
924 if (cur.from != cur.to && sticky != "before") { found = i }
925 else { bidiOther = i }
926 }
927 }
928 return found != null ? found : bidiOther
929 }
930
931 // Bidirectional ordering algorithm
932 // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
933 // that this (partially) implements.
934
935 // One-char codes used for character types:
936 // L (L): Left-to-Right
937 // R (R): Right-to-Left
938 // r (AL): Right-to-Left Arabic
939 // 1 (EN): European Number
940 // + (ES): European Number Separator
941 // % (ET): European Number Terminator
942 // n (AN): Arabic Number
943 // , (CS): Common Number Separator
944 // m (NSM): Non-Spacing Mark
945 // b (BN): Boundary Neutral
946 // s (B): Paragraph Separator
947 // t (S): Segment Separator
948 // w (WS): Whitespace
949 // N (ON): Other Neutrals
950
951 // Returns null if characters are ordered as they appear
952 // (left-to-right), or an array of sections ({from, to, level}
953 // objects) in the order in which they occur visually.
954 var bidiOrdering = (function() {
955 // Character types for codepoints 0 to 0xff
956 var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNN NNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbb bbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLL LLLLLLLLLLLLLLLLLLLLLLLLLN"
957 // Character types for codepoints 0x600 to 0x6f9
958 var arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr rrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrr rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmm mmnNmmmmmmrrmmNmmmmrr1111111111"
959 function charType(code) {
960 if (code <= 0xf7) { return lowTypes.charAt(code) }
961 else if (0x590 <= code && code <= 0x5f4) { return "R" }
962 else if (0x600 <= code && code <= 0x6f9) { return arabicTypes.charAt(code - 0x600) }
963 else if (0x6ee <= code && code <= 0x8ac) { return "r" }
964 else if (0x2000 <= code && code <= 0x200b) { return "w" }
965 else if (code == 0x200c) { return "b" }
966 else { return "L" }
967 }
968
969 var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/
970 var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsA sNum = /[1n]/
971
972 function BidiSpan(level, from, to) {
973 this.level = level
974 this.from = from; this.to = to
975 }
976
977 return function(str, direction) {
978 var outerType = direction == "ltr" ? "L" : "R"
979
980 if (str.length == 0 || direction == "ltr" && !bidiRE.test(str)) { return fal se }
981 var len = str.length, types = []
982 for (var i = 0; i < len; ++i)
983 { types.push(charType(str.charCodeAt(i))) }
984
985 // W1. Examine each non-spacing mark (NSM) in the level run, and
986 // change the type of the NSM to the type of the previous
987 // character. If the NSM is at the start of the level run, it will
988 // get the type of sor.
989 for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) {
990 var type = types[i$1]
991 if (type == "m") { types[i$1] = prev }
992 else { prev = type }
993 }
994
995 // W2. Search backwards from each instance of a European number
996 // until the first strong type (R, L, AL, or sor) is found. If an
997 // AL is found, change the type of the European number to Arabic
998 // number.
999 // W3. Change all ALs to R.
1000 for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) {
1001 var type$1 = types[i$2]
1002 if (type$1 == "1" && cur == "r") { types[i$2] = "n" }
1003 else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == "r") { types [i$2] = "R" } }
1004 }
1005
1006 // W4. A single European separator between two European numbers
1007 // changes to a European number. A single common separator between
1008 // two numbers of the same type changes to that type.
1009 for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) {
1010 var type$2 = types[i$3]
1011 if (type$2 == "+" && prev$1 == "1" && types[i$3+1] == "1") { types[i$3] = "1" }
1012 else if (type$2 == "," && prev$1 == types[i$3+1] &&
1013 (prev$1 == "1" || prev$1 == "n")) { types[i$3] = prev$1 }
1014 prev$1 = type$2
1015 }
1016
1017 // W5. A sequence of European terminators adjacent to European
1018 // numbers changes to all European numbers.
1019 // W6. Otherwise, separators and terminators change to Other
1020 // Neutral.
1021 for (var i$4 = 0; i$4 < len; ++i$4) {
1022 var type$3 = types[i$4]
1023 if (type$3 == ",") { types[i$4] = "N" }
1024 else if (type$3 == "%") {
1025 var end = (void 0)
1026 for (end = i$4 + 1; end < len && types[end] == "%"; ++end) {}
1027 var replace = (i$4 && types[i$4-1] == "!") || (end < len && types[end] = = "1") ? "1" : "N"
1028 for (var j = i$4; j < end; ++j) { types[j] = replace }
1029 i$4 = end - 1
1030 }
1031 }
1032
1033 // W7. Search backwards from each instance of a European number
1034 // until the first strong type (R, L, or sor) is found. If an L is
1035 // found, then change the type of the European number to L.
1036 for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) {
1037 var type$4 = types[i$5]
1038 if (cur$1 == "L" && type$4 == "1") { types[i$5] = "L" }
1039 else if (isStrong.test(type$4)) { cur$1 = type$4 }
1040 }
1041
1042 // N1. A sequence of neutrals takes the direction of the
1043 // surrounding strong text if the text on both sides has the same
1044 // direction. European and Arabic numbers act as if they were R in
1045 // terms of their influence on neutrals. Start-of-level-run (sor)
1046 // and end-of-level-run (eor) are used at level run boundaries.
1047 // N2. Any remaining neutrals take the embedding direction.
1048 for (var i$6 = 0; i$6 < len; ++i$6) {
1049 if (isNeutral.test(types[i$6])) {
1050 var end$1 = (void 0)
1051 for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end $1) {}
1052 var before = (i$6 ? types[i$6-1] : outerType) == "L"
1053 var after = (end$1 < len ? types[end$1] : outerType) == "L"
1054 var replace$1 = before == after ? (before ? "L" : "R") : outerType
1055 for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1 }
1056 i$6 = end$1 - 1
1057 }
1058 }
1059
1060 // Here we depart from the documented algorithm, in order to avoid
1061 // building up an actual levels array. Since there are only three
1062 // levels (0, 1, 2) in an implementation that doesn't take
1063 // explicit embedding into account, we can build up the order on
1064 // the fly, without following the level-based algorithm.
1065 var order = [], m
1066 for (var i$7 = 0; i$7 < len;) {
1067 if (countsAsLeft.test(types[i$7])) {
1068 var start = i$7
1069 for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {}
1070 order.push(new BidiSpan(0, start, i$7))
1071 } else {
1072 var pos = i$7, at = order.length
1073 for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {}
1074 for (var j$2 = pos; j$2 < i$7;) {
1075 if (countsAsNum.test(types[j$2])) {
1076 if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)) }
1077 var nstart = j$2
1078 for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {}
1079 order.splice(at, 0, new BidiSpan(2, nstart, j$2))
1080 pos = j$2
1081 } else { ++j$2 }
1082 }
1083 if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)) }
1084 }
1085 }
1086 if (order[0].level == 1 && (m = str.match(/^\s+/))) {
1087 order[0].from = m[0].length
1088 order.unshift(new BidiSpan(0, 0, m[0].length))
1089 }
1090 if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
1091 lst(order).to -= m[0].length
1092 order.push(new BidiSpan(0, len - m[0].length, len))
1093 }
1094
1095 return direction == "rtl" ? order.reverse() : order
1096 }
1097 })()
1098
1099 // Get the bidi ordering for the given line (and cache it). Returns
1100 // false for lines that are fully left-to-right, and an array of
1101 // BidiSpan objects otherwise.
1102 function getOrder(line, direction) {
1103 var order = line.order
1104 if (order == null) { order = line.order = bidiOrdering(line.text, direction) }
1105 return order
1106 }
1107
1108 function moveCharLogically(line, ch, dir) {
1109 var target = skipExtendingChars(line.text, ch + dir, dir)
1110 return target < 0 || target > line.text.length ? null : target
1111 }
1112
1113 function moveLogically(line, start, dir) {
1114 var ch = moveCharLogically(line, start.ch, dir)
1115 return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before ")
1116 }
1117
1118 function endOfLine(visually, cm, lineObj, lineNo, dir) {
1119 if (visually) {
1120 var order = getOrder(lineObj, cm.doc.direction)
1121 if (order) {
1122 var part = dir < 0 ? lst(order) : order[0]
1123 var moveInStorageOrder = (dir < 0) == (part.level == 1)
1124 var sticky = moveInStorageOrder ? "after" : "before"
1125 var ch
1126 // With a wrapped rtl chunk (possibly spanning multiple bidi parts),
1127 // it could be that the last bidi part is not on the last visual line,
1128 // since visual lines contain content order-consecutive chunks.
1129 // Thus, in rtl, we are looking for the first (content-order) character
1130 // in the rtl chunk that is on the last line (that is, the same line
1131 // as the last (content-order) character).
1132 if (part.level > 0) {
1133 var prep = prepareMeasureForLine(cm, lineObj)
1134 ch = dir < 0 ? lineObj.text.length - 1 : 0
1135 var targetTop = measureCharPrepared(cm, prep, ch).top
1136 ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch). top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, c h)
1137 if (sticky == "before") { ch = moveCharLogically(lineObj, ch, 1, true) }
1138 } else { ch = dir < 0 ? part.to : part.from }
1139 return new Pos(lineNo, ch, sticky)
1140 }
1141 }
1142 return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after")
1143 }
1144
1145 function moveVisually(cm, line, start, dir) {
1146 var bidi = getOrder(line, cm.doc.direction)
1147 if (!bidi) { return moveLogically(line, start, dir) }
1148 if (start.ch >= line.text.length) {
1149 start.ch = line.text.length
1150 start.sticky = "before"
1151 } else if (start.ch <= 0) {
1152 start.ch = 0
1153 start.sticky = "after"
1154 }
1155 var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos ]
1156 if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > s tart.ch : part.from < start.ch)) {
1157 // Case 1: We move within an ltr part in an ltr editor. Even with wrapped li nes,
1158 // nothing interesting happens.
1159 return moveLogically(line, start, dir)
1160 }
1161
1162 var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof P os ? pos.ch : pos, dir); }
1163 var prep
1164 var getWrappedLineExtent = function (ch) {
1165 if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} }
1166 prep = prep || prepareMeasureForLine(cm, line)
1167 return wrappedLineExtentChar(cm, line, prep, ch)
1168 }
1169 var wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(sta rt, -1) : start.ch)
1170
1171 if (cm.doc.direction == "rtl" || part.level == 1) {
1172 var moveInStorageOrder = (part.level == 1) == (dir < 0)
1173 var ch = mv(start, moveInStorageOrder ? 1 : -1)
1174 if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLin eExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) {
1175 // Case 2: We move within an rtl part or in an rtl editor on the same visu al line
1176 var sticky = moveInStorageOrder ? "before" : "after"
1177 return new Pos(start.line, ch, sticky)
1178 }
1179 }
1180
1181 // Case 3: Could not move within this bidi part in this visual line, so leave
1182 // the current bidi part
1183
1184 var searchInVisualLine = function (partPos, dir, wrappedLineExtent) {
1185 var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder
1186 ? new Pos(start.line, mv(ch, 1), "before")
1187 : new Pos(start.line, ch, "after"); }
1188
1189 for (; partPos >= 0 && partPos < bidi.length; partPos += dir) {
1190 var part = bidi[partPos]
1191 var moveInStorageOrder = (dir > 0) == (part.level != 1)
1192 var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExte nt.end, -1)
1193 if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrde r) }
1194 ch = moveInStorageOrder ? part.from : mv(part.to, -1)
1195 if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) }
1196 }
1197 }
1198
1199 // Case 3a: Look for other bidi parts on the same visual line
1200 var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent)
1201 if (res) { return res }
1202
1203 // Case 3b: Look for other bidi parts on the next visual line
1204 var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1)
1205 if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) {
1206 res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineE xtent(nextCh))
1207 if (res) { return res }
1208 }
1209
1210 // Case 4: Nowhere to move
1211 return null
1212 }
1213
1214 // EVENT HANDLING
1215
1216 // Lightweight event framework. on/off also work on DOM nodes,
1217 // registering native DOM handlers.
1218
1219 var noHandlers = []
1220
1221 var on = function(emitter, type, f) {
1222 if (emitter.addEventListener) {
1223 emitter.addEventListener(type, f, false)
1224 } else if (emitter.attachEvent) {
1225 emitter.attachEvent("on" + type, f)
1226 } else {
1227 var map = emitter._handlers || (emitter._handlers = {})
1228 map[type] = (map[type] || noHandlers).concat(f)
1229 }
1230 }
1231
1232 function getHandlers(emitter, type) {
1233 return emitter._handlers && emitter._handlers[type] || noHandlers
1234 }
1235
1236 function off(emitter, type, f) {
1237 if (emitter.removeEventListener) {
1238 emitter.removeEventListener(type, f, false)
1239 } else if (emitter.detachEvent) {
1240 emitter.detachEvent("on" + type, f)
1241 } else {
1242 var map = emitter._handlers, arr = map && map[type]
1243 if (arr) {
1244 var index = indexOf(arr, f)
1245 if (index > -1)
1246 { map[type] = arr.slice(0, index).concat(arr.slice(index + 1)) }
1247 }
1248 }
1249 }
1250
1251 function signal(emitter, type /*, values...*/) {
1252 var handlers = getHandlers(emitter, type)
1253 if (!handlers.length) { return }
1254 var args = Array.prototype.slice.call(arguments, 2)
1255 for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args) }
1256 }
1257
1258 // The DOM events that CodeMirror handles can be overridden by
1259 // registering a (non-DOM) handler on the editor for the event name,
1260 // and preventDefault-ing the event in that handler.
1261 function signalDOMEvent(cm, e, override) {
1262 if (typeof e == "string")
1263 { e = {type: e, preventDefault: function() { this.defaultPrevented = true }} }
1264 signal(cm, override || e.type, cm, e)
1265 return e_defaultPrevented(e) || e.codemirrorIgnore
1266 }
1267
1268 function signalCursorActivity(cm) {
1269 var arr = cm._handlers && cm._handlers.cursorActivity
1270 if (!arr) { return }
1271 var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = [])
1272 for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1)
1273 { set.push(arr[i]) } }
1274 }
1275
1276 function hasHandler(emitter, type) {
1277 return getHandlers(emitter, type).length > 0
1278 }
1279
1280 // Add on and off methods to a constructor's prototype, to make
1281 // registering events on such objects more convenient.
1282 function eventMixin(ctor) {
1283 ctor.prototype.on = function(type, f) {on(this, type, f)}
1284 ctor.prototype.off = function(type, f) {off(this, type, f)}
1285 }
1286
1287 // Due to the fact that we still support jurassic IE versions, some
1288 // compatibility wrappers are needed.
1289
1290 function e_preventDefault(e) {
1291 if (e.preventDefault) { e.preventDefault() }
1292 else { e.returnValue = false }
1293 }
1294 function e_stopPropagation(e) {
1295 if (e.stopPropagation) { e.stopPropagation() }
1296 else { e.cancelBubble = true }
1297 }
1298 function e_defaultPrevented(e) {
1299 return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == fals e
1300 }
1301 function e_stop(e) {e_preventDefault(e); e_stopPropagation(e)}
1302
1303 function e_target(e) {return e.target || e.srcElement}
1304 function e_button(e) {
1305 var b = e.which
1306 if (b == null) {
1307 if (e.button & 1) { b = 1 }
1308 else if (e.button & 2) { b = 3 }
1309 else if (e.button & 4) { b = 2 }
1310 }
1311 if (mac && e.ctrlKey && b == 1) { b = 3 }
1312 return b
1313 }
1314
1315 // Detect drag-and-drop
1316 var dragAndDrop = function() {
1317 // There is *some* kind of drag-and-drop support in IE6-8, but I
1318 // couldn't get it to work yet.
1319 if (ie && ie_version < 9) { return false }
1320 var div = elt('div')
1321 return "draggable" in div || "dragDrop" in div
1322 }()
1323
1324 var zwspSupported
1325 function zeroWidthElement(measure) {
1326 if (zwspSupported == null) {
1327 var test = elt("span", "\u200b")
1328 removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x" )]))
1329 if (measure.firstChild.offsetHeight != 0)
1330 { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie & & ie_version < 8) }
1331 }
1332 var node = zwspSupported ? elt("span", "\u200b") :
1333 elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right : -1px")
1334 node.setAttribute("cm-text", "")
1335 return node
1336 }
1337
1338 // Feature-detect IE's crummy client rect reporting for bidi text
1339 var badBidiRects
1340 function hasBadBidiRects(measure) {
1341 if (badBidiRects != null) { return badBidiRects }
1342 var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA"))
1343 var r0 = range(txt, 0, 1).getBoundingClientRect()
1344 var r1 = range(txt, 1, 2).getBoundingClientRect()
1345 removeChildren(measure)
1346 if (!r0 || r0.left == r0.right) { return false } // Safari returns null in som e cases (#2780)
1347 return badBidiRects = (r1.right - r0.right < 3)
1348 }
1349
1350 // See if "".split is the broken IE version, if so, provide an
1351 // alternative way to split lines.
1352 var splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function (string) {
1353 var pos = 0, result = [], l = string.length
1354 while (pos <= l) {
1355 var nl = string.indexOf("\n", pos)
1356 if (nl == -1) { nl = string.length }
1357 var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl)
1358 var rt = line.indexOf("\r")
1359 if (rt != -1) {
1360 result.push(line.slice(0, rt))
1361 pos += rt + 1
260 } else { 1362 } else {
261 rmClass(cm.display.wrapper, "CodeMirror-wrap"); 1363 result.push(line)
262 findMaxLine(cm); 1364 pos = nl + 1
263 } 1365 }
264 estimateLineHeights(cm); 1366 }
265 regChange(cm); 1367 return result
266 clearCaches(cm); 1368 } : function (string) { return string.split(/\r\n?|\n/); }
267 setTimeout(function(){updateScrollbars(cm);}, 100); 1369
268 } 1370 var hasSelection = window.getSelection ? function (te) {
269 1371 try { return te.selectionStart != te.selectionEnd }
270 // Returns a function that estimates the height of a line, to use as 1372 catch(e) { return false }
271 // first approximation until the line becomes visible (and is thus 1373 } : function (te) {
272 // properly measurable). 1374 var range
273 function estimateHeight(cm) { 1375 try {range = te.ownerDocument.selection.createRange()}
274 var th = textHeight(cm.display), wrapping = cm.options.lineWrapping; 1376 catch(e) {}
275 var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / char Width(cm.display) - 3); 1377 if (!range || range.parentElement() != te) { return false }
276 return function(line) { 1378 return range.compareEndPoints("StartToEnd", range) != 0
277 if (lineIsHidden(cm.doc, line)) return 0; 1379 }
278 1380
279 var widgetsHeight = 0; 1381 var hasCopyEvent = (function () {
280 if (line.widgets) for (var i = 0; i < line.widgets.length; i++) { 1382 var e = elt("div")
281 if (line.widgets[i].height) widgetsHeight += line.widgets[i].height; 1383 if ("oncopy" in e) { return true }
1384 e.setAttribute("oncopy", "return;")
1385 return typeof e.oncopy == "function"
1386 })()
1387
1388 var badZoomedRects = null
1389 function hasBadZoomedRects(measure) {
1390 if (badZoomedRects != null) { return badZoomedRects }
1391 var node = removeChildrenAndAdd(measure, elt("span", "x"))
1392 var normal = node.getBoundingClientRect()
1393 var fromRange = range(node, 0, 1).getBoundingClientRect()
1394 return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1
1395 }
1396
1397 var modes = {};
1398 var mimeModes = {};
1399 // Extra arguments are stored as the mode's dependencies, which is
1400 // used by (legacy) mechanisms like loadmode.js to automatically
1401 // load a mode. (Preferred mechanism is the require/define calls.)
1402 function defineMode(name, mode) {
1403 if (arguments.length > 2)
1404 { mode.dependencies = Array.prototype.slice.call(arguments, 2) }
1405 modes[name] = mode
1406 }
1407
1408 function defineMIME(mime, spec) {
1409 mimeModes[mime] = spec
1410 }
1411
1412 // Given a MIME type, a {name, ...options} config object, or a name
1413 // string, return a mode config object.
1414 function resolveMode(spec) {
1415 if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
1416 spec = mimeModes[spec]
1417 } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(sp ec.name)) {
1418 var found = mimeModes[spec.name]
1419 if (typeof found == "string") { found = {name: found} }
1420 spec = createObj(found, spec)
1421 spec.name = found.name
1422 } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
1423 return resolveMode("application/xml")
1424 } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) {
1425 return resolveMode("application/json")
1426 }
1427 if (typeof spec == "string") { return {name: spec} }
1428 else { return spec || {name: "null"} }
1429 }
1430
1431 // Given a mode spec (anything that resolveMode accepts), find and
1432 // initialize an actual mode object.
1433 function getMode(options, spec) {
1434 spec = resolveMode(spec)
1435 var mfactory = modes[spec.name]
1436 if (!mfactory) { return getMode(options, "text/plain") }
1437 var modeObj = mfactory(options, spec)
1438 if (modeExtensions.hasOwnProperty(spec.name)) {
1439 var exts = modeExtensions[spec.name]
1440 for (var prop in exts) {
1441 if (!exts.hasOwnProperty(prop)) { continue }
1442 if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop] }
1443 modeObj[prop] = exts[prop]
1444 }
1445 }
1446 modeObj.name = spec.name
1447 if (spec.helperType) { modeObj.helperType = spec.helperType }
1448 if (spec.modeProps) { for (var prop$1 in spec.modeProps)
1449 { modeObj[prop$1] = spec.modeProps[prop$1] } }
1450
1451 return modeObj
1452 }
1453
1454 // This can be used to attach properties to mode objects from
1455 // outside the actual mode definition.
1456 var modeExtensions = {}
1457 function extendMode(mode, properties) {
1458 var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeE xtensions[mode] = {})
1459 copyObj(properties, exts)
1460 }
1461
1462 function copyState(mode, state) {
1463 if (state === true) { return state }
1464 if (mode.copyState) { return mode.copyState(state) }
1465 var nstate = {}
1466 for (var n in state) {
1467 var val = state[n]
1468 if (val instanceof Array) { val = val.concat([]) }
1469 nstate[n] = val
1470 }
1471 return nstate
1472 }
1473
1474 // Given a mode and a state (for that mode), find the inner mode and
1475 // state at the position that the state refers to.
1476 function innerMode(mode, state) {
1477 var info
1478 while (mode.innerMode) {
1479 info = mode.innerMode(state)
1480 if (!info || info.mode == mode) { break }
1481 state = info.state
1482 mode = info.mode
1483 }
1484 return info || {mode: mode, state: state}
1485 }
1486
1487 function startState(mode, a1, a2) {
1488 return mode.startState ? mode.startState(a1, a2) : true
1489 }
1490
1491 // STRING STREAM
1492
1493 // Fed to the mode parsers, provides helper functions to make
1494 // parsers more succinct.
1495
1496 var StringStream = function StringStream(string, tabSize) {
1497 this.pos = this.start = 0
1498 this.string = string
1499 this.tabSize = tabSize || 8
1500 this.lastColumnPos = this.lastColumnValue = 0
1501 this.lineStart = 0
1502 };
1503
1504 StringStream.prototype.eol = function eol () {return this.pos >= this.string.len gth};
1505 StringStream.prototype.sol = function sol () {return this.pos == this.lineStart} ;
1506 StringStream.prototype.peek = function peek () {return this.string.charAt(this.p os) || undefined};
1507 StringStream.prototype.next = function next () {
1508 if (this.pos < this.string.length)
1509 { return this.string.charAt(this.pos++) }
1510 };
1511 StringStream.prototype.eat = function eat (match) {
1512 var ch = this.string.charAt(this.pos)
1513 var ok
1514 if (typeof match == "string") { ok = ch == match }
1515 else { ok = ch && (match.test ? match.test(ch) : match(ch)) }
1516 if (ok) {++this.pos; return ch}
1517 };
1518 StringStream.prototype.eatWhile = function eatWhile (match) {
1519 var start = this.pos
1520 while (this.eat(match)){}
1521 return this.pos > start
1522 };
1523 StringStream.prototype.eatSpace = function eatSpace () {
1524 var this$1 = this;
1525
1526 var start = this.pos
1527 while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this$1.pos }
1528 return this.pos > start
1529 };
1530 StringStream.prototype.skipToEnd = function skipToEnd () {this.pos = this.string .length};
1531 StringStream.prototype.skipTo = function skipTo (ch) {
1532 var found = this.string.indexOf(ch, this.pos)
1533 if (found > -1) {this.pos = found; return true}
1534 };
1535 StringStream.prototype.backUp = function backUp (n) {this.pos -= n};
1536 StringStream.prototype.column = function column () {
1537 if (this.lastColumnPos < this.start) {
1538 this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, th is.lastColumnPos, this.lastColumnValue)
1539 this.lastColumnPos = this.start
1540 }
1541 return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this. lineStart, this.tabSize) : 0)
1542 };
1543 StringStream.prototype.indentation = function indentation () {
1544 return countColumn(this.string, null, this.tabSize) -
1545 (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0 )
1546 };
1547 StringStream.prototype.match = function match (pattern, consume, caseInsensitive ) {
1548 if (typeof pattern == "string") {
1549 var cased = function (str) { return caseInsensitive ? str.toLowerCase() : st r; }
1550 var substr = this.string.substr(this.pos, pattern.length)
1551 if (cased(substr) == cased(pattern)) {
1552 if (consume !== false) { this.pos += pattern.length }
1553 return true
1554 }
1555 } else {
1556 var match = this.string.slice(this.pos).match(pattern)
1557 if (match && match.index > 0) { return null }
1558 if (match && consume !== false) { this.pos += match[0].length }
1559 return match
1560 }
1561 };
1562 StringStream.prototype.current = function current (){return this.string.slice(th is.start, this.pos)};
1563 StringStream.prototype.hideFirstChars = function hideFirstChars (n, inner) {
1564 this.lineStart += n
1565 try { return inner() }
1566 finally { this.lineStart -= n }
1567 };
1568
1569 // Compute a style array (an array starting with a mode generation
1570 // -- for invalidation -- followed by pairs of end positions and
1571 // style strings), which is used to highlight the tokens on the
1572 // line.
1573 function highlightLine(cm, line, state, forceToEnd) {
1574 // A styles array always starts with a number identifying the
1575 // mode/overlays that it is based on (for easy invalidation).
1576 var st = [cm.state.modeGen], lineClasses = {}
1577 // Compute the base array of styles
1578 runMode(cm, line.text, cm.doc.mode, state, function (end, style) { return st.p ush(end, style); },
1579 lineClasses, forceToEnd)
1580
1581 // Run overlays, adjust style array.
1582 var loop = function ( o ) {
1583 var overlay = cm.state.overlays[o], i = 1, at = 0
1584 runMode(cm, line.text, overlay.mode, true, function (end, style) {
1585 var start = i
1586 // Ensure there's a token end at the current position, and that i points a t it
1587 while (at < end) {
1588 var i_end = st[i]
1589 if (i_end > end)
1590 { st.splice(i, 1, end, st[i+1], i_end) }
1591 i += 2
1592 at = Math.min(end, i_end)
282 } 1593 }
283 1594 if (!style) { return }
284 if (wrapping) 1595 if (overlay.opaque) {
285 return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th ; 1596 st.splice(start, i - start, end, "overlay " + style)
286 else 1597 i = start + 2
287 return widgetsHeight + th;
288 };
289 }
290
291 function estimateLineHeights(cm) {
292 var doc = cm.doc, est = estimateHeight(cm);
293 doc.iter(function(line) {
294 var estHeight = est(line);
295 if (estHeight != line.height) updateLineHeight(line, estHeight);
296 });
297 }
298
299 function themeChanged(cm) {
300 cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s -\S+/g, "") +
301 cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
302 clearCaches(cm);
303 }
304
305 function guttersChanged(cm) {
306 updateGutters(cm);
307 regChange(cm);
308 setTimeout(function(){alignHorizontally(cm);}, 20);
309 }
310
311 // Rebuild the gutter elements, ensure the margin to the left of the
312 // code matches their width.
313 function updateGutters(cm) {
314 var gutters = cm.display.gutters, specs = cm.options.gutters;
315 removeChildren(gutters);
316 for (var i = 0; i < specs.length; ++i) {
317 var gutterClass = specs[i];
318 var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gut terClass));
319 if (gutterClass == "CodeMirror-linenumbers") {
320 cm.display.lineGutter = gElt;
321 gElt.style.width = (cm.display.lineNumWidth || 1) + "px";
322 }
323 }
324 gutters.style.display = i ? "" : "none";
325 updateGutterSpace(cm);
326 }
327
328 function updateGutterSpace(cm) {
329 var width = cm.display.gutters.offsetWidth;
330 cm.display.sizer.style.marginLeft = width + "px";
331 }
332
333 // Compute the character length of a line, taking into account
334 // collapsed ranges (see markText) that might hide parts, and join
335 // other lines onto it.
336 function lineLength(line) {
337 if (line.height == 0) return 0;
338 var len = line.text.length, merged, cur = line;
339 while (merged = collapsedSpanAtStart(cur)) {
340 var found = merged.find(0, true);
341 cur = found.from.line;
342 len += found.from.ch - found.to.ch;
343 }
344 cur = line;
345 while (merged = collapsedSpanAtEnd(cur)) {
346 var found = merged.find(0, true);
347 len -= cur.text.length - found.from.ch;
348 cur = found.to.line;
349 len += cur.text.length - found.to.ch;
350 }
351 return len;
352 }
353
354 // Find the longest line in the document.
355 function findMaxLine(cm) {
356 var d = cm.display, doc = cm.doc;
357 d.maxLine = getLine(doc, doc.first);
358 d.maxLineLength = lineLength(d.maxLine);
359 d.maxLineChanged = true;
360 doc.iter(function(line) {
361 var len = lineLength(line);
362 if (len > d.maxLineLength) {
363 d.maxLineLength = len;
364 d.maxLine = line;
365 }
366 });
367 }
368
369 // Make sure the gutters options contains the element
370 // "CodeMirror-linenumbers" when the lineNumbers option is true.
371 function setGuttersForLineNumbers(options) {
372 var found = indexOf(options.gutters, "CodeMirror-linenumbers");
373 if (found == -1 && options.lineNumbers) {
374 options.gutters = options.gutters.concat(["CodeMirror-linenumbers"]);
375 } else if (found > -1 && !options.lineNumbers) {
376 options.gutters = options.gutters.slice(0);
377 options.gutters.splice(found, 1);
378 }
379 }
380
381 // SCROLLBARS
382
383 // Prepare DOM reads needed to update the scrollbars. Done in one
384 // shot to minimize update/measure roundtrips.
385 function measureForScrollbars(cm) {
386 var d = cm.display, gutterW = d.gutters.offsetWidth;
387 var docH = Math.round(cm.doc.height + paddingVert(cm.display));
388 return {
389 clientHeight: d.scroller.clientHeight,
390 viewHeight: d.wrapper.clientHeight,
391 scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,
392 viewWidth: d.wrapper.clientWidth,
393 barLeft: cm.options.fixedGutter ? gutterW : 0,
394 docHeight: docH,
395 scrollHeight: docH + scrollGap(cm) + d.barHeight,
396 nativeBarWidth: d.nativeBarWidth,
397 gutterWidth: gutterW
398 };
399 }
400
401 function NativeScrollbars(place, scroll, cm) {
402 this.cm = cm;
403 var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")] , "CodeMirror-vscrollbar");
404 var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; m in-height: 1px")], "CodeMirror-hscrollbar");
405 place(vert); place(horiz);
406
407 on(vert, "scroll", function() {
408 if (vert.clientHeight) scroll(vert.scrollTop, "vertical");
409 });
410 on(horiz, "scroll", function() {
411 if (horiz.clientWidth) scroll(horiz.scrollLeft, "horizontal");
412 });
413
414 this.checkedZeroWidth = false;
415 // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
416 if (ie && ie_version < 8) this.horiz.style.minHeight = this.vert.style.minWi dth = "18px";
417 }
418
419 NativeScrollbars.prototype = copyObj({
420 update: function(measure) {
421 var needsH = measure.scrollWidth > measure.clientWidth + 1;
422 var needsV = measure.scrollHeight > measure.clientHeight + 1;
423 var sWidth = measure.nativeBarWidth;
424
425 if (needsV) {
426 this.vert.style.display = "block";
427 this.vert.style.bottom = needsH ? sWidth + "px" : "0";
428 var totalHeight = measure.viewHeight - (needsH ? sWidth : 0);
429 // A bug in IE8 can cause this value to be negative, so guard it.
430 this.vert.firstChild.style.height =
431 Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px";
432 } else { 1598 } else {
433 this.vert.style.display = ""; 1599 for (; start < i; start += 2) {
434 this.vert.firstChild.style.height = "0"; 1600 var cur = st[start+1]
435 } 1601 st[start+1] = (cur ? cur + " " : "") + "overlay " + style
436
437 if (needsH) {
438 this.horiz.style.display = "block";
439 this.horiz.style.right = needsV ? sWidth + "px" : "0";
440 this.horiz.style.left = measure.barLeft + "px";
441 var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0);
442 this.horiz.firstChild.style.width =
443 (measure.scrollWidth - measure.clientWidth + totalWidth) + "px";
444 } else {
445 this.horiz.style.display = "";
446 this.horiz.firstChild.style.width = "0";
447 }
448
449 if (!this.checkedZeroWidth && measure.clientHeight > 0) {
450 if (sWidth == 0) this.zeroWidthHack();
451 this.checkedZeroWidth = true;
452 }
453
454 return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0};
455 },
456 setScrollLeft: function(pos) {
457 if (this.horiz.scrollLeft != pos) this.horiz.scrollLeft = pos;
458 if (this.disableHoriz) this.enableZeroWidthBar(this.horiz, this.disableHor iz);
459 },
460 setScrollTop: function(pos) {
461 if (this.vert.scrollTop != pos) this.vert.scrollTop = pos;
462 if (this.disableVert) this.enableZeroWidthBar(this.vert, this.disableVert) ;
463 },
464 zeroWidthHack: function() {
465 var w = mac && !mac_geMountainLion ? "12px" : "18px";
466 this.horiz.style.height = this.vert.style.width = w;
467 this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none";
468 this.disableHoriz = new Delayed;
469 this.disableVert = new Delayed;
470 },
471 enableZeroWidthBar: function(bar, delay) {
472 bar.style.pointerEvents = "auto";
473 function maybeDisable() {
474 // To find out whether the scrollbar is still visible, we
475 // check whether the element under the pixel in the bottom
476 // left corner of the scrollbar box is the scrollbar box
477 // itself (when the bar is still visible) or its filler child
478 // (when the bar is hidden). If it is still visible, we keep
479 // it enabled, if it's hidden, we disable pointer events.
480 var box = bar.getBoundingClientRect();
481 var elt = document.elementFromPoint(box.left + 1, box.bottom - 1);
482 if (elt != bar) bar.style.pointerEvents = "none";
483 else delay.set(1000, maybeDisable);
484 }
485 delay.set(1000, maybeDisable);
486 },
487 clear: function() {
488 var parent = this.horiz.parentNode;
489 parent.removeChild(this.horiz);
490 parent.removeChild(this.vert);
491 }
492 }, NativeScrollbars.prototype);
493
494 function NullScrollbars() {}
495
496 NullScrollbars.prototype = copyObj({
497 update: function() { return {bottom: 0, right: 0}; },
498 setScrollLeft: function() {},
499 setScrollTop: function() {},
500 clear: function() {}
501 }, NullScrollbars.prototype);
502
503 CodeMirror.scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbar s};
504
505 function initScrollbars(cm) {
506 if (cm.display.scrollbars) {
507 cm.display.scrollbars.clear();
508 if (cm.display.scrollbars.addClass)
509 rmClass(cm.display.wrapper, cm.display.scrollbars.addClass);
510 }
511
512 cm.display.scrollbars = new CodeMirror.scrollbarModel[cm.options.scrollbarSt yle](function(node) {
513 cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller);
514 // Prevent clicks in the scrollbars from killing focus
515 on(node, "mousedown", function() {
516 if (cm.state.focused) setTimeout(function() { cm.display.input.focus(); }, 0);
517 });
518 node.setAttribute("cm-not-content", "true");
519 }, function(pos, axis) {
520 if (axis == "horizontal") setScrollLeft(cm, pos);
521 else setScrollTop(cm, pos);
522 }, cm);
523 if (cm.display.scrollbars.addClass)
524 addClass(cm.display.wrapper, cm.display.scrollbars.addClass);
525 }
526
527 function updateScrollbars(cm, measure) {
528 if (!measure) measure = measureForScrollbars(cm);
529 var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight;
530 updateScrollbarsInner(cm, measure);
531 for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) {
532 if (startWidth != cm.display.barWidth && cm.options.lineWrapping)
533 updateHeightsInViewport(cm);
534 updateScrollbarsInner(cm, measureForScrollbars(cm));
535 startWidth = cm.display.barWidth; startHeight = cm.display.barHeight;
536 }
537 }
538
539 // Re-synchronize the fake scrollbars with the actual size of the
540 // content.
541 function updateScrollbarsInner(cm, measure) {
542 var d = cm.display;
543 var sizes = d.scrollbars.update(measure);
544
545 d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px";
546 d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px";
547 d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent"
548
549 if (sizes.right && sizes.bottom) {
550 d.scrollbarFiller.style.display = "block";
551 d.scrollbarFiller.style.height = sizes.bottom + "px";
552 d.scrollbarFiller.style.width = sizes.right + "px";
553 } else d.scrollbarFiller.style.display = "";
554 if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixe dGutter) {
555 d.gutterFiller.style.display = "block";
556 d.gutterFiller.style.height = sizes.bottom + "px";
557 d.gutterFiller.style.width = measure.gutterWidth + "px";
558 } else d.gutterFiller.style.display = "";
559 }
560
561 // Compute the lines that are visible in a given viewport (defaults
562 // the the current scroll position). viewport may contain top,
563 // height, and ensure (see op.scrollToPos) properties.
564 function visibleLines(display, doc, viewport) {
565 var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : dis play.scroller.scrollTop;
566 top = Math.floor(top - paddingTop(display));
567 var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + d isplay.wrapper.clientHeight;
568
569 var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom);
570 // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
571 // forces those lines into the viewport (if possible).
572 if (viewport && viewport.ensure) {
573 var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to. line;
574 if (ensureFrom < from) {
575 from = ensureFrom;
576 to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display. wrapper.clientHeight);
577 } else if (Math.min(ensureTo, doc.lastLine()) >= to) {
578 from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display. wrapper.clientHeight);
579 to = ensureTo;
580 }
581 }
582 return {from: from, to: Math.max(to, from + 1)};
583 }
584
585 // LINE NUMBERS
586
587 // Re-align line numbers and gutter marks to compensate for
588 // horizontal scrolling.
589 function alignHorizontally(cm) {
590 var display = cm.display, view = display.view;
591 if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fix edGutter)) return;
592 var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm. doc.scrollLeft;
593 var gutterW = display.gutters.offsetWidth, left = comp + "px";
594 for (var i = 0; i < view.length; i++) if (!view[i].hidden) {
595 if (cm.options.fixedGutter) {
596 if (view[i].gutter)
597 view[i].gutter.style.left = left;
598 if (view[i].gutterBackground)
599 view[i].gutterBackground.style.left = left;
600 }
601 var align = view[i].alignable;
602 if (align) for (var j = 0; j < align.length; j++)
603 align[j].style.left = left;
604 }
605 if (cm.options.fixedGutter)
606 display.gutters.style.left = (comp + gutterW) + "px";
607 }
608
609 // Used to ensure that the line number gutter is still the right
610 // size for the current document size. Returns true when an update
611 // is needed.
612 function maybeUpdateLineNumberWidth(cm) {
613 if (!cm.options.lineNumbers) return false;
614 var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1) , display = cm.display;
615 if (last.length != display.lineNumChars) {
616 var test = display.measure.appendChild(elt("div", [elt("div", last)],
617 "CodeMirror-linenumber CodeMirr or-gutter-elt"));
618 var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - inn erW;
619 display.lineGutter.style.width = "";
620 display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidt h - padding) + 1;
621 display.lineNumWidth = display.lineNumInnerWidth + padding;
622 display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
623 display.lineGutter.style.width = display.lineNumWidth + "px";
624 updateGutterSpace(cm);
625 return true;
626 }
627 return false;
628 }
629
630 function lineNumberFor(options, i) {
631 return String(options.lineNumberFormatter(i + options.firstLineNumber));
632 }
633
634 // Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
635 // but using getBoundingClientRect to get a sub-pixel-accurate
636 // result.
637 function compensateForHScroll(display) {
638 return display.scroller.getBoundingClientRect().left - display.sizer.getBoun dingClientRect().left;
639 }
640
641 // DISPLAY DRAWING
642
643 function DisplayUpdate(cm, viewport, force) {
644 var display = cm.display;
645
646 this.viewport = viewport;
647 // Store some values that we'll need later (but don't want to force a relayo ut for)
648 this.visible = visibleLines(display, cm.doc, viewport);
649 this.editorIsHidden = !display.wrapper.offsetWidth;
650 this.wrapperHeight = display.wrapper.clientHeight;
651 this.wrapperWidth = display.wrapper.clientWidth;
652 this.oldDisplayWidth = displayWidth(cm);
653 this.force = force;
654 this.dims = getDimensions(cm);
655 this.events = [];
656 }
657
658 DisplayUpdate.prototype.signal = function(emitter, type) {
659 if (hasHandler(emitter, type))
660 this.events.push(arguments);
661 };
662 DisplayUpdate.prototype.finish = function() {
663 for (var i = 0; i < this.events.length; i++)
664 signal.apply(null, this.events[i]);
665 };
666
667 function maybeClipScrollbars(cm) {
668 var display = cm.display;
669 if (!display.scrollbarsClipped && display.scroller.offsetWidth) {
670 display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.c lientWidth;
671 display.heightForcer.style.height = scrollGap(cm) + "px";
672 display.sizer.style.marginBottom = -display.nativeBarWidth + "px";
673 display.sizer.style.borderRightWidth = scrollGap(cm) + "px";
674 display.scrollbarsClipped = true;
675 }
676 }
677
678 // Does the actual updating of the line display. Bails out
679 // (returning false) when there is nothing to be done and forced is
680 // false.
681 function updateDisplayIfNeeded(cm, update) {
682 var display = cm.display, doc = cm.doc;
683
684 if (update.editorIsHidden) {
685 resetView(cm);
686 return false;
687 }
688
689 // Bail out if the visible area is already rendered and nothing changed.
690 if (!update.force &&
691 update.visible.from >= display.viewFrom && update.visible.to <= display. viewTo &&
692 (display.updateLineNumbers == null || display.updateLineNumbers >= displ ay.viewTo) &&
693 display.renderedView == display.view && countDirtyView(cm) == 0)
694 return false;
695
696 if (maybeUpdateLineNumberWidth(cm)) {
697 resetView(cm);
698 update.dims = getDimensions(cm);
699 }
700
701 // Compute a suitable new viewport (from & to)
702 var end = doc.first + doc.size;
703 var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.fir st);
704 var to = Math.min(end, update.visible.to + cm.options.viewportMargin);
705 if (display.viewFrom < from && from - display.viewFrom < 20) from = Math.max (doc.first, display.viewFrom);
706 if (display.viewTo > to && display.viewTo - to < 20) to = Math.min(end, disp lay.viewTo);
707 if (sawCollapsedSpans) {
708 from = visualLineNo(cm.doc, from);
709 to = visualLineEndNo(cm.doc, to);
710 }
711
712 var different = from != display.viewFrom || to != display.viewTo ||
713 display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth;
714 adjustView(cm, from, to);
715
716 display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom));
717 // Position the mover div to align with the current scroll position
718 cm.display.mover.style.top = display.viewOffset + "px";
719
720 var toUpdate = countDirtyView(cm);
721 if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view &&
722 (display.updateLineNumbers == null || display.updateLineNumbers >= displ ay.viewTo))
723 return false;
724
725 // For big changes, we hide the enclosing element during the
726 // update, since that speeds up the operations on most browsers.
727 var focused = activeElt();
728 if (toUpdate > 4) display.lineDiv.style.display = "none";
729 patchDisplay(cm, display.updateLineNumbers, update.dims);
730 if (toUpdate > 4) display.lineDiv.style.display = "";
731 display.renderedView = display.view;
732 // There might have been a widget with a focused element that got
733 // hidden or updated, if so re-focus it.
734 if (focused && activeElt() != focused && focused.offsetHeight) focused.focus ();
735
736 // Prevent selection and cursors from interfering with the scroll
737 // width and height.
738 removeChildren(display.cursorDiv);
739 removeChildren(display.selectionDiv);
740 display.gutters.style.height = display.sizer.style.minHeight = 0;
741
742 if (different) {
743 display.lastWrapHeight = update.wrapperHeight;
744 display.lastWrapWidth = update.wrapperWidth;
745 startWorker(cm, 400);
746 }
747
748 display.updateLineNumbers = null;
749
750 return true;
751 }
752
753 function postUpdateDisplay(cm, update) {
754 var viewport = update.viewport;
755
756 for (var first = true;; first = false) {
757 if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displa yWidth(cm)) {
758 // Clip forced viewport to actual scrollable area.
759 if (viewport && viewport.top != null)
760 viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - di splayHeight(cm), viewport.top)};
761 // Updated line heights might result in the drawn area not
762 // actually covering the viewport. Keep looping until it does.
763 update.visible = visibleLines(cm.display, cm.doc, viewport);
764 if (update.visible.from >= cm.display.viewFrom && update.visible.to <= c m.display.viewTo)
765 break;
766 }
767 if (!updateDisplayIfNeeded(cm, update)) break;
768 updateHeightsInViewport(cm);
769 var barMeasure = measureForScrollbars(cm);
770 updateSelection(cm);
771 updateScrollbars(cm, barMeasure);
772 setDocumentHeight(cm, barMeasure);
773 }
774
775 update.signal(cm, "update", cm);
776 if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {
777 update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.vi ewTo);
778 cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedView To = cm.display.viewTo;
779 }
780 }
781
782 function updateDisplaySimple(cm, viewport) {
783 var update = new DisplayUpdate(cm, viewport);
784 if (updateDisplayIfNeeded(cm, update)) {
785 updateHeightsInViewport(cm);
786 postUpdateDisplay(cm, update);
787 var barMeasure = measureForScrollbars(cm);
788 updateSelection(cm);
789 updateScrollbars(cm, barMeasure);
790 setDocumentHeight(cm, barMeasure);
791 update.finish();
792 }
793 }
794
795 function setDocumentHeight(cm, measure) {
796 cm.display.sizer.style.minHeight = measure.docHeight + "px";
797 cm.display.heightForcer.style.top = measure.docHeight + "px";
798 cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px";
799 }
800
801 // Read the actual heights of the rendered lines, and update their
802 // stored heights to match.
803 function updateHeightsInViewport(cm) {
804 var display = cm.display;
805 var prevBottom = display.lineDiv.offsetTop;
806 for (var i = 0; i < display.view.length; i++) {
807 var cur = display.view[i], height;
808 if (cur.hidden) continue;
809 if (ie && ie_version < 8) {
810 var bot = cur.node.offsetTop + cur.node.offsetHeight;
811 height = bot - prevBottom;
812 prevBottom = bot;
813 } else {
814 var box = cur.node.getBoundingClientRect();
815 height = box.bottom - box.top;
816 }
817 var diff = cur.line.height - height;
818 if (height < 2) height = textHeight(display);
819 if (diff > .001 || diff < -.001) {
820 updateLineHeight(cur.line, height);
821 updateWidgetHeight(cur.line);
822 if (cur.rest) for (var j = 0; j < cur.rest.length; j++)
823 updateWidgetHeight(cur.rest[j]);
824 }
825 }
826 }
827
828 // Read and store the height of line widgets associated with the
829 // given line.
830 function updateWidgetHeight(line) {
831 if (line.widgets) for (var i = 0; i < line.widgets.length; ++i)
832 line.widgets[i].height = line.widgets[i].node.parentNode.offsetHeight;
833 }
834
835 // Do a bulk-read of the DOM positions and sizes needed to draw the
836 // view, so that we don't interleave reading and writing to the DOM.
837 function getDimensions(cm) {
838 var d = cm.display, left = {}, width = {};
839 var gutterLeft = d.gutters.clientLeft;
840 for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
841 left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft;
842 width[cm.options.gutters[i]] = n.clientWidth;
843 }
844 return {fixedPos: compensateForHScroll(d),
845 gutterTotalWidth: d.gutters.offsetWidth,
846 gutterLeft: left,
847 gutterWidth: width,
848 wrapperWidth: d.wrapper.clientWidth};
849 }
850
851 // Sync the actual display DOM structure with display.view, removing
852 // nodes for lines that are no longer in view, and creating the ones
853 // that are not there yet, and updating the ones that are out of
854 // date.
855 function patchDisplay(cm, updateNumbersFrom, dims) {
856 var display = cm.display, lineNumbers = cm.options.lineNumbers;
857 var container = display.lineDiv, cur = container.firstChild;
858
859 function rm(node) {
860 var next = node.nextSibling;
861 // Works around a throw-scroll bug in OS X Webkit
862 if (webkit && mac && cm.display.currentWheelTarget == node)
863 node.style.display = "none";
864 else
865 node.parentNode.removeChild(node);
866 return next;
867 }
868
869 var view = display.view, lineN = display.viewFrom;
870 // Loop over the elements in the view, syncing cur (the DOM nodes
871 // in display.lineDiv) with the view as we go.
872 for (var i = 0; i < view.length; i++) {
873 var lineView = view[i];
874 if (lineView.hidden) {
875 } else if (!lineView.node || lineView.node.parentNode != container) { // N ot drawn yet
876 var node = buildLineElement(cm, lineView, lineN, dims);
877 container.insertBefore(node, cur);
878 } else { // Already drawn
879 while (cur != lineView.node) cur = rm(cur);
880 var updateNumber = lineNumbers && updateNumbersFrom != null &&
881 updateNumbersFrom <= lineN && lineView.lineNumber;
882 if (lineView.changes) {
883 if (indexOf(lineView.changes, "gutter") > -1) updateNumber = false;
884 updateLineForChanges(cm, lineView, lineN, dims);
885 }
886 if (updateNumber) {
887 removeChildren(lineView.lineNumber);
888 lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor( cm.options, lineN)));
889 }
890 cur = lineView.node.nextSibling;
891 }
892 lineN += lineView.size;
893 }
894 while (cur) cur = rm(cur);
895 }
896
897 // When an aspect of a line changes, a string is added to
898 // lineView.changes. This updates the relevant part of the line's
899 // DOM structure.
900 function updateLineForChanges(cm, lineView, lineN, dims) {
901 for (var j = 0; j < lineView.changes.length; j++) {
902 var type = lineView.changes[j];
903 if (type == "text") updateLineText(cm, lineView);
904 else if (type == "gutter") updateLineGutter(cm, lineView, lineN, dims);
905 else if (type == "class") updateLineClasses(lineView);
906 else if (type == "widget") updateLineWidgets(cm, lineView, dims);
907 }
908 lineView.changes = null;
909 }
910
911 // Lines with gutter elements, widgets or a background class need to
912 // be wrapped, and have the extra elements added to the wrapper div
913 function ensureLineWrapped(lineView) {
914 if (lineView.node == lineView.text) {
915 lineView.node = elt("div", null, null, "position: relative");
916 if (lineView.text.parentNode)
917 lineView.text.parentNode.replaceChild(lineView.node, lineView.text);
918 lineView.node.appendChild(lineView.text);
919 if (ie && ie_version < 8) lineView.node.style.zIndex = 2;
920 }
921 return lineView.node;
922 }
923
924 function updateLineBackground(lineView) {
925 var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass;
926 if (cls) cls += " CodeMirror-linebackground";
927 if (lineView.background) {
928 if (cls) lineView.background.className = cls;
929 else { lineView.background.parentNode.removeChild(lineView.background); li neView.background = null; }
930 } else if (cls) {
931 var wrap = ensureLineWrapped(lineView);
932 lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstC hild);
933 }
934 }
935
936 // Wrapper around buildLineContent which will reuse the structure
937 // in display.externalMeasured when possible.
938 function getLineContent(cm, lineView) {
939 var ext = cm.display.externalMeasured;
940 if (ext && ext.line == lineView.line) {
941 cm.display.externalMeasured = null;
942 lineView.measure = ext.measure;
943 return ext.built;
944 }
945 return buildLineContent(cm, lineView);
946 }
947
948 // Redraw the line's text. Interacts with the background and text
949 // classes because the mode may output tokens that influence these
950 // classes.
951 function updateLineText(cm, lineView) {
952 var cls = lineView.text.className;
953 var built = getLineContent(cm, lineView);
954 if (lineView.text == lineView.node) lineView.node = built.pre;
955 lineView.text.parentNode.replaceChild(built.pre, lineView.text);
956 lineView.text = built.pre;
957 if (built.bgClass != lineView.bgClass || built.textClass != lineView.textCla ss) {
958 lineView.bgClass = built.bgClass;
959 lineView.textClass = built.textClass;
960 updateLineClasses(lineView);
961 } else if (cls) {
962 lineView.text.className = cls;
963 }
964 }
965
966 function updateLineClasses(lineView) {
967 updateLineBackground(lineView);
968 if (lineView.line.wrapClass)
969 ensureLineWrapped(lineView).className = lineView.line.wrapClass;
970 else if (lineView.node != lineView.text)
971 lineView.node.className = "";
972 var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.li ne.textClass || "") : lineView.line.textClass;
973 lineView.text.className = textClass || "";
974 }
975
976 function updateLineGutter(cm, lineView, lineN, dims) {
977 if (lineView.gutter) {
978 lineView.node.removeChild(lineView.gutter);
979 lineView.gutter = null;
980 }
981 if (lineView.gutterBackground) {
982 lineView.node.removeChild(lineView.gutterBackground);
983 lineView.gutterBackground = null;
984 }
985 if (lineView.line.gutterClass) {
986 var wrap = ensureLineWrapped(lineView);
987 lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass,
988 "left: " + (cm.options.fixedGutter ? dims. fixedPos : -dims.gutterTotalWidth) +
989 "px; width: " + dims.gutterTotalWidth + "p x");
990 wrap.insertBefore(lineView.gutterBackground, lineView.text);
991 }
992 var markers = lineView.line.gutterMarkers;
993 if (cm.options.lineNumbers || markers) {
994 var wrap = ensureLineWrapped(lineView);
995 var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wra pper", "left: " +
996 (cm.options.fixedGutter ? dims.fixe dPos : -dims.gutterTotalWidth) + "px");
997 cm.display.input.setUneditable(gutterWrap);
998 wrap.insertBefore(gutterWrap, lineView.text);
999 if (lineView.line.gutterClass)
1000 gutterWrap.className += " " + lineView.line.gutterClass;
1001 if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumber s"]))
1002 lineView.lineNumber = gutterWrap.appendChild(
1003 elt("div", lineNumberFor(cm.options, lineN),
1004 "CodeMirror-linenumber CodeMirror-gutter-elt",
1005 "left: " + dims.gutterLeft["CodeMirror-linenumbers"] + "px; width: "
1006 + cm.display.lineNumInnerWidth + "px"));
1007 if (markers) for (var k = 0; k < cm.options.gutters.length; ++k) {
1008 var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && ma rkers[id];
1009 if (found)
1010 gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", "l eft: " +
1011 dims.gutterLeft[id] + "px; width: " + dims. gutterWidth[id] + "px"));
1012 }
1013 }
1014 }
1015
1016 function updateLineWidgets(cm, lineView, dims) {
1017 if (lineView.alignable) lineView.alignable = null;
1018 for (var node = lineView.node.firstChild, next; node; node = next) {
1019 var next = node.nextSibling;
1020 if (node.className == "CodeMirror-linewidget")
1021 lineView.node.removeChild(node);
1022 }
1023 insertLineWidgets(cm, lineView, dims);
1024 }
1025
1026 // Build a line's DOM representation from scratch
1027 function buildLineElement(cm, lineView, lineN, dims) {
1028 var built = getLineContent(cm, lineView);
1029 lineView.text = lineView.node = built.pre;
1030 if (built.bgClass) lineView.bgClass = built.bgClass;
1031 if (built.textClass) lineView.textClass = built.textClass;
1032
1033 updateLineClasses(lineView);
1034 updateLineGutter(cm, lineView, lineN, dims);
1035 insertLineWidgets(cm, lineView, dims);
1036 return lineView.node;
1037 }
1038
1039 // A lineView may contain multiple logical lines (when merged by
1040 // collapsed spans). The widgets for all of them need to be drawn.
1041 function insertLineWidgets(cm, lineView, dims) {
1042 insertLineWidgetsFor(cm, lineView.line, lineView, dims, true);
1043 if (lineView.rest) for (var i = 0; i < lineView.rest.length; i++)
1044 insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false);
1045 }
1046
1047 function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {
1048 if (!line.widgets) return;
1049 var wrap = ensureLineWrapped(lineView);
1050 for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
1051 var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidge t");
1052 if (!widget.handleMouseEvents) node.setAttribute("cm-ignore-events", "true ");
1053 positionLineWidget(widget, node, lineView, dims);
1054 cm.display.input.setUneditable(node);
1055 if (allowAbove && widget.above)
1056 wrap.insertBefore(node, lineView.gutter || lineView.text);
1057 else
1058 wrap.appendChild(node);
1059 signalLater(widget, "redraw");
1060 }
1061 }
1062
1063 function positionLineWidget(widget, node, lineView, dims) {
1064 if (widget.noHScroll) {
1065 (lineView.alignable || (lineView.alignable = [])).push(node);
1066 var width = dims.wrapperWidth;
1067 node.style.left = dims.fixedPos + "px";
1068 if (!widget.coverGutter) {
1069 width -= dims.gutterTotalWidth;
1070 node.style.paddingLeft = dims.gutterTotalWidth + "px";
1071 }
1072 node.style.width = width + "px";
1073 }
1074 if (widget.coverGutter) {
1075 node.style.zIndex = 5;
1076 node.style.position = "relative";
1077 if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + "p x";
1078 }
1079 }
1080
1081 // POSITION OBJECT
1082
1083 // A Pos instance represents a position within the text.
1084 var Pos = CodeMirror.Pos = function(line, ch) {
1085 if (!(this instanceof Pos)) return new Pos(line, ch);
1086 this.line = line; this.ch = ch;
1087 };
1088
1089 // Compare two positions, return 0 if they are the same, a negative
1090 // number when a is less, and a positive number otherwise.
1091 var cmp = CodeMirror.cmpPos = function(a, b) { return a.line - b.line || a.ch - b.ch; };
1092
1093 function copyPos(x) {return Pos(x.line, x.ch);}
1094 function maxPos(a, b) { return cmp(a, b) < 0 ? b : a; }
1095 function minPos(a, b) { return cmp(a, b) < 0 ? a : b; }
1096
1097 // INPUT HANDLING
1098
1099 function ensureFocus(cm) {
1100 if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); }
1101 }
1102
1103 // This will be set to a {lineWise: bool, text: [string]} object, so
1104 // that, when pasting, we know what kind of selections the copied
1105 // text was made out of.
1106 var lastCopied = null;
1107
1108 function applyTextInput(cm, inserted, deleted, sel, origin) {
1109 var doc = cm.doc;
1110 cm.display.shift = false;
1111 if (!sel) sel = doc.sel;
1112
1113 var paste = cm.state.pasteIncoming || origin == "paste";
1114 var textLines = doc.splitLines(inserted), multiPaste = null
1115 // When pasing N lines into N selections, insert one line per selection
1116 if (paste && sel.ranges.length > 1) {
1117 if (lastCopied && lastCopied.text.join("\n") == inserted) {
1118 if (sel.ranges.length % lastCopied.text.length == 0) {
1119 multiPaste = [];
1120 for (var i = 0; i < lastCopied.text.length; i++)
1121 multiPaste.push(doc.splitLines(lastCopied.text[i]));
1122 }
1123 } else if (textLines.length == sel.ranges.length) {
1124 multiPaste = map(textLines, function(l) { return [l]; });
1125 }
1126 }
1127
1128 // Normal behavior is to insert the new text into every selection
1129 for (var i = sel.ranges.length - 1; i >= 0; i--) {
1130 var range = sel.ranges[i];
1131 var from = range.from(), to = range.to();
1132 if (range.empty()) {
1133 if (deleted && deleted > 0) // Handle deletion
1134 from = Pos(from.line, from.ch - deleted);
1135 else if (cm.state.overwrite && !paste) // Handle overwrite
1136 to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length));
1137 else if (lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == inserted)
1138 from = to = Pos(from.line, 0)
1139 }
1140 var updateInput = cm.curOp.updateInput;
1141 var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i % m ultiPaste.length] : textLines,
1142 origin: origin || (paste ? "paste" : cm.state.cutIncomi ng ? "cut" : "+input")};
1143 makeChange(cm.doc, changeEvent);
1144 signalLater(cm, "inputRead", cm, changeEvent);
1145 }
1146 if (inserted && !paste)
1147 triggerElectric(cm, inserted);
1148
1149 ensureCursorVisible(cm);
1150 cm.curOp.updateInput = updateInput;
1151 cm.curOp.typing = true;
1152 cm.state.pasteIncoming = cm.state.cutIncoming = false;
1153 }
1154
1155 function handlePaste(e, cm) {
1156 var pasted = e.clipboardData && e.clipboardData.getData("text/plain");
1157 if (pasted) {
1158 e.preventDefault();
1159 if (!cm.isReadOnly() && !cm.options.disableInput)
1160 runInOp(cm, function() { applyTextInput(cm, pasted, 0, null, "paste"); } );
1161 return true;
1162 }
1163 }
1164
1165 function triggerElectric(cm, inserted) {
1166 // When an 'electric' character is inserted, immediately trigger a reindent
1167 if (!cm.options.electricChars || !cm.options.smartIndent) return;
1168 var sel = cm.doc.sel;
1169
1170 for (var i = sel.ranges.length - 1; i >= 0; i--) {
1171 var range = sel.ranges[i];
1172 if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head .line)) continue;
1173 var mode = cm.getModeAt(range.head);
1174 var indented = false;
1175 if (mode.electricChars) {
1176 for (var j = 0; j < mode.electricChars.length; j++)
1177 if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
1178 indented = indentLine(cm, range.head.line, "smart");
1179 break;
1180 }
1181 } else if (mode.electricInput) {
1182 if (mode.electricInput.test(getLine(cm.doc, range.head.line).text.slice( 0, range.head.ch)))
1183 indented = indentLine(cm, range.head.line, "smart");
1184 }
1185 if (indented) signalLater(cm, "electricInput", cm, range.head.line);
1186 }
1187 }
1188
1189 function copyableRanges(cm) {
1190 var text = [], ranges = [];
1191 for (var i = 0; i < cm.doc.sel.ranges.length; i++) {
1192 var line = cm.doc.sel.ranges[i].head.line;
1193 var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)};
1194 ranges.push(lineRange);
1195 text.push(cm.getRange(lineRange.anchor, lineRange.head));
1196 }
1197 return {text: text, ranges: ranges};
1198 }
1199
1200 function disableBrowserMagic(field) {
1201 field.setAttribute("autocorrect", "off");
1202 field.setAttribute("autocapitalize", "off");
1203 field.setAttribute("spellcheck", "false");
1204 }
1205
1206 // TEXTAREA INPUT STYLE
1207
1208 function TextareaInput(cm) {
1209 this.cm = cm;
1210 // See input.poll and input.reset
1211 this.prevInput = "";
1212
1213 // Flag that indicates whether we expect input to appear real soon
1214 // now (after some event like 'keypress' or 'input') and are
1215 // polling intensively.
1216 this.pollingFast = false;
1217 // Self-resetting timeout for the poller
1218 this.polling = new Delayed();
1219 // Tracks when input.reset has punted to just putting a short
1220 // string into the textarea instead of the full selection.
1221 this.inaccurateSelection = false;
1222 // Used to work around IE issue with selection being forgotten when focus mo ves away from textarea
1223 this.hasSelection = false;
1224 this.composing = null;
1225 };
1226
1227 function hiddenTextarea() {
1228 var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padd ing: 0; width: 1px; height: 1em; outline: none");
1229 var div = elt("div", [te], null, "overflow: hidden; position: relative; widt h: 3px; height: 0px;");
1230 // The textarea is kept positioned near the cursor to prevent the
1231 // fact that it'll be scrolled into view on input from scrolling
1232 // our fake cursor out of view. On webkit, when wrap=off, paste is
1233 // very slow. So make the area wide instead.
1234 if (webkit) te.style.width = "1000px";
1235 else te.setAttribute("wrap", "off");
1236 // If border: 0; -- iOS fails to open keyboard (issue #1287)
1237 if (ios) te.style.border = "1px solid black";
1238 disableBrowserMagic(te);
1239 return div;
1240 }
1241
1242 TextareaInput.prototype = copyObj({
1243 init: function(display) {
1244 var input = this, cm = this.cm;
1245
1246 // Wraps and hides input textarea
1247 var div = this.wrapper = hiddenTextarea();
1248 // The semihidden textarea that is focused when the editor is
1249 // focused, and receives input.
1250 var te = this.textarea = div.firstChild;
1251 display.wrapper.insertBefore(div, display.wrapper.firstChild);
1252
1253 // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)
1254 if (ios) te.style.width = "0px";
1255
1256 on(te, "input", function() {
1257 if (ie && ie_version >= 9 && input.hasSelection) input.hasSelection = nu ll;
1258 input.poll();
1259 });
1260
1261 on(te, "paste", function(e) {
1262 if (signalDOMEvent(cm, e) || handlePaste(e, cm)) return
1263
1264 cm.state.pasteIncoming = true;
1265 input.fastPoll();
1266 });
1267
1268 function prepareCopyCut(e) {
1269 if (signalDOMEvent(cm, e)) return
1270 if (cm.somethingSelected()) {
1271 lastCopied = {lineWise: false, text: cm.getSelections()};
1272 if (input.inaccurateSelection) {
1273 input.prevInput = "";
1274 input.inaccurateSelection = false;
1275 te.value = lastCopied.text.join("\n");
1276 selectInput(te);
1277 }
1278 } else if (!cm.options.lineWiseCopyCut) {
1279 return;
1280 } else {
1281 var ranges = copyableRanges(cm);
1282 lastCopied = {lineWise: true, text: ranges.text};
1283 if (e.type == "cut") {
1284 cm.setSelections(ranges.ranges, null, sel_dontScroll);
1285 } else {
1286 input.prevInput = "";
1287 te.value = ranges.text.join("\n");
1288 selectInput(te);
1289 }
1290 }
1291 if (e.type == "cut") cm.state.cutIncoming = true;
1292 }
1293 on(te, "cut", prepareCopyCut);
1294 on(te, "copy", prepareCopyCut);
1295
1296 on(display.scroller, "paste", function(e) {
1297 if (eventInWidget(display, e) || signalDOMEvent(cm, e)) return;
1298 cm.state.pasteIncoming = true;
1299 input.focus();
1300 });
1301
1302 // Prevent normal selection in the editor (we handle our own)
1303 on(display.lineSpace, "selectstart", function(e) {
1304 if (!eventInWidget(display, e)) e_preventDefault(e);
1305 });
1306
1307 on(te, "compositionstart", function() {
1308 var start = cm.getCursor("from");
1309 if (input.composing) input.composing.range.clear()
1310 input.composing = {
1311 start: start,
1312 range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror- composing"})
1313 };
1314 });
1315 on(te, "compositionend", function() {
1316 if (input.composing) {
1317 input.poll();
1318 input.composing.range.clear();
1319 input.composing = null;
1320 }
1321 });
1322 },
1323
1324 prepareSelection: function() {
1325 // Redraw the selection and/or cursor
1326 var cm = this.cm, display = cm.display, doc = cm.doc;
1327 var result = prepareSelection(cm);
1328
1329 // Move the hidden textarea near the cursor to prevent scrolling artifacts
1330 if (cm.options.moveInputWithCursor) {
1331 var headPos = cursorCoords(cm, doc.sel.primary().head, "div");
1332 var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display .lineDiv.getBoundingClientRect();
1333 result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
1334 headPos.top + lineOff.top - wrapOff. top));
1335 result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
1336 headPos.left + lineOff.left - wrapO ff.left));
1337 }
1338
1339 return result;
1340 },
1341
1342 showSelection: function(drawn) {
1343 var cm = this.cm, display = cm.display;
1344 removeChildrenAndAdd(display.cursorDiv, drawn.cursors);
1345 removeChildrenAndAdd(display.selectionDiv, drawn.selection);
1346 if (drawn.teTop != null) {
1347 this.wrapper.style.top = drawn.teTop + "px";
1348 this.wrapper.style.left = drawn.teLeft + "px";
1349 }
1350 },
1351
1352 // Reset the input to correspond to the selection (or to be empty,
1353 // when not typing and nothing is selected)
1354 reset: function(typing) {
1355 if (this.contextMenuPending) return;
1356 var minimal, selected, cm = this.cm, doc = cm.doc;
1357 if (cm.somethingSelected()) {
1358 this.prevInput = "";
1359 var range = doc.sel.primary();
1360 minimal = hasCopyEvent &&
1361 (range.to().line - range.from().line > 100 || (selected = cm.getSelect ion()).length > 1000);
1362 var content = minimal ? "-" : selected || cm.getSelection();
1363 this.textarea.value = content;
1364 if (cm.state.focused) selectInput(this.textarea);
1365 if (ie && ie_version >= 9) this.hasSelection = content;
1366 } else if (!typing) {
1367 this.prevInput = this.textarea.value = "";
1368 if (ie && ie_version >= 9) this.hasSelection = null;
1369 }
1370 this.inaccurateSelection = minimal;
1371 },
1372
1373 getField: function() { return this.textarea; },
1374
1375 supportsTouch: function() { return false; },
1376
1377 focus: function() {
1378 if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != t his.textarea)) {
1379 try { this.textarea.focus(); }
1380 catch (e) {} // IE8 will throw if the textarea is display: none or not i n DOM
1381 }
1382 },
1383
1384 blur: function() { this.textarea.blur(); },
1385
1386 resetPosition: function() {
1387 this.wrapper.style.top = this.wrapper.style.left = 0;
1388 },
1389
1390 receivedFocus: function() { this.slowPoll(); },
1391
1392 // Poll for input changes, using the normal rate of polling. This
1393 // runs as long as the editor is focused.
1394 slowPoll: function() {
1395 var input = this;
1396 if (input.pollingFast) return;
1397 input.polling.set(this.cm.options.pollInterval, function() {
1398 input.poll();
1399 if (input.cm.state.focused) input.slowPoll();
1400 });
1401 },
1402
1403 // When an event has just come in that is likely to add or change
1404 // something in the input textarea, we poll faster, to ensure that
1405 // the change appears on the screen quickly.
1406 fastPoll: function() {
1407 var missed = false, input = this;
1408 input.pollingFast = true;
1409 function p() {
1410 var changed = input.poll();
1411 if (!changed && !missed) {missed = true; input.polling.set(60, p);}
1412 else {input.pollingFast = false; input.slowPoll();}
1413 }
1414 input.polling.set(20, p);
1415 },
1416
1417 // Read input from the textarea, and update the document to match.
1418 // When something is selected, it is present in the textarea, and
1419 // selected (unless it is huge, in which case a placeholder is
1420 // used). When nothing is selected, the cursor sits after previously
1421 // seen text (can be empty), which is stored in prevInput (we must
1422 // not reset the textarea when typing, because that breaks IME).
1423 poll: function() {
1424 var cm = this.cm, input = this.textarea, prevInput = this.prevInput;
1425 // Since this is called a *lot*, try to bail out as cheaply as
1426 // possible when it is clear that nothing happened. hasSelection
1427 // will be the case when there is a lot of text in the textarea,
1428 // in which case reading its value would be expensive.
1429 if (this.contextMenuPending || !cm.state.focused ||
1430 (hasSelection(input) && !prevInput && !this.composing) ||
1431 cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq)
1432 return false;
1433
1434 var text = input.value;
1435 // If nothing changed, bail.
1436 if (text == prevInput && !cm.somethingSelected()) return false;
1437 // Work around nonsensical selection resetting in IE9/10, and
1438 // inexplicable appearance of private area unicode characters on
1439 // some key combos in Mac (#2689).
1440 if (ie && ie_version >= 9 && this.hasSelection === text ||
1441 mac && /[\uf700-\uf7ff]/.test(text)) {
1442 cm.display.input.reset();
1443 return false;
1444 }
1445
1446 if (cm.doc.sel == cm.display.selForContextMenu) {
1447 var first = text.charCodeAt(0);
1448 if (first == 0x200b && !prevInput) prevInput = "\u200b";
1449 if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo"); }
1450 }
1451 // Find the part of the input that is actually new
1452 var same = 0, l = Math.min(prevInput.length, text.length);
1453 while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++ same;
1454
1455 var self = this;
1456 runInOp(cm, function() {
1457 applyTextInput(cm, text.slice(same), prevInput.length - same,
1458 null, self.composing ? "*compose" : null);
1459
1460 // Don't leave long text in the textarea, since it makes further polling slow
1461 if (text.length > 1000 || text.indexOf("\n") > -1) input.value = self.pr evInput = "";
1462 else self.prevInput = text;
1463
1464 if (self.composing) {
1465 self.composing.range.clear();
1466 self.composing.range = cm.markText(self.composing.start, cm.getCursor( "to"),
1467 {className: "CodeMirror-composing"} );
1468 }
1469 });
1470 return true;
1471 },
1472
1473 ensurePolled: function() {
1474 if (this.pollingFast && this.poll()) this.pollingFast = false;
1475 },
1476
1477 onKeyPress: function() {
1478 if (ie && ie_version >= 9) this.hasSelection = null;
1479 this.fastPoll();
1480 },
1481
1482 onContextMenu: function(e) {
1483 var input = this, cm = input.cm, display = cm.display, te = input.textarea ;
1484 var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
1485 if (!pos || presto) return; // Opera is difficult.
1486
1487 // Reset the current text selection only if the click is done outside of t he selection
1488 // and 'resetSelectionOnContextMenu' option is true.
1489 var reset = cm.options.resetSelectionOnContextMenu;
1490 if (reset && cm.doc.sel.contains(pos) == -1)
1491 operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll );
1492
1493 var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText ;
1494 input.wrapper.style.cssText = "position: absolute"
1495 var wrapperBox = input.wrapper.getBoundingClientRect()
1496 te.style.cssText = "position: absolute; width: 30px; height: 30px; top: " + (e.clientY - wrapperBox.top - 5) +
1497 "px; left: " + (e.clientX - wrapperBox.left - 5) + "px; z-index: 1000; b ackground: " +
1498 (ie ? "rgba(255, 255, 255, .05)" : "transparent") +
1499 "; outline: none; border-width: 0; outline: none; overflow: hidden; opac ity: .05; filter: alpha(opacity=5);";
1500 if (webkit) var oldScrollY = window.scrollY; // Work around Chrome issue ( #2712)
1501 display.input.focus();
1502 if (webkit) window.scrollTo(null, oldScrollY);
1503 display.input.reset();
1504 // Adds "Select all" to context menu in FF
1505 if (!cm.somethingSelected()) te.value = input.prevInput = " ";
1506 input.contextMenuPending = true;
1507 display.selForContextMenu = cm.doc.sel;
1508 clearTimeout(display.detectingSelectAll);
1509
1510 // Select-all will be greyed out if there's nothing to select, so
1511 // this adds a zero-width space so that we can later check whether
1512 // it got selected.
1513 function prepareSelectAllHack() {
1514 if (te.selectionStart != null) {
1515 var selected = cm.somethingSelected();
1516 var extval = "\u200b" + (selected ? te.value : "");
1517 te.value = "\u21da"; // Used to catch context-menu undo
1518 te.value = extval;
1519 input.prevInput = selected ? "" : "\u200b";
1520 te.selectionStart = 1; te.selectionEnd = extval.length;
1521 // Re-set this, in case some other handler touched the
1522 // selection in the meantime.
1523 display.selForContextMenu = cm.doc.sel;
1524 } 1602 }
1525 } 1603 }
1526 function rehide() { 1604 }, lineClasses)
1527 input.contextMenuPending = false; 1605 };
1528 input.wrapper.style.cssText = oldWrapperCSS 1606
1529 te.style.cssText = oldCSS; 1607 for (var o = 0; o < cm.state.overlays.length; ++o) loop( o );
1530 if (ie && ie_version < 9) display.scrollbars.setScrollTop(display.scroll er.scrollTop = scrollPos); 1608
1531 1609 return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? li neClasses : null}
1532 // Try to detect the user choosing select-all 1610 }
1533 if (te.selectionStart != null) { 1611
1534 if (!ie || (ie && ie_version < 9)) prepareSelectAllHack(); 1612 function getLineStyles(cm, line, updateFrontier) {
1535 var i = 0, poll = function() { 1613 if (!line.styles || line.styles[0] != cm.state.modeGen) {
1536 if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 && 1614 var state = getStateBefore(cm, lineNo(line))
1537 te.selectionEnd > 0 && input.prevInput == "\u200b") 1615 var result = highlightLine(cm, line, line.text.length > cm.options.maxHighli ghtLength ? copyState(cm.doc.mode, state) : state)
1538 operation(cm, commands.selectAll)(cm); 1616 line.stateAfter = state
1539 else if (i++ < 10) display.detectingSelectAll = setTimeout(poll, 500 ); 1617 line.styles = result.styles
1540 else display.input.reset(); 1618 if (result.classes) { line.styleClasses = result.classes }
1541 }; 1619 else if (line.styleClasses) { line.styleClasses = null }
1542 display.detectingSelectAll = setTimeout(poll, 200); 1620 if (updateFrontier === cm.doc.frontier) { cm.doc.frontier++ }
1621 }
1622 return line.styles
1623 }
1624
1625 function getStateBefore(cm, n, precise) {
1626 var doc = cm.doc, display = cm.display
1627 if (!doc.mode.startState) { return true }
1628 var pos = findStartLine(cm, n, precise), state = pos > doc.first && getLine(do c, pos-1).stateAfter
1629 if (!state) { state = startState(doc.mode) }
1630 else { state = copyState(doc.mode, state) }
1631 doc.iter(pos, n, function (line) {
1632 processLine(cm, line.text, state)
1633 var save = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo
1634 line.stateAfter = save ? copyState(doc.mode, state) : null
1635 ++pos
1636 })
1637 if (precise) { doc.frontier = pos }
1638 return state
1639 }
1640
1641 // Lightweight form of highlight -- proceed over this line and
1642 // update state, but don't save a style array. Used for lines that
1643 // aren't currently visible.
1644 function processLine(cm, text, state, startAt) {
1645 var mode = cm.doc.mode
1646 var stream = new StringStream(text, cm.options.tabSize)
1647 stream.start = stream.pos = startAt || 0
1648 if (text == "") { callBlankLine(mode, state) }
1649 while (!stream.eol()) {
1650 readToken(mode, stream, state)
1651 stream.start = stream.pos
1652 }
1653 }
1654
1655 function callBlankLine(mode, state) {
1656 if (mode.blankLine) { return mode.blankLine(state) }
1657 if (!mode.innerMode) { return }
1658 var inner = innerMode(mode, state)
1659 if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) }
1660 }
1661
1662 function readToken(mode, stream, state, inner) {
1663 for (var i = 0; i < 10; i++) {
1664 if (inner) { inner[0] = innerMode(mode, state).mode }
1665 var style = mode.token(stream, state)
1666 if (stream.pos > stream.start) { return style }
1667 }
1668 throw new Error("Mode " + mode.name + " failed to advance stream.")
1669 }
1670
1671 // Utility for getTokenAt and getLineTokens
1672 function takeToken(cm, pos, precise, asArray) {
1673 var getObj = function (copy) { return ({
1674 start: stream.start, end: stream.pos,
1675 string: stream.current(),
1676 type: style || null,
1677 state: copy ? copyState(doc.mode, state) : state
1678 }); }
1679
1680 var doc = cm.doc, mode = doc.mode, style
1681 pos = clipPos(doc, pos)
1682 var line = getLine(doc, pos.line), state = getStateBefore(cm, pos.line, precis e)
1683 var stream = new StringStream(line.text, cm.options.tabSize), tokens
1684 if (asArray) { tokens = [] }
1685 while ((asArray || stream.pos < pos.ch) && !stream.eol()) {
1686 stream.start = stream.pos
1687 style = readToken(mode, stream, state)
1688 if (asArray) { tokens.push(getObj(true)) }
1689 }
1690 return asArray ? tokens : getObj()
1691 }
1692
1693 function extractLineClasses(type, output) {
1694 if (type) { for (;;) {
1695 var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/)
1696 if (!lineClass) { break }
1697 type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineCla ss[0].length)
1698 var prop = lineClass[1] ? "bgClass" : "textClass"
1699 if (output[prop] == null)
1700 { output[prop] = lineClass[2] }
1701 else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[p rop]))
1702 { output[prop] += " " + lineClass[2] }
1703 } }
1704 return type
1705 }
1706
1707 // Run the given mode's parser over a line, calling f for each token.
1708 function runMode(cm, text, mode, state, f, lineClasses, forceToEnd) {
1709 var flattenSpans = mode.flattenSpans
1710 if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans }
1711 var curStart = 0, curStyle = null
1712 var stream = new StringStream(text, cm.options.tabSize), style
1713 var inner = cm.options.addModeClass && [null]
1714 if (text == "") { extractLineClasses(callBlankLine(mode, state), lineClasses) }
1715 while (!stream.eol()) {
1716 if (stream.pos > cm.options.maxHighlightLength) {
1717 flattenSpans = false
1718 if (forceToEnd) { processLine(cm, text, state, stream.pos) }
1719 stream.pos = text.length
1720 style = null
1721 } else {
1722 style = extractLineClasses(readToken(mode, stream, state, inner), lineClas ses)
1723 }
1724 if (inner) {
1725 var mName = inner[0].name
1726 if (mName) { style = "m-" + (style ? mName + " " + style : mName) }
1727 }
1728 if (!flattenSpans || curStyle != style) {
1729 while (curStart < stream.start) {
1730 curStart = Math.min(stream.start, curStart + 5000)
1731 f(curStart, curStyle)
1732 }
1733 curStyle = style
1734 }
1735 stream.start = stream.pos
1736 }
1737 while (curStart < stream.pos) {
1738 // Webkit seems to refuse to render text nodes longer than 57444
1739 // characters, and returns inaccurate measurements in nodes
1740 // starting around 5000 chars.
1741 var pos = Math.min(stream.pos, curStart + 5000)
1742 f(pos, curStyle)
1743 curStart = pos
1744 }
1745 }
1746
1747 // Finds the line to start with when starting a parse. Tries to
1748 // find a line with a stateAfter, so that it can start with a
1749 // valid state. If that fails, it returns the line with the
1750 // smallest indentation, which tends to need the least context to
1751 // parse correctly.
1752 function findStartLine(cm, n, precise) {
1753 var minindent, minline, doc = cm.doc
1754 var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100)
1755 for (var search = n; search > lim; --search) {
1756 if (search <= doc.first) { return doc.first }
1757 var line = getLine(doc, search - 1)
1758 if (line.stateAfter && (!precise || search <= doc.frontier)) { return search }
1759 var indented = countColumn(line.text, null, cm.options.tabSize)
1760 if (minline == null || minindent > indented) {
1761 minline = search - 1
1762 minindent = indented
1763 }
1764 }
1765 return minline
1766 }
1767
1768 // LINE DATA STRUCTURE
1769
1770 // Line objects. These hold state related to a line, including
1771 // highlighting info (the styles array).
1772 var Line = function Line(text, markedSpans, estimateHeight) {
1773 this.text = text
1774 attachMarkedSpans(this, markedSpans)
1775 this.height = estimateHeight ? estimateHeight(this) : 1
1776 };
1777
1778 Line.prototype.lineNo = function lineNo$1 () { return lineNo(this) };
1779 eventMixin(Line)
1780
1781 // Change the content (text, markers) of a line. Automatically
1782 // invalidates cached information and tries to re-estimate the
1783 // line's height.
1784 function updateLine(line, text, markedSpans, estimateHeight) {
1785 line.text = text
1786 if (line.stateAfter) { line.stateAfter = null }
1787 if (line.styles) { line.styles = null }
1788 if (line.order != null) { line.order = null }
1789 detachMarkedSpans(line)
1790 attachMarkedSpans(line, markedSpans)
1791 var estHeight = estimateHeight ? estimateHeight(line) : 1
1792 if (estHeight != line.height) { updateLineHeight(line, estHeight) }
1793 }
1794
1795 // Detach a line from the document tree and its markers.
1796 function cleanUpLine(line) {
1797 line.parent = null
1798 detachMarkedSpans(line)
1799 }
1800
1801 // Convert a style as returned by a mode (either null, or a string
1802 // containing one or more styles) to a CSS style. This is cached,
1803 // and also looks for line-wide styles.
1804 var styleToClassCache = {};
1805 var styleToClassCacheWithMode = {};
1806 function interpretTokenStyle(style, options) {
1807 if (!style || /^\s*$/.test(style)) { return null }
1808 var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCac he
1809 return cache[style] ||
1810 (cache[style] = style.replace(/\S+/g, "cm-$&"))
1811 }
1812
1813 // Render the DOM representation of the text of a line. Also builds
1814 // up a 'line map', which points at the DOM nodes that represent
1815 // specific stretches of text, and is used by the measuring code.
1816 // The returned object contains the DOM node, this map, and
1817 // information about line-wide styles that were set by the mode.
1818 function buildLineContent(cm, lineView) {
1819 // The padding-right forces the element to have a 'border', which
1820 // is needed on Webkit to be able to get line-level bounding
1821 // rectangles for it (in measureChar).
1822 var content = eltP("span", null, null, webkit ? "padding-right: .1px" : null)
1823 var builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: conten t,
1824 col: 0, pos: 0, cm: cm,
1825 trailingSpace: false,
1826 splitSpaces: (ie || webkit) && cm.getOption("lineWrapping")}
1827 lineView.measure = {}
1828
1829 // Iterate over the logical lines that make up this visual line.
1830 for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {
1831 var line = i ? lineView.rest[i - 1] : lineView.line, order = (void 0)
1832 builder.pos = 0
1833 builder.addToken = buildToken
1834 // Optionally wire in some hacks into the token-rendering
1835 // algorithm, to deal with browser quirks.
1836 if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.di rection)))
1837 { builder.addToken = buildTokenBadBidi(builder.addToken, order) }
1838 builder.map = []
1839 var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo( line)
1840 insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate ))
1841 if (line.styleClasses) {
1842 if (line.styleClasses.bgClass)
1843 { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgCla ss || "") }
1844 if (line.styleClasses.textClass)
1845 { builder.textClass = joinClasses(line.styleClasses.textClass, builder.t extClass || "") }
1846 }
1847
1848 // Ensure at least a single node is present, for measuring.
1849 if (builder.map.length == 0)
1850 { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.d isplay.measure))) }
1851
1852 // Store the map and a cache object for the current logical line
1853 if (i == 0) {
1854 lineView.measure.map = builder.map
1855 lineView.measure.cache = {}
1856 } else {
1857 ;(lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map)
1858 ;(lineView.measure.caches || (lineView.measure.caches = [])).push({})
1859 }
1860 }
1861
1862 // See issue #2901
1863 if (webkit) {
1864 var last = builder.content.lastChild
1865 if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySe lector(".cm-tab")))
1866 { builder.content.className = "cm-tab-wrap-hack" }
1867 }
1868
1869 signal(cm, "renderLine", cm, lineView.line, builder.pre)
1870 if (builder.pre.className)
1871 { builder.textClass = joinClasses(builder.pre.className, builder.textClass | | "") }
1872
1873 return builder
1874 }
1875
1876 function defaultSpecialCharPlaceholder(ch) {
1877 var token = elt("span", "\u2022", "cm-invalidchar")
1878 token.title = "\\u" + ch.charCodeAt(0).toString(16)
1879 token.setAttribute("aria-label", token.title)
1880 return token
1881 }
1882
1883 // Build up the DOM representation for a single token, and add it to
1884 // the line map. Takes care to render special characters separately.
1885 function buildToken(builder, text, style, startStyle, endStyle, title, css) {
1886 if (!text) { return }
1887 var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpac e) : text
1888 var special = builder.cm.state.specialChars, mustWrap = false
1889 var content
1890 if (!special.test(text)) {
1891 builder.col += text.length
1892 content = document.createTextNode(displayText)
1893 builder.map.push(builder.pos, builder.pos + text.length, content)
1894 if (ie && ie_version < 9) { mustWrap = true }
1895 builder.pos += text.length
1896 } else {
1897 content = document.createDocumentFragment()
1898 var pos = 0
1899 while (true) {
1900 special.lastIndex = pos
1901 var m = special.exec(text)
1902 var skipped = m ? m.index - pos : text.length - pos
1903 if (skipped) {
1904 var txt = document.createTextNode(displayText.slice(pos, pos + skipped))
1905 if (ie && ie_version < 9) { content.appendChild(elt("span", [txt])) }
1906 else { content.appendChild(txt) }
1907 builder.map.push(builder.pos, builder.pos + skipped, txt)
1908 builder.col += skipped
1909 builder.pos += skipped
1910 }
1911 if (!m) { break }
1912 pos += skipped + 1
1913 var txt$1 = (void 0)
1914 if (m[0] == "\t") {
1915 var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.c ol % tabSize
1916 txt$1 = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"))
1917 txt$1.setAttribute("role", "presentation")
1918 txt$1.setAttribute("cm-text", "\t")
1919 builder.col += tabWidth
1920 } else if (m[0] == "\r" || m[0] == "\n") {
1921 txt$1 = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u242 4", "cm-invalidchar"))
1922 txt$1.setAttribute("cm-text", m[0])
1923 builder.col += 1
1924 } else {
1925 txt$1 = builder.cm.options.specialCharPlaceholder(m[0])
1926 txt$1.setAttribute("cm-text", m[0])
1927 if (ie && ie_version < 9) { content.appendChild(elt("span", [txt$1])) }
1928 else { content.appendChild(txt$1) }
1929 builder.col += 1
1930 }
1931 builder.map.push(builder.pos, builder.pos + 1, txt$1)
1932 builder.pos++
1933 }
1934 }
1935 builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32
1936 if (style || startStyle || endStyle || mustWrap || css) {
1937 var fullStyle = style || ""
1938 if (startStyle) { fullStyle += startStyle }
1939 if (endStyle) { fullStyle += endStyle }
1940 var token = elt("span", [content], fullStyle, css)
1941 if (title) { token.title = title }
1942 return builder.content.appendChild(token)
1943 }
1944 builder.content.appendChild(content)
1945 }
1946
1947 function splitSpaces(text, trailingBefore) {
1948 if (text.length > 1 && !/ /.test(text)) { return text }
1949 var spaceBefore = trailingBefore, result = ""
1950 for (var i = 0; i < text.length; i++) {
1951 var ch = text.charAt(i)
1952 if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32))
1953 { ch = "\u00a0" }
1954 result += ch
1955 spaceBefore = ch == " "
1956 }
1957 return result
1958 }
1959
1960 // Work around nonsense dimensions being reported for stretches of
1961 // right-to-left text.
1962 function buildTokenBadBidi(inner, order) {
1963 return function (builder, text, style, startStyle, endStyle, title, css) {
1964 style = style ? style + " cm-force-border" : "cm-force-border"
1965 var start = builder.pos, end = start + text.length
1966 for (;;) {
1967 // Find the part that overlaps with the start of this text
1968 var part = (void 0)
1969 for (var i = 0; i < order.length; i++) {
1970 part = order[i]
1971 if (part.to > start && part.from <= start) { break }
1972 }
1973 if (part.to >= end) { return inner(builder, text, style, startStyle, endSt yle, title, css) }
1974 inner(builder, text.slice(0, part.to - start), style, startStyle, null, ti tle, css)
1975 startStyle = null
1976 text = text.slice(part.to - start)
1977 start = part.to
1978 }
1979 }
1980 }
1981
1982 function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
1983 var widget = !ignoreWidget && marker.widgetNode
1984 if (widget) { builder.map.push(builder.pos, builder.pos + size, widget) }
1985 if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {
1986 if (!widget)
1987 { widget = builder.content.appendChild(document.createElement("span")) }
1988 widget.setAttribute("cm-marker", marker.id)
1989 }
1990 if (widget) {
1991 builder.cm.display.input.setUneditable(widget)
1992 builder.content.appendChild(widget)
1993 }
1994 builder.pos += size
1995 builder.trailingSpace = false
1996 }
1997
1998 // Outputs a number of spans to make up a line, taking highlighting
1999 // and marked text into account.
2000 function insertLineContent(line, builder, styles) {
2001 var spans = line.markedSpans, allText = line.text, at = 0
2002 if (!spans) {
2003 for (var i$1 = 1; i$1 < styles.length; i$1+=2)
2004 { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpret TokenStyle(styles[i$1+1], builder.cm.options)) }
2005 return
2006 }
2007
2008 var len = allText.length, pos = 0, i = 1, text = "", style, css
2009 var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed
2010 for (;;) {
2011 if (nextChange == pos) { // Update current marker set
2012 spanStyle = spanEndStyle = spanStartStyle = title = css = ""
2013 collapsed = null; nextChange = Infinity
2014 var foundBookmarks = [], endStyles = (void 0)
2015 for (var j = 0; j < spans.length; ++j) {
2016 var sp = spans[j], m = sp.marker
2017 if (m.type == "bookmark" && sp.from == pos && m.widgetNode) {
2018 foundBookmarks.push(m)
2019 } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapse d && sp.to == pos && sp.from == pos)) {
2020 if (sp.to != null && sp.to != pos && nextChange > sp.to) {
2021 nextChange = sp.to
2022 spanEndStyle = ""
2023 }
2024 if (m.className) { spanStyle += " " + m.className }
2025 if (m.css) { css = (css ? css + ";" : "") + m.css }
2026 if (m.startStyle && sp.from == pos) { spanStartStyle += " " + m.startS tyle }
2027 if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [] )).push(m.endStyle, sp.to) }
2028 if (m.title && !title) { title = m.title }
2029 if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.ma rker, m) < 0))
2030 { collapsed = sp }
2031 } else if (sp.from > pos && nextChange > sp.from) {
2032 nextChange = sp.from
1543 } 2033 }
1544 } 2034 }
1545 2035 if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2)
1546 if (ie && ie_version >= 9) prepareSelectAllHack(); 2036 { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += " " + endStyle s[j$1] } } }
1547 if (captureRightClick) { 2037
1548 e_stop(e); 2038 if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBo okmarks.length; ++j$2)
1549 var mouseup = function() { 2039 { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]) } }
1550 off(window, "mouseup", mouseup); 2040 if (collapsed && (collapsed.from || 0) == pos) {
1551 setTimeout(rehide, 20); 2041 buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed. to) - pos,
1552 }; 2042 collapsed.marker, collapsed.from == null)
1553 on(window, "mouseup", mouseup); 2043 if (collapsed.to == null) { return }
2044 if (collapsed.to == pos) { collapsed = false }
2045 }
2046 }
2047 if (pos >= len) { break }
2048
2049 var upto = Math.min(len, nextChange)
2050 while (true) {
2051 if (text) {
2052 var end = pos + text.length
2053 if (!collapsed) {
2054 var tokenText = end > upto ? text.slice(0, upto - pos) : text
2055 builder.addToken(builder, tokenText, style ? style + spanStyle : spanS tyle,
2056 spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", title, css)
2057 }
2058 if (end >= upto) {text = text.slice(upto - pos); pos = upto; break}
2059 pos = end
2060 spanStartStyle = ""
2061 }
2062 text = allText.slice(at, at = styles[i++])
2063 style = interpretTokenStyle(styles[i++], builder.cm.options)
2064 }
2065 }
2066 }
2067
2068
2069 // These objects are used to represent the visible (currently drawn)
2070 // part of the document. A LineView may correspond to multiple
2071 // logical lines, if those are connected by collapsed ranges.
2072 function LineView(doc, line, lineN) {
2073 // The starting line
2074 this.line = line
2075 // Continuing lines, if any
2076 this.rest = visualLineContinued(line)
2077 // Number of logical lines in this visual line
2078 this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1
2079 this.node = this.text = null
2080 this.hidden = lineIsHidden(doc, line)
2081 }
2082
2083 // Create a range of LineView objects for the given lines.
2084 function buildViewArray(cm, from, to) {
2085 var array = [], nextPos
2086 for (var pos = from; pos < to; pos = nextPos) {
2087 var view = new LineView(cm.doc, getLine(cm.doc, pos), pos)
2088 nextPos = pos + view.size
2089 array.push(view)
2090 }
2091 return array
2092 }
2093
2094 var operationGroup = null
2095
2096 function pushOperation(op) {
2097 if (operationGroup) {
2098 operationGroup.ops.push(op)
2099 } else {
2100 op.ownsGroup = operationGroup = {
2101 ops: [op],
2102 delayedCallbacks: []
2103 }
2104 }
2105 }
2106
2107 function fireCallbacksForOps(group) {
2108 // Calls delayed callbacks and cursorActivity handlers until no
2109 // new ones appear
2110 var callbacks = group.delayedCallbacks, i = 0
2111 do {
2112 for (; i < callbacks.length; i++)
2113 { callbacks[i].call(null) }
2114 for (var j = 0; j < group.ops.length; j++) {
2115 var op = group.ops[j]
2116 if (op.cursorActivityHandlers)
2117 { while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
2118 { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.c m) } }
2119 }
2120 } while (i < callbacks.length)
2121 }
2122
2123 function finishOperation(op, endCb) {
2124 var group = op.ownsGroup
2125 if (!group) { return }
2126
2127 try { fireCallbacksForOps(group) }
2128 finally {
2129 operationGroup = null
2130 endCb(group)
2131 }
2132 }
2133
2134 var orphanDelayedCallbacks = null
2135
2136 // Often, we want to signal events at a point where we are in the
2137 // middle of some work, but don't want the handler to start calling
2138 // other methods on the editor, which might be in an inconsistent
2139 // state or simply not expect any other events to happen.
2140 // signalLater looks whether there are any handlers, and schedules
2141 // them to be executed when the last operation ends, or, if no
2142 // operation is active, when a timeout fires.
2143 function signalLater(emitter, type /*, values...*/) {
2144 var arr = getHandlers(emitter, type)
2145 if (!arr.length) { return }
2146 var args = Array.prototype.slice.call(arguments, 2), list
2147 if (operationGroup) {
2148 list = operationGroup.delayedCallbacks
2149 } else if (orphanDelayedCallbacks) {
2150 list = orphanDelayedCallbacks
2151 } else {
2152 list = orphanDelayedCallbacks = []
2153 setTimeout(fireOrphanDelayed, 0)
2154 }
2155 var loop = function ( i ) {
2156 list.push(function () { return arr[i].apply(null, args); })
2157 };
2158
2159 for (var i = 0; i < arr.length; ++i)
2160 loop( i );
2161 }
2162
2163 function fireOrphanDelayed() {
2164 var delayed = orphanDelayedCallbacks
2165 orphanDelayedCallbacks = null
2166 for (var i = 0; i < delayed.length; ++i) { delayed[i]() }
2167 }
2168
2169 // When an aspect of a line changes, a string is added to
2170 // lineView.changes. This updates the relevant part of the line's
2171 // DOM structure.
2172 function updateLineForChanges(cm, lineView, lineN, dims) {
2173 for (var j = 0; j < lineView.changes.length; j++) {
2174 var type = lineView.changes[j]
2175 if (type == "text") { updateLineText(cm, lineView) }
2176 else if (type == "gutter") { updateLineGutter(cm, lineView, lineN, dims) }
2177 else if (type == "class") { updateLineClasses(cm, lineView) }
2178 else if (type == "widget") { updateLineWidgets(cm, lineView, dims) }
2179 }
2180 lineView.changes = null
2181 }
2182
2183 // Lines with gutter elements, widgets or a background class need to
2184 // be wrapped, and have the extra elements added to the wrapper div
2185 function ensureLineWrapped(lineView) {
2186 if (lineView.node == lineView.text) {
2187 lineView.node = elt("div", null, null, "position: relative")
2188 if (lineView.text.parentNode)
2189 { lineView.text.parentNode.replaceChild(lineView.node, lineView.text) }
2190 lineView.node.appendChild(lineView.text)
2191 if (ie && ie_version < 8) { lineView.node.style.zIndex = 2 }
2192 }
2193 return lineView.node
2194 }
2195
2196 function updateLineBackground(cm, lineView) {
2197 var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass | | "") : lineView.line.bgClass
2198 if (cls) { cls += " CodeMirror-linebackground" }
2199 if (lineView.background) {
2200 if (cls) { lineView.background.className = cls }
2201 else { lineView.background.parentNode.removeChild(lineView.background); line View.background = null }
2202 } else if (cls) {
2203 var wrap = ensureLineWrapped(lineView)
2204 lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChi ld)
2205 cm.display.input.setUneditable(lineView.background)
2206 }
2207 }
2208
2209 // Wrapper around buildLineContent which will reuse the structure
2210 // in display.externalMeasured when possible.
2211 function getLineContent(cm, lineView) {
2212 var ext = cm.display.externalMeasured
2213 if (ext && ext.line == lineView.line) {
2214 cm.display.externalMeasured = null
2215 lineView.measure = ext.measure
2216 return ext.built
2217 }
2218 return buildLineContent(cm, lineView)
2219 }
2220
2221 // Redraw the line's text. Interacts with the background and text
2222 // classes because the mode may output tokens that influence these
2223 // classes.
2224 function updateLineText(cm, lineView) {
2225 var cls = lineView.text.className
2226 var built = getLineContent(cm, lineView)
2227 if (lineView.text == lineView.node) { lineView.node = built.pre }
2228 lineView.text.parentNode.replaceChild(built.pre, lineView.text)
2229 lineView.text = built.pre
2230 if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass ) {
2231 lineView.bgClass = built.bgClass
2232 lineView.textClass = built.textClass
2233 updateLineClasses(cm, lineView)
2234 } else if (cls) {
2235 lineView.text.className = cls
2236 }
2237 }
2238
2239 function updateLineClasses(cm, lineView) {
2240 updateLineBackground(cm, lineView)
2241 if (lineView.line.wrapClass)
2242 { ensureLineWrapped(lineView).className = lineView.line.wrapClass }
2243 else if (lineView.node != lineView.text)
2244 { lineView.node.className = "" }
2245 var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line .textClass || "") : lineView.line.textClass
2246 lineView.text.className = textClass || ""
2247 }
2248
2249 function updateLineGutter(cm, lineView, lineN, dims) {
2250 if (lineView.gutter) {
2251 lineView.node.removeChild(lineView.gutter)
2252 lineView.gutter = null
2253 }
2254 if (lineView.gutterBackground) {
2255 lineView.node.removeChild(lineView.gutterBackground)
2256 lineView.gutterBackground = null
2257 }
2258 if (lineView.line.gutterClass) {
2259 var wrap = ensureLineWrapped(lineView)
2260 lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass,
2261 ("left: " + (cm.options.fixedGutter ? dims.f ixedPos : -dims.gutterTotalWidth) + "px; width: " + (dims.gutterTotalWidth) + "p x"))
2262 cm.display.input.setUneditable(lineView.gutterBackground)
2263 wrap.insertBefore(lineView.gutterBackground, lineView.text)
2264 }
2265 var markers = lineView.line.gutterMarkers
2266 if (cm.options.lineNumbers || markers) {
2267 var wrap$1 = ensureLineWrapped(lineView)
2268 var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapp er", ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidt h) + "px"))
2269 cm.display.input.setUneditable(gutterWrap)
2270 wrap$1.insertBefore(gutterWrap, lineView.text)
2271 if (lineView.line.gutterClass)
2272 { gutterWrap.className += " " + lineView.line.gutterClass }
2273 if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers" ]))
2274 { lineView.lineNumber = gutterWrap.appendChild(
2275 elt("div", lineNumberFor(cm.options, lineN),
2276 "CodeMirror-linenumber CodeMirror-gutter-elt",
2277 ("left: " + (dims.gutterLeft["CodeMirror-linenumbers"]) + "px; width : " + (cm.display.lineNumInnerWidth) + "px"))) }
2278 if (markers) { for (var k = 0; k < cm.options.gutters.length; ++k) {
2279 var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && mark ers[id]
2280 if (found)
2281 { gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt",
2282 ("left: " + (dims.gutterLeft[id]) + "px; widt h: " + (dims.gutterWidth[id]) + "px"))) }
2283 } }
2284 }
2285 }
2286
2287 function updateLineWidgets(cm, lineView, dims) {
2288 if (lineView.alignable) { lineView.alignable = null }
2289 for (var node = lineView.node.firstChild, next = (void 0); node; node = next) {
2290 next = node.nextSibling
2291 if (node.className == "CodeMirror-linewidget")
2292 { lineView.node.removeChild(node) }
2293 }
2294 insertLineWidgets(cm, lineView, dims)
2295 }
2296
2297 // Build a line's DOM representation from scratch
2298 function buildLineElement(cm, lineView, lineN, dims) {
2299 var built = getLineContent(cm, lineView)
2300 lineView.text = lineView.node = built.pre
2301 if (built.bgClass) { lineView.bgClass = built.bgClass }
2302 if (built.textClass) { lineView.textClass = built.textClass }
2303
2304 updateLineClasses(cm, lineView)
2305 updateLineGutter(cm, lineView, lineN, dims)
2306 insertLineWidgets(cm, lineView, dims)
2307 return lineView.node
2308 }
2309
2310 // A lineView may contain multiple logical lines (when merged by
2311 // collapsed spans). The widgets for all of them need to be drawn.
2312 function insertLineWidgets(cm, lineView, dims) {
2313 insertLineWidgetsFor(cm, lineView.line, lineView, dims, true)
2314 if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
2315 { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false) } }
2316 }
2317
2318 function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {
2319 if (!line.widgets) { return }
2320 var wrap = ensureLineWrapped(lineView)
2321 for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
2322 var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget" )
2323 if (!widget.handleMouseEvents) { node.setAttribute("cm-ignore-events", "true ") }
2324 positionLineWidget(widget, node, lineView, dims)
2325 cm.display.input.setUneditable(node)
2326 if (allowAbove && widget.above)
2327 { wrap.insertBefore(node, lineView.gutter || lineView.text) }
2328 else
2329 { wrap.appendChild(node) }
2330 signalLater(widget, "redraw")
2331 }
2332 }
2333
2334 function positionLineWidget(widget, node, lineView, dims) {
2335 if (widget.noHScroll) {
2336 ;(lineView.alignable || (lineView.alignable = [])).push(node)
2337 var width = dims.wrapperWidth
2338 node.style.left = dims.fixedPos + "px"
2339 if (!widget.coverGutter) {
2340 width -= dims.gutterTotalWidth
2341 node.style.paddingLeft = dims.gutterTotalWidth + "px"
2342 }
2343 node.style.width = width + "px"
2344 }
2345 if (widget.coverGutter) {
2346 node.style.zIndex = 5
2347 node.style.position = "relative"
2348 if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + "p x" }
2349 }
2350 }
2351
2352 function widgetHeight(widget) {
2353 if (widget.height != null) { return widget.height }
2354 var cm = widget.doc.cm
2355 if (!cm) { return 0 }
2356 if (!contains(document.body, widget.node)) {
2357 var parentStyle = "position: relative;"
2358 if (widget.coverGutter)
2359 { parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;" }
2360 if (widget.noHScroll)
2361 { parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;" }
2362 removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, par entStyle))
2363 }
2364 return widget.height = widget.node.parentNode.offsetHeight
2365 }
2366
2367 // Return true when the given mouse event happened in a widget
2368 function eventInWidget(display, e) {
2369 for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
2370 if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") ||
2371 (n.parentNode == display.sizer && n != display.mover))
2372 { return true }
2373 }
2374 }
2375
2376 // POSITION MEASUREMENT
2377
2378 function paddingTop(display) {return display.lineSpace.offsetTop}
2379 function paddingVert(display) {return display.mover.offsetHeight - display.lineS pace.offsetHeight}
2380 function paddingH(display) {
2381 if (display.cachedPaddingH) { return display.cachedPaddingH }
2382 var e = removeChildrenAndAdd(display.measure, elt("pre", "x"))
2383 var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentSt yle
2384 var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRi ght)}
2385 if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data }
2386 return data
2387 }
2388
2389 function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth }
2390 function displayWidth(cm) {
2391 return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth
2392 }
2393 function displayHeight(cm) {
2394 return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight
2395 }
2396
2397 // Ensure the lineView.wrapping.heights array is populated. This is
2398 // an array of bottom offsets for the lines that make up a drawn
2399 // line. When lineWrapping is on, there might be more than one
2400 // height.
2401 function ensureLineHeights(cm, lineView, rect) {
2402 var wrapping = cm.options.lineWrapping
2403 var curWidth = wrapping && displayWidth(cm)
2404 if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidt h) {
2405 var heights = lineView.measure.heights = []
2406 if (wrapping) {
2407 lineView.measure.width = curWidth
2408 var rects = lineView.text.firstChild.getClientRects()
2409 for (var i = 0; i < rects.length - 1; i++) {
2410 var cur = rects[i], next = rects[i + 1]
2411 if (Math.abs(cur.bottom - next.bottom) > 2)
2412 { heights.push((cur.bottom + next.top) / 2 - rect.top) }
2413 }
2414 }
2415 heights.push(rect.bottom - rect.top)
2416 }
2417 }
2418
2419 // Find a line map (mapping character offsets to text nodes) and a
2420 // measurement cache for the given line number. (A line view might
2421 // contain multiple lines when collapsed ranges are present.)
2422 function mapFromLineView(lineView, line, lineN) {
2423 if (lineView.line == line)
2424 { return {map: lineView.measure.map, cache: lineView.measure.cache} }
2425 for (var i = 0; i < lineView.rest.length; i++)
2426 { if (lineView.rest[i] == line)
2427 { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i] } } }
2428 for (var i$1 = 0; i$1 < lineView.rest.length; i$1++)
2429 { if (lineNo(lineView.rest[i$1]) > lineN)
2430 { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[ i$1], before: true} } }
2431 }
2432
2433 // Render a line into the hidden node display.externalMeasured. Used
2434 // when measurement is needed for a line that's not in the viewport.
2435 function updateExternalMeasurement(cm, line) {
2436 line = visualLine(line)
2437 var lineN = lineNo(line)
2438 var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN)
2439 view.lineN = lineN
2440 var built = view.built = buildLineContent(cm, view)
2441 view.text = built.pre
2442 removeChildrenAndAdd(cm.display.lineMeasure, built.pre)
2443 return view
2444 }
2445
2446 // Get a {top, bottom, left, right} box (in line-local coordinates)
2447 // for a given character.
2448 function measureChar(cm, line, ch, bias) {
2449 return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias)
2450 }
2451
2452 // Find a line view that corresponds to the given line number.
2453 function findViewForLine(cm, lineN) {
2454 if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo)
2455 { return cm.display.view[findViewIndex(cm, lineN)] }
2456 var ext = cm.display.externalMeasured
2457 if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size)
2458 { return ext }
2459 }
2460
2461 // Measurement can be split in two steps, the set-up work that
2462 // applies to the whole line, and the measurement of the actual
2463 // character. Functions like coordsChar, that need to do a lot of
2464 // measurements in a row, can thus ensure that the set-up work is
2465 // only done once.
2466 function prepareMeasureForLine(cm, line) {
2467 var lineN = lineNo(line)
2468 var view = findViewForLine(cm, lineN)
2469 if (view && !view.text) {
2470 view = null
2471 } else if (view && view.changes) {
2472 updateLineForChanges(cm, view, lineN, getDimensions(cm))
2473 cm.curOp.forceUpdate = true
2474 }
2475 if (!view)
2476 { view = updateExternalMeasurement(cm, line) }
2477
2478 var info = mapFromLineView(view, line, lineN)
2479 return {
2480 line: line, view: view, rect: null,
2481 map: info.map, cache: info.cache, before: info.before,
2482 hasHeights: false
2483 }
2484 }
2485
2486 // Given a prepared measurement object, measures the position of an
2487 // actual character (or fetches it from the cache).
2488 function measureCharPrepared(cm, prepared, ch, bias, varHeight) {
2489 if (prepared.before) { ch = -1 }
2490 var key = ch + (bias || ""), found
2491 if (prepared.cache.hasOwnProperty(key)) {
2492 found = prepared.cache[key]
2493 } else {
2494 if (!prepared.rect)
2495 { prepared.rect = prepared.view.text.getBoundingClientRect() }
2496 if (!prepared.hasHeights) {
2497 ensureLineHeights(cm, prepared.view, prepared.rect)
2498 prepared.hasHeights = true
2499 }
2500 found = measureCharInner(cm, prepared, ch, bias)
2501 if (!found.bogus) { prepared.cache[key] = found }
2502 }
2503 return {left: found.left, right: found.right,
2504 top: varHeight ? found.rtop : found.top,
2505 bottom: varHeight ? found.rbottom : found.bottom}
2506 }
2507
2508 var nullRect = {left: 0, right: 0, top: 0, bottom: 0}
2509
2510 function nodeAndOffsetInLineMap(map, ch, bias) {
2511 var node, start, end, collapse, mStart, mEnd
2512 // First, search the line map for the text node corresponding to,
2513 // or closest to, the target character.
2514 for (var i = 0; i < map.length; i += 3) {
2515 mStart = map[i]
2516 mEnd = map[i + 1]
2517 if (ch < mStart) {
2518 start = 0; end = 1
2519 collapse = "left"
2520 } else if (ch < mEnd) {
2521 start = ch - mStart
2522 end = start + 1
2523 } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) {
2524 end = mEnd - mStart
2525 start = end - 1
2526 if (ch >= mEnd) { collapse = "right" }
2527 }
2528 if (start != null) {
2529 node = map[i + 2]
2530 if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right"))
2531 { collapse = bias }
2532 if (bias == "left" && start == 0)
2533 { while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) {
2534 node = map[(i -= 3) + 2]
2535 collapse = "left"
2536 } }
2537 if (bias == "right" && start == mEnd - mStart)
2538 { while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].i nsertLeft) {
2539 node = map[(i += 3) + 2]
2540 collapse = "right"
2541 } }
2542 break
2543 }
2544 }
2545 return {node: node, start: start, end: end, collapse: collapse, coverStart: mS tart, coverEnd: mEnd}
2546 }
2547
2548 function getUsefulRect(rects, bias) {
2549 var rect = nullRect
2550 if (bias == "left") { for (var i = 0; i < rects.length; i++) {
2551 if ((rect = rects[i]).left != rect.right) { break }
2552 } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) {
2553 if ((rect = rects[i$1]).left != rect.right) { break }
2554 } }
2555 return rect
2556 }
2557
2558 function measureCharInner(cm, prepared, ch, bias) {
2559 var place = nodeAndOffsetInLineMap(prepared.map, ch, bias)
2560 var node = place.node, start = place.start, end = place.end, collapse = place. collapse
2561
2562 var rect
2563 if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.
2564 for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonse nse rectangles are returned
2565 while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start }
2566 while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared .line.text.charAt(place.coverStart + end))) { ++end }
2567 if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.co verStart)
2568 { rect = node.parentNode.getBoundingClientRect() }
2569 else
2570 { rect = getUsefulRect(range(node, start, end).getClientRects(), bias) }
2571 if (rect.left || rect.right || start == 0) { break }
2572 end = start
2573 start = start - 1
2574 collapse = "right"
2575 }
2576 if (ie && ie_version < 11) { rect = maybeUpdateRectForZooming(cm.display.mea sure, rect) }
2577 } else { // If it is a widget, simply get the box for the whole widget.
2578 if (start > 0) { collapse = bias = "right" }
2579 var rects
2580 if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)
2581 { rect = rects[bias == "right" ? rects.length - 1 : 0] }
2582 else
2583 { rect = node.getBoundingClientRect() }
2584 }
2585 if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {
2586 var rSpan = node.parentNode.getClientRects()[0]
2587 if (rSpan)
2588 { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top : rSpan.top, bottom: rSpan.bottom} }
2589 else
2590 { rect = nullRect }
2591 }
2592
2593 var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.to p
2594 var mid = (rtop + rbot) / 2
2595 var heights = prepared.view.measure.heights
2596 var i = 0
2597 for (; i < heights.length - 1; i++)
2598 { if (mid < heights[i]) { break } }
2599 var top = i ? heights[i - 1] : 0, bot = heights[i]
2600 var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared. rect.left,
2601 right: (collapse == "left" ? rect.left : rect.right) - prepared. rect.left,
2602 top: top, bottom: bot}
2603 if (!rect.left && !rect.right) { result.bogus = true }
2604 if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbotto m = rbot }
2605
2606 return result
2607 }
2608
2609 // Work around problem with bounding client rects on ranges being
2610 // returned incorrectly when zoomed on IE10 and below.
2611 function maybeUpdateRectForZooming(measure, rect) {
2612 if (!window.screen || screen.logicalXDPI == null ||
2613 screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))
2614 { return rect }
2615 var scaleX = screen.logicalXDPI / screen.deviceXDPI
2616 var scaleY = screen.logicalYDPI / screen.deviceYDPI
2617 return {left: rect.left * scaleX, right: rect.right * scaleX,
2618 top: rect.top * scaleY, bottom: rect.bottom * scaleY}
2619 }
2620
2621 function clearLineMeasurementCacheFor(lineView) {
2622 if (lineView.measure) {
2623 lineView.measure.cache = {}
2624 lineView.measure.heights = null
2625 if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
2626 { lineView.measure.caches[i] = {} } }
2627 }
2628 }
2629
2630 function clearLineMeasurementCache(cm) {
2631 cm.display.externalMeasure = null
2632 removeChildren(cm.display.lineMeasure)
2633 for (var i = 0; i < cm.display.view.length; i++)
2634 { clearLineMeasurementCacheFor(cm.display.view[i]) }
2635 }
2636
2637 function clearCaches(cm) {
2638 clearLineMeasurementCache(cm)
2639 cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPa ddingH = null
2640 if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true }
2641 cm.display.lineNumChars = null
2642 }
2643
2644 function pageScrollX() { return window.pageXOffset || (document.documentElement || document.body).scrollLeft }
2645 function pageScrollY() { return window.pageYOffset || (document.documentElement || document.body).scrollTop }
2646
2647 // Converts a {top, bottom, left, right} box from line-local
2648 // coordinates into another coordinate system. Context may be one of
2649 // "line", "div" (display.lineDiv), "local"./null (editor), "window",
2650 // or "page".
2651 function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) {
2652 if (!includeWidgets && lineObj.widgets) { for (var i = 0; i < lineObj.widgets. length; ++i) { if (lineObj.widgets[i].above) {
2653 var size = widgetHeight(lineObj.widgets[i])
2654 rect.top += size; rect.bottom += size
2655 } } }
2656 if (context == "line") { return rect }
2657 if (!context) { context = "local" }
2658 var yOff = heightAtLine(lineObj)
2659 if (context == "local") { yOff += paddingTop(cm.display) }
2660 else { yOff -= cm.display.viewOffset }
2661 if (context == "page" || context == "window") {
2662 var lOff = cm.display.lineSpace.getBoundingClientRect()
2663 yOff += lOff.top + (context == "window" ? 0 : pageScrollY())
2664 var xOff = lOff.left + (context == "window" ? 0 : pageScrollX())
2665 rect.left += xOff; rect.right += xOff
2666 }
2667 rect.top += yOff; rect.bottom += yOff
2668 return rect
2669 }
2670
2671 // Coverts a box from "div" coords to another coordinate system.
2672 // Context may be "window", "page", "div", or "local"./null.
2673 function fromCoordSystem(cm, coords, context) {
2674 if (context == "div") { return coords }
2675 var left = coords.left, top = coords.top
2676 // First move into "page" coordinate system
2677 if (context == "page") {
2678 left -= pageScrollX()
2679 top -= pageScrollY()
2680 } else if (context == "local" || !context) {
2681 var localBox = cm.display.sizer.getBoundingClientRect()
2682 left += localBox.left
2683 top += localBox.top
2684 }
2685
2686 var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect()
2687 return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top}
2688 }
2689
2690 function charCoords(cm, pos, context, lineObj, bias) {
2691 if (!lineObj) { lineObj = getLine(cm.doc, pos.line) }
2692 return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), co ntext)
2693 }
2694
2695 // Returns a box for a given cursor position, which may have an
2696 // 'other' property containing the position of the secondary cursor
2697 // on a bidi boundary.
2698 // A cursor Pos(line, char, "before") is on the same visual line as `char - 1`
2699 // and after `char - 1` in writing order of `char - 1`
2700 // A cursor Pos(line, char, "after") is on the same visual line as `char`
2701 // and before `char` in writing order of `char`
2702 // Examples (upper-case letters are RTL, lower-case are LTR):
2703 // Pos(0, 1, ...)
2704 // before after
2705 // ab a|b a|b
2706 // aB a|B aB|
2707 // Ab |Ab A|b
2708 // AB B|A B|A
2709 // Every position after the last character on a line is considered to stick
2710 // to the last character on the line.
2711 function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
2712 lineObj = lineObj || getLine(cm.doc, pos.line)
2713 if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj) }
2714 function get(ch, right) {
2715 var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left ", varHeight)
2716 if (right) { m.left = m.right; } else { m.right = m.left }
2717 return intoCoordSystem(cm, lineObj, m, context)
2718 }
2719 var order = getOrder(lineObj, cm.doc.direction), ch = pos.ch, sticky = pos.sti cky
2720 if (ch >= lineObj.text.length) {
2721 ch = lineObj.text.length
2722 sticky = "before"
2723 } else if (ch <= 0) {
2724 ch = 0
2725 sticky = "after"
2726 }
2727 if (!order) { return get(sticky == "before" ? ch - 1 : ch, sticky == "before") }
2728
2729 function getBidi(ch, partPos, invert) {
2730 var part = order[partPos], right = (part.level % 2) != 0
2731 return get(invert ? ch - 1 : ch, right != invert)
2732 }
2733 var partPos = getBidiPartAt(order, ch, sticky)
2734 var other = bidiOther
2735 var val = getBidi(ch, partPos, sticky == "before")
2736 if (other != null) { val.other = getBidi(ch, other, sticky != "before") }
2737 return val
2738 }
2739
2740 // Used to cheaply estimate the coordinates for a position. Used for
2741 // intermediate scroll updates.
2742 function estimateCoords(cm, pos) {
2743 var left = 0
2744 pos = clipPos(cm.doc, pos)
2745 if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch }
2746 var lineObj = getLine(cm.doc, pos.line)
2747 var top = heightAtLine(lineObj) + paddingTop(cm.display)
2748 return {left: left, right: left, top: top, bottom: top + lineObj.height}
2749 }
2750
2751 // Positions returned by coordsChar contain some extra information.
2752 // xRel is the relative x position of the input coordinates compared
2753 // to the found position (so xRel > 0 means the coordinates are to
2754 // the right of the character position, for example). When outside
2755 // is true, that means the coordinates lie outside the line's
2756 // vertical range.
2757 function PosWithInfo(line, ch, sticky, outside, xRel) {
2758 var pos = Pos(line, ch, sticky)
2759 pos.xRel = xRel
2760 if (outside) { pos.outside = true }
2761 return pos
2762 }
2763
2764 // Compute the character position closest to the given coordinates.
2765 // Input must be lineSpace-local ("div" coordinate system).
2766 function coordsChar(cm, x, y) {
2767 var doc = cm.doc
2768 y += cm.display.viewOffset
2769 if (y < 0) { return PosWithInfo(doc.first, 0, null, true, -1) }
2770 var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1
2771 if (lineN > last)
2772 { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.lengt h, null, true, 1) }
2773 if (x < 0) { x = 0 }
2774
2775 var lineObj = getLine(doc, lineN)
2776 for (;;) {
2777 var found = coordsCharInner(cm, lineObj, lineN, x, y)
2778 var merged = collapsedSpanAtEnd(lineObj)
2779 var mergedPos = merged && merged.find(0, true)
2780 if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0))
2781 { lineN = lineNo(lineObj = mergedPos.to.line) }
2782 else
2783 { return found }
2784 }
2785 }
2786
2787 function wrappedLineExtent(cm, lineObj, preparedMeasure, y) {
2788 var measure = function (ch) { return intoCoordSystem(cm, lineObj, measureCharP repared(cm, preparedMeasure, ch), "line"); }
2789 var end = lineObj.text.length
2790 var begin = findFirst(function (ch) { return measure(ch - 1).bottom <= y; }, e nd, 0)
2791 end = findFirst(function (ch) { return measure(ch).top > y; }, begin, end)
2792 return {begin: begin, end: end}
2793 }
2794
2795 function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) {
2796 var targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedM easure, target), "line").top
2797 return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop)
2798 }
2799
2800 function coordsCharInner(cm, lineObj, lineNo, x, y) {
2801 y -= heightAtLine(lineObj)
2802 var begin = 0, end = lineObj.text.length
2803 var preparedMeasure = prepareMeasureForLine(cm, lineObj)
2804 var pos
2805 var order = getOrder(lineObj, cm.doc.direction)
2806 if (order) {
2807 if (cm.options.lineWrapping) {
2808 ;var assign;
2809 ((assign = wrappedLineExtent(cm, lineObj, preparedMeasure, y), begin = ass ign.begin, end = assign.end))
2810 }
2811 pos = new Pos(lineNo, begin)
2812 var beginLeft = cursorCoords(cm, pos, "line", lineObj, preparedMeasure).left
2813 var dir = beginLeft < x ? 1 : -1
2814 var prevDiff, diff = beginLeft - x, prevPos
2815 do {
2816 prevDiff = diff
2817 prevPos = pos
2818 pos = moveVisually(cm, lineObj, pos, dir)
2819 if (pos == null || pos.ch < begin || end <= (pos.sticky == "before" ? pos. ch - 1 : pos.ch)) {
2820 pos = prevPos
2821 break
2822 }
2823 diff = cursorCoords(cm, pos, "line", lineObj, preparedMeasure).left - x
2824 } while ((dir < 0) != (diff < 0) && (Math.abs(diff) <= Math.abs(prevDiff)))
2825 if (Math.abs(diff) > Math.abs(prevDiff)) {
2826 if ((diff < 0) == (prevDiff < 0)) { throw new Error("Broke out of infinite loop in coordsCharInner") }
2827 pos = prevPos
2828 }
2829 } else {
2830 var ch = findFirst(function (ch) {
2831 var box = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMea sure, ch), "line")
2832 if (box.top > y) {
2833 // For the cursor stickiness
2834 end = Math.min(ch, end)
2835 return true
2836 }
2837 else if (box.bottom <= y) { return false }
2838 else if (box.left > x) { return true }
2839 else if (box.right < x) { return false }
2840 else { return (x - box.left < box.right - x) }
2841 }, begin, end)
2842 ch = skipExtendingChars(lineObj.text, ch, 1)
2843 pos = new Pos(lineNo, ch, ch == end ? "before" : "after")
2844 }
2845 var coords = cursorCoords(cm, pos, "line", lineObj, preparedMeasure)
2846 if (y < coords.top || coords.bottom < y) { pos.outside = true }
2847 pos.xRel = x < coords.left ? -1 : (x > coords.right ? 1 : 0)
2848 return pos
2849 }
2850
2851 var measureText
2852 // Compute the default text height.
2853 function textHeight(display) {
2854 if (display.cachedTextHeight != null) { return display.cachedTextHeight }
2855 if (measureText == null) {
2856 measureText = elt("pre")
2857 // Measure a bunch of lines, for browsers that compute
2858 // fractional heights.
2859 for (var i = 0; i < 49; ++i) {
2860 measureText.appendChild(document.createTextNode("x"))
2861 measureText.appendChild(elt("br"))
2862 }
2863 measureText.appendChild(document.createTextNode("x"))
2864 }
2865 removeChildrenAndAdd(display.measure, measureText)
2866 var height = measureText.offsetHeight / 50
2867 if (height > 3) { display.cachedTextHeight = height }
2868 removeChildren(display.measure)
2869 return height || 1
2870 }
2871
2872 // Compute the default character width.
2873 function charWidth(display) {
2874 if (display.cachedCharWidth != null) { return display.cachedCharWidth }
2875 var anchor = elt("span", "xxxxxxxxxx")
2876 var pre = elt("pre", [anchor])
2877 removeChildrenAndAdd(display.measure, pre)
2878 var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10
2879 if (width > 2) { display.cachedCharWidth = width }
2880 return width || 10
2881 }
2882
2883 // Do a bulk-read of the DOM positions and sizes needed to draw the
2884 // view, so that we don't interleave reading and writing to the DOM.
2885 function getDimensions(cm) {
2886 var d = cm.display, left = {}, width = {}
2887 var gutterLeft = d.gutters.clientLeft
2888 for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
2889 left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft
2890 width[cm.options.gutters[i]] = n.clientWidth
2891 }
2892 return {fixedPos: compensateForHScroll(d),
2893 gutterTotalWidth: d.gutters.offsetWidth,
2894 gutterLeft: left,
2895 gutterWidth: width,
2896 wrapperWidth: d.wrapper.clientWidth}
2897 }
2898
2899 // Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
2900 // but using getBoundingClientRect to get a sub-pixel-accurate
2901 // result.
2902 function compensateForHScroll(display) {
2903 return display.scroller.getBoundingClientRect().left - display.sizer.getBoundi ngClientRect().left
2904 }
2905
2906 // Returns a function that estimates the height of a line, to use as
2907 // first approximation until the line becomes visible (and is thus
2908 // properly measurable).
2909 function estimateHeight(cm) {
2910 var th = textHeight(cm.display), wrapping = cm.options.lineWrapping
2911 var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWi dth(cm.display) - 3)
2912 return function (line) {
2913 if (lineIsHidden(cm.doc, line)) { return 0 }
2914
2915 var widgetsHeight = 0
2916 if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) {
2917 if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height }
2918 } }
2919
2920 if (wrapping)
2921 { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th }
2922 else
2923 { return widgetsHeight + th }
2924 }
2925 }
2926
2927 function estimateLineHeights(cm) {
2928 var doc = cm.doc, est = estimateHeight(cm)
2929 doc.iter(function (line) {
2930 var estHeight = est(line)
2931 if (estHeight != line.height) { updateLineHeight(line, estHeight) }
2932 })
2933 }
2934
2935 // Given a mouse event, find the corresponding position. If liberal
2936 // is false, it checks whether a gutter or scrollbar was clicked,
2937 // and returns null if it was. forRect is used by rectangular
2938 // selections, and tries to estimate a character position even for
2939 // coordinates beyond the right of the text.
2940 function posFromMouse(cm, e, liberal, forRect) {
2941 var display = cm.display
2942 if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") { return null }
2943
2944 var x, y, space = display.lineSpace.getBoundingClientRect()
2945 // Fails unpredictably on IE[67] when mouse is dragged around quickly.
2946 try { x = e.clientX - space.left; y = e.clientY - space.top }
2947 catch (e) { return null }
2948 var coords = coordsChar(cm, x, y), line
2949 if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text). length == coords.ch) {
2950 var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.leng th
2951 coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).l eft) / charWidth(cm.display)) - colDiff))
2952 }
2953 return coords
2954 }
2955
2956 // Find the view element corresponding to a given line. Return null
2957 // when the line isn't visible.
2958 function findViewIndex(cm, n) {
2959 if (n >= cm.display.viewTo) { return null }
2960 n -= cm.display.viewFrom
2961 if (n < 0) { return null }
2962 var view = cm.display.view
2963 for (var i = 0; i < view.length; i++) {
2964 n -= view[i].size
2965 if (n < 0) { return i }
2966 }
2967 }
2968
2969 function updateSelection(cm) {
2970 cm.display.input.showSelection(cm.display.input.prepareSelection())
2971 }
2972
2973 function prepareSelection(cm, primary) {
2974 var doc = cm.doc, result = {}
2975 var curFragment = result.cursors = document.createDocumentFragment()
2976 var selFragment = result.selection = document.createDocumentFragment()
2977
2978 for (var i = 0; i < doc.sel.ranges.length; i++) {
2979 if (primary === false && i == doc.sel.primIndex) { continue }
2980 var range = doc.sel.ranges[i]
2981 if (range.from().line >= cm.display.viewTo || range.to().line < cm.display.v iewFrom) { continue }
2982 var collapsed = range.empty()
2983 if (collapsed || cm.options.showCursorWhenSelecting)
2984 { drawSelectionCursor(cm, range.head, curFragment) }
2985 if (!collapsed)
2986 { drawSelectionRange(cm, range, selFragment) }
2987 }
2988 return result
2989 }
2990
2991 // Draws a cursor for the given range
2992 function drawSelectionCursor(cm, head, output) {
2993 var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHe ightPerLine)
2994
2995 var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor"))
2996 cursor.style.left = pos.left + "px"
2997 cursor.style.top = pos.top + "px"
2998 cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHei ght + "px"
2999
3000 if (pos.other) {
3001 // Secondary cursor, shown when on a 'jump' in bi-directional text
3002 var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"))
3003 otherCursor.style.display = ""
3004 otherCursor.style.left = pos.other.left + "px"
3005 otherCursor.style.top = pos.other.top + "px"
3006 otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px"
3007 }
3008 }
3009
3010 // Draws the given range as a highlighted selection
3011 function drawSelectionRange(cm, range, output) {
3012 var display = cm.display, doc = cm.doc
3013 var fragment = document.createDocumentFragment()
3014 var padding = paddingH(cm.display), leftSide = padding.left
3015 var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer. offsetLeft) - padding.right
3016
3017 function add(left, top, width, bottom) {
3018 if (top < 0) { top = 0 }
3019 top = Math.round(top)
3020 bottom = Math.round(bottom)
3021 fragment.appendChild(elt("div", null, "CodeMirror-selected", ("position: abs olute; left: " + left + "px;\n top: " + top + "px; w idth: " + (width == null ? rightSide - left : width) + "px;\n height: " + (bottom - top) + "px")))
3022 }
3023
3024 function drawForLine(line, fromArg, toArg) {
3025 var lineObj = getLine(doc, line)
3026 var lineLen = lineObj.text.length
3027 var start, end
3028 function coords(ch, bias) {
3029 return charCoords(cm, Pos(line, ch), "div", lineObj, bias)
3030 }
3031
3032 iterateBidiSections(getOrder(lineObj, doc.direction), fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir) {
3033 var leftPos = coords(from, "left"), rightPos, left, right
3034 if (from == to) {
3035 rightPos = leftPos
3036 left = right = leftPos.left
1554 } else { 3037 } else {
1555 setTimeout(rehide, 50); 3038 rightPos = coords(to - 1, "right")
3039 if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tm p }
3040 left = leftPos.left
3041 right = rightPos.right
1556 } 3042 }
1557 }, 3043 if (fromArg == null && from == 0) { left = leftSide }
1558 3044 if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
1559 readOnlyChanged: function(val) { 3045 add(left, leftPos.top, null, leftPos.bottom)
1560 if (!val) this.reset(); 3046 left = leftSide
1561 }, 3047 if (leftPos.bottom < rightPos.top) { add(left, leftPos.bottom, null, rig htPos.top) }
1562 3048 }
1563 setUneditable: nothing, 3049 if (toArg == null && to == lineLen) { right = rightSide }
1564 3050 if (!start || leftPos.top < start.top || leftPos.top == start.top && leftP os.left < start.left)
1565 needsContentAttribute: false 3051 { start = leftPos }
1566 }, TextareaInput.prototype); 3052 if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.bottom && rightPos.right > end.right)
1567 3053 { end = rightPos }
1568 // CONTENTEDITABLE INPUT STYLE 3054 if (left < leftSide + 1) { left = leftSide }
1569 3055 add(left, rightPos.top, right - left, rightPos.bottom)
1570 function ContentEditableInput(cm) { 3056 })
1571 this.cm = cm; 3057 return {start: start, end: end}
1572 this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.last FocusOffset = null; 3058 }
1573 this.polling = new Delayed(); 3059
1574 this.gracePeriod = false; 3060 var sFrom = range.from(), sTo = range.to()
1575 } 3061 if (sFrom.line == sTo.line) {
1576 3062 drawForLine(sFrom.line, sFrom.ch, sTo.ch)
1577 ContentEditableInput.prototype = copyObj({ 3063 } else {
1578 init: function(display) { 3064 var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line)
1579 var input = this, cm = input.cm; 3065 var singleVLine = visualLine(fromLine) == visualLine(toLine)
1580 var div = input.div = display.lineDiv; 3066 var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text. length + 1 : null).end
1581 disableBrowserMagic(div); 3067 var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start
1582 3068 if (singleVLine) {
1583 on(div, "paste", function(e) { 3069 if (leftEnd.top < rightStart.top - 2) {
1584 if (!signalDOMEvent(cm, e)) handlePaste(e, cm); 3070 add(leftEnd.right, leftEnd.top, null, leftEnd.bottom)
1585 }) 3071 add(leftSide, rightStart.top, rightStart.left, rightStart.bottom)
1586 3072 } else {
1587 on(div, "compositionstart", function(e) { 3073 add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd .bottom)
1588 var data = e.data; 3074 }
1589 input.composing = {sel: cm.doc.sel, data: data, startData: data}; 3075 }
1590 if (!data) return; 3076 if (leftEnd.bottom < rightStart.top)
1591 var prim = cm.doc.sel.primary(); 3077 { add(leftSide, leftEnd.bottom, null, rightStart.top) }
1592 var line = cm.getLine(prim.head.line); 3078 }
1593 var found = line.indexOf(data, Math.max(0, prim.head.ch - data.length)); 3079
1594 if (found > -1 && found <= prim.head.ch) 3080 output.appendChild(fragment)
1595 input.composing.sel = simpleSelection(Pos(prim.head.line, found), 3081 }
1596 Pos(prim.head.line, found + data .length)); 3082
1597 }); 3083 // Cursor-blinking
1598 on(div, "compositionupdate", function(e) { 3084 function restartBlink(cm) {
1599 input.composing.data = e.data; 3085 if (!cm.state.focused) { return }
1600 }); 3086 var display = cm.display
1601 on(div, "compositionend", function(e) { 3087 clearInterval(display.blinker)
1602 var ours = input.composing; 3088 var on = true
1603 if (!ours) return; 3089 display.cursorDiv.style.visibility = ""
1604 if (e.data != ours.startData && !/\u200b/.test(e.data)) 3090 if (cm.options.cursorBlinkRate > 0)
1605 ours.data = e.data; 3091 { display.blinker = setInterval(function () { return display.cursorDiv.style .visibility = (on = !on) ? "" : "hidden"; },
1606 // Need a small delay to prevent other code (input event, 3092 cm.options.cursorBlinkRate) }
1607 // selection polling) from doing damage when fired right after 3093 else if (cm.options.cursorBlinkRate < 0)
1608 // compositionend. 3094 { display.cursorDiv.style.visibility = "hidden" }
1609 setTimeout(function() { 3095 }
1610 if (!ours.handled) 3096
1611 input.applyComposition(ours); 3097 function ensureFocus(cm) {
1612 if (input.composing == ours) 3098 if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm) }
1613 input.composing = null; 3099 }
1614 }, 50); 3100
1615 }); 3101 function delayBlurEvent(cm) {
1616 3102 cm.state.delayingBlurEvent = true
1617 on(div, "touchstart", function() { 3103 setTimeout(function () { if (cm.state.delayingBlurEvent) {
1618 input.forceCompositionEnd(); 3104 cm.state.delayingBlurEvent = false
1619 }); 3105 onBlur(cm)
1620 3106 } }, 100)
1621 on(div, "input", function() { 3107 }
1622 if (input.composing) return; 3108
1623 if (cm.isReadOnly() || !input.pollContent()) 3109 function onFocus(cm, e) {
1624 runInOp(input.cm, function() {regChange(cm);}); 3110 if (cm.state.delayingBlurEvent) { cm.state.delayingBlurEvent = false }
1625 }); 3111
1626 3112 if (cm.options.readOnly == "nocursor") { return }
1627 function onCopyCut(e) { 3113 if (!cm.state.focused) {
1628 if (signalDOMEvent(cm, e)) return 3114 signal(cm, "focus", cm, e)
1629 if (cm.somethingSelected()) { 3115 cm.state.focused = true
1630 lastCopied = {lineWise: false, text: cm.getSelections()}; 3116 addClass(cm.display.wrapper, "CodeMirror-focused")
1631 if (e.type == "cut") cm.replaceSelection("", null, "cut"); 3117 // This test prevents this from firing when a context
1632 } else if (!cm.options.lineWiseCopyCut) { 3118 // menu is closed (since the input reset would kill the
1633 return; 3119 // select-all detection hack)
1634 } else { 3120 if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {
1635 var ranges = copyableRanges(cm); 3121 cm.display.input.reset()
1636 lastCopied = {lineWise: true, text: ranges.text}; 3122 if (webkit) { setTimeout(function () { return cm.display.input.reset(true) ; }, 20) } // Issue #1730
1637 if (e.type == "cut") { 3123 }
1638 cm.operation(function() { 3124 cm.display.input.receivedFocus()
1639 cm.setSelections(ranges.ranges, 0, sel_dontScroll); 3125 }
1640 cm.replaceSelection("", null, "cut"); 3126 restartBlink(cm)
1641 }); 3127 }
1642 } 3128 function onBlur(cm, e) {
1643 } 3129 if (cm.state.delayingBlurEvent) { return }
1644 // iOS exposes the clipboard API, but seems to discard content inserted into it 3130
1645 if (e.clipboardData && !ios) { 3131 if (cm.state.focused) {
1646 e.preventDefault(); 3132 signal(cm, "blur", cm, e)
1647 e.clipboardData.clearData(); 3133 cm.state.focused = false
1648 e.clipboardData.setData("text/plain", lastCopied.text.join("\n")); 3134 rmClass(cm.display.wrapper, "CodeMirror-focused")
1649 } else { 3135 }
1650 // Old-fashioned briefly-focus-a-textarea hack 3136 clearInterval(cm.display.blinker)
1651 var kludge = hiddenTextarea(), te = kludge.firstChild; 3137 setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false } } , 150)
1652 cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstCh ild); 3138 }
1653 te.value = lastCopied.text.join("\n"); 3139
1654 var hadFocus = document.activeElement; 3140 // Re-align line numbers and gutter marks to compensate for
1655 selectInput(te); 3141 // horizontal scrolling.
1656 setTimeout(function() { 3142 function alignHorizontally(cm) {
1657 cm.display.lineSpace.removeChild(kludge); 3143 var display = cm.display, view = display.view
1658 hadFocus.focus(); 3144 if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixed Gutter)) { return }
1659 }, 50); 3145 var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.do c.scrollLeft
3146 var gutterW = display.gutters.offsetWidth, left = comp + "px"
3147 for (var i = 0; i < view.length; i++) { if (!view[i].hidden) {
3148 if (cm.options.fixedGutter) {
3149 if (view[i].gutter)
3150 { view[i].gutter.style.left = left }
3151 if (view[i].gutterBackground)
3152 { view[i].gutterBackground.style.left = left }
3153 }
3154 var align = view[i].alignable
3155 if (align) { for (var j = 0; j < align.length; j++)
3156 { align[j].style.left = left } }
3157 } }
3158 if (cm.options.fixedGutter)
3159 { display.gutters.style.left = (comp + gutterW) + "px" }
3160 }
3161
3162 // Used to ensure that the line number gutter is still the right
3163 // size for the current document size. Returns true when an update
3164 // is needed.
3165 function maybeUpdateLineNumberWidth(cm) {
3166 if (!cm.options.lineNumbers) { return false }
3167 var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display
3168 if (last.length != display.lineNumChars) {
3169 var test = display.measure.appendChild(elt("div", [elt("div", last)],
3170 "CodeMirror-linenumber CodeMirror -gutter-elt"))
3171 var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - inner W
3172 display.lineGutter.style.width = ""
3173 display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1
3174 display.lineNumWidth = display.lineNumInnerWidth + padding
3175 display.lineNumChars = display.lineNumInnerWidth ? last.length : -1
3176 display.lineGutter.style.width = display.lineNumWidth + "px"
3177 updateGutterSpace(cm)
3178 return true
3179 }
3180 return false
3181 }
3182
3183 // Read the actual heights of the rendered lines, and update their
3184 // stored heights to match.
3185 function updateHeightsInViewport(cm) {
3186 var display = cm.display
3187 var prevBottom = display.lineDiv.offsetTop
3188 for (var i = 0; i < display.view.length; i++) {
3189 var cur = display.view[i], height = (void 0)
3190 if (cur.hidden) { continue }
3191 if (ie && ie_version < 8) {
3192 var bot = cur.node.offsetTop + cur.node.offsetHeight
3193 height = bot - prevBottom
3194 prevBottom = bot
3195 } else {
3196 var box = cur.node.getBoundingClientRect()
3197 height = box.bottom - box.top
3198 }
3199 var diff = cur.line.height - height
3200 if (height < 2) { height = textHeight(display) }
3201 if (diff > .001 || diff < -.001) {
3202 updateLineHeight(cur.line, height)
3203 updateWidgetHeight(cur.line)
3204 if (cur.rest) { for (var j = 0; j < cur.rest.length; j++)
3205 { updateWidgetHeight(cur.rest[j]) } }
3206 }
3207 }
3208 }
3209
3210 // Read and store the height of line widgets associated with the
3211 // given line.
3212 function updateWidgetHeight(line) {
3213 if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i)
3214 { line.widgets[i].height = line.widgets[i].node.parentNode.offsetHeight } }
3215 }
3216
3217 // Compute the lines that are visible in a given viewport (defaults
3218 // the the current scroll position). viewport may contain top,
3219 // height, and ensure (see op.scrollToPos) properties.
3220 function visibleLines(display, doc, viewport) {
3221 var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : displ ay.scroller.scrollTop
3222 top = Math.floor(top - paddingTop(display))
3223 var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + dis play.wrapper.clientHeight
3224
3225 var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom)
3226 // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
3227 // forces those lines into the viewport (if possible).
3228 if (viewport && viewport.ensure) {
3229 var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.li ne
3230 if (ensureFrom < from) {
3231 from = ensureFrom
3232 to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wr apper.clientHeight)
3233 } else if (Math.min(ensureTo, doc.lastLine()) >= to) {
3234 from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wr apper.clientHeight)
3235 to = ensureTo
3236 }
3237 }
3238 return {from: from, to: Math.max(to, from + 1)}
3239 }
3240
3241 // Sync the scrollable area and scrollbars, ensure the viewport
3242 // covers the visible area.
3243 function setScrollTop(cm, val) {
3244 if (Math.abs(cm.doc.scrollTop - val) < 2) { return }
3245 cm.doc.scrollTop = val
3246 if (!gecko) { updateDisplaySimple(cm, {top: val}) }
3247 if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = va l }
3248 cm.display.scrollbars.setScrollTop(val)
3249 if (gecko) { updateDisplaySimple(cm) }
3250 startWorker(cm, 100)
3251 }
3252 // Sync scroller and scrollbar, ensure the gutter elements are
3253 // aligned.
3254 function setScrollLeft(cm, val, isScroller) {
3255 if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) { return }
3256 val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clie ntWidth)
3257 cm.doc.scrollLeft = val
3258 alignHorizontally(cm)
3259 if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val }
3260 cm.display.scrollbars.setScrollLeft(val)
3261 }
3262
3263 // Since the delta values reported on mouse wheel events are
3264 // unstandardized between browsers and even browser versions, and
3265 // generally horribly unpredictable, this code starts by measuring
3266 // the scroll effect that the first few mouse wheel events have,
3267 // and, from that, detects the way it can convert deltas to pixel
3268 // offsets afterwards.
3269 //
3270 // The reason we want to know the amount a wheel event will scroll
3271 // is that it gives us a chance to update the display before the
3272 // actual scrolling happens, reducing flickering.
3273
3274 var wheelSamples = 0;
3275 var wheelPixelsPerUnit = null;
3276 // Fill in a browser-detected starting value on browsers where we
3277 // know one. These don't have to be accurate -- the result of them
3278 // being wrong would just be a slight flicker on the first wheel
3279 // scroll (if it is large enough).
3280 if (ie) { wheelPixelsPerUnit = -.53 }
3281 else if (gecko) { wheelPixelsPerUnit = 15 }
3282 else if (chrome) { wheelPixelsPerUnit = -.7 }
3283 else if (safari) { wheelPixelsPerUnit = -1/3 }
3284
3285 function wheelEventDelta(e) {
3286 var dx = e.wheelDeltaX, dy = e.wheelDeltaY
3287 if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail }
3288 if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail }
3289 else if (dy == null) { dy = e.wheelDelta }
3290 return {x: dx, y: dy}
3291 }
3292 function wheelEventPixels(e) {
3293 var delta = wheelEventDelta(e)
3294 delta.x *= wheelPixelsPerUnit
3295 delta.y *= wheelPixelsPerUnit
3296 return delta
3297 }
3298
3299 function onScrollWheel(cm, e) {
3300 var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y
3301
3302 var display = cm.display, scroll = display.scroller
3303 // Quit if there's nothing to scroll here
3304 var canScrollX = scroll.scrollWidth > scroll.clientWidth
3305 var canScrollY = scroll.scrollHeight > scroll.clientHeight
3306 if (!(dx && canScrollX || dy && canScrollY)) { return }
3307
3308 // Webkit browsers on OS X abort momentum scrolls when the target
3309 // of the scroll event is removed from the scrollable element.
3310 // This hack (see related code in patchDisplay) makes sure the
3311 // element is kept around.
3312 if (dy && mac && webkit) {
3313 outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cu r.parentNode) {
3314 for (var i = 0; i < view.length; i++) {
3315 if (view[i].node == cur) {
3316 cm.display.currentWheelTarget = cur
3317 break outer
1660 } 3318 }
1661 } 3319 }
1662 on(div, "copy", onCopyCut); 3320 }
1663 on(div, "cut", onCopyCut); 3321 }
3322
3323 // On some browsers, horizontal scrolling will cause redraws to
3324 // happen before the gutter has been realigned, causing it to
3325 // wriggle around in a most unseemly way. When we have an
3326 // estimated pixels/delta value, we just handle horizontal
3327 // scrolling entirely here. It'll be slightly off from native, but
3328 // better than glitching out.
3329 if (dx && !gecko && !presto && wheelPixelsPerUnit != null) {
3330 if (dy && canScrollY)
3331 { setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixel sPerUnit, scroll.scrollHeight - scroll.clientHeight))) }
3332 setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsP erUnit, scroll.scrollWidth - scroll.clientWidth)))
3333 // Only prevent default scrolling if vertical scrolling is
3334 // actually possible. Otherwise, it causes vertical scroll
3335 // jitter on OSX trackpads when deltaX is small and deltaY
3336 // is large (issue #3579)
3337 if (!dy || (dy && canScrollY))
3338 { e_preventDefault(e) }
3339 display.wheelStartX = null // Abort measurement, if in progress
3340 return
3341 }
3342
3343 // 'Project' the visible viewport to cover the area that is being
3344 // scrolled into view (if we know enough to estimate it).
3345 if (dy && wheelPixelsPerUnit != null) {
3346 var pixels = dy * wheelPixelsPerUnit
3347 var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight
3348 if (pixels < 0) { top = Math.max(0, top + pixels - 50) }
3349 else { bot = Math.min(cm.doc.height, bot + pixels + 50) }
3350 updateDisplaySimple(cm, {top: top, bottom: bot})
3351 }
3352
3353 if (wheelSamples < 20) {
3354 if (display.wheelStartX == null) {
3355 display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scro llTop
3356 display.wheelDX = dx; display.wheelDY = dy
3357 setTimeout(function () {
3358 if (display.wheelStartX == null) { return }
3359 var movedX = scroll.scrollLeft - display.wheelStartX
3360 var movedY = scroll.scrollTop - display.wheelStartY
3361 var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
3362 (movedX && display.wheelDX && movedX / display.wheelDX)
3363 display.wheelStartX = display.wheelStartY = null
3364 if (!sample) { return }
3365 wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (whe elSamples + 1)
3366 ++wheelSamples
3367 }, 200)
3368 } else {
3369 display.wheelDX += dx; display.wheelDY += dy
3370 }
3371 }
3372 }
3373
3374 // SCROLLBARS
3375
3376 // Prepare DOM reads needed to update the scrollbars. Done in one
3377 // shot to minimize update/measure roundtrips.
3378 function measureForScrollbars(cm) {
3379 var d = cm.display, gutterW = d.gutters.offsetWidth
3380 var docH = Math.round(cm.doc.height + paddingVert(cm.display))
3381 return {
3382 clientHeight: d.scroller.clientHeight,
3383 viewHeight: d.wrapper.clientHeight,
3384 scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,
3385 viewWidth: d.wrapper.clientWidth,
3386 barLeft: cm.options.fixedGutter ? gutterW : 0,
3387 docHeight: docH,
3388 scrollHeight: docH + scrollGap(cm) + d.barHeight,
3389 nativeBarWidth: d.nativeBarWidth,
3390 gutterWidth: gutterW
3391 }
3392 }
3393
3394 var NativeScrollbars = function NativeScrollbars(place, scroll, cm) {
3395 this.cm = cm
3396 var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar")
3397 var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min -height: 1px")], "CodeMirror-hscrollbar")
3398 place(vert); place(horiz)
3399
3400 on(vert, "scroll", function () {
3401 if (vert.clientHeight) { scroll(vert.scrollTop, "vertical") }
3402 })
3403 on(horiz, "scroll", function () {
3404 if (horiz.clientWidth) { scroll(horiz.scrollLeft, "horizontal") }
3405 })
3406
3407 this.checkedZeroWidth = false
3408 // Need to set a minimum width to see the scrollbar on IE7 (but must not set i t on IE8).
3409 if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWi dth = "18px" }
3410 };
3411
3412 NativeScrollbars.prototype.update = function update (measure) {
3413 var needsH = measure.scrollWidth > measure.clientWidth + 1
3414 var needsV = measure.scrollHeight > measure.clientHeight + 1
3415 var sWidth = measure.nativeBarWidth
3416
3417 if (needsV) {
3418 this.vert.style.display = "block"
3419 this.vert.style.bottom = needsH ? sWidth + "px" : "0"
3420 var totalHeight = measure.viewHeight - (needsH ? sWidth : 0)
3421 // A bug in IE8 can cause this value to be negative, so guard it.
3422 this.vert.firstChild.style.height =
3423 Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + " px"
3424 } else {
3425 this.vert.style.display = ""
3426 this.vert.firstChild.style.height = "0"
3427 }
3428
3429 if (needsH) {
3430 this.horiz.style.display = "block"
3431 this.horiz.style.right = needsV ? sWidth + "px" : "0"
3432 this.horiz.style.left = measure.barLeft + "px"
3433 var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0)
3434 this.horiz.firstChild.style.width =
3435 Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + "px"
3436 } else {
3437 this.horiz.style.display = ""
3438 this.horiz.firstChild.style.width = "0"
3439 }
3440
3441 if (!this.checkedZeroWidth && measure.clientHeight > 0) {
3442 if (sWidth == 0) { this.zeroWidthHack() }
3443 this.checkedZeroWidth = true
3444 }
3445
3446 return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0}
3447 };
3448
3449 NativeScrollbars.prototype.setScrollLeft = function setScrollLeft$1 (pos) {
3450 if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos }
3451 if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz ) }
3452 };
3453
3454 NativeScrollbars.prototype.setScrollTop = function setScrollTop$1 (pos) {
3455 if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos }
3456 if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert) }
3457 };
3458
3459 NativeScrollbars.prototype.zeroWidthHack = function zeroWidthHack () {
3460 var w = mac && !mac_geMountainLion ? "12px" : "18px"
3461 this.horiz.style.height = this.vert.style.width = w
3462 this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none"
3463 this.disableHoriz = new Delayed
3464 this.disableVert = new Delayed
3465 };
3466
3467 NativeScrollbars.prototype.enableZeroWidthBar = function enableZeroWidthBar (bar , delay) {
3468 bar.style.pointerEvents = "auto"
3469 function maybeDisable() {
3470 // To find out whether the scrollbar is still visible, we
3471 // check whether the element under the pixel in the bottom
3472 // left corner of the scrollbar box is the scrollbar box
3473 // itself (when the bar is still visible) or its filler child
3474 // (when the bar is hidden). If it is still visible, we keep
3475 // it enabled, if it's hidden, we disable pointer events.
3476 var box = bar.getBoundingClientRect()
3477 var elt = document.elementFromPoint(box.left + 1, box.bottom - 1)
3478 if (elt != bar) { bar.style.pointerEvents = "none" }
3479 else { delay.set(1000, maybeDisable) }
3480 }
3481 delay.set(1000, maybeDisable)
3482 };
3483
3484 NativeScrollbars.prototype.clear = function clear () {
3485 var parent = this.horiz.parentNode
3486 parent.removeChild(this.horiz)
3487 parent.removeChild(this.vert)
3488 };
3489
3490 var NullScrollbars = function NullScrollbars () {};
3491
3492 NullScrollbars.prototype.update = function update () { return {bottom: 0, right: 0} };
3493 NullScrollbars.prototype.setScrollLeft = function setScrollLeft$2 () {};
3494 NullScrollbars.prototype.setScrollTop = function setScrollTop$2 () {};
3495 NullScrollbars.prototype.clear = function clear () {};
3496
3497 function updateScrollbars(cm, measure) {
3498 if (!measure) { measure = measureForScrollbars(cm) }
3499 var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight
3500 updateScrollbarsInner(cm, measure)
3501 for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != c m.display.barHeight; i++) {
3502 if (startWidth != cm.display.barWidth && cm.options.lineWrapping)
3503 { updateHeightsInViewport(cm) }
3504 updateScrollbarsInner(cm, measureForScrollbars(cm))
3505 startWidth = cm.display.barWidth; startHeight = cm.display.barHeight
3506 }
3507 }
3508
3509 // Re-synchronize the fake scrollbars with the actual size of the
3510 // content.
3511 function updateScrollbarsInner(cm, measure) {
3512 var d = cm.display
3513 var sizes = d.scrollbars.update(measure)
3514
3515 d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px"
3516 d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px"
3517 d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent"
3518
3519 if (sizes.right && sizes.bottom) {
3520 d.scrollbarFiller.style.display = "block"
3521 d.scrollbarFiller.style.height = sizes.bottom + "px"
3522 d.scrollbarFiller.style.width = sizes.right + "px"
3523 } else { d.scrollbarFiller.style.display = "" }
3524 if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedG utter) {
3525 d.gutterFiller.style.display = "block"
3526 d.gutterFiller.style.height = sizes.bottom + "px"
3527 d.gutterFiller.style.width = measure.gutterWidth + "px"
3528 } else { d.gutterFiller.style.display = "" }
3529 }
3530
3531 var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars}
3532
3533 function initScrollbars(cm) {
3534 if (cm.display.scrollbars) {
3535 cm.display.scrollbars.clear()
3536 if (cm.display.scrollbars.addClass)
3537 { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass) }
3538 }
3539
3540 cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) {
3541 cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller)
3542 // Prevent clicks in the scrollbars from killing focus
3543 on(node, "mousedown", function () {
3544 if (cm.state.focused) { setTimeout(function () { return cm.display.input.f ocus(); }, 0) }
3545 })
3546 node.setAttribute("cm-not-content", "true")
3547 }, function (pos, axis) {
3548 if (axis == "horizontal") { setScrollLeft(cm, pos) }
3549 else { setScrollTop(cm, pos) }
3550 }, cm)
3551 if (cm.display.scrollbars.addClass)
3552 { addClass(cm.display.wrapper, cm.display.scrollbars.addClass) }
3553 }
3554
3555 // SCROLLING THINGS INTO VIEW
3556
3557 // If an editor sits on the top or bottom of the window, partially
3558 // scrolled out of view, this ensures that the cursor is visible.
3559 function maybeScrollWindow(cm, rect) {
3560 if (signalDOMEvent(cm, "scrollCursorIntoView")) { return }
3561
3562 var display = cm.display, box = display.sizer.getBoundingClientRect(), doScrol l = null
3563 if (rect.top + box.top < 0) { doScroll = true }
3564 else if (rect.bottom + box.top > (window.innerHeight || document.documentEleme nt.clientHeight)) { doScroll = false }
3565 if (doScroll != null && !phantom) {
3566 var scrollNode = elt("div", "\u200b", null, ("position: absolute;\n top: " + (rect.top - display.viewOffset - paddingTop(cm.display) ) + "px;\n height: " + (rect.bottom - rect.top + scrollG ap(cm) + display.barHeight) + "px;\n left: " + (rect.lef t) + "px; width: " + (Math.max(2, rect.right - rect.left)) + "px;"))
3567 cm.display.lineSpace.appendChild(scrollNode)
3568 scrollNode.scrollIntoView(doScroll)
3569 cm.display.lineSpace.removeChild(scrollNode)
3570 }
3571 }
3572
3573 // Scroll a given position into view (immediately), verifying that
3574 // it actually became visible (as line heights are accurately
3575 // measured, the position of something may 'drift' during drawing).
3576 function scrollPosIntoView(cm, pos, end, margin) {
3577 if (margin == null) { margin = 0 }
3578 var rect
3579 for (var limit = 0; limit < 5; limit++) {
3580 var changed = false
3581 var coords = cursorCoords(cm, pos)
3582 var endCoords = !end || end == pos ? coords : cursorCoords(cm, end)
3583 rect = {left: Math.min(coords.left, endCoords.left),
3584 top: Math.min(coords.top, endCoords.top) - margin,
3585 right: Math.max(coords.left, endCoords.left),
3586 bottom: Math.max(coords.bottom, endCoords.bottom) + margin}
3587 var scrollPos = calculateScrollPos(cm, rect)
3588 var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft
3589 if (scrollPos.scrollTop != null) {
3590 setScrollTop(cm, scrollPos.scrollTop)
3591 if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true }
3592 }
3593 if (scrollPos.scrollLeft != null) {
3594 setScrollLeft(cm, scrollPos.scrollLeft)
3595 if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true }
3596 }
3597 if (!changed) { break }
3598 }
3599 return rect
3600 }
3601
3602 // Scroll a given set of coordinates into view (immediately).
3603 function scrollIntoView(cm, rect) {
3604 var scrollPos = calculateScrollPos(cm, rect)
3605 if (scrollPos.scrollTop != null) { setScrollTop(cm, scrollPos.scrollTop) }
3606 if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft) }
3607 }
3608
3609 // Calculate a new scroll position needed to scroll the given
3610 // rectangle into view. Returns an object with scrollTop and
3611 // scrollLeft properties. When these are undefined, the
3612 // vertical/horizontal position does not need to be adjusted.
3613 function calculateScrollPos(cm, rect) {
3614 var display = cm.display, snapMargin = textHeight(cm.display)
3615 if (rect.top < 0) { rect.top = 0 }
3616 var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop
3617 var screen = displayHeight(cm), result = {}
3618 if (rect.bottom - rect.top > screen) { rect.bottom = rect.top + screen }
3619 var docBottom = cm.doc.height + paddingVert(display)
3620 var atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMa rgin
3621 if (rect.top < screentop) {
3622 result.scrollTop = atTop ? 0 : rect.top
3623 } else if (rect.bottom > screentop + screen) {
3624 var newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - scree n)
3625 if (newTop != screentop) { result.scrollTop = newTop }
3626 }
3627
3628 var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft
3629 var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.off setWidth : 0)
3630 var tooWide = rect.right - rect.left > screenw
3631 if (tooWide) { rect.right = rect.left + screenw }
3632 if (rect.left < 10)
3633 { result.scrollLeft = 0 }
3634 else if (rect.left < screenleft)
3635 { result.scrollLeft = Math.max(0, rect.left - (tooWide ? 0 : 10)) }
3636 else if (rect.right > screenw + screenleft - 3)
3637 { result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw }
3638 return result
3639 }
3640
3641 // Store a relative adjustment to the scroll position in the current
3642 // operation (to be applied when the operation finishes).
3643 function addToScrollPos(cm, left, top) {
3644 if (left != null || top != null) { resolveScrollToPos(cm) }
3645 if (left != null)
3646 { cm.curOp.scrollLeft = (cm.curOp.scrollLeft == null ? cm.doc.scrollLeft : c m.curOp.scrollLeft) + left }
3647 if (top != null)
3648 { cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.c urOp.scrollTop) + top }
3649 }
3650
3651 // Make sure that at the end of the operation the current cursor is
3652 // shown.
3653 function ensureCursorVisible(cm) {
3654 resolveScrollToPos(cm)
3655 var cur = cm.getCursor(), from = cur, to = cur
3656 if (!cm.options.lineWrapping) {
3657 from = cur.ch ? Pos(cur.line, cur.ch - 1) : cur
3658 to = Pos(cur.line, cur.ch + 1)
3659 }
3660 cm.curOp.scrollToPos = {from: from, to: to, margin: cm.options.cursorScrollMar gin}
3661 }
3662
3663 // When an operation has its scrollToPos property set, and another
3664 // scroll action is applied before the end of the operation, this
3665 // 'simulates' scrolling that position into view in a cheap way, so
3666 // that the effect of intermediate scroll commands is not ignored.
3667 function resolveScrollToPos(cm) {
3668 var range = cm.curOp.scrollToPos
3669 if (range) {
3670 cm.curOp.scrollToPos = null
3671 var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to)
3672 var sPos = calculateScrollPos(cm, {
3673 left: Math.min(from.left, to.left),
3674 top: Math.min(from.top, to.top) - range.margin,
3675 right: Math.max(from.right, to.right),
3676 bottom: Math.max(from.bottom, to.bottom) + range.margin
3677 })
3678 cm.scrollTo(sPos.scrollLeft, sPos.scrollTop)
3679 }
3680 }
3681
3682 // Operations are used to wrap a series of changes to the editor
3683 // state in such a way that each change won't have to update the
3684 // cursor and display (which would be awkward, slow, and
3685 // error-prone). Instead, display updates are batched and then all
3686 // combined and executed at once.
3687
3688 var nextOpId = 0
3689 // Start a new operation.
3690 function startOperation(cm) {
3691 cm.curOp = {
3692 cm: cm,
3693 viewChanged: false, // Flag that indicates that lines might need to be redrawn
3694 startHeight: cm.doc.height, // Used to detect need to update scrollbar
3695 forceUpdate: false, // Used to force a redraw
3696 updateInput: null, // Whether to reset the input textarea
3697 typing: false, // Whether this reset should be careful to leave ex isting text (for compositing)
3698 changeObjs: null, // Accumulated changes, for firing change events
3699 cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on
3700 cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been c alled already
3701 selectionChanged: false, // Whether the selection needs to be redrawn
3702 updateMaxLine: false, // Set when the widest line needs to be determined anew
3703 scrollLeft: null, scrollTop: null, // Intermediate scroll position, not push ed to DOM yet
3704 scrollToPos: null, // Used to scroll to a specific position
3705 focus: false,
3706 id: ++nextOpId // Unique ID
3707 }
3708 pushOperation(cm.curOp)
3709 }
3710
3711 // Finish an operation, updating the display and signalling delayed events
3712 function endOperation(cm) {
3713 var op = cm.curOp
3714 finishOperation(op, function (group) {
3715 for (var i = 0; i < group.ops.length; i++)
3716 { group.ops[i].cm.curOp = null }
3717 endOperations(group)
3718 })
3719 }
3720
3721 // The DOM updates done when an operation finishes are batched so
3722 // that the minimum number of relayouts are required.
3723 function endOperations(group) {
3724 var ops = group.ops
3725 for (var i = 0; i < ops.length; i++) // Read DOM
3726 { endOperation_R1(ops[i]) }
3727 for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe)
3728 { endOperation_W1(ops[i$1]) }
3729 for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM
3730 { endOperation_R2(ops[i$2]) }
3731 for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe)
3732 { endOperation_W2(ops[i$3]) }
3733 for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM
3734 { endOperation_finish(ops[i$4]) }
3735 }
3736
3737 function endOperation_R1(op) {
3738 var cm = op.cm, display = cm.display
3739 maybeClipScrollbars(cm)
3740 if (op.updateMaxLine) { findMaxLine(cm) }
3741
3742 op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||
3743 op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
3744 op.scrollToPos.to.line >= display.viewTo) ||
3745 display.maxLineChanged && cm.options.lineWrapping
3746 op.update = op.mustUpdate &&
3747 new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scroll ToPos}, op.forceUpdate)
3748 }
3749
3750 function endOperation_W1(op) {
3751 op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update)
3752 }
3753
3754 function endOperation_R2(op) {
3755 var cm = op.cm, display = cm.display
3756 if (op.updatedDisplay) { updateHeightsInViewport(cm) }
3757
3758 op.barMeasure = measureForScrollbars(cm)
3759
3760 // If the max line changed since it was last measured, measure it,
3761 // and ensure the document's width matches it.
3762 // updateDisplay_W2 will use these properties to do the actual resizing
3763 if (display.maxLineChanged && !cm.options.lineWrapping) {
3764 op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.len gth).left + 3
3765 cm.display.sizerWidth = op.adjustWidthTo
3766 op.barMeasure.scrollWidth =
3767 Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjus tWidthTo + scrollGap(cm) + cm.display.barWidth)
3768 op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm))
3769 }
3770
3771 if (op.updatedDisplay || op.selectionChanged)
3772 { op.preparedSelection = display.input.prepareSelection(op.focus) }
3773 }
3774
3775 function endOperation_W2(op) {
3776 var cm = op.cm
3777
3778 if (op.adjustWidthTo != null) {
3779 cm.display.sizer.style.minWidth = op.adjustWidthTo + "px"
3780 if (op.maxScrollLeft < cm.doc.scrollLeft)
3781 { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollL eft), true) }
3782 cm.display.maxLineChanged = false
3783 }
3784
3785 var takeFocus = op.focus && op.focus == activeElt() && (!document.hasFocus || document.hasFocus())
3786 if (op.preparedSelection)
3787 { cm.display.input.showSelection(op.preparedSelection, takeFocus) }
3788 if (op.updatedDisplay || op.startHeight != cm.doc.height)
3789 { updateScrollbars(cm, op.barMeasure) }
3790 if (op.updatedDisplay)
3791 { setDocumentHeight(cm, op.barMeasure) }
3792
3793 if (op.selectionChanged) { restartBlink(cm) }
3794
3795 if (cm.state.focused && op.updateInput)
3796 { cm.display.input.reset(op.typing) }
3797 if (takeFocus) { ensureFocus(op.cm) }
3798 }
3799
3800 function endOperation_finish(op) {
3801 var cm = op.cm, display = cm.display, doc = cm.doc
3802
3803 if (op.updatedDisplay) { postUpdateDisplay(cm, op.update) }
3804
3805 // Abort mouse wheel delta measurement, when scrolling explicitly
3806 if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != n ull || op.scrollToPos))
3807 { display.wheelStartX = display.wheelStartY = null }
3808
3809 // Propagate the scroll position to the actual DOM scroller
3810 if (op.scrollTop != null && (display.scroller.scrollTop != op.scrollTop || op. forceScroll)) {
3811 doc.scrollTop = Math.max(0, Math.min(display.scroller.scrollHeight - display .scroller.clientHeight, op.scrollTop))
3812 display.scrollbars.setScrollTop(doc.scrollTop)
3813 display.scroller.scrollTop = doc.scrollTop
3814 }
3815 if (op.scrollLeft != null && (display.scroller.scrollLeft != op.scrollLeft || op.forceScroll)) {
3816 doc.scrollLeft = Math.max(0, Math.min(display.scroller.scrollWidth - display .scroller.clientWidth, op.scrollLeft))
3817 display.scrollbars.setScrollLeft(doc.scrollLeft)
3818 display.scroller.scrollLeft = doc.scrollLeft
3819 alignHorizontally(cm)
3820 }
3821 // If we need to scroll a specific position into view, do so.
3822 if (op.scrollToPos) {
3823 var rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
3824 clipPos(doc, op.scrollToPos.to), op.scrollToPos .margin)
3825 maybeScrollWindow(cm, rect)
3826 }
3827
3828 // Fire events for markers that are hidden/unidden by editing or
3829 // undoing
3830 var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers
3831 if (hidden) { for (var i = 0; i < hidden.length; ++i)
3832 { if (!hidden[i].lines.length) { signal(hidden[i], "hide") } } }
3833 if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1)
3834 { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], "unhide") } } }
3835
3836 if (display.wrapper.offsetHeight)
3837 { doc.scrollTop = cm.display.scroller.scrollTop }
3838
3839 // Fire change events, and delayed event handlers
3840 if (op.changeObjs)
3841 { signal(cm, "changes", cm, op.changeObjs) }
3842 if (op.update)
3843 { op.update.finish() }
3844 }
3845
3846 // Run the given function in an operation
3847 function runInOp(cm, f) {
3848 if (cm.curOp) { return f() }
3849 startOperation(cm)
3850 try { return f() }
3851 finally { endOperation(cm) }
3852 }
3853 // Wraps a function in an operation. Returns the wrapped function.
3854 function operation(cm, f) {
3855 return function() {
3856 if (cm.curOp) { return f.apply(cm, arguments) }
3857 startOperation(cm)
3858 try { return f.apply(cm, arguments) }
3859 finally { endOperation(cm) }
3860 }
3861 }
3862 // Used to add methods to editor and doc instances, wrapping them in
3863 // operations.
3864 function methodOp(f) {
3865 return function() {
3866 if (this.curOp) { return f.apply(this, arguments) }
3867 startOperation(this)
3868 try { return f.apply(this, arguments) }
3869 finally { endOperation(this) }
3870 }
3871 }
3872 function docMethodOp(f) {
3873 return function() {
3874 var cm = this.cm
3875 if (!cm || cm.curOp) { return f.apply(this, arguments) }
3876 startOperation(cm)
3877 try { return f.apply(this, arguments) }
3878 finally { endOperation(cm) }
3879 }
3880 }
3881
3882 // Updates the display.view data structure for a given change to the
3883 // document. From and to are in pre-change coordinates. Lendiff is
3884 // the amount of lines added or subtracted by the change. This is
3885 // used for changes that span multiple lines, or change the way
3886 // lines are divided into visual lines. regLineChange (below)
3887 // registers single-line changes.
3888 function regChange(cm, from, to, lendiff) {
3889 if (from == null) { from = cm.doc.first }
3890 if (to == null) { to = cm.doc.first + cm.doc.size }
3891 if (!lendiff) { lendiff = 0 }
3892
3893 var display = cm.display
3894 if (lendiff && to < display.viewTo &&
3895 (display.updateLineNumbers == null || display.updateLineNumbers > from))
3896 { display.updateLineNumbers = from }
3897
3898 cm.curOp.viewChanged = true
3899
3900 if (from >= display.viewTo) { // Change after
3901 if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)
3902 { resetView(cm) }
3903 } else if (to <= display.viewFrom) { // Change before
3904 if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.vie wFrom) {
3905 resetView(cm)
3906 } else {
3907 display.viewFrom += lendiff
3908 display.viewTo += lendiff
3909 }
3910 } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap
3911 resetView(cm)
3912 } else if (from <= display.viewFrom) { // Top overlap
3913 var cut = viewCuttingPoint(cm, to, to + lendiff, 1)
3914 if (cut) {
3915 display.view = display.view.slice(cut.index)
3916 display.viewFrom = cut.lineN
3917 display.viewTo += lendiff
3918 } else {
3919 resetView(cm)
3920 }
3921 } else if (to >= display.viewTo) { // Bottom overlap
3922 var cut$1 = viewCuttingPoint(cm, from, from, -1)
3923 if (cut$1) {
3924 display.view = display.view.slice(0, cut$1.index)
3925 display.viewTo = cut$1.lineN
3926 } else {
3927 resetView(cm)
3928 }
3929 } else { // Gap in the middle
3930 var cutTop = viewCuttingPoint(cm, from, from, -1)
3931 var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1)
3932 if (cutTop && cutBot) {
3933 display.view = display.view.slice(0, cutTop.index)
3934 .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))
3935 .concat(display.view.slice(cutBot.index))
3936 display.viewTo += lendiff
3937 } else {
3938 resetView(cm)
3939 }
3940 }
3941
3942 var ext = display.externalMeasured
3943 if (ext) {
3944 if (to < ext.lineN)
3945 { ext.lineN += lendiff }
3946 else if (from < ext.lineN + ext.size)
3947 { display.externalMeasured = null }
3948 }
3949 }
3950
3951 // Register a change to a single line. Type must be one of "text",
3952 // "gutter", "class", "widget"
3953 function regLineChange(cm, line, type) {
3954 cm.curOp.viewChanged = true
3955 var display = cm.display, ext = cm.display.externalMeasured
3956 if (ext && line >= ext.lineN && line < ext.lineN + ext.size)
3957 { display.externalMeasured = null }
3958
3959 if (line < display.viewFrom || line >= display.viewTo) { return }
3960 var lineView = display.view[findViewIndex(cm, line)]
3961 if (lineView.node == null) { return }
3962 var arr = lineView.changes || (lineView.changes = [])
3963 if (indexOf(arr, type) == -1) { arr.push(type) }
3964 }
3965
3966 // Clear the view.
3967 function resetView(cm) {
3968 cm.display.viewFrom = cm.display.viewTo = cm.doc.first
3969 cm.display.view = []
3970 cm.display.viewOffset = 0
3971 }
3972
3973 function viewCuttingPoint(cm, oldN, newN, dir) {
3974 var index = findViewIndex(cm, oldN), diff, view = cm.display.view
3975 if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)
3976 { return {index: index, lineN: newN} }
3977 var n = cm.display.viewFrom
3978 for (var i = 0; i < index; i++)
3979 { n += view[i].size }
3980 if (n != oldN) {
3981 if (dir > 0) {
3982 if (index == view.length - 1) { return null }
3983 diff = (n + view[index].size) - oldN
3984 index++
3985 } else {
3986 diff = n - oldN
3987 }
3988 oldN += diff; newN += diff
3989 }
3990 while (visualLineNo(cm.doc, newN) != newN) {
3991 if (index == (dir < 0 ? 0 : view.length - 1)) { return null }
3992 newN += dir * view[index - (dir < 0 ? 1 : 0)].size
3993 index += dir
3994 }
3995 return {index: index, lineN: newN}
3996 }
3997
3998 // Force the view to cover a given range, adding empty view element
3999 // or clipping off existing ones as needed.
4000 function adjustView(cm, from, to) {
4001 var display = cm.display, view = display.view
4002 if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {
4003 display.view = buildViewArray(cm, from, to)
4004 display.viewFrom = from
4005 } else {
4006 if (display.viewFrom > from)
4007 { display.view = buildViewArray(cm, from, display.viewFrom).concat(display .view) }
4008 else if (display.viewFrom < from)
4009 { display.view = display.view.slice(findViewIndex(cm, from)) }
4010 display.viewFrom = from
4011 if (display.viewTo < to)
4012 { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to )) }
4013 else if (display.viewTo > to)
4014 { display.view = display.view.slice(0, findViewIndex(cm, to)) }
4015 }
4016 display.viewTo = to
4017 }
4018
4019 // Count the number of lines in the view whose DOM representation is
4020 // out of date (or nonexistent).
4021 function countDirtyView(cm) {
4022 var view = cm.display.view, dirty = 0
4023 for (var i = 0; i < view.length; i++) {
4024 var lineView = view[i]
4025 if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty }
4026 }
4027 return dirty
4028 }
4029
4030 // HIGHLIGHT WORKER
4031
4032 function startWorker(cm, time) {
4033 if (cm.doc.mode.startState && cm.doc.frontier < cm.display.viewTo)
4034 { cm.state.highlight.set(time, bind(highlightWorker, cm)) }
4035 }
4036
4037 function highlightWorker(cm) {
4038 var doc = cm.doc
4039 if (doc.frontier < doc.first) { doc.frontier = doc.first }
4040 if (doc.frontier >= cm.display.viewTo) { return }
4041 var end = +new Date + cm.options.workTime
4042 var state = copyState(doc.mode, getStateBefore(cm, doc.frontier))
4043 var changedLines = []
4044
4045 doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.viewTo + 500) , function (line) {
4046 if (doc.frontier >= cm.display.viewFrom) { // Visible
4047 var oldStyles = line.styles, tooLong = line.text.length > cm.options.maxHi ghlightLength
4048 var highlighted = highlightLine(cm, line, tooLong ? copyState(doc.mode, st ate) : state, true)
4049 line.styles = highlighted.styles
4050 var oldCls = line.styleClasses, newCls = highlighted.classes
4051 if (newCls) { line.styleClasses = newCls }
4052 else if (oldCls) { line.styleClasses = null }
4053 var ischange = !oldStyles || oldStyles.length != line.styles.length ||
4054 oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgCl ass || oldCls.textClass != newCls.textClass)
4055 for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldSt yles[i] != line.styles[i] }
4056 if (ischange) { changedLines.push(doc.frontier) }
4057 line.stateAfter = tooLong ? state : copyState(doc.mode, state)
4058 } else {
4059 if (line.text.length <= cm.options.maxHighlightLength)
4060 { processLine(cm, line.text, state) }
4061 line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : nul l
4062 }
4063 ++doc.frontier
4064 if (+new Date > end) {
4065 startWorker(cm, cm.options.workDelay)
4066 return true
4067 }
4068 })
4069 if (changedLines.length) { runInOp(cm, function () {
4070 for (var i = 0; i < changedLines.length; i++)
4071 { regLineChange(cm, changedLines[i], "text") }
4072 }) }
4073 }
4074
4075 // DISPLAY DRAWING
4076
4077 var DisplayUpdate = function DisplayUpdate(cm, viewport, force) {
4078 var display = cm.display
4079
4080 this.viewport = viewport
4081 // Store some values that we'll need later (but don't want to force a relayout for)
4082 this.visible = visibleLines(display, cm.doc, viewport)
4083 this.editorIsHidden = !display.wrapper.offsetWidth
4084 this.wrapperHeight = display.wrapper.clientHeight
4085 this.wrapperWidth = display.wrapper.clientWidth
4086 this.oldDisplayWidth = displayWidth(cm)
4087 this.force = force
4088 this.dims = getDimensions(cm)
4089 this.events = []
4090 };
4091
4092 DisplayUpdate.prototype.signal = function signal$1 (emitter, type) {
4093 if (hasHandler(emitter, type))
4094 { this.events.push(arguments) }
4095 };
4096 DisplayUpdate.prototype.finish = function finish () {
4097 var this$1 = this;
4098
4099 for (var i = 0; i < this.events.length; i++)
4100 { signal.apply(null, this$1.events[i]) }
4101 };
4102
4103 function maybeClipScrollbars(cm) {
4104 var display = cm.display
4105 if (!display.scrollbarsClipped && display.scroller.offsetWidth) {
4106 display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.cli entWidth
4107 display.heightForcer.style.height = scrollGap(cm) + "px"
4108 display.sizer.style.marginBottom = -display.nativeBarWidth + "px"
4109 display.sizer.style.borderRightWidth = scrollGap(cm) + "px"
4110 display.scrollbarsClipped = true
4111 }
4112 }
4113
4114 // Does the actual updating of the line display. Bails out
4115 // (returning false) when there is nothing to be done and forced is
4116 // false.
4117 function updateDisplayIfNeeded(cm, update) {
4118 var display = cm.display, doc = cm.doc
4119
4120 if (update.editorIsHidden) {
4121 resetView(cm)
4122 return false
4123 }
4124
4125 // Bail out if the visible area is already rendered and nothing changed.
4126 if (!update.force &&
4127 update.visible.from >= display.viewFrom && update.visible.to <= display.vi ewTo &&
4128 (display.updateLineNumbers == null || display.updateLineNumbers >= display .viewTo) &&
4129 display.renderedView == display.view && countDirtyView(cm) == 0)
4130 { return false }
4131
4132 if (maybeUpdateLineNumberWidth(cm)) {
4133 resetView(cm)
4134 update.dims = getDimensions(cm)
4135 }
4136
4137 // Compute a suitable new viewport (from & to)
4138 var end = doc.first + doc.size
4139 var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first )
4140 var to = Math.min(end, update.visible.to + cm.options.viewportMargin)
4141 if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max (doc.first, display.viewFrom) }
4142 if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, disp lay.viewTo) }
4143 if (sawCollapsedSpans) {
4144 from = visualLineNo(cm.doc, from)
4145 to = visualLineEndNo(cm.doc, to)
4146 }
4147
4148 var different = from != display.viewFrom || to != display.viewTo ||
4149 display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != u pdate.wrapperWidth
4150 adjustView(cm, from, to)
4151
4152 display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom))
4153 // Position the mover div to align with the current scroll position
4154 cm.display.mover.style.top = display.viewOffset + "px"
4155
4156 var toUpdate = countDirtyView(cm)
4157 if (!different && toUpdate == 0 && !update.force && display.renderedView == di splay.view &&
4158 (display.updateLineNumbers == null || display.updateLineNumbers >= display .viewTo))
4159 { return false }
4160
4161 // For big changes, we hide the enclosing element during the
4162 // update, since that speeds up the operations on most browsers.
4163 var focused = activeElt()
4164 if (toUpdate > 4) { display.lineDiv.style.display = "none" }
4165 patchDisplay(cm, display.updateLineNumbers, update.dims)
4166 if (toUpdate > 4) { display.lineDiv.style.display = "" }
4167 display.renderedView = display.view
4168 // There might have been a widget with a focused element that got
4169 // hidden or updated, if so re-focus it.
4170 if (focused && activeElt() != focused && focused.offsetHeight) { focused.focus () }
4171
4172 // Prevent selection and cursors from interfering with the scroll
4173 // width and height.
4174 removeChildren(display.cursorDiv)
4175 removeChildren(display.selectionDiv)
4176 display.gutters.style.height = display.sizer.style.minHeight = 0
4177
4178 if (different) {
4179 display.lastWrapHeight = update.wrapperHeight
4180 display.lastWrapWidth = update.wrapperWidth
4181 startWorker(cm, 400)
4182 }
4183
4184 display.updateLineNumbers = null
4185
4186 return true
4187 }
4188
4189 function postUpdateDisplay(cm, update) {
4190 var viewport = update.viewport
4191
4192 for (var first = true;; first = false) {
4193 if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayW idth(cm)) {
4194 // Clip forced viewport to actual scrollable area.
4195 if (viewport && viewport.top != null)
4196 { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - di splayHeight(cm), viewport.top)} }
4197 // Updated line heights might result in the drawn area not
4198 // actually covering the viewport. Keep looping until it does.
4199 update.visible = visibleLines(cm.display, cm.doc, viewport)
4200 if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm. display.viewTo)
4201 { break }
4202 }
4203 if (!updateDisplayIfNeeded(cm, update)) { break }
4204 updateHeightsInViewport(cm)
4205 var barMeasure = measureForScrollbars(cm)
4206 updateSelection(cm)
4207 updateScrollbars(cm, barMeasure)
4208 setDocumentHeight(cm, barMeasure)
4209 }
4210
4211 update.signal(cm, "update", cm)
4212 if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {
4213 update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.view To)
4214 cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo
4215 }
4216 }
4217
4218 function updateDisplaySimple(cm, viewport) {
4219 var update = new DisplayUpdate(cm, viewport)
4220 if (updateDisplayIfNeeded(cm, update)) {
4221 updateHeightsInViewport(cm)
4222 postUpdateDisplay(cm, update)
4223 var barMeasure = measureForScrollbars(cm)
4224 updateSelection(cm)
4225 updateScrollbars(cm, barMeasure)
4226 setDocumentHeight(cm, barMeasure)
4227 update.finish()
4228 }
4229 }
4230
4231 // Sync the actual display DOM structure with display.view, removing
4232 // nodes for lines that are no longer in view, and creating the ones
4233 // that are not there yet, and updating the ones that are out of
4234 // date.
4235 function patchDisplay(cm, updateNumbersFrom, dims) {
4236 var display = cm.display, lineNumbers = cm.options.lineNumbers
4237 var container = display.lineDiv, cur = container.firstChild
4238
4239 function rm(node) {
4240 var next = node.nextSibling
4241 // Works around a throw-scroll bug in OS X Webkit
4242 if (webkit && mac && cm.display.currentWheelTarget == node)
4243 { node.style.display = "none" }
4244 else
4245 { node.parentNode.removeChild(node) }
4246 return next
4247 }
4248
4249 var view = display.view, lineN = display.viewFrom
4250 // Loop over the elements in the view, syncing cur (the DOM nodes
4251 // in display.lineDiv) with the view as we go.
4252 for (var i = 0; i < view.length; i++) {
4253 var lineView = view[i]
4254 if (lineView.hidden) {
4255 } else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet
4256 var node = buildLineElement(cm, lineView, lineN, dims)
4257 container.insertBefore(node, cur)
4258 } else { // Already drawn
4259 while (cur != lineView.node) { cur = rm(cur) }
4260 var updateNumber = lineNumbers && updateNumbersFrom != null &&
4261 updateNumbersFrom <= lineN && lineView.lineNumber
4262 if (lineView.changes) {
4263 if (indexOf(lineView.changes, "gutter") > -1) { updateNumber = false }
4264 updateLineForChanges(cm, lineView, lineN, dims)
4265 }
4266 if (updateNumber) {
4267 removeChildren(lineView.lineNumber)
4268 lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm .options, lineN)))
4269 }
4270 cur = lineView.node.nextSibling
4271 }
4272 lineN += lineView.size
4273 }
4274 while (cur) { cur = rm(cur) }
4275 }
4276
4277 function updateGutterSpace(cm) {
4278 var width = cm.display.gutters.offsetWidth
4279 cm.display.sizer.style.marginLeft = width + "px"
4280 }
4281
4282 function setDocumentHeight(cm, measure) {
4283 cm.display.sizer.style.minHeight = measure.docHeight + "px"
4284 cm.display.heightForcer.style.top = measure.docHeight + "px"
4285 cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px"
4286 }
4287
4288 // Rebuild the gutter elements, ensure the margin to the left of the
4289 // code matches their width.
4290 function updateGutters(cm) {
4291 var gutters = cm.display.gutters, specs = cm.options.gutters
4292 removeChildren(gutters)
4293 var i = 0
4294 for (; i < specs.length; ++i) {
4295 var gutterClass = specs[i]
4296 var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutte rClass))
4297 if (gutterClass == "CodeMirror-linenumbers") {
4298 cm.display.lineGutter = gElt
4299 gElt.style.width = (cm.display.lineNumWidth || 1) + "px"
4300 }
4301 }
4302 gutters.style.display = i ? "" : "none"
4303 updateGutterSpace(cm)
4304 }
4305
4306 // Make sure the gutters options contains the element
4307 // "CodeMirror-linenumbers" when the lineNumbers option is true.
4308 function setGuttersForLineNumbers(options) {
4309 var found = indexOf(options.gutters, "CodeMirror-linenumbers")
4310 if (found == -1 && options.lineNumbers) {
4311 options.gutters = options.gutters.concat(["CodeMirror-linenumbers"])
4312 } else if (found > -1 && !options.lineNumbers) {
4313 options.gutters = options.gutters.slice(0)
4314 options.gutters.splice(found, 1)
4315 }
4316 }
4317
4318 // Selection objects are immutable. A new one is created every time
4319 // the selection changes. A selection is one or more non-overlapping
4320 // (and non-touching) ranges, sorted, and an integer that indicates
4321 // which one is the primary selection (the one that's scrolled into
4322 // view, that getCursor returns, etc).
4323 var Selection = function Selection(ranges, primIndex) {
4324 this.ranges = ranges
4325 this.primIndex = primIndex
4326 };
4327
4328 Selection.prototype.primary = function primary () { return this.ranges[this.prim Index] };
4329
4330 Selection.prototype.equals = function equals (other) {
4331 var this$1 = this;
4332
4333 if (other == this) { return true }
4334 if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.le ngth) { return false }
4335 for (var i = 0; i < this.ranges.length; i++) {
4336 var here = this$1.ranges[i], there = other.ranges[i]
4337 if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) { return false }
4338 }
4339 return true
4340 };
4341
4342 Selection.prototype.deepCopy = function deepCopy () {
4343 var this$1 = this;
4344
4345 var out = []
4346 for (var i = 0; i < this.ranges.length; i++)
4347 { out[i] = new Range(copyPos(this$1.ranges[i].anchor), copyPos(this$1.ranges [i].head)) }
4348 return new Selection(out, this.primIndex)
4349 };
4350
4351 Selection.prototype.somethingSelected = function somethingSelected () {
4352 var this$1 = this;
4353
4354 for (var i = 0; i < this.ranges.length; i++)
4355 { if (!this$1.ranges[i].empty()) { return true } }
4356 return false
4357 };
4358
4359 Selection.prototype.contains = function contains (pos, end) {
4360 var this$1 = this;
4361
4362 if (!end) { end = pos }
4363 for (var i = 0; i < this.ranges.length; i++) {
4364 var range = this$1.ranges[i]
4365 if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)
4366 { return i }
4367 }
4368 return -1
4369 };
4370
4371 var Range = function Range(anchor, head) {
4372 this.anchor = anchor; this.head = head
4373 };
4374
4375 Range.prototype.from = function from () { return minPos(this.anchor, this.head) };
4376 Range.prototype.to = function to () { return maxPos(this.anchor, this.head) };
4377 Range.prototype.empty = function empty () { return this.head.line == this.anchor .line && this.head.ch == this.anchor.ch };
4378
4379 // Take an unsorted, potentially overlapping set of ranges, and
4380 // build a selection out of it. 'Consumes' ranges array (modifying
4381 // it).
4382 function normalizeSelection(ranges, primIndex) {
4383 var prim = ranges[primIndex]
4384 ranges.sort(function (a, b) { return cmp(a.from(), b.from()); })
4385 primIndex = indexOf(ranges, prim)
4386 for (var i = 1; i < ranges.length; i++) {
4387 var cur = ranges[i], prev = ranges[i - 1]
4388 if (cmp(prev.to(), cur.from()) >= 0) {
4389 var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to( ))
4390 var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head
4391 if (i <= primIndex) { --primIndex }
4392 ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to))
4393 }
4394 }
4395 return new Selection(ranges, primIndex)
4396 }
4397
4398 function simpleSelection(anchor, head) {
4399 return new Selection([new Range(anchor, head || anchor)], 0)
4400 }
4401
4402 // Compute the position of the end of a change (its 'to' property
4403 // refers to the pre-change end).
4404 function changeEnd(change) {
4405 if (!change.text) { return change.to }
4406 return Pos(change.from.line + change.text.length - 1,
4407 lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0))
4408 }
4409
4410 // Adjust a position to refer to the post-change position of the
4411 // same text, or the end of the change if the change covers it.
4412 function adjustForChange(pos, change) {
4413 if (cmp(pos, change.from) < 0) { return pos }
4414 if (cmp(pos, change.to) <= 0) { return changeEnd(change) }
4415
4416 var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch
4417 if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch }
4418 return Pos(line, ch)
4419 }
4420
4421 function computeSelAfterChange(doc, change) {
4422 var out = []
4423 for (var i = 0; i < doc.sel.ranges.length; i++) {
4424 var range = doc.sel.ranges[i]
4425 out.push(new Range(adjustForChange(range.anchor, change),
4426 adjustForChange(range.head, change)))
4427 }
4428 return normalizeSelection(out, doc.sel.primIndex)
4429 }
4430
4431 function offsetPos(pos, old, nw) {
4432 if (pos.line == old.line)
4433 { return Pos(nw.line, pos.ch - old.ch + nw.ch) }
4434 else
4435 { return Pos(nw.line + (pos.line - old.line), pos.ch) }
4436 }
4437
4438 // Used by replaceSelections to allow moving the selection to the
4439 // start or around the replaced test. Hint may be "start" or "around".
4440 function computeReplacedSel(doc, changes, hint) {
4441 var out = []
4442 var oldPrev = Pos(doc.first, 0), newPrev = oldPrev
4443 for (var i = 0; i < changes.length; i++) {
4444 var change = changes[i]
4445 var from = offsetPos(change.from, oldPrev, newPrev)
4446 var to = offsetPos(changeEnd(change), oldPrev, newPrev)
4447 oldPrev = change.to
4448 newPrev = to
4449 if (hint == "around") {
4450 var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0
4451 out[i] = new Range(inv ? to : from, inv ? from : to)
4452 } else {
4453 out[i] = new Range(from, from)
4454 }
4455 }
4456 return new Selection(out, doc.sel.primIndex)
4457 }
4458
4459 // Used to get the editor into a consistent state again when options change.
4460
4461 function loadMode(cm) {
4462 cm.doc.mode = getMode(cm.options, cm.doc.modeOption)
4463 resetModeState(cm)
4464 }
4465
4466 function resetModeState(cm) {
4467 cm.doc.iter(function (line) {
4468 if (line.stateAfter) { line.stateAfter = null }
4469 if (line.styles) { line.styles = null }
4470 })
4471 cm.doc.frontier = cm.doc.first
4472 startWorker(cm, 100)
4473 cm.state.modeGen++
4474 if (cm.curOp) { regChange(cm) }
4475 }
4476
4477 // DOCUMENT DATA STRUCTURE
4478
4479 // By default, updates that start and end at the beginning of a line
4480 // are treated specially, in order to make the association of line
4481 // widgets and marker elements with the text behave more intuitive.
4482 function isWholeLineUpdate(doc, change) {
4483 return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" &&
4484 (!doc.cm || doc.cm.options.wholeLineUpdateBefore)
4485 }
4486
4487 // Perform a change on the document data structure.
4488 function updateDoc(doc, change, markedSpans, estimateHeight) {
4489 function spansFor(n) {return markedSpans ? markedSpans[n] : null}
4490 function update(line, text, spans) {
4491 updateLine(line, text, spans, estimateHeight)
4492 signalLater(line, "change", line, change)
4493 }
4494 function linesFor(start, end) {
4495 var result = []
4496 for (var i = start; i < end; ++i)
4497 { result.push(new Line(text[i], spansFor(i), estimateHeight)) }
4498 return result
4499 }
4500
4501 var from = change.from, to = change.to, text = change.text
4502 var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line)
4503 var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.l ine - from.line
4504
4505 // Adjust the line structure
4506 if (change.full) {
4507 doc.insert(0, linesFor(0, text.length))
4508 doc.remove(text.length, doc.size - text.length)
4509 } else if (isWholeLineUpdate(doc, change)) {
4510 // This is a whole-line replace. Treated specially to make
4511 // sure line objects move the way they are supposed to.
4512 var added = linesFor(0, text.length - 1)
4513 update(lastLine, lastLine.text, lastSpans)
4514 if (nlines) { doc.remove(from.line, nlines) }
4515 if (added.length) { doc.insert(from.line, added) }
4516 } else if (firstLine == lastLine) {
4517 if (text.length == 1) {
4518 update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine. text.slice(to.ch), lastSpans)
4519 } else {
4520 var added$1 = linesFor(1, text.length - 1)
4521 added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, e stimateHeight))
4522 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0))
4523 doc.insert(from.line + 1, added$1)
4524 }
4525 } else if (text.length == 1) {
4526 update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text .slice(to.ch), spansFor(0))
4527 doc.remove(from.line + 1, nlines)
4528 } else {
4529 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0))
4530 update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans)
4531 var added$2 = linesFor(1, text.length - 1)
4532 if (nlines > 1) { doc.remove(from.line + 1, nlines - 1) }
4533 doc.insert(from.line + 1, added$2)
4534 }
4535
4536 signalLater(doc, "change", doc, change)
4537 }
4538
4539 // Call f for all linked documents.
4540 function linkedDocs(doc, f, sharedHistOnly) {
4541 function propagate(doc, skip, sharedHist) {
4542 if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) {
4543 var rel = doc.linked[i]
4544 if (rel.doc == skip) { continue }
4545 var shared = sharedHist && rel.sharedHist
4546 if (sharedHistOnly && !shared) { continue }
4547 f(rel.doc, shared)
4548 propagate(rel.doc, doc, shared)
4549 } }
4550 }
4551 propagate(doc, null, true)
4552 }
4553
4554 // Attach a document to an editor.
4555 function attachDoc(cm, doc) {
4556 if (doc.cm) { throw new Error("This document is already in use.") }
4557 cm.doc = doc
4558 doc.cm = cm
4559 estimateLineHeights(cm)
4560 loadMode(cm)
4561 setDirectionClass(cm)
4562 if (!cm.options.lineWrapping) { findMaxLine(cm) }
4563 cm.options.mode = doc.modeOption
4564 regChange(cm)
4565 }
4566
4567 function setDirectionClass(cm) {
4568 ;(cm.doc.direction == "rtl" ? addClass : rmClass)(cm.display.lineDiv, "CodeMir ror-rtl")
4569 }
4570
4571 function directionChanged(cm) {
4572 runInOp(cm, function () {
4573 setDirectionClass(cm)
4574 regChange(cm)
4575 })
4576 }
4577
4578 function History(startGen) {
4579 // Arrays of change events and selections. Doing something adds an
4580 // event to done and clears undo. Undoing moves events from done
4581 // to undone, redoing moves them in the other direction.
4582 this.done = []; this.undone = []
4583 this.undoDepth = Infinity
4584 // Used to track when changes can be merged into a single undo
4585 // event
4586 this.lastModTime = this.lastSelTime = 0
4587 this.lastOp = this.lastSelOp = null
4588 this.lastOrigin = this.lastSelOrigin = null
4589 // Used by the isClean() method
4590 this.generation = this.maxGeneration = startGen || 1
4591 }
4592
4593 // Create a history change event from an updateDoc-style change
4594 // object.
4595 function historyChangeFromChange(doc, change) {
4596 var histChange = {from: copyPos(change.from), to: changeEnd(change), text: get Between(doc, change.from, change.to)}
4597 attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1)
4598 linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, chan ge.from.line, change.to.line + 1); }, true)
4599 return histChange
4600 }
4601
4602 // Pop all selection events off the end of a history array. Stop at
4603 // a change event.
4604 function clearSelectionEvents(array) {
4605 while (array.length) {
4606 var last = lst(array)
4607 if (last.ranges) { array.pop() }
4608 else { break }
4609 }
4610 }
4611
4612 // Find the top change event in the history. Pop off selection
4613 // events that are in the way.
4614 function lastChangeEvent(hist, force) {
4615 if (force) {
4616 clearSelectionEvents(hist.done)
4617 return lst(hist.done)
4618 } else if (hist.done.length && !lst(hist.done).ranges) {
4619 return lst(hist.done)
4620 } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) {
4621 hist.done.pop()
4622 return lst(hist.done)
4623 }
4624 }
4625
4626 // Register a change in the history. Merges changes that are within
4627 // a single operation, or are close together with an origin that
4628 // allows merging (starting with "+") into a single event.
4629 function addChangeToHistory(doc, change, selAfter, opId) {
4630 var hist = doc.history
4631 hist.undone.length = 0
4632 var time = +new Date, cur
4633 var last
4634
4635 if ((hist.lastOp == opId ||
4636 hist.lastOrigin == change.origin && change.origin &&
4637 ((change.origin.charAt(0) == "+" && doc.cm && hist.lastModTime > time - d oc.cm.options.historyEventDelay) ||
4638 change.origin.charAt(0) == "*")) &&
4639 (cur = lastChangeEvent(hist, hist.lastOp == opId))) {
4640 // Merge this change into the last event
4641 last = lst(cur.changes)
4642 if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) {
4643 // Optimized case for simple insertion -- don't want to add
4644 // new changesets for every character typed
4645 last.to = changeEnd(change)
4646 } else {
4647 // Add new sub-event
4648 cur.changes.push(historyChangeFromChange(doc, change))
4649 }
4650 } else {
4651 // Can not be merged, start a new event.
4652 var before = lst(hist.done)
4653 if (!before || !before.ranges)
4654 { pushSelectionToHistory(doc.sel, hist.done) }
4655 cur = {changes: [historyChangeFromChange(doc, change)],
4656 generation: hist.generation}
4657 hist.done.push(cur)
4658 while (hist.done.length > hist.undoDepth) {
4659 hist.done.shift()
4660 if (!hist.done[0].ranges) { hist.done.shift() }
4661 }
4662 }
4663 hist.done.push(selAfter)
4664 hist.generation = ++hist.maxGeneration
4665 hist.lastModTime = hist.lastSelTime = time
4666 hist.lastOp = hist.lastSelOp = opId
4667 hist.lastOrigin = hist.lastSelOrigin = change.origin
4668
4669 if (!last) { signal(doc, "historyAdded") }
4670 }
4671
4672 function selectionEventCanBeMerged(doc, origin, prev, sel) {
4673 var ch = origin.charAt(0)
4674 return ch == "*" ||
4675 ch == "+" &&
4676 prev.ranges.length == sel.ranges.length &&
4677 prev.somethingSelected() == sel.somethingSelected() &&
4678 new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventD elay : 500)
4679 }
4680
4681 // Called whenever the selection changes, sets the new selection as
4682 // the pending selection in the history, and pushes the old pending
4683 // selection into the 'done' array when it was significantly
4684 // different (in number of selected ranges, emptiness, or time).
4685 function addSelectionToHistory(doc, sel, opId, options) {
4686 var hist = doc.history, origin = options && options.origin
4687
4688 // A new event is started when the previous origin does not match
4689 // the current, or the origins don't allow matching. Origins
4690 // starting with * are always merged, those starting with + are
4691 // merged when similar and close together in time.
4692 if (opId == hist.lastSelOp ||
4693 (origin && hist.lastSelOrigin == origin &&
4694 (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||
4695 selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))
4696 { hist.done[hist.done.length - 1] = sel }
4697 else
4698 { pushSelectionToHistory(sel, hist.done) }
4699
4700 hist.lastSelTime = +new Date
4701 hist.lastSelOrigin = origin
4702 hist.lastSelOp = opId
4703 if (options && options.clearRedo !== false)
4704 { clearSelectionEvents(hist.undone) }
4705 }
4706
4707 function pushSelectionToHistory(sel, dest) {
4708 var top = lst(dest)
4709 if (!(top && top.ranges && top.equals(sel)))
4710 { dest.push(sel) }
4711 }
4712
4713 // Used to store marked span information in the history.
4714 function attachLocalSpans(doc, change, from, to) {
4715 var existing = change["spans_" + doc.id], n = 0
4716 doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), functi on (line) {
4717 if (line.markedSpans)
4718 { (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.mark edSpans }
4719 ++n
4720 })
4721 }
4722
4723 // When un/re-doing restores text containing marked spans, those
4724 // that have been explicitly cleared should not be restored.
4725 function removeClearedSpans(spans) {
4726 if (!spans) { return null }
4727 var out
4728 for (var i = 0; i < spans.length; ++i) {
4729 if (spans[i].marker.explicitlyCleared) { if (!out) { out = spans.slice(0, i) } }
4730 else if (out) { out.push(spans[i]) }
4731 }
4732 return !out ? spans : out.length ? out : null
4733 }
4734
4735 // Retrieve and filter the old marked spans stored in a change event.
4736 function getOldSpans(doc, change) {
4737 var found = change["spans_" + doc.id]
4738 if (!found) { return null }
4739 var nw = []
4740 for (var i = 0; i < change.text.length; ++i)
4741 { nw.push(removeClearedSpans(found[i])) }
4742 return nw
4743 }
4744
4745 // Used for un/re-doing changes from the history. Combines the
4746 // result of computing the existing spans with the set of spans that
4747 // existed in the history (so that deleting around a span and then
4748 // undoing brings back the span).
4749 function mergeOldSpans(doc, change) {
4750 var old = getOldSpans(doc, change)
4751 var stretched = stretchSpansOverChange(doc, change)
4752 if (!old) { return stretched }
4753 if (!stretched) { return old }
4754
4755 for (var i = 0; i < old.length; ++i) {
4756 var oldCur = old[i], stretchCur = stretched[i]
4757 if (oldCur && stretchCur) {
4758 spans: for (var j = 0; j < stretchCur.length; ++j) {
4759 var span = stretchCur[j]
4760 for (var k = 0; k < oldCur.length; ++k)
4761 { if (oldCur[k].marker == span.marker) { continue spans } }
4762 oldCur.push(span)
4763 }
4764 } else if (stretchCur) {
4765 old[i] = stretchCur
4766 }
4767 }
4768 return old
4769 }
4770
4771 // Used both to provide a JSON-safe object in .getHistory, and, when
4772 // detaching a document, to split the history in two
4773 function copyHistoryArray(events, newGroup, instantiateSel) {
4774 var copy = []
4775 for (var i = 0; i < events.length; ++i) {
4776 var event = events[i]
4777 if (event.ranges) {
4778 copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : even t)
4779 continue
4780 }
4781 var changes = event.changes, newChanges = []
4782 copy.push({changes: newChanges})
4783 for (var j = 0; j < changes.length; ++j) {
4784 var change = changes[j], m = (void 0)
4785 newChanges.push({from: change.from, to: change.to, text: change.text})
4786 if (newGroup) { for (var prop in change) { if (m = prop.match(/^spans_(\d+ )$/)) {
4787 if (indexOf(newGroup, Number(m[1])) > -1) {
4788 lst(newChanges)[prop] = change[prop]
4789 delete change[prop]
4790 }
4791 } } }
4792 }
4793 }
4794 return copy
4795 }
4796
4797 // The 'scroll' parameter given to many of these indicated whether
4798 // the new cursor position should be scrolled into view after
4799 // modifying the selection.
4800
4801 // If shift is held or the extend flag is set, extends a range to
4802 // include a given position (and optionally a second position).
4803 // Otherwise, simply returns the range between the given positions.
4804 // Used for cursor motion and such.
4805 function extendRange(doc, range, head, other) {
4806 if (doc.cm && doc.cm.display.shift || doc.extend) {
4807 var anchor = range.anchor
4808 if (other) {
4809 var posBefore = cmp(head, anchor) < 0
4810 if (posBefore != (cmp(other, anchor) < 0)) {
4811 anchor = head
4812 head = other
4813 } else if (posBefore != (cmp(head, other) < 0)) {
4814 head = other
4815 }
4816 }
4817 return new Range(anchor, head)
4818 } else {
4819 return new Range(other || head, head)
4820 }
4821 }
4822
4823 // Extend the primary selection range, discard the rest.
4824 function extendSelection(doc, head, other, options) {
4825 setSelection(doc, new Selection([extendRange(doc, doc.sel.primary(), head, oth er)], 0), options)
4826 }
4827
4828 // Extend all selections (pos is an array of selections with length
4829 // equal the number of selections)
4830 function extendSelections(doc, heads, options) {
4831 var out = []
4832 for (var i = 0; i < doc.sel.ranges.length; i++)
4833 { out[i] = extendRange(doc, doc.sel.ranges[i], heads[i], null) }
4834 var newSel = normalizeSelection(out, doc.sel.primIndex)
4835 setSelection(doc, newSel, options)
4836 }
4837
4838 // Updates a single range in the selection.
4839 function replaceOneSelection(doc, i, range, options) {
4840 var ranges = doc.sel.ranges.slice(0)
4841 ranges[i] = range
4842 setSelection(doc, normalizeSelection(ranges, doc.sel.primIndex), options)
4843 }
4844
4845 // Reset the selection to a single range.
4846 function setSimpleSelection(doc, anchor, head, options) {
4847 setSelection(doc, simpleSelection(anchor, head), options)
4848 }
4849
4850 // Give beforeSelectionChange handlers a change to influence a
4851 // selection update.
4852 function filterSelectionChange(doc, sel, options) {
4853 var obj = {
4854 ranges: sel.ranges,
4855 update: function(ranges) {
4856 var this$1 = this;
4857
4858 this.ranges = []
4859 for (var i = 0; i < ranges.length; i++)
4860 { this$1.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),
4861 clipPos(doc, ranges[i].head)) }
1664 }, 4862 },
1665 4863 origin: options && options.origin
1666 prepareSelection: function() { 4864 }
1667 var result = prepareSelection(this.cm, false); 4865 signal(doc, "beforeSelectionChange", doc, obj)
1668 result.focus = this.cm.state.focused; 4866 if (doc.cm) { signal(doc.cm, "beforeSelectionChange", doc.cm, obj) }
1669 return result; 4867 if (obj.ranges != sel.ranges) { return normalizeSelection(obj.ranges, obj.rang es.length - 1) }
1670 }, 4868 else { return sel }
1671 4869 }
1672 showSelection: function(info, takeFocus) { 4870
1673 if (!info || !this.cm.display.view.length) return; 4871 function setSelectionReplaceHistory(doc, sel, options) {
1674 if (info.focus || takeFocus) this.showPrimarySelection(); 4872 var done = doc.history.done, last = lst(done)
1675 this.showMultipleSelections(info); 4873 if (last && last.ranges) {
1676 }, 4874 done[done.length - 1] = sel
1677 4875 setSelectionNoUndo(doc, sel, options)
1678 showPrimarySelection: function() { 4876 } else {
1679 var sel = window.getSelection(), prim = this.cm.doc.sel.primary(); 4877 setSelection(doc, sel, options)
1680 var curAnchor = domToPos(this.cm, sel.anchorNode, sel.anchorOffset); 4878 }
1681 var curFocus = domToPos(this.cm, sel.focusNode, sel.focusOffset); 4879 }
1682 if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad && 4880
1683 cmp(minPos(curAnchor, curFocus), prim.from()) == 0 && 4881 // Set a new selection.
1684 cmp(maxPos(curAnchor, curFocus), prim.to()) == 0) 4882 function setSelection(doc, sel, options) {
1685 return; 4883 setSelectionNoUndo(doc, sel, options)
1686 4884 addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options)
1687 var start = posToDOM(this.cm, prim.from()); 4885 }
1688 var end = posToDOM(this.cm, prim.to()); 4886
1689 if (!start && !end) return; 4887 function setSelectionNoUndo(doc, sel, options) {
1690 4888 if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, " beforeSelectionChange"))
1691 var view = this.cm.display.view; 4889 { sel = filterSelectionChange(doc, sel, options) }
1692 var old = sel.rangeCount && sel.getRangeAt(0); 4890
1693 if (!start) { 4891 var bias = options && options.bias ||
1694 start = {node: view[0].measure.map[2], offset: 0}; 4892 (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1)
1695 } else if (!end) { // FIXME dangerously hacky 4893 setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true))
1696 var measure = view[view.length - 1].measure; 4894
1697 var map = measure.maps ? measure.maps[measure.maps.length - 1] : measure .map; 4895 if (!(options && options.scroll === false) && doc.cm)
1698 end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map. length - 3]}; 4896 { ensureCursorVisible(doc.cm) }
1699 } 4897 }
1700 4898
1701 try { var rng = range(start.node, start.offset, end.offset, end.node); } 4899 function setSelectionInner(doc, sel) {
1702 catch(e) {} // Our model of the DOM might be outdated, in which case the r ange we try to set can be impossible 4900 if (sel.equals(doc.sel)) { return }
1703 if (rng) { 4901
1704 if (!gecko && this.cm.state.focused) { 4902 doc.sel = sel
1705 sel.collapse(start.node, start.offset); 4903
1706 if (!rng.collapsed) sel.addRange(rng); 4904 if (doc.cm) {
1707 } else { 4905 doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true
1708 sel.removeAllRanges(); 4906 signalCursorActivity(doc.cm)
1709 sel.addRange(rng); 4907 }
1710 } 4908 signalLater(doc, "cursorActivity", doc)
1711 if (old && sel.anchorNode == null) sel.addRange(old); 4909 }
1712 else if (gecko) this.startGracePeriod(); 4910
1713 } 4911 // Verify that the selection does not partially select any atomic
1714 this.rememberSelection(); 4912 // marked ranges.
1715 }, 4913 function reCheckSelection(doc) {
1716 4914 setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false), sel_d ontScroll)
1717 startGracePeriod: function() { 4915 }
1718 var input = this; 4916
1719 clearTimeout(this.gracePeriod); 4917 // Return a selection that does not partially select any atomic
1720 this.gracePeriod = setTimeout(function() { 4918 // ranges.
1721 input.gracePeriod = false; 4919 function skipAtomicInSelection(doc, sel, bias, mayClear) {
1722 if (input.selectionChanged()) 4920 var out
1723 input.cm.operation(function() { input.cm.curOp.selectionChanged = true ; }); 4921 for (var i = 0; i < sel.ranges.length; i++) {
1724 }, 20); 4922 var range = sel.ranges[i]
1725 }, 4923 var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i]
1726 4924 var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayCl ear)
1727 showMultipleSelections: function(info) { 4925 var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear)
1728 removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors); 4926 if (out || newAnchor != range.anchor || newHead != range.head) {
1729 removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection); 4927 if (!out) { out = sel.ranges.slice(0, i) }
1730 }, 4928 out[i] = new Range(newAnchor, newHead)
1731 4929 }
1732 rememberSelection: function() { 4930 }
1733 var sel = window.getSelection(); 4931 return out ? normalizeSelection(out, sel.primIndex) : sel
1734 this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOf fset; 4932 }
1735 this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset ; 4933
1736 }, 4934 function skipAtomicInner(doc, pos, oldPos, dir, mayClear) {
1737 4935 var line = getLine(doc, pos.line)
1738 selectionInEditor: function() { 4936 if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
1739 var sel = window.getSelection(); 4937 var sp = line.markedSpans[i], m = sp.marker
1740 if (!sel.rangeCount) return false; 4938 if ((sp.from == null || (m.inclusiveLeft ? sp.from <= pos.ch : sp.from < pos .ch)) &&
1741 var node = sel.getRangeAt(0).commonAncestorContainer; 4939 (sp.to == null || (m.inclusiveRight ? sp.to >= pos.ch : sp.to > pos.ch)) ) {
1742 return contains(this.div, node); 4940 if (mayClear) {
1743 }, 4941 signal(m, "beforeCursorEnter")
1744 4942 if (m.explicitlyCleared) {
1745 focus: function() { 4943 if (!line.markedSpans) { break }
1746 if (this.cm.options.readOnly != "nocursor") this.div.focus(); 4944 else {--i; continue}
1747 },
1748 blur: function() { this.div.blur(); },
1749 getField: function() { return this.div; },
1750
1751 supportsTouch: function() { return true; },
1752
1753 receivedFocus: function() {
1754 var input = this;
1755 if (this.selectionInEditor())
1756 this.pollSelection();
1757 else
1758 runInOp(this.cm, function() { input.cm.curOp.selectionChanged = true; }) ;
1759
1760 function poll() {
1761 if (input.cm.state.focused) {
1762 input.pollSelection();
1763 input.polling.set(input.cm.options.pollInterval, poll);
1764 } 4945 }
1765 } 4946 }
1766 this.polling.set(this.cm.options.pollInterval, poll); 4947 if (!m.atomic) { continue }
1767 }, 4948
1768 4949 if (oldPos) {
1769 selectionChanged: function() { 4950 var near = m.find(dir < 0 ? 1 : -1), diff = (void 0)
1770 var sel = window.getSelection(); 4951 if (dir < 0 ? m.inclusiveRight : m.inclusiveLeft)
1771 return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.l astAnchorOffset || 4952 { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null) }
1772 sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocus Offset; 4953 if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0))
1773 }, 4954 { return skipAtomicInner(doc, near, pos, dir, mayClear) }
1774
1775 pollSelection: function() {
1776 if (!this.composing && !this.gracePeriod && this.selectionChanged()) {
1777 var sel = window.getSelection(), cm = this.cm;
1778 this.rememberSelection();
1779 var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
1780 var head = domToPos(cm, sel.focusNode, sel.focusOffset);
1781 if (anchor && head) runInOp(cm, function() {
1782 setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll);
1783 if (anchor.bad || head.bad) cm.curOp.selectionChanged = true;
1784 });
1785 } 4955 }
1786 }, 4956
1787 4957 var far = m.find(dir < 0 ? -1 : 1)
1788 pollContent: function() { 4958 if (dir < 0 ? m.inclusiveLeft : m.inclusiveRight)
1789 var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary(); 4959 { far = movePos(doc, far, dir, far.line == pos.line ? line : null) }
1790 var from = sel.from(), to = sel.to(); 4960 return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null
1791 if (from.line < display.viewFrom || to.line > display.viewTo - 1) return f alse; 4961 }
1792 4962 } }
1793 var fromIndex; 4963 return pos
1794 if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.l ine)) == 0) { 4964 }
1795 var fromLine = lineNo(display.view[0].line); 4965
1796 var fromNode = display.view[0].node; 4966 // Ensure a given position is not inside an atomic range.
4967 function skipAtomic(doc, pos, oldPos, bias, mayClear) {
4968 var dir = bias || 1
4969 var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) ||
4970 (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) ||
4971 skipAtomicInner(doc, pos, oldPos, -dir, mayClear) ||
4972 (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true))
4973 if (!found) {
4974 doc.cantEdit = true
4975 return Pos(doc.first, 0)
4976 }
4977 return found
4978 }
4979
4980 function movePos(doc, pos, dir, line) {
4981 if (dir < 0 && pos.ch == 0) {
4982 if (pos.line > doc.first) { return clipPos(doc, Pos(pos.line - 1)) }
4983 else { return null }
4984 } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) {
4985 if (pos.line < doc.first + doc.size - 1) { return Pos(pos.line + 1, 0) }
4986 else { return null }
4987 } else {
4988 return new Pos(pos.line, pos.ch + dir)
4989 }
4990 }
4991
4992 function selectAll(cm) {
4993 cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll)
4994 }
4995
4996 // UPDATING
4997
4998 // Allow "beforeChange" event handlers to influence a change
4999 function filterChange(doc, change, update) {
5000 var obj = {
5001 canceled: false,
5002 from: change.from,
5003 to: change.to,
5004 text: change.text,
5005 origin: change.origin,
5006 cancel: function () { return obj.canceled = true; }
5007 }
5008 if (update) { obj.update = function (from, to, text, origin) {
5009 if (from) { obj.from = clipPos(doc, from) }
5010 if (to) { obj.to = clipPos(doc, to) }
5011 if (text) { obj.text = text }
5012 if (origin !== undefined) { obj.origin = origin }
5013 } }
5014 signal(doc, "beforeChange", doc, obj)
5015 if (doc.cm) { signal(doc.cm, "beforeChange", doc.cm, obj) }
5016
5017 if (obj.canceled) { return null }
5018 return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin}
5019 }
5020
5021 // Apply a change to a document, and add it to the document's
5022 // history, and propagating it to all linked documents.
5023 function makeChange(doc, change, ignoreReadOnly) {
5024 if (doc.cm) {
5025 if (!doc.cm.curOp) { return operation(doc.cm, makeChange)(doc, change, ignor eReadOnly) }
5026 if (doc.cm.state.suppressEdits) { return }
5027 }
5028
5029 if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeCha nge")) {
5030 change = filterChange(doc, change, true)
5031 if (!change) { return }
5032 }
5033
5034 // Possibly split or suppress the update based on the presence
5035 // of read-only spans in its range.
5036 var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, c hange.from, change.to)
5037 if (split) {
5038 for (var i = split.length - 1; i >= 0; --i)
5039 { makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [" "] : change.text}) }
5040 } else {
5041 makeChangeInner(doc, change)
5042 }
5043 }
5044
5045 function makeChangeInner(doc, change) {
5046 if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change .to) == 0) { return }
5047 var selAfter = computeSelAfterChange(doc, change)
5048 addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN)
5049
5050 makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change) )
5051 var rebased = []
5052
5053 linkedDocs(doc, function (doc, sharedHist) {
5054 if (!sharedHist && indexOf(rebased, doc.history) == -1) {
5055 rebaseHist(doc.history, change)
5056 rebased.push(doc.history)
5057 }
5058 makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change))
5059 })
5060 }
5061
5062 // Revert a change stored in a document's history.
5063 function makeChangeFromHistory(doc, type, allowSelectionOnly) {
5064 if (doc.cm && doc.cm.state.suppressEdits && !allowSelectionOnly) { return }
5065
5066 var hist = doc.history, event, selAfter = doc.sel
5067 var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done
5068
5069 // Verify that there is a useable event (so that ctrl-z won't
5070 // needlessly clear selection events)
5071 var i = 0
5072 for (; i < source.length; i++) {
5073 event = source[i]
5074 if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ran ges)
5075 { break }
5076 }
5077 if (i == source.length) { return }
5078 hist.lastOrigin = hist.lastSelOrigin = null
5079
5080 for (;;) {
5081 event = source.pop()
5082 if (event.ranges) {
5083 pushSelectionToHistory(event, dest)
5084 if (allowSelectionOnly && !event.equals(doc.sel)) {
5085 setSelection(doc, event, {clearRedo: false})
5086 return
5087 }
5088 selAfter = event
5089 }
5090 else { break }
5091 }
5092
5093 // Build up a reverse change object to add to the opposite history
5094 // stack (redo when undoing, and vice versa).
5095 var antiChanges = []
5096 pushSelectionToHistory(selAfter, dest)
5097 dest.push({changes: antiChanges, generation: hist.generation})
5098 hist.generation = event.generation || ++hist.maxGeneration
5099
5100 var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, " beforeChange")
5101
5102 var loop = function ( i ) {
5103 var change = event.changes[i]
5104 change.origin = type
5105 if (filter && !filterChange(doc, change, false)) {
5106 source.length = 0
5107 return {}
5108 }
5109
5110 antiChanges.push(historyChangeFromChange(doc, change))
5111
5112 var after = i ? computeSelAfterChange(doc, change) : lst(source)
5113 makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change))
5114 if (!i && doc.cm) { doc.cm.scrollIntoView({from: change.from, to: changeEnd( change)}) }
5115 var rebased = []
5116
5117 // Propagate to the linked documents
5118 linkedDocs(doc, function (doc, sharedHist) {
5119 if (!sharedHist && indexOf(rebased, doc.history) == -1) {
5120 rebaseHist(doc.history, change)
5121 rebased.push(doc.history)
5122 }
5123 makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change))
5124 })
5125 };
5126
5127 for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) {
5128 var returned = loop( i$1 );
5129
5130 if ( returned ) return returned.v;
5131 }
5132 }
5133
5134 // Sub-views need their line numbers shifted when text is added
5135 // above or below them in the parent document.
5136 function shiftDoc(doc, distance) {
5137 if (distance == 0) { return }
5138 doc.first += distance
5139 doc.sel = new Selection(map(doc.sel.ranges, function (range) { return new Rang e(
5140 Pos(range.anchor.line + distance, range.anchor.ch),
5141 Pos(range.head.line + distance, range.head.ch)
5142 ); }), doc.sel.primIndex)
5143 if (doc.cm) {
5144 regChange(doc.cm, doc.first, doc.first - distance, distance)
5145 for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++)
5146 { regLineChange(doc.cm, l, "gutter") }
5147 }
5148 }
5149
5150 // More lower-level change function, handling only a single document
5151 // (not linked ones).
5152 function makeChangeSingleDoc(doc, change, selAfter, spans) {
5153 if (doc.cm && !doc.cm.curOp)
5154 { return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans ) }
5155
5156 if (change.to.line < doc.first) {
5157 shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line))
5158 return
5159 }
5160 if (change.from.line > doc.lastLine()) { return }
5161
5162 // Clip the change to the size of this doc
5163 if (change.from.line < doc.first) {
5164 var shift = change.text.length - 1 - (doc.first - change.from.line)
5165 shiftDoc(doc, shift)
5166 change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to .ch),
5167 text: [lst(change.text)], origin: change.origin}
5168 }
5169 var last = doc.lastLine()
5170 if (change.to.line > last) {
5171 change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
5172 text: [change.text[0]], origin: change.origin}
5173 }
5174
5175 change.removed = getBetween(doc, change.from, change.to)
5176
5177 if (!selAfter) { selAfter = computeSelAfterChange(doc, change) }
5178 if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans) }
5179 else { updateDoc(doc, change, spans) }
5180 setSelectionNoUndo(doc, selAfter, sel_dontScroll)
5181 }
5182
5183 // Handle the interaction of a change to a document with the editor
5184 // that this document is part of.
5185 function makeChangeSingleDocInEditor(cm, change, spans) {
5186 var doc = cm.doc, display = cm.display, from = change.from, to = change.to
5187
5188 var recomputeMaxLength = false, checkWidthStart = from.line
5189 if (!cm.options.lineWrapping) {
5190 checkWidthStart = lineNo(visualLine(getLine(doc, from.line)))
5191 doc.iter(checkWidthStart, to.line + 1, function (line) {
5192 if (line == display.maxLine) {
5193 recomputeMaxLength = true
5194 return true
5195 }
5196 })
5197 }
5198
5199 if (doc.sel.contains(change.from, change.to) > -1)
5200 { signalCursorActivity(cm) }
5201
5202 updateDoc(doc, change, spans, estimateHeight(cm))
5203
5204 if (!cm.options.lineWrapping) {
5205 doc.iter(checkWidthStart, from.line + change.text.length, function (line) {
5206 var len = lineLength(line)
5207 if (len > display.maxLineLength) {
5208 display.maxLine = line
5209 display.maxLineLength = len
5210 display.maxLineChanged = true
5211 recomputeMaxLength = false
5212 }
5213 })
5214 if (recomputeMaxLength) { cm.curOp.updateMaxLine = true }
5215 }
5216
5217 // Adjust frontier, schedule worker
5218 doc.frontier = Math.min(doc.frontier, from.line)
5219 startWorker(cm, 400)
5220
5221 var lendiff = change.text.length - (to.line - from.line) - 1
5222 // Remember that these lines changed, for updating the display
5223 if (change.full)
5224 { regChange(cm) }
5225 else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate (cm.doc, change))
5226 { regLineChange(cm, from.line, "text") }
5227 else
5228 { regChange(cm, from.line, to.line + 1, lendiff) }
5229
5230 var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change")
5231 if (changeHandler || changesHandler) {
5232 var obj = {
5233 from: from, to: to,
5234 text: change.text,
5235 removed: change.removed,
5236 origin: change.origin
5237 }
5238 if (changeHandler) { signalLater(cm, "change", cm, obj) }
5239 if (changesHandler) { (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).pu sh(obj) }
5240 }
5241 cm.display.selForContextMenu = null
5242 }
5243
5244 function replaceRange(doc, code, from, to, origin) {
5245 if (!to) { to = from }
5246 if (cmp(to, from) < 0) { var tmp = to; to = from; from = tmp }
5247 if (typeof code == "string") { code = doc.splitLines(code) }
5248 makeChange(doc, {from: from, to: to, text: code, origin: origin})
5249 }
5250
5251 // Rebasing/resetting history to deal with externally-sourced changes
5252
5253 function rebaseHistSelSingle(pos, from, to, diff) {
5254 if (to < pos.line) {
5255 pos.line += diff
5256 } else if (from < pos.line) {
5257 pos.line = from
5258 pos.ch = 0
5259 }
5260 }
5261
5262 // Tries to rebase an array of history events given a change in the
5263 // document. If the change touches the same lines as the event, the
5264 // event, and everything 'behind' it, is discarded. If the change is
5265 // before the event, the event's positions are updated. Uses a
5266 // copy-on-write scheme for the positions, to avoid having to
5267 // reallocate them all on every rebase, but also avoid problems with
5268 // shared position objects being unsafely updated.
5269 function rebaseHistArray(array, from, to, diff) {
5270 for (var i = 0; i < array.length; ++i) {
5271 var sub = array[i], ok = true
5272 if (sub.ranges) {
5273 if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true }
5274 for (var j = 0; j < sub.ranges.length; j++) {
5275 rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff)
5276 rebaseHistSelSingle(sub.ranges[j].head, from, to, diff)
5277 }
5278 continue
5279 }
5280 for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) {
5281 var cur = sub.changes[j$1]
5282 if (to < cur.from.line) {
5283 cur.from = Pos(cur.from.line + diff, cur.from.ch)
5284 cur.to = Pos(cur.to.line + diff, cur.to.ch)
5285 } else if (from <= cur.to.line) {
5286 ok = false
5287 break
5288 }
5289 }
5290 if (!ok) {
5291 array.splice(0, i + 1)
5292 i = 0
5293 }
5294 }
5295 }
5296
5297 function rebaseHist(hist, change) {
5298 var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1
5299 rebaseHistArray(hist.done, from, to, diff)
5300 rebaseHistArray(hist.undone, from, to, diff)
5301 }
5302
5303 // Utility for applying a change to a line by handle or number,
5304 // returning the number and optionally registering the line as
5305 // changed.
5306 function changeLine(doc, handle, changeType, op) {
5307 var no = handle, line = handle
5308 if (typeof handle == "number") { line = getLine(doc, clipLine(doc, handle)) }
5309 else { no = lineNo(handle) }
5310 if (no == null) { return null }
5311 if (op(line, no) && doc.cm) { regLineChange(doc.cm, no, changeType) }
5312 return line
5313 }
5314
5315 // The document is represented as a BTree consisting of leaves, with
5316 // chunk of lines in them, and branches, with up to ten leaves or
5317 // other branch nodes below them. The top node is always a branch
5318 // node, and is the document object itself (meaning it has
5319 // additional methods and properties).
5320 //
5321 // All nodes have parent links. The tree is used both to go from
5322 // line numbers to line objects, and to go from objects to numbers.
5323 // It also indexes by height, and is used to convert between height
5324 // and line object, and to find the total height of the document.
5325 //
5326 // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html
5327
5328 var LeafChunk = function LeafChunk(lines) {
5329 var this$1 = this;
5330
5331 this.lines = lines
5332 this.parent = null
5333 var height = 0
5334 for (var i = 0; i < lines.length; ++i) {
5335 lines[i].parent = this$1
5336 height += lines[i].height
5337 }
5338 this.height = height
5339 };
5340
5341 LeafChunk.prototype.chunkSize = function chunkSize () { return this.lines.length };
5342
5343 // Remove the n lines at offset 'at'.
5344 LeafChunk.prototype.removeInner = function removeInner (at, n) {
5345 var this$1 = this;
5346
5347 for (var i = at, e = at + n; i < e; ++i) {
5348 var line = this$1.lines[i]
5349 this$1.height -= line.height
5350 cleanUpLine(line)
5351 signalLater(line, "delete")
5352 }
5353 this.lines.splice(at, n)
5354 };
5355
5356 // Helper used to collapse a small branch into a single leaf.
5357 LeafChunk.prototype.collapse = function collapse (lines) {
5358 lines.push.apply(lines, this.lines)
5359 };
5360
5361 // Insert the given array of lines at offset 'at', count them as
5362 // having the given height.
5363 LeafChunk.prototype.insertInner = function insertInner (at, lines, height) {
5364 var this$1 = this;
5365
5366 this.height += height
5367 this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at) )
5368 for (var i = 0; i < lines.length; ++i) { lines[i].parent = this$1 }
5369 };
5370
5371 // Used to iterate over a part of the tree.
5372 LeafChunk.prototype.iterN = function iterN (at, n, op) {
5373 var this$1 = this;
5374
5375 for (var e = at + n; at < e; ++at)
5376 { if (op(this$1.lines[at])) { return true } }
5377 };
5378
5379 var BranchChunk = function BranchChunk(children) {
5380 var this$1 = this;
5381
5382 this.children = children
5383 var size = 0, height = 0
5384 for (var i = 0; i < children.length; ++i) {
5385 var ch = children[i]
5386 size += ch.chunkSize(); height += ch.height
5387 ch.parent = this$1
5388 }
5389 this.size = size
5390 this.height = height
5391 this.parent = null
5392 };
5393
5394 BranchChunk.prototype.chunkSize = function chunkSize () { return this.size };
5395
5396 BranchChunk.prototype.removeInner = function removeInner (at, n) {
5397 var this$1 = this;
5398
5399 this.size -= n
5400 for (var i = 0; i < this.children.length; ++i) {
5401 var child = this$1.children[i], sz = child.chunkSize()
5402 if (at < sz) {
5403 var rm = Math.min(n, sz - at), oldHeight = child.height
5404 child.removeInner(at, rm)
5405 this$1.height -= oldHeight - child.height
5406 if (sz == rm) { this$1.children.splice(i--, 1); child.parent = null }
5407 if ((n -= rm) == 0) { break }
5408 at = 0
5409 } else { at -= sz }
5410 }
5411 // If the result is smaller than 25 lines, ensure that it is a
5412 // single leaf node.
5413 if (this.size - n < 25 &&
5414 (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) {
5415 var lines = []
5416 this.collapse(lines)
5417 this.children = [new LeafChunk(lines)]
5418 this.children[0].parent = this
5419 }
5420 };
5421
5422 BranchChunk.prototype.collapse = function collapse (lines) {
5423 var this$1 = this;
5424
5425 for (var i = 0; i < this.children.length; ++i) { this$1.children[i].collapse(l ines) }
5426 };
5427
5428 BranchChunk.prototype.insertInner = function insertInner (at, lines, height) {
5429 var this$1 = this;
5430
5431 this.size += lines.length
5432 this.height += height
5433 for (var i = 0; i < this.children.length; ++i) {
5434 var child = this$1.children[i], sz = child.chunkSize()
5435 if (at <= sz) {
5436 child.insertInner(at, lines, height)
5437 if (child.lines && child.lines.length > 50) {
5438 // To avoid memory thrashing when child.lines is huge (e.g. first view o f a large file), it's never spliced.
5439 // Instead, small slices are taken. They're taken in order because seque ntial memory accesses are fastest.
5440 var remaining = child.lines.length % 25 + 25
5441 for (var pos = remaining; pos < child.lines.length;) {
5442 var leaf = new LeafChunk(child.lines.slice(pos, pos += 25))
5443 child.height -= leaf.height
5444 this$1.children.splice(++i, 0, leaf)
5445 leaf.parent = this$1
5446 }
5447 child.lines = child.lines.slice(0, remaining)
5448 this$1.maybeSpill()
5449 }
5450 break
5451 }
5452 at -= sz
5453 }
5454 };
5455
5456 // When a node has grown, check whether it should be split.
5457 BranchChunk.prototype.maybeSpill = function maybeSpill () {
5458 if (this.children.length <= 10) { return }
5459 var me = this
5460 do {
5461 var spilled = me.children.splice(me.children.length - 5, 5)
5462 var sibling = new BranchChunk(spilled)
5463 if (!me.parent) { // Become the parent node
5464 var copy = new BranchChunk(me.children)
5465 copy.parent = me
5466 me.children = [copy, sibling]
5467 me = copy
5468 } else {
5469 me.size -= sibling.size
5470 me.height -= sibling.height
5471 var myIndex = indexOf(me.parent.children, me)
5472 me.parent.children.splice(myIndex + 1, 0, sibling)
5473 }
5474 sibling.parent = me.parent
5475 } while (me.children.length > 10)
5476 me.parent.maybeSpill()
5477 };
5478
5479 BranchChunk.prototype.iterN = function iterN (at, n, op) {
5480 var this$1 = this;
5481
5482 for (var i = 0; i < this.children.length; ++i) {
5483 var child = this$1.children[i], sz = child.chunkSize()
5484 if (at < sz) {
5485 var used = Math.min(n, sz - at)
5486 if (child.iterN(at, used, op)) { return true }
5487 if ((n -= used) == 0) { break }
5488 at = 0
5489 } else { at -= sz }
5490 }
5491 };
5492
5493 // Line widgets are block elements displayed above or below a line.
5494
5495 var LineWidget = function LineWidget(doc, node, options) {
5496 var this$1 = this;
5497
5498 if (options) { for (var opt in options) { if (options.hasOwnProperty(opt))
5499 { this$1[opt] = options[opt] } } }
5500 this.doc = doc
5501 this.node = node
5502 };
5503
5504 LineWidget.prototype.clear = function clear () {
5505 var this$1 = this;
5506
5507 var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(li ne)
5508 if (no == null || !ws) { return }
5509 for (var i = 0; i < ws.length; ++i) { if (ws[i] == this$1) { ws.splice(i--, 1) } }
5510 if (!ws.length) { line.widgets = null }
5511 var height = widgetHeight(this)
5512 updateLineHeight(line, Math.max(0, line.height - height))
5513 if (cm) {
5514 runInOp(cm, function () {
5515 adjustScrollWhenAboveVisible(cm, line, -height)
5516 regLineChange(cm, no, "widget")
5517 })
5518 signalLater(cm, "lineWidgetCleared", cm, this, no)
5519 }
5520 };
5521
5522 LineWidget.prototype.changed = function changed () {
5523 var this$1 = this;
5524
5525 var oldH = this.height, cm = this.doc.cm, line = this.line
5526 this.height = null
5527 var diff = widgetHeight(this) - oldH
5528 if (!diff) { return }
5529 updateLineHeight(line, line.height + diff)
5530 if (cm) {
5531 runInOp(cm, function () {
5532 cm.curOp.forceUpdate = true
5533 adjustScrollWhenAboveVisible(cm, line, diff)
5534 signalLater(cm, "lineWidgetChanged", cm, this$1, lineNo(line))
5535 })
5536 }
5537 };
5538 eventMixin(LineWidget)
5539
5540 function adjustScrollWhenAboveVisible(cm, line, diff) {
5541 if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop ))
5542 { addToScrollPos(cm, null, diff) }
5543 }
5544
5545 function addLineWidget(doc, handle, node, options) {
5546 var widget = new LineWidget(doc, node, options)
5547 var cm = doc.cm
5548 if (cm && widget.noHScroll) { cm.display.alignWidgets = true }
5549 changeLine(doc, handle, "widget", function (line) {
5550 var widgets = line.widgets || (line.widgets = [])
5551 if (widget.insertAt == null) { widgets.push(widget) }
5552 else { widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insert At)), 0, widget) }
5553 widget.line = line
5554 if (cm && !lineIsHidden(doc, line)) {
5555 var aboveVisible = heightAtLine(line) < doc.scrollTop
5556 updateLineHeight(line, line.height + widgetHeight(widget))
5557 if (aboveVisible) { addToScrollPos(cm, null, widget.height) }
5558 cm.curOp.forceUpdate = true
5559 }
5560 return true
5561 })
5562 signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? han dle : lineNo(handle))
5563 return widget
5564 }
5565
5566 // TEXTMARKERS
5567
5568 // Created with markText and setBookmark methods. A TextMarker is a
5569 // handle that can be used to clear or find a marked position in the
5570 // document. Line objects hold arrays (markedSpans) containing
5571 // {from, to, marker} object pointing to such marker objects, and
5572 // indicating that such a marker is present on that line. Multiple
5573 // lines may point to the same marker when it spans across lines.
5574 // The spans will have null for their from/to properties when the
5575 // marker continues beyond the start/end of the line. Markers have
5576 // links back to the lines they currently touch.
5577
5578 // Collapsed markers have unique ids, in order to be able to order
5579 // them, which is needed for uniquely determining an outer marker
5580 // when they overlap (they may nest, but not partially overlap).
5581 var nextMarkerId = 0
5582
5583 var TextMarker = function TextMarker(doc, type) {
5584 this.lines = []
5585 this.type = type
5586 this.doc = doc
5587 this.id = ++nextMarkerId
5588 };
5589
5590 // Clear the marker.
5591 TextMarker.prototype.clear = function clear () {
5592 var this$1 = this;
5593
5594 if (this.explicitlyCleared) { return }
5595 var cm = this.doc.cm, withOp = cm && !cm.curOp
5596 if (withOp) { startOperation(cm) }
5597 if (hasHandler(this, "clear")) {
5598 var found = this.find()
5599 if (found) { signalLater(this, "clear", found.from, found.to) }
5600 }
5601 var min = null, max = null
5602 for (var i = 0; i < this.lines.length; ++i) {
5603 var line = this$1.lines[i]
5604 var span = getMarkedSpanFor(line.markedSpans, this$1)
5605 if (cm && !this$1.collapsed) { regLineChange(cm, lineNo(line), "text") }
5606 else if (cm) {
5607 if (span.to != null) { max = lineNo(line) }
5608 if (span.from != null) { min = lineNo(line) }
5609 }
5610 line.markedSpans = removeMarkedSpan(line.markedSpans, span)
5611 if (span.from == null && this$1.collapsed && !lineIsHidden(this$1.doc, line) && cm)
5612 { updateLineHeight(line, textHeight(cm.display)) }
5613 }
5614 if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) {
5615 var visual = visualLine(this$1.lines[i$1]), len = lineLength(visual)
5616 if (len > cm.display.maxLineLength) {
5617 cm.display.maxLine = visual
5618 cm.display.maxLineLength = len
5619 cm.display.maxLineChanged = true
5620 }
5621 } }
5622
5623 if (min != null && cm && this.collapsed) { regChange(cm, min, max + 1) }
5624 this.lines.length = 0
5625 this.explicitlyCleared = true
5626 if (this.atomic && this.doc.cantEdit) {
5627 this.doc.cantEdit = false
5628 if (cm) { reCheckSelection(cm.doc) }
5629 }
5630 if (cm) { signalLater(cm, "markerCleared", cm, this, min, max) }
5631 if (withOp) { endOperation(cm) }
5632 if (this.parent) { this.parent.clear() }
5633 };
5634
5635 // Find the position of the marker in the document. Returns a {from,
5636 // to} object by default. Side can be passed to get a specific side
5637 // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the
5638 // Pos objects returned contain a line object, rather than a line
5639 // number (used to prevent looking up the same line twice).
5640 TextMarker.prototype.find = function find (side, lineObj) {
5641 var this$1 = this;
5642
5643 if (side == null && this.type == "bookmark") { side = 1 }
5644 var from, to
5645 for (var i = 0; i < this.lines.length; ++i) {
5646 var line = this$1.lines[i]
5647 var span = getMarkedSpanFor(line.markedSpans, this$1)
5648 if (span.from != null) {
5649 from = Pos(lineObj ? line : lineNo(line), span.from)
5650 if (side == -1) { return from }
5651 }
5652 if (span.to != null) {
5653 to = Pos(lineObj ? line : lineNo(line), span.to)
5654 if (side == 1) { return to }
5655 }
5656 }
5657 return from && {from: from, to: to}
5658 };
5659
5660 // Signals that the marker's widget changed, and surrounding layout
5661 // should be recomputed.
5662 TextMarker.prototype.changed = function changed () {
5663 var this$1 = this;
5664
5665 var pos = this.find(-1, true), widget = this, cm = this.doc.cm
5666 if (!pos || !cm) { return }
5667 runInOp(cm, function () {
5668 var line = pos.line, lineN = lineNo(pos.line)
5669 var view = findViewForLine(cm, lineN)
5670 if (view) {
5671 clearLineMeasurementCacheFor(view)
5672 cm.curOp.selectionChanged = cm.curOp.forceUpdate = true
5673 }
5674 cm.curOp.updateMaxLine = true
5675 if (!lineIsHidden(widget.doc, line) && widget.height != null) {
5676 var oldHeight = widget.height
5677 widget.height = null
5678 var dHeight = widgetHeight(widget) - oldHeight
5679 if (dHeight)
5680 { updateLineHeight(line, line.height + dHeight) }
5681 }
5682 signalLater(cm, "markerChanged", cm, this$1)
5683 })
5684 };
5685
5686 TextMarker.prototype.attachLine = function attachLine (line) {
5687 if (!this.lines.length && this.doc.cm) {
5688 var op = this.doc.cm.curOp
5689 if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
5690 { (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this) }
5691 }
5692 this.lines.push(line)
5693 };
5694
5695 TextMarker.prototype.detachLine = function detachLine (line) {
5696 this.lines.splice(indexOf(this.lines, line), 1)
5697 if (!this.lines.length && this.doc.cm) {
5698 var op = this.doc.cm.curOp
5699 ;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this)
5700 }
5701 };
5702 eventMixin(TextMarker)
5703
5704 // Create a marker, wire it up to the right lines, and
5705 function markText(doc, from, to, options, type) {
5706 // Shared markers (across linked documents) are handled separately
5707 // (markTextShared will call out to this again, once per
5708 // document).
5709 if (options && options.shared) { return markTextShared(doc, from, to, options, type) }
5710 // Ensure we are in an operation.
5711 if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, markText)(doc, from, t o, options, type) }
5712
5713 var marker = new TextMarker(doc, type), diff = cmp(from, to)
5714 if (options) { copyObj(options, marker, false) }
5715 // Don't connect empty markers unless clearWhenEmpty is false
5716 if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false)
5717 { return marker }
5718 if (marker.replacedWith) {
5719 // Showing up as a widget implies collapsed (widget replaces text)
5720 marker.collapsed = true
5721 marker.widgetNode = eltP("span", [marker.replacedWith], "CodeMirror-widget")
5722 if (!options.handleMouseEvents) { marker.widgetNode.setAttribute("cm-ignore- events", "true") }
5723 if (options.insertLeft) { marker.widgetNode.insertLeft = true }
5724 }
5725 if (marker.collapsed) {
5726 if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||
5727 from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to , marker))
5728 { throw new Error("Inserting collapsed marker partially overlapping an exi sting one") }
5729 seeCollapsedSpans()
5730 }
5731
5732 if (marker.addToHistory)
5733 { addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN) }
5734
5735 var curLine = from.line, cm = doc.cm, updateMaxLine
5736 doc.iter(curLine, to.line + 1, function (line) {
5737 if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) = = cm.display.maxLine)
5738 { updateMaxLine = true }
5739 if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0) }
5740 addMarkedSpan(line, new MarkedSpan(marker,
5741 curLine == from.line ? from.ch : null,
5742 curLine == to.line ? to.ch : null))
5743 ++curLine
5744 })
5745 // lineIsHidden depends on the presence of the spans, so needs a second pass
5746 if (marker.collapsed) { doc.iter(from.line, to.line + 1, function (line) {
5747 if (lineIsHidden(doc, line)) { updateLineHeight(line, 0) }
5748 }) }
5749
5750 if (marker.clearOnEnter) { on(marker, "beforeCursorEnter", function () { retur n marker.clear(); }) }
5751
5752 if (marker.readOnly) {
5753 seeReadOnlySpans()
5754 if (doc.history.done.length || doc.history.undone.length)
5755 { doc.clearHistory() }
5756 }
5757 if (marker.collapsed) {
5758 marker.id = ++nextMarkerId
5759 marker.atomic = true
5760 }
5761 if (cm) {
5762 // Sync editor state
5763 if (updateMaxLine) { cm.curOp.updateMaxLine = true }
5764 if (marker.collapsed)
5765 { regChange(cm, from.line, to.line + 1) }
5766 else if (marker.className || marker.title || marker.startStyle || marker.end Style || marker.css)
5767 { for (var i = from.line; i <= to.line; i++) { regLineChange(cm, i, "text" ) } }
5768 if (marker.atomic) { reCheckSelection(cm.doc) }
5769 signalLater(cm, "markerAdded", cm, marker)
5770 }
5771 return marker
5772 }
5773
5774 // SHARED TEXTMARKERS
5775
5776 // A shared marker spans multiple linked documents. It is
5777 // implemented as a meta-marker-object controlling multiple normal
5778 // markers.
5779 var SharedTextMarker = function SharedTextMarker(markers, primary) {
5780 var this$1 = this;
5781
5782 this.markers = markers
5783 this.primary = primary
5784 for (var i = 0; i < markers.length; ++i)
5785 { markers[i].parent = this$1 }
5786 };
5787
5788 SharedTextMarker.prototype.clear = function clear () {
5789 var this$1 = this;
5790
5791 if (this.explicitlyCleared) { return }
5792 this.explicitlyCleared = true
5793 for (var i = 0; i < this.markers.length; ++i)
5794 { this$1.markers[i].clear() }
5795 signalLater(this, "clear")
5796 };
5797
5798 SharedTextMarker.prototype.find = function find (side, lineObj) {
5799 return this.primary.find(side, lineObj)
5800 };
5801 eventMixin(SharedTextMarker)
5802
5803 function markTextShared(doc, from, to, options, type) {
5804 options = copyObj(options)
5805 options.shared = false
5806 var markers = [markText(doc, from, to, options, type)], primary = markers[0]
5807 var widget = options.widgetNode
5808 linkedDocs(doc, function (doc) {
5809 if (widget) { options.widgetNode = widget.cloneNode(true) }
5810 markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, ty pe))
5811 for (var i = 0; i < doc.linked.length; ++i)
5812 { if (doc.linked[i].isParent) { return } }
5813 primary = lst(markers)
5814 })
5815 return new SharedTextMarker(markers, primary)
5816 }
5817
5818 function findSharedMarkers(doc) {
5819 return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), func tion (m) { return m.parent; })
5820 }
5821
5822 function copySharedMarkers(doc, markers) {
5823 for (var i = 0; i < markers.length; i++) {
5824 var marker = markers[i], pos = marker.find()
5825 var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to)
5826 if (cmp(mFrom, mTo)) {
5827 var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.typ e)
5828 marker.markers.push(subMark)
5829 subMark.parent = marker
5830 }
5831 }
5832 }
5833
5834 function detachSharedMarkers(markers) {
5835 var loop = function ( i ) {
5836 var marker = markers[i], linked = [marker.primary.doc]
5837 linkedDocs(marker.primary.doc, function (d) { return linked.push(d); })
5838 for (var j = 0; j < marker.markers.length; j++) {
5839 var subMarker = marker.markers[j]
5840 if (indexOf(linked, subMarker.doc) == -1) {
5841 subMarker.parent = null
5842 marker.markers.splice(j--, 1)
5843 }
5844 }
5845 };
5846
5847 for (var i = 0; i < markers.length; i++) loop( i );
5848 }
5849
5850 var nextDocId = 0
5851 var Doc = function(text, mode, firstLine, lineSep, direction) {
5852 if (!(this instanceof Doc)) { return new Doc(text, mode, firstLine, lineSep, d irection) }
5853 if (firstLine == null) { firstLine = 0 }
5854
5855 BranchChunk.call(this, [new LeafChunk([new Line("", null)])])
5856 this.first = firstLine
5857 this.scrollTop = this.scrollLeft = 0
5858 this.cantEdit = false
5859 this.cleanGeneration = 1
5860 this.frontier = firstLine
5861 var start = Pos(firstLine, 0)
5862 this.sel = simpleSelection(start)
5863 this.history = new History(null)
5864 this.id = ++nextDocId
5865 this.modeOption = mode
5866 this.lineSep = lineSep
5867 this.direction = (direction == "rtl") ? "rtl" : "ltr"
5868 this.extend = false
5869
5870 if (typeof text == "string") { text = this.splitLines(text) }
5871 updateDoc(this, {from: start, to: start, text: text})
5872 setSelection(this, simpleSelection(start), sel_dontScroll)
5873 }
5874
5875 Doc.prototype = createObj(BranchChunk.prototype, {
5876 constructor: Doc,
5877 // Iterate over the document. Supports two forms -- with only one
5878 // argument, it calls that for each line in the document. With
5879 // three, it iterates over the range given by the first two (with
5880 // the second being non-inclusive).
5881 iter: function(from, to, op) {
5882 if (op) { this.iterN(from - this.first, to - from, op) }
5883 else { this.iterN(this.first, this.first + this.size, from) }
5884 },
5885
5886 // Non-public interface for adding and removing lines.
5887 insert: function(at, lines) {
5888 var height = 0
5889 for (var i = 0; i < lines.length; ++i) { height += lines[i].height }
5890 this.insertInner(at - this.first, lines, height)
5891 },
5892 remove: function(at, n) { this.removeInner(at - this.first, n) },
5893
5894 // From here, the methods are part of the public interface. Most
5895 // are also available from CodeMirror (editor) instances.
5896
5897 getValue: function(lineSep) {
5898 var lines = getLines(this, this.first, this.first + this.size)
5899 if (lineSep === false) { return lines }
5900 return lines.join(lineSep || this.lineSeparator())
5901 },
5902 setValue: docMethodOp(function(code) {
5903 var top = Pos(this.first, 0), last = this.first + this.size - 1
5904 makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
5905 text: this.splitLines(code), origin: "setValue", full: tru e}, true)
5906 setSelection(this, simpleSelection(top))
5907 }),
5908 replaceRange: function(code, from, to, origin) {
5909 from = clipPos(this, from)
5910 to = to ? clipPos(this, to) : from
5911 replaceRange(this, code, from, to, origin)
5912 },
5913 getRange: function(from, to, lineSep) {
5914 var lines = getBetween(this, clipPos(this, from), clipPos(this, to))
5915 if (lineSep === false) { return lines }
5916 return lines.join(lineSep || this.lineSeparator())
5917 },
5918
5919 getLine: function(line) {var l = this.getLineHandle(line); return l && l.text} ,
5920
5921 getLineHandle: function(line) {if (isLine(this, line)) { return getLine(this, line) }},
5922 getLineNumber: function(line) {return lineNo(line)},
5923
5924 getLineHandleVisualStart: function(line) {
5925 if (typeof line == "number") { line = getLine(this, line) }
5926 return visualLine(line)
5927 },
5928
5929 lineCount: function() {return this.size},
5930 firstLine: function() {return this.first},
5931 lastLine: function() {return this.first + this.size - 1},
5932
5933 clipPos: function(pos) {return clipPos(this, pos)},
5934
5935 getCursor: function(start) {
5936 var range = this.sel.primary(), pos
5937 if (start == null || start == "head") { pos = range.head }
5938 else if (start == "anchor") { pos = range.anchor }
5939 else if (start == "end" || start == "to" || start === false) { pos = range.t o() }
5940 else { pos = range.from() }
5941 return pos
5942 },
5943 listSelections: function() { return this.sel.ranges },
5944 somethingSelected: function() {return this.sel.somethingSelected()},
5945
5946 setCursor: docMethodOp(function(line, ch, options) {
5947 setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, c h || 0) : line), null, options)
5948 }),
5949 setSelection: docMethodOp(function(anchor, head, options) {
5950 setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor ), options)
5951 }),
5952 extendSelection: docMethodOp(function(head, other, options) {
5953 extendSelection(this, clipPos(this, head), other && clipPos(this, other), op tions)
5954 }),
5955 extendSelections: docMethodOp(function(heads, options) {
5956 extendSelections(this, clipPosArray(this, heads), options)
5957 }),
5958 extendSelectionsBy: docMethodOp(function(f, options) {
5959 var heads = map(this.sel.ranges, f)
5960 extendSelections(this, clipPosArray(this, heads), options)
5961 }),
5962 setSelections: docMethodOp(function(ranges, primary, options) {
5963 var this$1 = this;
5964
5965 if (!ranges.length) { return }
5966 var out = []
5967 for (var i = 0; i < ranges.length; i++)
5968 { out[i] = new Range(clipPos(this$1, ranges[i].anchor),
5969 clipPos(this$1, ranges[i].head)) }
5970 if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIn dex) }
5971 setSelection(this, normalizeSelection(out, primary), options)
5972 }),
5973 addSelection: docMethodOp(function(anchor, head, options) {
5974 var ranges = this.sel.ranges.slice(0)
5975 ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)))
5976 setSelection(this, normalizeSelection(ranges, ranges.length - 1), options)
5977 }),
5978
5979 getSelection: function(lineSep) {
5980 var this$1 = this;
5981
5982 var ranges = this.sel.ranges, lines
5983 for (var i = 0; i < ranges.length; i++) {
5984 var sel = getBetween(this$1, ranges[i].from(), ranges[i].to())
5985 lines = lines ? lines.concat(sel) : sel
5986 }
5987 if (lineSep === false) { return lines }
5988 else { return lines.join(lineSep || this.lineSeparator()) }
5989 },
5990 getSelections: function(lineSep) {
5991 var this$1 = this;
5992
5993 var parts = [], ranges = this.sel.ranges
5994 for (var i = 0; i < ranges.length; i++) {
5995 var sel = getBetween(this$1, ranges[i].from(), ranges[i].to())
5996 if (lineSep !== false) { sel = sel.join(lineSep || this$1.lineSeparator()) }
5997 parts[i] = sel
5998 }
5999 return parts
6000 },
6001 replaceSelection: function(code, collapse, origin) {
6002 var dup = []
6003 for (var i = 0; i < this.sel.ranges.length; i++)
6004 { dup[i] = code }
6005 this.replaceSelections(dup, collapse, origin || "+input")
6006 },
6007 replaceSelections: docMethodOp(function(code, collapse, origin) {
6008 var this$1 = this;
6009
6010 var changes = [], sel = this.sel
6011 for (var i = 0; i < sel.ranges.length; i++) {
6012 var range = sel.ranges[i]
6013 changes[i] = {from: range.from(), to: range.to(), text: this$1.splitLines( code[i]), origin: origin}
6014 }
6015 var newSel = collapse && collapse != "end" && computeReplacedSel(this, chang es, collapse)
6016 for (var i$1 = changes.length - 1; i$1 >= 0; i$1--)
6017 { makeChange(this$1, changes[i$1]) }
6018 if (newSel) { setSelectionReplaceHistory(this, newSel) }
6019 else if (this.cm) { ensureCursorVisible(this.cm) }
6020 }),
6021 undo: docMethodOp(function() {makeChangeFromHistory(this, "undo")}),
6022 redo: docMethodOp(function() {makeChangeFromHistory(this, "redo")}),
6023 undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", tru e)}),
6024 redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", tru e)}),
6025
6026 setExtending: function(val) {this.extend = val},
6027 getExtending: function() {return this.extend},
6028
6029 historySize: function() {
6030 var hist = this.history, done = 0, undone = 0
6031 for (var i = 0; i < hist.done.length; i++) { if (!hist.done[i].ranges) { ++d one } }
6032 for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].r anges) { ++undone } }
6033 return {undo: done, redo: undone}
6034 },
6035 clearHistory: function() {this.history = new History(this.history.maxGeneratio n)},
6036
6037 markClean: function() {
6038 this.cleanGeneration = this.changeGeneration(true)
6039 },
6040 changeGeneration: function(forceSplit) {
6041 if (forceSplit)
6042 { this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null }
6043 return this.history.generation
6044 },
6045 isClean: function (gen) {
6046 return this.history.generation == (gen || this.cleanGeneration)
6047 },
6048
6049 getHistory: function() {
6050 return {done: copyHistoryArray(this.history.done),
6051 undone: copyHistoryArray(this.history.undone)}
6052 },
6053 setHistory: function(histData) {
6054 var hist = this.history = new History(this.history.maxGeneration)
6055 hist.done = copyHistoryArray(histData.done.slice(0), null, true)
6056 hist.undone = copyHistoryArray(histData.undone.slice(0), null, true)
6057 },
6058
6059 setGutterMarker: docMethodOp(function(line, gutterID, value) {
6060 return changeLine(this, line, "gutter", function (line) {
6061 var markers = line.gutterMarkers || (line.gutterMarkers = {})
6062 markers[gutterID] = value
6063 if (!value && isEmpty(markers)) { line.gutterMarkers = null }
6064 return true
6065 })
6066 }),
6067
6068 clearGutter: docMethodOp(function(gutterID) {
6069 var this$1 = this;
6070
6071 this.iter(function (line) {
6072 if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
6073 changeLine(this$1, line, "gutter", function () {
6074 line.gutterMarkers[gutterID] = null
6075 if (isEmpty(line.gutterMarkers)) { line.gutterMarkers = null }
6076 return true
6077 })
6078 }
6079 })
6080 }),
6081
6082 lineInfo: function(line) {
6083 var n
6084 if (typeof line == "number") {
6085 if (!isLine(this, line)) { return null }
6086 n = line
6087 line = getLine(this, line)
6088 if (!line) { return null }
6089 } else {
6090 n = lineNo(line)
6091 if (n == null) { return null }
6092 }
6093 return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMa rkers,
6094 textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wr apClass,
6095 widgets: line.widgets}
6096 },
6097
6098 addLineClass: docMethodOp(function(handle, where, cls) {
6099 return changeLine(this, handle, where == "gutter" ? "gutter" : "class", func tion (line) {
6100 var prop = where == "text" ? "textClass"
6101 : where == "background" ? "bgClass"
6102 : where == "gutter" ? "gutterClass" : "wrapClass"
6103 if (!line[prop]) { line[prop] = cls }
6104 else if (classTest(cls).test(line[prop])) { return false }
6105 else { line[prop] += " " + cls }
6106 return true
6107 })
6108 }),
6109 removeLineClass: docMethodOp(function(handle, where, cls) {
6110 return changeLine(this, handle, where == "gutter" ? "gutter" : "class", func tion (line) {
6111 var prop = where == "text" ? "textClass"
6112 : where == "background" ? "bgClass"
6113 : where == "gutter" ? "gutterClass" : "wrapClass"
6114 var cur = line[prop]
6115 if (!cur) { return false }
6116 else if (cls == null) { line[prop] = null }
6117 else {
6118 var found = cur.match(classTest(cls))
6119 if (!found) { return false }
6120 var end = found.index + found[0].length
6121 line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.len gth ? "" : " ") + cur.slice(end) || null
6122 }
6123 return true
6124 })
6125 }),
6126
6127 addLineWidget: docMethodOp(function(handle, node, options) {
6128 return addLineWidget(this, handle, node, options)
6129 }),
6130 removeLineWidget: function(widget) { widget.clear() },
6131
6132 markText: function(from, to, options) {
6133 return markText(this, clipPos(this, from), clipPos(this, to), options, optio ns && options.type || "range")
6134 },
6135 setBookmark: function(pos, options) {
6136 var realOpts = {replacedWith: options && (options.nodeType == null ? options .widget : options),
6137 insertLeft: options && options.insertLeft,
6138 clearWhenEmpty: false, shared: options && options.shared,
6139 handleMouseEvents: options && options.handleMouseEvents}
6140 pos = clipPos(this, pos)
6141 return markText(this, pos, pos, realOpts, "bookmark")
6142 },
6143 findMarksAt: function(pos) {
6144 pos = clipPos(this, pos)
6145 var markers = [], spans = getLine(this, pos.line).markedSpans
6146 if (spans) { for (var i = 0; i < spans.length; ++i) {
6147 var span = spans[i]
6148 if ((span.from == null || span.from <= pos.ch) &&
6149 (span.to == null || span.to >= pos.ch))
6150 { markers.push(span.marker.parent || span.marker) }
6151 } }
6152 return markers
6153 },
6154 findMarks: function(from, to, filter) {
6155 from = clipPos(this, from); to = clipPos(this, to)
6156 var found = [], lineNo = from.line
6157 this.iter(from.line, to.line + 1, function (line) {
6158 var spans = line.markedSpans
6159 if (spans) { for (var i = 0; i < spans.length; i++) {
6160 var span = spans[i]
6161 if (!(span.to != null && lineNo == from.line && from.ch >= span.to ||
6162 span.from == null && lineNo != from.line ||
6163 span.from != null && lineNo == to.line && span.from >= to.ch) &&
6164 (!filter || filter(span.marker)))
6165 { found.push(span.marker.parent || span.marker) }
6166 } }
6167 ++lineNo
6168 })
6169 return found
6170 },
6171 getAllMarks: function() {
6172 var markers = []
6173 this.iter(function (line) {
6174 var sps = line.markedSpans
6175 if (sps) { for (var i = 0; i < sps.length; ++i)
6176 { if (sps[i].from != null) { markers.push(sps[i].marker) } } }
6177 })
6178 return markers
6179 },
6180
6181 posFromIndex: function(off) {
6182 var ch, lineNo = this.first, sepSize = this.lineSeparator().length
6183 this.iter(function (line) {
6184 var sz = line.text.length + sepSize
6185 if (sz > off) { ch = off; return true }
6186 off -= sz
6187 ++lineNo
6188 })
6189 return clipPos(this, Pos(lineNo, ch))
6190 },
6191 indexFromPos: function (coords) {
6192 coords = clipPos(this, coords)
6193 var index = coords.ch
6194 if (coords.line < this.first || coords.ch < 0) { return 0 }
6195 var sepSize = this.lineSeparator().length
6196 this.iter(this.first, coords.line, function (line) { // iter aborts when cal lback returns a truthy value
6197 index += line.text.length + sepSize
6198 })
6199 return index
6200 },
6201
6202 copy: function(copyHistory) {
6203 var doc = new Doc(getLines(this, this.first, this.first + this.size),
6204 this.modeOption, this.first, this.lineSep, this.direction)
6205 doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft
6206 doc.sel = this.sel
6207 doc.extend = false
6208 if (copyHistory) {
6209 doc.history.undoDepth = this.history.undoDepth
6210 doc.setHistory(this.getHistory())
6211 }
6212 return doc
6213 },
6214
6215 linkedDoc: function(options) {
6216 if (!options) { options = {} }
6217 var from = this.first, to = this.first + this.size
6218 if (options.from != null && options.from > from) { from = options.from }
6219 if (options.to != null && options.to < to) { to = options.to }
6220 var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption , from, this.lineSep, this.direction)
6221 if (options.sharedHist) { copy.history = this.history
6222 ; }(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options. sharedHist})
6223 copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}]
6224 copySharedMarkers(copy, findSharedMarkers(this))
6225 return copy
6226 },
6227 unlinkDoc: function(other) {
6228 var this$1 = this;
6229
6230 if (other instanceof CodeMirror) { other = other.doc }
6231 if (this.linked) { for (var i = 0; i < this.linked.length; ++i) {
6232 var link = this$1.linked[i]
6233 if (link.doc != other) { continue }
6234 this$1.linked.splice(i, 1)
6235 other.unlinkDoc(this$1)
6236 detachSharedMarkers(findSharedMarkers(this$1))
6237 break
6238 } }
6239 // If the histories were shared, split them again
6240 if (other.history == this.history) {
6241 var splitIds = [other.id]
6242 linkedDocs(other, function (doc) { return splitIds.push(doc.id); }, true)
6243 other.history = new History(null)
6244 other.history.done = copyHistoryArray(this.history.done, splitIds)
6245 other.history.undone = copyHistoryArray(this.history.undone, splitIds)
6246 }
6247 },
6248 iterLinkedDocs: function(f) {linkedDocs(this, f)},
6249
6250 getMode: function() {return this.mode},
6251 getEditor: function() {return this.cm},
6252
6253 splitLines: function(str) {
6254 if (this.lineSep) { return str.split(this.lineSep) }
6255 return splitLinesAuto(str)
6256 },
6257 lineSeparator: function() { return this.lineSep || "\n" },
6258
6259 setDirection: docMethodOp(function (dir) {
6260 if (dir != "rtl") { dir = "ltr" }
6261 if (dir == this.direction) { return }
6262 this.direction = dir
6263 this.iter(function (line) { return line.order = null; })
6264 if (this.cm) { directionChanged(this.cm) }
6265 })
6266 })
6267
6268 // Public alias.
6269 Doc.prototype.eachLine = Doc.prototype.iter
6270
6271 // Kludge to work around strange IE behavior where it'll sometimes
6272 // re-fire a series of drag-related events right after the drop (#1551)
6273 var lastDrop = 0
6274
6275 function onDrop(e) {
6276 var cm = this
6277 clearDragCursor(cm)
6278 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))
6279 { return }
6280 e_preventDefault(e)
6281 if (ie) { lastDrop = +new Date }
6282 var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files
6283 if (!pos || cm.isReadOnly()) { return }
6284 // Might be a file drop, in which case we simply extract the text
6285 // and insert it.
6286 if (files && files.length && window.FileReader && window.File) {
6287 var n = files.length, text = Array(n), read = 0
6288 var loadFile = function (file, i) {
6289 if (cm.options.allowDropFileTypes &&
6290 indexOf(cm.options.allowDropFileTypes, file.type) == -1)
6291 { return }
6292
6293 var reader = new FileReader
6294 reader.onload = operation(cm, function () {
6295 var content = reader.result
6296 if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) { content = "" }
6297 text[i] = content
6298 if (++read == n) {
6299 pos = clipPos(cm.doc, pos)
6300 var change = {from: pos, to: pos,
6301 text: cm.doc.splitLines(text.join(cm.doc.lineSeparator() )),
6302 origin: "paste"}
6303 makeChange(cm.doc, change)
6304 setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(chan ge)))
6305 }
6306 })
6307 reader.readAsText(file)
6308 }
6309 for (var i = 0; i < n; ++i) { loadFile(files[i], i) }
6310 } else { // Normal drop
6311 // Don't do a replace if the drop happened inside of the selected text.
6312 if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {
6313 cm.state.draggingText(e)
6314 // Ensure the editor is re-focused
6315 setTimeout(function () { return cm.display.input.focus(); }, 20)
6316 return
6317 }
6318 try {
6319 var text$1 = e.dataTransfer.getData("Text")
6320 if (text$1) {
6321 var selected
6322 if (cm.state.draggingText && !cm.state.draggingText.copy)
6323 { selected = cm.listSelections() }
6324 setSelectionNoUndo(cm.doc, simpleSelection(pos, pos))
6325 if (selected) { for (var i$1 = 0; i$1 < selected.length; ++i$1)
6326 { replaceRange(cm.doc, "", selected[i$1].anchor, selected[i$1].head, " drag") } }
6327 cm.replaceSelection(text$1, "around", "paste")
6328 cm.display.input.focus()
6329 }
6330 }
6331 catch(e){}
6332 }
6333 }
6334
6335 function onDragStart(cm, e) {
6336 if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return }
6337 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return }
6338
6339 e.dataTransfer.setData("Text", cm.getSelection())
6340 e.dataTransfer.effectAllowed = "copyMove"
6341
6342 // Use dummy image instead of default browsers image.
6343 // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
6344 if (e.dataTransfer.setDragImage && !safari) {
6345 var img = elt("img", null, null, "position: fixed; left: 0; top: 0;")
6346 img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAI CTAEAOw=="
6347 if (presto) {
6348 img.width = img.height = 1
6349 cm.display.wrapper.appendChild(img)
6350 // Force a relayout, or Opera won't use our image for some obscure reason
6351 img._top = img.offsetTop
6352 }
6353 e.dataTransfer.setDragImage(img, 0, 0)
6354 if (presto) { img.parentNode.removeChild(img) }
6355 }
6356 }
6357
6358 function onDragOver(cm, e) {
6359 var pos = posFromMouse(cm, e)
6360 if (!pos) { return }
6361 var frag = document.createDocumentFragment()
6362 drawSelectionCursor(cm, pos, frag)
6363 if (!cm.display.dragCursor) {
6364 cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-drag cursors")
6365 cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDi v)
6366 }
6367 removeChildrenAndAdd(cm.display.dragCursor, frag)
6368 }
6369
6370 function clearDragCursor(cm) {
6371 if (cm.display.dragCursor) {
6372 cm.display.lineSpace.removeChild(cm.display.dragCursor)
6373 cm.display.dragCursor = null
6374 }
6375 }
6376
6377 // These must be handled carefully, because naively registering a
6378 // handler for each editor will cause the editors to never be
6379 // garbage collected.
6380
6381 function forEachCodeMirror(f) {
6382 if (!document.body.getElementsByClassName) { return }
6383 var byClass = document.body.getElementsByClassName("CodeMirror")
6384 for (var i = 0; i < byClass.length; i++) {
6385 var cm = byClass[i].CodeMirror
6386 if (cm) { f(cm) }
6387 }
6388 }
6389
6390 var globalsRegistered = false
6391 function ensureGlobalHandlers() {
6392 if (globalsRegistered) { return }
6393 registerGlobalHandlers()
6394 globalsRegistered = true
6395 }
6396 function registerGlobalHandlers() {
6397 // When the window resizes, we need to refresh active editors.
6398 var resizeTimer
6399 on(window, "resize", function () {
6400 if (resizeTimer == null) { resizeTimer = setTimeout(function () {
6401 resizeTimer = null
6402 forEachCodeMirror(onResize)
6403 }, 100) }
6404 })
6405 // When the window loses focus, we want to show the editor as blurred
6406 on(window, "blur", function () { return forEachCodeMirror(onBlur); })
6407 }
6408 // Called when the window resizes
6409 function onResize(cm) {
6410 var d = cm.display
6411 if (d.lastWrapHeight == d.wrapper.clientHeight && d.lastWrapWidth == d.wrapper .clientWidth)
6412 { return }
6413 // Might be a text scaling operation, clear size caches.
6414 d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null
6415 d.scrollbarsClipped = false
6416 cm.setSize()
6417 }
6418
6419 var keyNames = {
6420 3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18 : "Alt",
6421 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDo wn", 35: "End",
6422 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45 : "Insert",
6423 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod",
6424 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 127: "Delete",
6425 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`" , 219: "[", 220: "\\",
6426 221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete",
6427 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Inser t"
6428 }
6429
6430 // Number keys
6431 for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i) }
6432 // Alphabetic keys
6433 for (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1) }
6434 // Function keys
6435 for (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235 ] = "F" + i$2 }
6436
6437 var keyMap = {}
6438
6439 keyMap.basic = {
6440 "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLin eDown",
6441 "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDow n": "goPageDown",
6442 "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "de lCharBefore",
6443 "Tab": "defaultTab", "Shift-Tab": "indentAuto",
6444 "Enter": "newlineAndIndent", "Insert": "toggleOverwrite",
6445 "Esc": "singleSelection"
6446 }
6447 // Note that the save and find-related commands aren't defined by
6448 // default. User code or addons can define them. Unknown commands
6449 // are simply ignored.
6450 keyMap.pcDefault = {
6451 "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z ": "redo", "Ctrl-Y": "redo",
6452 "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctr l-Down": "goLineDown",
6453 "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineS tart", "Alt-Right": "goLineEnd",
6454 "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
6455 "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", " Shift-Ctrl-R": "replaceAll",
6456 "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
6457 "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSele ction",
6458 fallthrough: "basic"
6459 }
6460 // Very basic readline/emacs-style bindings, which are standard on Mac.
6461 keyMap.emacsy = {
6462 "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N ": "goLineDown",
6463 "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl- E": "goLineEnd",
6464 "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
6465 "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine ", "Ctrl-T": "transposeChars",
6466 "Ctrl-O": "openLine"
6467 }
6468 keyMap.macDefault = {
6469 "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": " redo", "Cmd-Y": "redo",
6470 "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd- Down": "goDocEnd", "Alt-Left": "goGroupLeft",
6471 "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRig ht", "Alt-Backspace": "delGroupBefore",
6472 "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
6473 "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift -Cmd-Alt-F": "replaceAll",
6474 "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLine Left", "Cmd-Delete": "delWrappedLineRight",
6475 "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocSta rt", "Ctrl-Down": "goDocEnd",
6476 fallthrough: ["basic", "emacsy"]
6477 }
6478 keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault
6479
6480 // KEYMAP DISPATCH
6481
6482 function normalizeKeyName(name) {
6483 var parts = name.split(/-(?!$)/)
6484 name = parts[parts.length - 1]
6485 var alt, ctrl, shift, cmd
6486 for (var i = 0; i < parts.length - 1; i++) {
6487 var mod = parts[i]
6488 if (/^(cmd|meta|m)$/i.test(mod)) { cmd = true }
6489 else if (/^a(lt)?$/i.test(mod)) { alt = true }
6490 else if (/^(c|ctrl|control)$/i.test(mod)) { ctrl = true }
6491 else if (/^s(hift)?$/i.test(mod)) { shift = true }
6492 else { throw new Error("Unrecognized modifier name: " + mod) }
6493 }
6494 if (alt) { name = "Alt-" + name }
6495 if (ctrl) { name = "Ctrl-" + name }
6496 if (cmd) { name = "Cmd-" + name }
6497 if (shift) { name = "Shift-" + name }
6498 return name
6499 }
6500
6501 // This is a kludge to keep keymaps mostly working as raw objects
6502 // (backwards compatibility) while at the same time support features
6503 // like normalization and multi-stroke key bindings. It compiles a
6504 // new normalized keymap, and then updates the old object to reflect
6505 // this.
6506 function normalizeKeyMap(keymap) {
6507 var copy = {}
6508 for (var keyname in keymap) { if (keymap.hasOwnProperty(keyname)) {
6509 var value = keymap[keyname]
6510 if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) { continue }
6511 if (value == "...") { delete keymap[keyname]; continue }
6512
6513 var keys = map(keyname.split(" "), normalizeKeyName)
6514 for (var i = 0; i < keys.length; i++) {
6515 var val = (void 0), name = (void 0)
6516 if (i == keys.length - 1) {
6517 name = keys.join(" ")
6518 val = value
1797 } else { 6519 } else {
1798 var fromLine = lineNo(display.view[fromIndex].line); 6520 name = keys.slice(0, i + 1).join(" ")
1799 var fromNode = display.view[fromIndex - 1].node.nextSibling; 6521 val = "..."
1800 } 6522 }
1801 var toIndex = findViewIndex(cm, to.line); 6523 var prev = copy[name]
1802 if (toIndex == display.view.length - 1) { 6524 if (!prev) { copy[name] = val }
1803 var toLine = display.viewTo - 1; 6525 else if (prev != val) { throw new Error("Inconsistent bindings for " + nam e) }
1804 var toNode = display.lineDiv.lastChild; 6526 }
1805 } else { 6527 delete keymap[keyname]
1806 var toLine = lineNo(display.view[toIndex + 1].line) - 1; 6528 } }
1807 var toNode = display.view[toIndex + 1].node.previousSibling; 6529 for (var prop in copy) { keymap[prop] = copy[prop] }
6530 return keymap
6531 }
6532
6533 function lookupKey(key, map, handle, context) {
6534 map = getKeyMap(map)
6535 var found = map.call ? map.call(key, context) : map[key]
6536 if (found === false) { return "nothing" }
6537 if (found === "...") { return "multi" }
6538 if (found != null && handle(found)) { return "handled" }
6539
6540 if (map.fallthrough) {
6541 if (Object.prototype.toString.call(map.fallthrough) != "[object Array]")
6542 { return lookupKey(key, map.fallthrough, handle, context) }
6543 for (var i = 0; i < map.fallthrough.length; i++) {
6544 var result = lookupKey(key, map.fallthrough[i], handle, context)
6545 if (result) { return result }
6546 }
6547 }
6548 }
6549
6550 // Modifier key presses don't count as 'real' key presses for the
6551 // purpose of keymap fallthrough.
6552 function isModifierKey(value) {
6553 var name = typeof value == "string" ? value : keyNames[value.keyCode]
6554 return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod"
6555 }
6556
6557 // Look up the name of a key as indicated by an event object.
6558 function keyName(event, noShift) {
6559 if (presto && event.keyCode == 34 && event["char"]) { return false }
6560 var base = keyNames[event.keyCode], name = base
6561 if (name == null || event.altGraphKey) { return false }
6562 if (event.altKey && base != "Alt") { name = "Alt-" + name }
6563 if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") { name = "Ctrl-" + name }
6564 if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") { name = " Cmd-" + name }
6565 if (!noShift && event.shiftKey && base != "Shift") { name = "Shift-" + name }
6566 return name
6567 }
6568
6569 function getKeyMap(val) {
6570 return typeof val == "string" ? keyMap[val] : val
6571 }
6572
6573 // Helper for deleting text near the selection(s), used to implement
6574 // backspace, delete, and similar functionality.
6575 function deleteNearSelection(cm, compute) {
6576 var ranges = cm.doc.sel.ranges, kill = []
6577 // Build up a set of ranges to kill first, merging overlapping
6578 // ranges.
6579 for (var i = 0; i < ranges.length; i++) {
6580 var toKill = compute(ranges[i])
6581 while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {
6582 var replaced = kill.pop()
6583 if (cmp(replaced.from, toKill.from) < 0) {
6584 toKill.from = replaced.from
6585 break
1808 } 6586 }
1809 6587 }
1810 var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromL ine, toLine)); 6588 kill.push(toKill)
1811 var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm. doc, toLine).text.length)); 6589 }
1812 while (newText.length > 1 && oldText.length > 1) { 6590 // Next, remove those actual ranges.
1813 if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine --; } 6591 runInOp(cm, function () {
1814 else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); f romLine++; } 6592 for (var i = kill.length - 1; i >= 0; i--)
1815 else break; 6593 { replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete") }
1816 } 6594 ensureCursorVisible(cm)
1817 6595 })
1818 var cutFront = 0, cutEnd = 0; 6596 }
1819 var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTo p.length, oldTop.length); 6597
1820 while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.cha rCodeAt(cutFront)) 6598 // Commands are parameter-less actions that can be performed on an
1821 ++cutFront; 6599 // editor, mostly used for keybindings.
1822 var newBot = lst(newText), oldBot = lst(oldText); 6600 var commands = {
1823 var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0), 6601 selectAll: selectAll,
1824 oldBot.length - (oldText.length == 1 ? cutFront : 0)); 6602 singleSelection: function (cm) { return cm.setSelection(cm.getCursor("anchor") , cm.getCursor("head"), sel_dontScroll); },
1825 while (cutEnd < maxCutEnd && 6603 killLine: function (cm) { return deleteNearSelection(cm, function (range) {
1826 newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt( oldBot.length - cutEnd - 1)) 6604 if (range.empty()) {
1827 ++cutEnd; 6605 var len = getLine(cm.doc, range.head.line).text.length
1828 6606 if (range.head.ch == len && range.head.line < cm.lastLine())
1829 newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd); 6607 { return {from: range.head, to: Pos(range.head.line + 1, 0)} }
1830 newText[0] = newText[0].slice(cutFront); 6608 else
1831 6609 { return {from: range.head, to: Pos(range.head.line, len)} }
1832 var chFrom = Pos(fromLine, cutFront);
1833 var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0);
1834 if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {
1835 replaceRange(cm.doc, newText, chFrom, chTo, "+input");
1836 return true;
1837 }
1838 },
1839
1840 ensurePolled: function() {
1841 this.forceCompositionEnd();
1842 },
1843 reset: function() {
1844 this.forceCompositionEnd();
1845 },
1846 forceCompositionEnd: function() {
1847 if (!this.composing || this.composing.handled) return;
1848 this.applyComposition(this.composing);
1849 this.composing.handled = true;
1850 this.div.blur();
1851 this.div.focus();
1852 },
1853 applyComposition: function(composing) {
1854 if (this.cm.isReadOnly())
1855 operation(this.cm, regChange)(this.cm)
1856 else if (composing.data && composing.data != composing.startData)
1857 operation(this.cm, applyTextInput)(this.cm, composing.data, 0, composing .sel);
1858 },
1859
1860 setUneditable: function(node) {
1861 node.contentEditable = "false"
1862 },
1863
1864 onKeyPress: function(e) {
1865 e.preventDefault();
1866 if (!this.cm.isReadOnly())
1867 operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCo de == null ? e.keyCode : e.charCode), 0);
1868 },
1869
1870 readOnlyChanged: function(val) {
1871 this.div.contentEditable = String(val != "nocursor")
1872 },
1873
1874 onContextMenu: nothing,
1875 resetPosition: nothing,
1876
1877 needsContentAttribute: true
1878 }, ContentEditableInput.prototype);
1879
1880 function posToDOM(cm, pos) {
1881 var view = findViewForLine(cm, pos.line);
1882 if (!view || view.hidden) return null;
1883 var line = getLine(cm.doc, pos.line);
1884 var info = mapFromLineView(view, line, pos.line);
1885
1886 var order = getOrder(line), side = "left";
1887 if (order) {
1888 var partPos = getBidiPartAt(order, pos.ch);
1889 side = partPos % 2 ? "right" : "left";
1890 }
1891 var result = nodeAndOffsetInLineMap(info.map, pos.ch, side);
1892 result.offset = result.collapse == "right" ? result.end : result.start;
1893 return result;
1894 }
1895
1896 function badPos(pos, bad) { if (bad) pos.bad = true; return pos; }
1897
1898 function domToPos(cm, node, offset) {
1899 var lineNode;
1900 if (node == cm.display.lineDiv) {
1901 lineNode = cm.display.lineDiv.childNodes[offset];
1902 if (!lineNode) return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) ;
1903 node = null; offset = 0;
1904 } else { 6610 } else {
1905 for (lineNode = node;; lineNode = lineNode.parentNode) { 6611 return {from: range.from(), to: range.to()}
1906 if (!lineNode || lineNode == cm.display.lineDiv) return null; 6612 }
1907 if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) br eak; 6613 }); },
1908 } 6614 deleteLine: function (cm) { return deleteNearSelection(cm, function (range) { return ({
1909 } 6615 from: Pos(range.from().line, 0),
1910 for (var i = 0; i < cm.display.view.length; i++) { 6616 to: clipPos(cm.doc, Pos(range.to().line + 1, 0))
1911 var lineView = cm.display.view[i]; 6617 }); }); },
1912 if (lineView.node == lineNode) 6618 delLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { return ({
1913 return locateNodeInLineView(lineView, node, offset); 6619 from: Pos(range.from().line, 0), to: range.from()
1914 } 6620 }); }); },
1915 } 6621 delWrappedLineLeft: function (cm) { return deleteNearSelection(cm, function (r ange) {
1916 6622 var top = cm.charCoords(range.head, "div").top + 5
1917 function locateNodeInLineView(lineView, node, offset) { 6623 var leftPos = cm.coordsChar({left: 0, top: top}, "div")
1918 var wrapper = lineView.text.firstChild, bad = false; 6624 return {from: leftPos, to: range.from()}
1919 if (!node || !contains(wrapper, node)) return badPos(Pos(lineNo(lineView.lin e), 0), true); 6625 }); },
1920 if (node == wrapper) { 6626 delWrappedLineRight: function (cm) { return deleteNearSelection(cm, function ( range) {
1921 bad = true; 6627 var top = cm.charCoords(range.head, "div").top + 5
1922 node = wrapper.childNodes[offset]; 6628 var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, to p: top}, "div")
1923 offset = 0; 6629 return {from: range.from(), to: rightPos }
1924 if (!node) { 6630 }); },
1925 var line = lineView.rest ? lst(lineView.rest) : lineView.line; 6631 undo: function (cm) { return cm.undo(); },
1926 return badPos(Pos(lineNo(line), line.text.length), bad); 6632 redo: function (cm) { return cm.redo(); },
1927 } 6633 undoSelection: function (cm) { return cm.undoSelection(); },
1928 } 6634 redoSelection: function (cm) { return cm.redoSelection(); },
1929 6635 goDocStart: function (cm) { return cm.extendSelection(Pos(cm.firstLine(), 0)); },
1930 var textNode = node.nodeType == 3 ? node : null, topNode = node; 6636 goDocEnd: function (cm) { return cm.extendSelection(Pos(cm.lastLine())); },
1931 if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) { 6637 goLineStart: function (cm) { return cm.extendSelectionsBy(function (range) { r eturn lineStart(cm, range.head.line); },
1932 textNode = node.firstChild; 6638 {origin: "+move", bias: 1}
1933 if (offset) offset = textNode.nodeValue.length; 6639 ); },
1934 } 6640 goLineStartSmart: function (cm) { return cm.extendSelectionsBy(function (range ) { return lineStartSmart(cm, range.head); },
1935 while (topNode.parentNode != wrapper) topNode = topNode.parentNode; 6641 {origin: "+move", bias: 1}
1936 var measure = lineView.measure, maps = measure.maps; 6642 ); },
1937 6643 goLineEnd: function (cm) { return cm.extendSelectionsBy(function (range) { ret urn lineEnd(cm, range.head.line); },
1938 function find(textNode, topNode, offset) { 6644 {origin: "+move", bias: -1}
1939 for (var i = -1; i < (maps ? maps.length : 0); i++) { 6645 ); },
1940 var map = i < 0 ? measure.map : maps[i]; 6646 goLineRight: function (cm) { return cm.extendSelectionsBy(function (range) {
1941 for (var j = 0; j < map.length; j += 3) { 6647 var top = cm.charCoords(range.head, "div").top + 5
1942 var curNode = map[j + 2]; 6648 return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div")
1943 if (curNode == textNode || curNode == topNode) { 6649 }, sel_move); },
1944 var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]); 6650 goLineLeft: function (cm) { return cm.extendSelectionsBy(function (range) {
1945 var ch = map[j] + offset; 6651 var top = cm.charCoords(range.head, "div").top + 5
1946 if (offset < 0 || curNode != textNode) ch = map[j + (offset ? 1 : 0) ]; 6652 return cm.coordsChar({left: 0, top: top}, "div")
1947 return Pos(line, ch); 6653 }, sel_move); },
6654 goLineLeftSmart: function (cm) { return cm.extendSelectionsBy(function (range) {
6655 var top = cm.charCoords(range.head, "div").top + 5
6656 var pos = cm.coordsChar({left: 0, top: top}, "div")
6657 if (pos.ch < cm.getLine(pos.line).search(/\S/)) { return lineStartSmart(cm, range.head) }
6658 return pos
6659 }, sel_move); },
6660 goLineUp: function (cm) { return cm.moveV(-1, "line"); },
6661 goLineDown: function (cm) { return cm.moveV(1, "line"); },
6662 goPageUp: function (cm) { return cm.moveV(-1, "page"); },
6663 goPageDown: function (cm) { return cm.moveV(1, "page"); },
6664 goCharLeft: function (cm) { return cm.moveH(-1, "char"); },
6665 goCharRight: function (cm) { return cm.moveH(1, "char"); },
6666 goColumnLeft: function (cm) { return cm.moveH(-1, "column"); },
6667 goColumnRight: function (cm) { return cm.moveH(1, "column"); },
6668 goWordLeft: function (cm) { return cm.moveH(-1, "word"); },
6669 goGroupRight: function (cm) { return cm.moveH(1, "group"); },
6670 goGroupLeft: function (cm) { return cm.moveH(-1, "group"); },
6671 goWordRight: function (cm) { return cm.moveH(1, "word"); },
6672 delCharBefore: function (cm) { return cm.deleteH(-1, "char"); },
6673 delCharAfter: function (cm) { return cm.deleteH(1, "char"); },
6674 delWordBefore: function (cm) { return cm.deleteH(-1, "word"); },
6675 delWordAfter: function (cm) { return cm.deleteH(1, "word"); },
6676 delGroupBefore: function (cm) { return cm.deleteH(-1, "group"); },
6677 delGroupAfter: function (cm) { return cm.deleteH(1, "group"); },
6678 indentAuto: function (cm) { return cm.indentSelection("smart"); },
6679 indentMore: function (cm) { return cm.indentSelection("add"); },
6680 indentLess: function (cm) { return cm.indentSelection("subtract"); },
6681 insertTab: function (cm) { return cm.replaceSelection("\t"); },
6682 insertSoftTab: function (cm) {
6683 var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize
6684 for (var i = 0; i < ranges.length; i++) {
6685 var pos = ranges[i].from()
6686 var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize)
6687 spaces.push(spaceStr(tabSize - col % tabSize))
6688 }
6689 cm.replaceSelections(spaces)
6690 },
6691 defaultTab: function (cm) {
6692 if (cm.somethingSelected()) { cm.indentSelection("add") }
6693 else { cm.execCommand("insertTab") }
6694 },
6695 // Swap the two chars left and right of each selection's head.
6696 // Move cursor behind the two swapped characters afterwards.
6697 //
6698 // Doesn't consider line feeds a character.
6699 // Doesn't scan more than one line above to find a character.
6700 // Doesn't do anything on an empty line.
6701 // Doesn't do anything with non-empty selections.
6702 transposeChars: function (cm) { return runInOp(cm, function () {
6703 var ranges = cm.listSelections(), newSel = []
6704 for (var i = 0; i < ranges.length; i++) {
6705 if (!ranges[i].empty()) { continue }
6706 var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text
6707 if (line) {
6708 if (cur.ch == line.length) { cur = new Pos(cur.line, cur.ch - 1) }
6709 if (cur.ch > 0) {
6710 cur = new Pos(cur.line, cur.ch + 1)
6711 cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),
6712 Pos(cur.line, cur.ch - 2), cur, "+transpose")
6713 } else if (cur.line > cm.doc.first) {
6714 var prev = getLine(cm.doc, cur.line - 1).text
6715 if (prev) {
6716 cur = new Pos(cur.line, 1)
6717 cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() +
6718 prev.charAt(prev.length - 1),
6719 Pos(cur.line - 1, prev.length - 1), cur, "+transpose ")
1948 } 6720 }
1949 } 6721 }
1950 } 6722 }
1951 } 6723 newSel.push(new Range(cur, cur))
1952 var found = find(textNode, topNode, offset); 6724 }
1953 if (found) return badPos(found, bad); 6725 cm.setSelections(newSel)
1954 6726 }); },
1955 // FIXME this is all really shaky. might handle the few cases it needs to ha ndle, but likely to cause problems 6727 newlineAndIndent: function (cm) { return runInOp(cm, function () {
1956 for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.l ength - offset : 0; after; after = after.nextSibling) { 6728 var sels = cm.listSelections()
1957 found = find(after, after.firstChild, 0); 6729 for (var i = sels.length - 1; i >= 0; i--)
1958 if (found) 6730 { cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+ input") }
1959 return badPos(Pos(found.line, found.ch - dist), bad); 6731 sels = cm.listSelections()
6732 for (var i$1 = 0; i$1 < sels.length; i$1++)
6733 { cm.indentLine(sels[i$1].from().line, null, true) }
6734 ensureCursorVisible(cm)
6735 }); },
6736 openLine: function (cm) { return cm.replaceSelection("\n", "start"); },
6737 toggleOverwrite: function (cm) { return cm.toggleOverwrite(); }
6738 }
6739
6740
6741 function lineStart(cm, lineN) {
6742 var line = getLine(cm.doc, lineN)
6743 var visual = visualLine(line)
6744 if (visual != line) { lineN = lineNo(visual) }
6745 return endOfLine(true, cm, visual, lineN, 1)
6746 }
6747 function lineEnd(cm, lineN) {
6748 var line = getLine(cm.doc, lineN)
6749 var visual = visualLineEnd(line)
6750 if (visual != line) { lineN = lineNo(visual) }
6751 return endOfLine(true, cm, line, lineN, -1)
6752 }
6753 function lineStartSmart(cm, pos) {
6754 var start = lineStart(cm, pos.line)
6755 var line = getLine(cm.doc, start.line)
6756 var order = getOrder(line, cm.doc.direction)
6757 if (!order || order[0].level == 0) {
6758 var firstNonWS = Math.max(0, line.text.search(/\S/))
6759 var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch
6760 return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky)
6761 }
6762 return start
6763 }
6764
6765 // Run a handler that was bound to a key.
6766 function doHandleBinding(cm, bound, dropShift) {
6767 if (typeof bound == "string") {
6768 bound = commands[bound]
6769 if (!bound) { return false }
6770 }
6771 // Ensure previous input has been read, so that the handler sees a
6772 // consistent view of the document
6773 cm.display.input.ensurePolled()
6774 var prevShift = cm.display.shift, done = false
6775 try {
6776 if (cm.isReadOnly()) { cm.state.suppressEdits = true }
6777 if (dropShift) { cm.display.shift = false }
6778 done = bound(cm) != Pass
6779 } finally {
6780 cm.display.shift = prevShift
6781 cm.state.suppressEdits = false
6782 }
6783 return done
6784 }
6785
6786 function lookupKeyForEditor(cm, name, handle) {
6787 for (var i = 0; i < cm.state.keyMaps.length; i++) {
6788 var result = lookupKey(name, cm.state.keyMaps[i], handle, cm)
6789 if (result) { return result }
6790 }
6791 return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm))
6792 || lookupKey(name, cm.options.keyMap, handle, cm)
6793 }
6794
6795 var stopSeq = new Delayed
6796 function dispatchKey(cm, name, e, handle) {
6797 var seq = cm.state.keySeq
6798 if (seq) {
6799 if (isModifierKey(name)) { return "handled" }
6800 stopSeq.set(50, function () {
6801 if (cm.state.keySeq == seq) {
6802 cm.state.keySeq = null
6803 cm.display.input.reset()
6804 }
6805 })
6806 name = seq + " " + name
6807 }
6808 var result = lookupKeyForEditor(cm, name, handle)
6809
6810 if (result == "multi")
6811 { cm.state.keySeq = name }
6812 if (result == "handled")
6813 { signalLater(cm, "keyHandled", cm, name, e) }
6814
6815 if (result == "handled" || result == "multi") {
6816 e_preventDefault(e)
6817 restartBlink(cm)
6818 }
6819
6820 if (seq && !result && /\'$/.test(name)) {
6821 e_preventDefault(e)
6822 return true
6823 }
6824 return !!result
6825 }
6826
6827 // Handle a key from the keydown event.
6828 function handleKeyBinding(cm, e) {
6829 var name = keyName(e, true)
6830 if (!name) { return false }
6831
6832 if (e.shiftKey && !cm.state.keySeq) {
6833 // First try to resolve full name (including 'Shift-'). Failing
6834 // that, see if there is a cursor-motion command (starting with
6835 // 'go') bound to the keyname without 'Shift-'.
6836 return dispatchKey(cm, "Shift-" + name, e, function (b) { return doHandleBin ding(cm, b, true); })
6837 || dispatchKey(cm, name, e, function (b) {
6838 if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
6839 { return doHandleBinding(cm, b) }
6840 })
6841 } else {
6842 return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b) ; })
6843 }
6844 }
6845
6846 // Handle a key from the keypress event
6847 function handleCharBinding(cm, e, ch) {
6848 return dispatchKey(cm, "'" + ch + "'", e, function (b) { return doHandleBindin g(cm, b, true); })
6849 }
6850
6851 var lastStoppedKey = null
6852 function onKeyDown(e) {
6853 var cm = this
6854 cm.curOp.focus = activeElt()
6855 if (signalDOMEvent(cm, e)) { return }
6856 // IE does strange things with escape.
6857 if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false }
6858 var code = e.keyCode
6859 cm.display.shift = code == 16 || e.shiftKey
6860 var handled = handleKeyBinding(cm, e)
6861 if (presto) {
6862 lastStoppedKey = handled ? code : null
6863 // Opera has no cut event... we try to at least catch the key combo
6864 if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey) )
6865 { cm.replaceSelection("", null, "cut") }
6866 }
6867
6868 // Turn mouse into crosshair when Alt is held on Mac.
6869 if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.classNam e))
6870 { showCrossHair(cm) }
6871 }
6872
6873 function showCrossHair(cm) {
6874 var lineDiv = cm.display.lineDiv
6875 addClass(lineDiv, "CodeMirror-crosshair")
6876
6877 function up(e) {
6878 if (e.keyCode == 18 || !e.altKey) {
6879 rmClass(lineDiv, "CodeMirror-crosshair")
6880 off(document, "keyup", up)
6881 off(document, "mouseover", up)
6882 }
6883 }
6884 on(document, "keyup", up)
6885 on(document, "mouseover", up)
6886 }
6887
6888 function onKeyUp(e) {
6889 if (e.keyCode == 16) { this.doc.sel.shift = false }
6890 signalDOMEvent(this, e)
6891 }
6892
6893 function onKeyPress(e) {
6894 var cm = this
6895 if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.a ltKey || mac && e.metaKey) { return }
6896 var keyCode = e.keyCode, charCode = e.charCode
6897 if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefa ult(e); return}
6898 if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) { retur n }
6899 var ch = String.fromCharCode(charCode == null ? keyCode : charCode)
6900 // Some browsers fire keypress events for backspace
6901 if (ch == "\x08") { return }
6902 if (handleCharBinding(cm, e, ch)) { return }
6903 cm.display.input.onKeyPress(e)
6904 }
6905
6906 // A mouse down can be a single click, double click, triple click,
6907 // start of selection drag, start of text drag, new cursor
6908 // (ctrl-click), rectangle drag (alt-drag), or xwin
6909 // middle-click-paste. Or it might be a click on something we should
6910 // not interfere with, such as a scrollbar or widget.
6911 function onMouseDown(e) {
6912 var cm = this, display = cm.display
6913 if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouc h()) { return }
6914 display.input.ensurePolled()
6915 display.shift = e.shiftKey
6916
6917 if (eventInWidget(display, e)) {
6918 if (!webkit) {
6919 // Briefly turn off draggability, to allow widgets to do
6920 // normal dragging things.
6921 display.scroller.draggable = false
6922 setTimeout(function () { return display.scroller.draggable = true; }, 100)
6923 }
6924 return
6925 }
6926 if (clickInGutter(cm, e)) { return }
6927 var start = posFromMouse(cm, e)
6928 window.focus()
6929
6930 switch (e_button(e)) {
6931 case 1:
6932 // #3261: make sure, that we're not starting a second selection
6933 if (cm.state.selectingText)
6934 { cm.state.selectingText(e) }
6935 else if (start)
6936 { leftButtonDown(cm, e, start) }
6937 else if (e_target(e) == display.scroller)
6938 { e_preventDefault(e) }
6939 break
6940 case 2:
6941 if (webkit) { cm.state.lastMiddleDown = +new Date }
6942 if (start) { extendSelection(cm.doc, start) }
6943 setTimeout(function () { return display.input.focus(); }, 20)
6944 e_preventDefault(e)
6945 break
6946 case 3:
6947 if (captureRightClick) { onContextMenu(cm, e) }
6948 else { delayBlurEvent(cm) }
6949 break
6950 }
6951 }
6952
6953 var lastClick;
6954 var lastDoubleClick;
6955 function leftButtonDown(cm, e, start) {
6956 if (ie) { setTimeout(bind(ensureFocus, cm), 0) }
6957 else { cm.curOp.focus = activeElt() }
6958
6959 var now = +new Date, type
6960 if (lastDoubleClick && lastDoubleClick.time > now - 400 && cmp(lastDoubleClick .pos, start) == 0) {
6961 type = "triple"
6962 } else if (lastClick && lastClick.time > now - 400 && cmp(lastClick.pos, start ) == 0) {
6963 type = "double"
6964 lastDoubleClick = {time: now, pos: start}
6965 } else {
6966 type = "single"
6967 lastClick = {time: now, pos: start}
6968 }
6969
6970 var sel = cm.doc.sel, modifier = mac ? e.metaKey : e.ctrlKey, contained
6971 if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() &&
6972 type == "single" && (contained = sel.contains(start)) > -1 &&
6973 (cmp((contained = sel.ranges[contained]).from(), start) < 0 || start.xRel > 0) &&
6974 (cmp(contained.to(), start) > 0 || start.xRel < 0))
6975 { leftButtonStartDrag(cm, e, start, modifier) }
6976 else
6977 { leftButtonSelect(cm, e, start, type, modifier) }
6978 }
6979
6980 // Start a text drag. When it ends, see if any dragging actually
6981 // happen, and treat as a click if it didn't.
6982 function leftButtonStartDrag(cm, e, start, modifier) {
6983 var display = cm.display, startTime = +new Date
6984 var dragEnd = operation(cm, function (e2) {
6985 if (webkit) { display.scroller.draggable = false }
6986 cm.state.draggingText = false
6987 off(document, "mouseup", dragEnd)
6988 off(display.scroller, "drop", dragEnd)
6989 if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10 ) {
6990 e_preventDefault(e2)
6991 if (!modifier && +new Date - 200 < startTime)
6992 { extendSelection(cm.doc, start) }
6993 // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#308 1)
6994 if (webkit || ie && ie_version == 9)
6995 { setTimeout(function () {document.body.focus(); display.input.focus()}, 20) }
1960 else 6996 else
1961 dist += after.textContent.length; 6997 { display.input.focus() }
1962 } 6998 }
1963 for (var before = topNode.previousSibling, dist = offset; before; before = b efore.previousSibling) { 6999 })
1964 found = find(before, before.firstChild, -1); 7000 // Let the drag handler handle this.
1965 if (found) 7001 if (webkit) { display.scroller.draggable = true }
1966 return badPos(Pos(found.line, found.ch + dist), bad); 7002 cm.state.draggingText = dragEnd
1967 else 7003 dragEnd.copy = mac ? e.altKey : e.ctrlKey
1968 dist += after.textContent.length; 7004 // IE's approach to draggable
1969 } 7005 if (display.scroller.dragDrop) { display.scroller.dragDrop() }
1970 } 7006 on(document, "mouseup", dragEnd)
1971 7007 on(display.scroller, "drop", dragEnd)
1972 function domTextBetween(cm, from, to, fromLine, toLine) { 7008 }
1973 var text = "", closing = false, lineSep = cm.doc.lineSeparator(); 7009
1974 function recognizeMarker(id) { return function(marker) { return marker.id == id; }; } 7010 // Normal selection, as opposed to text dragging.
1975 function walk(node) { 7011 function leftButtonSelect(cm, e, start, type, addNew) {
1976 if (node.nodeType == 1) { 7012 var display = cm.display, doc = cm.doc
1977 var cmText = node.getAttribute("cm-text"); 7013 e_preventDefault(e)
1978 if (cmText != null) { 7014
1979 if (cmText == "") cmText = node.textContent.replace(/\u200b/g, ""); 7015 var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges
1980 text += cmText; 7016 if (addNew && !e.shiftKey) {
1981 return; 7017 ourIndex = doc.sel.contains(start)
1982 } 7018 if (ourIndex > -1)
1983 var markerID = node.getAttribute("cm-marker"), range; 7019 { ourRange = ranges[ourIndex] }
1984 if (markerID) { 7020 else
1985 var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recogni zeMarker(+markerID)); 7021 { ourRange = new Range(start, start) }
1986 if (found.length && (range = found[0].find())) 7022 } else {
1987 text += getBetween(cm.doc, range.from, range.to).join(lineSep); 7023 ourRange = doc.sel.primary()
1988 return; 7024 ourIndex = doc.sel.primIndex
1989 } 7025 }
1990 if (node.getAttribute("contenteditable") == "false") return; 7026
1991 for (var i = 0; i < node.childNodes.length; i++) 7027 if (chromeOS ? e.shiftKey && e.metaKey : e.altKey) {
1992 walk(node.childNodes[i]); 7028 type = "rect"
1993 if (/^(pre|div|p)$/i.test(node.nodeName)) 7029 if (!addNew) { ourRange = new Range(start, start) }
1994 closing = true; 7030 start = posFromMouse(cm, e, true, true)
1995 } else if (node.nodeType == 3) { 7031 ourIndex = -1
1996 var val = node.nodeValue; 7032 } else if (type == "double") {
1997 if (!val) return; 7033 var word = cm.findWordAt(start)
1998 if (closing) { 7034 if (cm.display.shift || doc.extend)
1999 text += lineSep; 7035 { ourRange = extendRange(doc, ourRange, word.anchor, word.head) }
2000 closing = false; 7036 else
2001 } 7037 { ourRange = word }
2002 text += val; 7038 } else if (type == "triple") {
7039 var line = new Range(Pos(start.line, 0), clipPos(doc, Pos(start.line + 1, 0) ))
7040 if (cm.display.shift || doc.extend)
7041 { ourRange = extendRange(doc, ourRange, line.anchor, line.head) }
7042 else
7043 { ourRange = line }
7044 } else {
7045 ourRange = extendRange(doc, ourRange, start)
7046 }
7047
7048 if (!addNew) {
7049 ourIndex = 0
7050 setSelection(doc, new Selection([ourRange], 0), sel_mouse)
7051 startSel = doc.sel
7052 } else if (ourIndex == -1) {
7053 ourIndex = ranges.length
7054 setSelection(doc, normalizeSelection(ranges.concat([ourRange]), ourIndex),
7055 {scroll: false, origin: "*mouse"})
7056 } else if (ranges.length > 1 && ranges[ourIndex].empty() && type == "single" & & !e.shiftKey) {
7057 setSelection(doc, normalizeSelection(ranges.slice(0, ourIndex).concat(ranges .slice(ourIndex + 1)), 0),
7058 {scroll: false, origin: "*mouse"})
7059 startSel = doc.sel
7060 } else {
7061 replaceOneSelection(doc, ourIndex, ourRange, sel_mouse)
7062 }
7063
7064 var lastPos = start
7065 function extendTo(pos) {
7066 if (cmp(lastPos, pos) == 0) { return }
7067 lastPos = pos
7068
7069 if (type == "rect") {
7070 var ranges = [], tabSize = cm.options.tabSize
7071 var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSiz e)
7072 var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize)
7073 var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol)
7074 for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine (), Math.max(start.line, pos.line));
7075 line <= end; line++) {
7076 var text = getLine(doc, line).text, leftPos = findColumn(text, left, tab Size)
7077 if (left == right)
7078 { ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))) }
7079 else if (text.length > leftPos)
7080 { ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))) }
2003 } 7081 }
2004 } 7082 if (!ranges.length) { ranges.push(new Range(start, start)) }
2005 for (;;) { 7083 setSelection(doc, normalizeSelection(startSel.ranges.slice(0, ourIndex).co ncat(ranges), ourIndex),
2006 walk(from); 7084 {origin: "*mouse", scroll: false})
2007 if (from == to) break; 7085 cm.scrollIntoView(pos)
2008 from = from.nextSibling; 7086 } else {
2009 } 7087 var oldRange = ourRange
2010 return text; 7088 var anchor = oldRange.anchor, head = pos
2011 } 7089 if (type != "single") {
2012 7090 var range
2013 CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": Conten tEditableInput}; 7091 if (type == "double")
2014 7092 { range = cm.findWordAt(pos) }
2015 // SELECTION / CURSOR 7093 else
2016 7094 { range = new Range(Pos(pos.line, 0), clipPos(doc, Pos(pos.line + 1, 0 ))) }
2017 // Selection objects are immutable. A new one is created every time 7095 if (cmp(range.anchor, anchor) > 0) {
2018 // the selection changes. A selection is one or more non-overlapping 7096 head = range.head
2019 // (and non-touching) ranges, sorted, and an integer that indicates 7097 anchor = minPos(oldRange.from(), range.anchor)
2020 // which one is the primary selection (the one that's scrolled into 7098 } else {
2021 // view, that getCursor returns, etc). 7099 head = range.anchor
2022 function Selection(ranges, primIndex) { 7100 anchor = maxPos(oldRange.to(), range.head)
2023 this.ranges = ranges;
2024 this.primIndex = primIndex;
2025 }
2026
2027 Selection.prototype = {
2028 primary: function() { return this.ranges[this.primIndex]; },
2029 equals: function(other) {
2030 if (other == this) return true;
2031 if (other.primIndex != this.primIndex || other.ranges.length != this.range s.length) return false;
2032 for (var i = 0; i < this.ranges.length; i++) {
2033 var here = this.ranges[i], there = other.ranges[i];
2034 if (cmp(here.anchor, there.anchor) != 0 || cmp(here.head, there.head) != 0) return false;
2035 }
2036 return true;
2037 },
2038 deepCopy: function() {
2039 for (var out = [], i = 0; i < this.ranges.length; i++)
2040 out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i ].head));
2041 return new Selection(out, this.primIndex);
2042 },
2043 somethingSelected: function() {
2044 for (var i = 0; i < this.ranges.length; i++)
2045 if (!this.ranges[i].empty()) return true;
2046 return false;
2047 },
2048 contains: function(pos, end) {
2049 if (!end) end = pos;
2050 for (var i = 0; i < this.ranges.length; i++) {
2051 var range = this.ranges[i];
2052 if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)
2053 return i;
2054 }
2055 return -1;
2056 }
2057 };
2058
2059 function Range(anchor, head) {
2060 this.anchor = anchor; this.head = head;
2061 }
2062
2063 Range.prototype = {
2064 from: function() { return minPos(this.anchor, this.head); },
2065 to: function() { return maxPos(this.anchor, this.head); },
2066 empty: function() {
2067 return this.head.line == this.anchor.line && this.head.ch == this.anchor.c h;
2068 }
2069 };
2070
2071 // Take an unsorted, potentially overlapping set of ranges, and
2072 // build a selection out of it. 'Consumes' ranges array (modifying
2073 // it).
2074 function normalizeSelection(ranges, primIndex) {
2075 var prim = ranges[primIndex];
2076 ranges.sort(function(a, b) { return cmp(a.from(), b.from()); });
2077 primIndex = indexOf(ranges, prim);
2078 for (var i = 1; i < ranges.length; i++) {
2079 var cur = ranges[i], prev = ranges[i - 1];
2080 if (cmp(prev.to(), cur.from()) >= 0) {
2081 var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.t o());
2082 var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.he ad;
2083 if (i <= primIndex) --primIndex;
2084 ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to));
2085 }
2086 }
2087 return new Selection(ranges, primIndex);
2088 }
2089
2090 function simpleSelection(anchor, head) {
2091 return new Selection([new Range(anchor, head || anchor)], 0);
2092 }
2093
2094 // Most of the external API clips given positions to make sure they
2095 // actually exist within the document.
2096 function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1));}
2097 function clipPos(doc, pos) {
2098 if (pos.line < doc.first) return Pos(doc.first, 0);
2099 var last = doc.first + doc.size - 1;
2100 if (pos.line > last) return Pos(last, getLine(doc, last).text.length);
2101 return clipToLen(pos, getLine(doc, pos.line).text.length);
2102 }
2103 function clipToLen(pos, linelen) {
2104 var ch = pos.ch;
2105 if (ch == null || ch > linelen) return Pos(pos.line, linelen);
2106 else if (ch < 0) return Pos(pos.line, 0);
2107 else return pos;
2108 }
2109 function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size;}
2110 function clipPosArray(doc, array) {
2111 for (var out = [], i = 0; i < array.length; i++) out[i] = clipPos(doc, array [i]);
2112 return out;
2113 }
2114
2115 // SELECTION UPDATES
2116
2117 // The 'scroll' parameter given to many of these indicated whether
2118 // the new cursor position should be scrolled into view after
2119 // modifying the selection.
2120
2121 // If shift is held or the extend flag is set, extends a range to
2122 // include a given position (and optionally a second position).
2123 // Otherwise, simply returns the range between the given positions.
2124 // Used for cursor motion and such.
2125 function extendRange(doc, range, head, other) {
2126 if (doc.cm && doc.cm.display.shift || doc.extend) {
2127 var anchor = range.anchor;
2128 if (other) {
2129 var posBefore = cmp(head, anchor) < 0;
2130 if (posBefore != (cmp(other, anchor) < 0)) {
2131 anchor = head;
2132 head = other;
2133 } else if (posBefore != (cmp(head, other) < 0)) {
2134 head = other;
2135 } 7101 }
2136 } 7102 }
2137 return new Range(anchor, head); 7103 var ranges$1 = startSel.ranges.slice(0)
7104 ranges$1[ourIndex] = new Range(clipPos(doc, anchor), head)
7105 setSelection(doc, normalizeSelection(ranges$1, ourIndex), sel_mouse)
7106 }
7107 }
7108
7109 var editorSize = display.wrapper.getBoundingClientRect()
7110 // Used to ensure timeout re-tries don't fire when another extend
7111 // happened in the meantime (clearTimeout isn't reliable -- at
7112 // least on Chrome, the timeouts still happen even when cleared,
7113 // if the clear happens after their scheduled firing time).
7114 var counter = 0
7115
7116 function extend(e) {
7117 var curCount = ++counter
7118 var cur = posFromMouse(cm, e, true, type == "rect")
7119 if (!cur) { return }
7120 if (cmp(cur, lastPos) != 0) {
7121 cm.curOp.focus = activeElt()
7122 extendTo(cur)
7123 var visible = visibleLines(display, doc)
7124 if (cur.line >= visible.to || cur.line < visible.from)
7125 { setTimeout(operation(cm, function () {if (counter == curCount) { exten d(e) }}), 150) }
2138 } else { 7126 } else {
2139 return new Range(other || head, head); 7127 var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bo ttom ? 20 : 0
2140 } 7128 if (outside) { setTimeout(operation(cm, function () {
2141 } 7129 if (counter != curCount) { return }
2142 7130 display.scroller.scrollTop += outside
2143 // Extend the primary selection range, discard the rest. 7131 extend(e)
2144 function extendSelection(doc, head, other, options) { 7132 }), 50) }
2145 setSelection(doc, new Selection([extendRange(doc, doc.sel.primary(), head, o ther)], 0), options); 7133 }
2146 } 7134 }
2147 7135
2148 // Extend all selections (pos is an array of selections with length 7136 function done(e) {
2149 // equal the number of selections) 7137 cm.state.selectingText = false
2150 function extendSelections(doc, heads, options) { 7138 counter = Infinity
2151 for (var out = [], i = 0; i < doc.sel.ranges.length; i++) 7139 e_preventDefault(e)
2152 out[i] = extendRange(doc, doc.sel.ranges[i], heads[i], null); 7140 display.input.focus()
2153 var newSel = normalizeSelection(out, doc.sel.primIndex); 7141 off(document, "mousemove", move)
2154 setSelection(doc, newSel, options); 7142 off(document, "mouseup", up)
2155 } 7143 doc.history.lastSelOrigin = null
2156 7144 }
2157 // Updates a single range in the selection. 7145
2158 function replaceOneSelection(doc, i, range, options) { 7146 var move = operation(cm, function (e) {
2159 var ranges = doc.sel.ranges.slice(0); 7147 if (!e_button(e)) { done(e) }
2160 ranges[i] = range; 7148 else { extend(e) }
2161 setSelection(doc, normalizeSelection(ranges, doc.sel.primIndex), options); 7149 })
2162 } 7150 var up = operation(cm, done)
2163 7151 cm.state.selectingText = up
2164 // Reset the selection to a single range. 7152 on(document, "mousemove", move)
2165 function setSimpleSelection(doc, anchor, head, options) { 7153 on(document, "mouseup", up)
2166 setSelection(doc, simpleSelection(anchor, head), options); 7154 }
2167 } 7155
2168 7156
2169 // Give beforeSelectionChange handlers a change to influence a 7157 // Determines whether an event happened in the gutter, and fires the
2170 // selection update. 7158 // handlers for the corresponding event.
2171 function filterSelectionChange(doc, sel, options) { 7159 function gutterEvent(cm, e, type, prevent) {
2172 var obj = { 7160 var mX, mY
2173 ranges: sel.ranges, 7161 try { mX = e.clientX; mY = e.clientY }
2174 update: function(ranges) { 7162 catch(e) { return false }
2175 this.ranges = []; 7163 if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { retu rn false }
2176 for (var i = 0; i < ranges.length; i++) 7164 if (prevent) { e_preventDefault(e) }
2177 this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor), 7165
2178 clipPos(doc, ranges[i].head)); 7166 var display = cm.display
2179 }, 7167 var lineBox = display.lineDiv.getBoundingClientRect()
2180 origin: options && options.origin 7168
2181 }; 7169 if (mY > lineBox.bottom || !hasHandler(cm, type)) { return e_defaultPrevented( e) }
2182 signal(doc, "beforeSelectionChange", doc, obj); 7170 mY -= lineBox.top - display.viewOffset
2183 if (doc.cm) signal(doc.cm, "beforeSelectionChange", doc.cm, obj); 7171
2184 if (obj.ranges != sel.ranges) return normalizeSelection(obj.ranges, obj.rang es.length - 1); 7172 for (var i = 0; i < cm.options.gutters.length; ++i) {
2185 else return sel; 7173 var g = display.gutters.childNodes[i]
2186 } 7174 if (g && g.getBoundingClientRect().right >= mX) {
2187 7175 var line = lineAtHeight(cm.doc, mY)
2188 function setSelectionReplaceHistory(doc, sel, options) { 7176 var gutter = cm.options.gutters[i]
2189 var done = doc.history.done, last = lst(done); 7177 signal(cm, type, cm, line, gutter, e)
2190 if (last && last.ranges) { 7178 return e_defaultPrevented(e)
2191 done[done.length - 1] = sel; 7179 }
2192 setSelectionNoUndo(doc, sel, options); 7180 }
7181 }
7182
7183 function clickInGutter(cm, e) {
7184 return gutterEvent(cm, e, "gutterClick", true)
7185 }
7186
7187 // CONTEXT MENU HANDLING
7188
7189 // To make the context menu work, we need to briefly unhide the
7190 // textarea (making it as unobtrusive as possible) to let the
7191 // right-click take effect on it.
7192 function onContextMenu(cm, e) {
7193 if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) { return }
7194 if (signalDOMEvent(cm, e, "contextmenu")) { return }
7195 cm.display.input.onContextMenu(e)
7196 }
7197
7198 function contextMenuInGutter(cm, e) {
7199 if (!hasHandler(cm, "gutterContextMenu")) { return false }
7200 return gutterEvent(cm, e, "gutterContextMenu", false)
7201 }
7202
7203 function themeChanged(cm) {
7204 cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\ S+/g, "") +
7205 cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-")
7206 clearCaches(cm)
7207 }
7208
7209 var Init = {toString: function(){return "CodeMirror.Init"}}
7210
7211 var defaults = {}
7212 var optionHandlers = {}
7213
7214 function defineOptions(CodeMirror) {
7215 var optionHandlers = CodeMirror.optionHandlers
7216
7217 function option(name, deflt, handle, notOnInit) {
7218 CodeMirror.defaults[name] = deflt
7219 if (handle) { optionHandlers[name] =
7220 notOnInit ? function (cm, val, old) {if (old != Init) { handle(cm, val, ol d) }} : handle }
7221 }
7222
7223 CodeMirror.defineOption = option
7224
7225 // Passed to option handlers when there is no old value.
7226 CodeMirror.Init = Init
7227
7228 // These two are, on init, called from the constructor because they
7229 // have to be initialized before the editor can start at all.
7230 option("value", "", function (cm, val) { return cm.setValue(val); }, true)
7231 option("mode", null, function (cm, val) {
7232 cm.doc.modeOption = val
7233 loadMode(cm)
7234 }, true)
7235
7236 option("indentUnit", 2, loadMode, true)
7237 option("indentWithTabs", false)
7238 option("smartIndent", true)
7239 option("tabSize", 4, function (cm) {
7240 resetModeState(cm)
7241 clearCaches(cm)
7242 regChange(cm)
7243 }, true)
7244 option("lineSeparator", null, function (cm, val) {
7245 cm.doc.lineSep = val
7246 if (!val) { return }
7247 var newBreaks = [], lineNo = cm.doc.first
7248 cm.doc.iter(function (line) {
7249 for (var pos = 0;;) {
7250 var found = line.text.indexOf(val, pos)
7251 if (found == -1) { break }
7252 pos = found + val.length
7253 newBreaks.push(Pos(lineNo, found))
7254 }
7255 lineNo++
7256 })
7257 for (var i = newBreaks.length - 1; i >= 0; i--)
7258 { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks [i].ch + val.length)) }
7259 })
7260 option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200f\u 2028\u2029\ufeff]/g, function (cm, val, old) {
7261 cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t" ), "g")
7262 if (old != Init) { cm.refresh() }
7263 })
7264 option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function (cm) { return cm.refresh(); }, true)
7265 option("electricChars", true)
7266 option("inputStyle", mobile ? "contenteditable" : "textarea", function () {
7267 throw new Error("inputStyle can not (yet) be changed in a running editor") / / FIXME
7268 }, true)
7269 option("spellcheck", false, function (cm, val) { return cm.getInputField().spe llcheck = val; }, true)
7270 option("rtlMoveVisually", !windows)
7271 option("wholeLineUpdateBefore", true)
7272
7273 option("theme", "default", function (cm) {
7274 themeChanged(cm)
7275 guttersChanged(cm)
7276 }, true)
7277 option("keyMap", "default", function (cm, val, old) {
7278 var next = getKeyMap(val)
7279 var prev = old != Init && getKeyMap(old)
7280 if (prev && prev.detach) { prev.detach(cm, next) }
7281 if (next.attach) { next.attach(cm, prev || null) }
7282 })
7283 option("extraKeys", null)
7284
7285 option("lineWrapping", false, wrappingChanged, true)
7286 option("gutters", [], function (cm) {
7287 setGuttersForLineNumbers(cm.options)
7288 guttersChanged(cm)
7289 }, true)
7290 option("fixedGutter", true, function (cm, val) {
7291 cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px " : "0"
7292 cm.refresh()
7293 }, true)
7294 option("coverGutterNextToScrollbar", false, function (cm) { return updateScrol lbars(cm); }, true)
7295 option("scrollbarStyle", "native", function (cm) {
7296 initScrollbars(cm)
7297 updateScrollbars(cm)
7298 cm.display.scrollbars.setScrollTop(cm.doc.scrollTop)
7299 cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft)
7300 }, true)
7301 option("lineNumbers", false, function (cm) {
7302 setGuttersForLineNumbers(cm.options)
7303 guttersChanged(cm)
7304 }, true)
7305 option("firstLineNumber", 1, guttersChanged, true)
7306 option("lineNumberFormatter", function (integer) { return integer; }, guttersC hanged, true)
7307 option("showCursorWhenSelecting", false, updateSelection, true)
7308
7309 option("resetSelectionOnContextMenu", true)
7310 option("lineWiseCopyCut", true)
7311
7312 option("readOnly", false, function (cm, val) {
7313 if (val == "nocursor") {
7314 onBlur(cm)
7315 cm.display.input.blur()
7316 cm.display.disabled = true
2193 } else { 7317 } else {
2194 setSelection(doc, sel, options); 7318 cm.display.disabled = false
2195 } 7319 }
2196 } 7320 cm.display.input.readOnlyChanged(val)
2197 7321 })
2198 // Set a new selection. 7322 option("disableInput", false, function (cm, val) {if (!val) { cm.display.input .reset() }}, true)
2199 function setSelection(doc, sel, options) { 7323 option("dragDrop", true, dragDropChanged)
2200 setSelectionNoUndo(doc, sel, options); 7324 option("allowDropFileTypes", null)
2201 addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options) ; 7325
2202 } 7326 option("cursorBlinkRate", 530)
2203 7327 option("cursorScrollMargin", 0)
2204 function setSelectionNoUndo(doc, sel, options) { 7328 option("cursorHeight", 1, updateSelection, true)
2205 if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange")) 7329 option("singleCursorHeightPerLine", true, updateSelection, true)
2206 sel = filterSelectionChange(doc, sel, options); 7330 option("workTime", 100)
2207 7331 option("workDelay", 100)
2208 var bias = options && options.bias || 7332 option("flattenSpans", true, resetModeState, true)
2209 (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1); 7333 option("addModeClass", false, resetModeState, true)
2210 setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true)); 7334 option("pollInterval", 100)
2211 7335 option("undoDepth", 200, function (cm, val) { return cm.doc.history.undoDepth = val; })
2212 if (!(options && options.scroll === false) && doc.cm) 7336 option("historyEventDelay", 1250)
2213 ensureCursorVisible(doc.cm); 7337 option("viewportMargin", 10, function (cm) { return cm.refresh(); }, true)
2214 } 7338 option("maxHighlightLength", 10000, resetModeState, true)
2215 7339 option("moveInputWithCursor", true, function (cm, val) {
2216 function setSelectionInner(doc, sel) { 7340 if (!val) { cm.display.input.resetPosition() }
2217 if (sel.equals(doc.sel)) return; 7341 })
2218 7342
2219 doc.sel = sel; 7343 option("tabindex", null, function (cm, val) { return cm.display.input.getField ().tabIndex = val || ""; })
2220 7344 option("autofocus", null)
2221 if (doc.cm) { 7345 option("direction", "ltr", function (cm, val) { return cm.doc.setDirection(val ); }, true)
2222 doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true; 7346 }
2223 signalCursorActivity(doc.cm); 7347
2224 } 7348 function guttersChanged(cm) {
2225 signalLater(doc, "cursorActivity", doc); 7349 updateGutters(cm)
2226 } 7350 regChange(cm)
2227 7351 alignHorizontally(cm)
2228 // Verify that the selection does not partially select any atomic 7352 }
2229 // marked ranges. 7353
2230 function reCheckSelection(doc) { 7354 function dragDropChanged(cm, value, old) {
2231 setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false), sel _dontScroll); 7355 var wasOn = old && old != Init
2232 } 7356 if (!value != !wasOn) {
2233 7357 var funcs = cm.display.dragFunctions
2234 // Return a selection that does not partially select any atomic 7358 var toggle = value ? on : off
2235 // ranges. 7359 toggle(cm.display.scroller, "dragstart", funcs.start)
2236 function skipAtomicInSelection(doc, sel, bias, mayClear) { 7360 toggle(cm.display.scroller, "dragenter", funcs.enter)
2237 var out; 7361 toggle(cm.display.scroller, "dragover", funcs.over)
2238 for (var i = 0; i < sel.ranges.length; i++) { 7362 toggle(cm.display.scroller, "dragleave", funcs.leave)
2239 var range = sel.ranges[i]; 7363 toggle(cm.display.scroller, "drop", funcs.drop)
2240 var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i]; 7364 }
2241 var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, may Clear); 7365 }
2242 var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear) ; 7366
2243 if (out || newAnchor != range.anchor || newHead != range.head) { 7367 function wrappingChanged(cm) {
2244 if (!out) out = sel.ranges.slice(0, i); 7368 if (cm.options.lineWrapping) {
2245 out[i] = new Range(newAnchor, newHead); 7369 addClass(cm.display.wrapper, "CodeMirror-wrap")
7370 cm.display.sizer.style.minWidth = ""
7371 cm.display.sizerWidth = null
7372 } else {
7373 rmClass(cm.display.wrapper, "CodeMirror-wrap")
7374 findMaxLine(cm)
7375 }
7376 estimateLineHeights(cm)
7377 regChange(cm)
7378 clearCaches(cm)
7379 setTimeout(function () { return updateScrollbars(cm); }, 100)
7380 }
7381
7382 // A CodeMirror instance represents an editor. This is the object
7383 // that user code is usually dealing with.
7384
7385 function CodeMirror(place, options) {
7386 var this$1 = this;
7387
7388 if (!(this instanceof CodeMirror)) { return new CodeMirror(place, options) }
7389
7390 this.options = options = options ? copyObj(options) : {}
7391 // Determine effective options based on given values and defaults.
7392 copyObj(defaults, options, false)
7393 setGuttersForLineNumbers(options)
7394
7395 var doc = options.value
7396 if (typeof doc == "string") { doc = new Doc(doc, options.mode, null, options.l ineSeparator, options.direction) }
7397 this.doc = doc
7398
7399 var input = new CodeMirror.inputStyles[options.inputStyle](this)
7400 var display = this.display = new Display(place, doc, input)
7401 display.wrapper.CodeMirror = this
7402 updateGutters(this)
7403 themeChanged(this)
7404 if (options.lineWrapping)
7405 { this.display.wrapper.className += " CodeMirror-wrap" }
7406 initScrollbars(this)
7407
7408 this.state = {
7409 keyMaps: [], // stores maps added by addKeyMap
7410 overlays: [], // highlighting overlays, as added by addOverlay
7411 modeGen: 0, // bumped when mode/overlay changes, used to invalidate highli ghting info
7412 overwrite: false,
7413 delayingBlurEvent: false,
7414 focused: false,
7415 suppressEdits: false, // used to disable editing during key handlers when in readOnly mode
7416 pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edits in input.poll
7417 selectingText: false,
7418 draggingText: false,
7419 highlight: new Delayed(), // stores highlight worker timeout
7420 keySeq: null, // Unfinished key sequence
7421 specialChars: null
7422 }
7423
7424 if (options.autofocus && !mobile) { display.input.focus() }
7425
7426 // Override magic textarea content restore that IE sometimes does
7427 // on our hidden textarea on reload
7428 if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.in put.reset(true); }, 20) }
7429
7430 registerEventHandlers(this)
7431 ensureGlobalHandlers()
7432
7433 startOperation(this)
7434 this.curOp.forceUpdate = true
7435 attachDoc(this, doc)
7436
7437 if ((options.autofocus && !mobile) || this.hasFocus())
7438 { setTimeout(bind(onFocus, this), 20) }
7439 else
7440 { onBlur(this) }
7441
7442 for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt))
7443 { optionHandlers[opt](this$1, options[opt], Init) } }
7444 maybeUpdateLineNumberWidth(this)
7445 if (options.finishInit) { options.finishInit(this) }
7446 for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this$1) }
7447 endOperation(this)
7448 // Suppress optimizelegibility in Webkit, since it breaks text
7449 // measuring on line wrapping boundaries.
7450 if (webkit && options.lineWrapping &&
7451 getComputedStyle(display.lineDiv).textRendering == "optimizelegibility")
7452 { display.lineDiv.style.textRendering = "auto" }
7453 }
7454
7455 // The default configuration options.
7456 CodeMirror.defaults = defaults
7457 // Functions to run when options are changed.
7458 CodeMirror.optionHandlers = optionHandlers
7459
7460 // Attach the necessary event handlers when initializing the editor
7461 function registerEventHandlers(cm) {
7462 var d = cm.display
7463 on(d.scroller, "mousedown", operation(cm, onMouseDown))
7464 // Older IE's will not fire a second mousedown for a double click
7465 if (ie && ie_version < 11)
7466 { on(d.scroller, "dblclick", operation(cm, function (e) {
7467 if (signalDOMEvent(cm, e)) { return }
7468 var pos = posFromMouse(cm, e)
7469 if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) { return }
7470 e_preventDefault(e)
7471 var word = cm.findWordAt(pos)
7472 extendSelection(cm.doc, word.anchor, word.head)
7473 })) }
7474 else
7475 { on(d.scroller, "dblclick", function (e) { return signalDOMEvent(cm, e) || e_preventDefault(e); }) }
7476 // Some browsers fire contextmenu *after* opening the menu, at
7477 // which point we can't mess with it anymore. Context menu is
7478 // handled in onMouseDown for these browsers.
7479 if (!captureRightClick) { on(d.scroller, "contextmenu", function (e) { return onContextMenu(cm, e); }) }
7480
7481 // Used to suppress mouse event handling when a touch happens
7482 var touchFinished, prevTouch = {end: 0}
7483 function finishTouch() {
7484 if (d.activeTouch) {
7485 touchFinished = setTimeout(function () { return d.activeTouch = null; }, 1 000)
7486 prevTouch = d.activeTouch
7487 prevTouch.end = +new Date
7488 }
7489 }
7490 function isMouseLikeTouchEvent(e) {
7491 if (e.touches.length != 1) { return false }
7492 var touch = e.touches[0]
7493 return touch.radiusX <= 1 && touch.radiusY <= 1
7494 }
7495 function farAway(touch, other) {
7496 if (other.left == null) { return true }
7497 var dx = other.left - touch.left, dy = other.top - touch.top
7498 return dx * dx + dy * dy > 20 * 20
7499 }
7500 on(d.scroller, "touchstart", function (e) {
7501 if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e)) {
7502 d.input.ensurePolled()
7503 clearTimeout(touchFinished)
7504 var now = +new Date
7505 d.activeTouch = {start: now, moved: false,
7506 prev: now - prevTouch.end <= 300 ? prevTouch : null}
7507 if (e.touches.length == 1) {
7508 d.activeTouch.left = e.touches[0].pageX
7509 d.activeTouch.top = e.touches[0].pageY
2246 } 7510 }
2247 } 7511 }
2248 return out ? normalizeSelection(out, sel.primIndex) : sel; 7512 })
2249 } 7513 on(d.scroller, "touchmove", function () {
2250 7514 if (d.activeTouch) { d.activeTouch.moved = true }
2251 function skipAtomicInner(doc, pos, oldPos, dir, mayClear) { 7515 })
2252 var line = getLine(doc, pos.line); 7516 on(d.scroller, "touchend", function (e) {
2253 if (line.markedSpans) for (var i = 0; i < line.markedSpans.length; ++i) { 7517 var touch = d.activeTouch
2254 var sp = line.markedSpans[i], m = sp.marker; 7518 if (touch && !eventInWidget(d, e) && touch.left != null &&
2255 if ((sp.from == null || (m.inclusiveLeft ? sp.from <= pos.ch : sp.from < p os.ch)) && 7519 !touch.moved && new Date - touch.start < 300) {
2256 (sp.to == null || (m.inclusiveRight ? sp.to >= pos.ch : sp.to > pos.ch ))) { 7520 var pos = cm.coordsChar(d.activeTouch, "page"), range
2257 if (mayClear) { 7521 if (!touch.prev || farAway(touch, touch.prev)) // Single tap
2258 signal(m, "beforeCursorEnter"); 7522 { range = new Range(pos, pos) }
2259 if (m.explicitlyCleared) { 7523 else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double ta p
2260 if (!line.markedSpans) break; 7524 { range = cm.findWordAt(pos) }
2261 else {--i; continue;} 7525 else // Triple tap
2262 } 7526 { range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) }
2263 } 7527 cm.setSelection(range.anchor, range.head)
2264 if (!m.atomic) continue; 7528 cm.focus()
2265 7529 e_preventDefault(e)
2266 if (oldPos) { 7530 }
2267 var near = m.find(dir < 0 ? 1 : -1), diff; 7531 finishTouch()
2268 if (dir < 0 ? m.inclusiveRight : m.inclusiveLeft) 7532 })
2269 near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null); 7533 on(d.scroller, "touchcancel", finishTouch)
2270 if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (di r < 0 ? diff < 0 : diff > 0)) 7534
2271 return skipAtomicInner(doc, near, pos, dir, mayClear); 7535 // Sync scrolling between fake scrollbars and real scrollable
2272 } 7536 // area, ensure viewport is updated when scrolling.
2273 7537 on(d.scroller, "scroll", function () {
2274 var far = m.find(dir < 0 ? -1 : 1); 7538 if (d.scroller.clientHeight) {
2275 if (dir < 0 ? m.inclusiveLeft : m.inclusiveRight) 7539 setScrollTop(cm, d.scroller.scrollTop)
2276 far = movePos(doc, far, dir, far.line == pos.line ? line : null); 7540 setScrollLeft(cm, d.scroller.scrollLeft, true)
2277 return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null; 7541 signal(cm, "scroll", cm)
7542 }
7543 })
7544
7545 // Listen to wheel events in order to try and update the viewport on time.
7546 on(d.scroller, "mousewheel", function (e) { return onScrollWheel(cm, e); })
7547 on(d.scroller, "DOMMouseScroll", function (e) { return onScrollWheel(cm, e); } )
7548
7549 // Prevent wrapper from ever scrolling
7550 on(d.wrapper, "scroll", function () { return d.wrapper.scrollTop = d.wrapper.s crollLeft = 0; })
7551
7552 d.dragFunctions = {
7553 enter: function (e) {if (!signalDOMEvent(cm, e)) { e_stop(e) }},
7554 over: function (e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop( e) }},
7555 start: function (e) { return onDragStart(cm, e); },
7556 drop: operation(cm, onDrop),
7557 leave: function (e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm) }}
7558 }
7559
7560 var inp = d.input.getField()
7561 on(inp, "keyup", function (e) { return onKeyUp.call(cm, e); })
7562 on(inp, "keydown", operation(cm, onKeyDown))
7563 on(inp, "keypress", operation(cm, onKeyPress))
7564 on(inp, "focus", function (e) { return onFocus(cm, e); })
7565 on(inp, "blur", function (e) { return onBlur(cm, e); })
7566 }
7567
7568 var initHooks = []
7569 CodeMirror.defineInitHook = function (f) { return initHooks.push(f); }
7570
7571 // Indent the given line. The how parameter can be "smart",
7572 // "add"/null, "subtract", or "prev". When aggressive is false
7573 // (typically set to true for forced single-line indents), empty
7574 // lines are not indented, and places where the mode returns Pass
7575 // are left alone.
7576 function indentLine(cm, n, how, aggressive) {
7577 var doc = cm.doc, state
7578 if (how == null) { how = "add" }
7579 if (how == "smart") {
7580 // Fall back to "prev" when the mode doesn't have an indentation
7581 // method.
7582 if (!doc.mode.indent) { how = "prev" }
7583 else { state = getStateBefore(cm, n) }
7584 }
7585
7586 var tabSize = cm.options.tabSize
7587 var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize)
7588 if (line.stateAfter) { line.stateAfter = null }
7589 var curSpaceString = line.text.match(/^\s*/)[0], indentation
7590 if (!aggressive && !/\S/.test(line.text)) {
7591 indentation = 0
7592 how = "not"
7593 } else if (how == "smart") {
7594 indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text)
7595 if (indentation == Pass || indentation > 150) {
7596 if (!aggressive) { return }
7597 how = "prev"
7598 }
7599 }
7600 if (how == "prev") {
7601 if (n > doc.first) { indentation = countColumn(getLine(doc, n-1).text, null, tabSize) }
7602 else { indentation = 0 }
7603 } else if (how == "add") {
7604 indentation = curSpace + cm.options.indentUnit
7605 } else if (how == "subtract") {
7606 indentation = curSpace - cm.options.indentUnit
7607 } else if (typeof how == "number") {
7608 indentation = curSpace + how
7609 }
7610 indentation = Math.max(0, indentation)
7611
7612 var indentString = "", pos = 0
7613 if (cm.options.indentWithTabs)
7614 { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; i ndentString += "\t"} }
7615 if (pos < indentation) { indentString += spaceStr(indentation - pos) }
7616
7617 if (indentString != curSpaceString) {
7618 replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+ input")
7619 line.stateAfter = null
7620 return true
7621 } else {
7622 // Ensure that, if the cursor was in the whitespace at the start
7623 // of the line, it is moved to the end of that space.
7624 for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) {
7625 var range = doc.sel.ranges[i$1]
7626 if (range.head.line == n && range.head.ch < curSpaceString.length) {
7627 var pos$1 = Pos(n, curSpaceString.length)
7628 replaceOneSelection(doc, i$1, new Range(pos$1, pos$1))
7629 break
2278 } 7630 }
2279 } 7631 }
2280 return pos; 7632 }
2281 } 7633 }
2282 7634
2283 // Ensure a given position is not inside an atomic range. 7635 // This will be set to a {lineWise: bool, text: [string]} object, so
2284 function skipAtomic(doc, pos, oldPos, bias, mayClear) { 7636 // that, when pasting, we know what kind of selections the copied
2285 var dir = bias || 1; 7637 // text was made out of.
2286 var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) || 7638 var lastCopied = null
2287 (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) || 7639
2288 skipAtomicInner(doc, pos, oldPos, -dir, mayClear) || 7640 function setLastCopied(newLastCopied) {
2289 (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true)); 7641 lastCopied = newLastCopied
2290 if (!found) { 7642 }
2291 doc.cantEdit = true; 7643
2292 return Pos(doc.first, 0); 7644 function applyTextInput(cm, inserted, deleted, sel, origin) {
2293 } 7645 var doc = cm.doc
2294 return found; 7646 cm.display.shift = false
2295 } 7647 if (!sel) { sel = doc.sel }
2296 7648
2297 function movePos(doc, pos, dir, line) { 7649 var paste = cm.state.pasteIncoming || origin == "paste"
2298 if (dir < 0 && pos.ch == 0) { 7650 var textLines = splitLinesAuto(inserted), multiPaste = null
2299 if (pos.line > doc.first) return clipPos(doc, Pos(pos.line - 1)); 7651 // When pasing N lines into N selections, insert one line per selection
2300 else return null; 7652 if (paste && sel.ranges.length > 1) {
2301 } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length ) { 7653 if (lastCopied && lastCopied.text.join("\n") == inserted) {
2302 if (pos.line < doc.first + doc.size - 1) return Pos(pos.line + 1, 0); 7654 if (sel.ranges.length % lastCopied.text.length == 0) {
2303 else return null; 7655 multiPaste = []
2304 } else { 7656 for (var i = 0; i < lastCopied.text.length; i++)
2305 return new Pos(pos.line, pos.ch + dir); 7657 { multiPaste.push(doc.splitLines(lastCopied.text[i])) }
2306 }
2307 }
2308
2309 // SELECTION DRAWING
2310
2311 function updateSelection(cm) {
2312 cm.display.input.showSelection(cm.display.input.prepareSelection());
2313 }
2314
2315 function prepareSelection(cm, primary) {
2316 var doc = cm.doc, result = {};
2317 var curFragment = result.cursors = document.createDocumentFragment();
2318 var selFragment = result.selection = document.createDocumentFragment();
2319
2320 for (var i = 0; i < doc.sel.ranges.length; i++) {
2321 if (primary === false && i == doc.sel.primIndex) continue;
2322 var range = doc.sel.ranges[i];
2323 if (range.from().line >= cm.display.viewTo || range.to().line < cm.display .viewFrom) continue;
2324 var collapsed = range.empty();
2325 if (collapsed || cm.options.showCursorWhenSelecting)
2326 drawSelectionCursor(cm, range.head, curFragment);
2327 if (!collapsed)
2328 drawSelectionRange(cm, range, selFragment);
2329 }
2330 return result;
2331 }
2332
2333 // Draws a cursor for the given range
2334 function drawSelectionCursor(cm, head, output) {
2335 var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursor HeightPerLine);
2336
2337 var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor"));
2338 cursor.style.left = pos.left + "px";
2339 cursor.style.top = pos.top + "px";
2340 cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorH eight + "px";
2341
2342 if (pos.other) {
2343 // Secondary cursor, shown when on a 'jump' in bi-directional text
2344 var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-curs or CodeMirror-secondarycursor"));
2345 otherCursor.style.display = "";
2346 otherCursor.style.left = pos.other.left + "px";
2347 otherCursor.style.top = pos.other.top + "px";
2348 otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px" ;
2349 }
2350 }
2351
2352 // Draws the given range as a highlighted selection
2353 function drawSelectionRange(cm, range, output) {
2354 var display = cm.display, doc = cm.doc;
2355 var fragment = document.createDocumentFragment();
2356 var padding = paddingH(cm.display), leftSide = padding.left;
2357 var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.size r.offsetLeft) - padding.right;
2358
2359 function add(left, top, width, bottom) {
2360 if (top < 0) top = 0;
2361 top = Math.round(top);
2362 bottom = Math.round(bottom);
2363 fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: ab solute; left: " + left +
2364 "px; top: " + top + "px; width: " + (width == nul l ? rightSide - left : width) +
2365 "px; height: " + (bottom - top) + "px"));
2366 }
2367
2368 function drawForLine(line, fromArg, toArg) {
2369 var lineObj = getLine(doc, line);
2370 var lineLen = lineObj.text.length;
2371 var start, end;
2372 function coords(ch, bias) {
2373 return charCoords(cm, Pos(line, ch), "div", lineObj, bias);
2374 } 7658 }
2375 7659 } else if (textLines.length == sel.ranges.length) {
2376 iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineL en : toArg, function(from, to, dir) { 7660 multiPaste = map(textLines, function (l) { return [l]; })
2377 var leftPos = coords(from, "left"), rightPos, left, right; 7661 }
2378 if (from == to) { 7662 }
2379 rightPos = leftPos; 7663
2380 left = right = leftPos.left; 7664 var updateInput
2381 } else { 7665 // Normal behavior is to insert the new text into every selection
2382 rightPos = coords(to - 1, "right"); 7666 for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) {
2383 if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp; } 7667 var range = sel.ranges[i$1]
2384 left = leftPos.left; 7668 var from = range.from(), to = range.to()
2385 right = rightPos.right; 7669 if (range.empty()) {
2386 } 7670 if (deleted && deleted > 0) // Handle deletion
2387 if (fromArg == null && from == 0) left = leftSide; 7671 { from = Pos(from.line, from.ch - deleted) }
2388 if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part 7672 else if (cm.state.overwrite && !paste) // Handle overwrite
2389 add(left, leftPos.top, null, leftPos.bottom); 7673 { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)) }
2390 left = leftSide; 7674 else if (lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") = = inserted)
2391 if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rig htPos.top); 7675 { from = to = Pos(from.line, 0) }
2392 } 7676 }
2393 if (toArg == null && to == lineLen) right = rightSide; 7677 updateInput = cm.curOp.updateInput
2394 if (!start || leftPos.top < start.top || leftPos.top == start.top && lef tPos.left < start.left) 7678 var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % m ultiPaste.length] : textLines,
2395 start = leftPos; 7679 origin: origin || (paste ? "paste" : cm.state.cutIncoming ? "cut" : "+input")}
2396 if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.botto m && rightPos.right > end.right) 7680 makeChange(cm.doc, changeEvent)
2397 end = rightPos; 7681 signalLater(cm, "inputRead", cm, changeEvent)
2398 if (left < leftSide + 1) left = leftSide; 7682 }
2399 add(left, rightPos.top, right - left, rightPos.bottom); 7683 if (inserted && !paste)
2400 }); 7684 { triggerElectric(cm, inserted) }
2401 return {start: start, end: end}; 7685
2402 } 7686 ensureCursorVisible(cm)
2403 7687 cm.curOp.updateInput = updateInput
2404 var sFrom = range.from(), sTo = range.to(); 7688 cm.curOp.typing = true
2405 if (sFrom.line == sTo.line) { 7689 cm.state.pasteIncoming = cm.state.cutIncoming = false
2406 drawForLine(sFrom.line, sFrom.ch, sTo.ch); 7690 }
2407 } else { 7691
2408 var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line); 7692 function handlePaste(e, cm) {
2409 var singleVLine = visualLine(fromLine) == visualLine(toLine); 7693 var pasted = e.clipboardData && e.clipboardData.getData("Text")
2410 var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.tex t.length + 1 : null).end; 7694 if (pasted) {
2411 var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).sta rt; 7695 e.preventDefault()
2412 if (singleVLine) { 7696 if (!cm.isReadOnly() && !cm.options.disableInput)
2413 if (leftEnd.top < rightStart.top - 2) { 7697 { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, "pa ste"); }) }
2414 add(leftEnd.right, leftEnd.top, null, leftEnd.bottom); 7698 return true
2415 add(leftSide, rightStart.top, rightStart.left, rightStart.bottom); 7699 }
2416 } else { 7700 }
2417 add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftE nd.bottom); 7701
2418 } 7702 function triggerElectric(cm, inserted) {
2419 } 7703 // When an 'electric' character is inserted, immediately trigger a reindent
2420 if (leftEnd.bottom < rightStart.top) 7704 if (!cm.options.electricChars || !cm.options.smartIndent) { return }
2421 add(leftSide, leftEnd.bottom, null, rightStart.top); 7705 var sel = cm.doc.sel
2422 } 7706
2423 7707 for (var i = sel.ranges.length - 1; i >= 0; i--) {
2424 output.appendChild(fragment); 7708 var range = sel.ranges[i]
2425 } 7709 if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head.l ine)) { continue }
2426 7710 var mode = cm.getModeAt(range.head)
2427 // Cursor-blinking 7711 var indented = false
2428 function restartBlink(cm) { 7712 if (mode.electricChars) {
2429 if (!cm.state.focused) return; 7713 for (var j = 0; j < mode.electricChars.length; j++)
2430 var display = cm.display; 7714 { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
2431 clearInterval(display.blinker); 7715 indented = indentLine(cm, range.head.line, "smart")
2432 var on = true; 7716 break
2433 display.cursorDiv.style.visibility = ""; 7717 } }
2434 if (cm.options.cursorBlinkRate > 0) 7718 } else if (mode.electricInput) {
2435 display.blinker = setInterval(function() { 7719 if (mode.electricInput.test(getLine(cm.doc, range.head.line).text.slice(0, range.head.ch)))
2436 display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; 7720 { indented = indentLine(cm, range.head.line, "smart") }
2437 }, cm.options.cursorBlinkRate); 7721 }
2438 else if (cm.options.cursorBlinkRate < 0) 7722 if (indented) { signalLater(cm, "electricInput", cm, range.head.line) }
2439 display.cursorDiv.style.visibility = "hidden"; 7723 }
2440 } 7724 }
2441 7725
2442 // HIGHLIGHT WORKER 7726 function copyableRanges(cm) {
2443 7727 var text = [], ranges = []
2444 function startWorker(cm, time) { 7728 for (var i = 0; i < cm.doc.sel.ranges.length; i++) {
2445 if (cm.doc.mode.startState && cm.doc.frontier < cm.display.viewTo) 7729 var line = cm.doc.sel.ranges[i].head.line
2446 cm.state.highlight.set(time, bind(highlightWorker, cm)); 7730 var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)}
2447 } 7731 ranges.push(lineRange)
2448 7732 text.push(cm.getRange(lineRange.anchor, lineRange.head))
2449 function highlightWorker(cm) { 7733 }
2450 var doc = cm.doc; 7734 return {text: text, ranges: ranges}
2451 if (doc.frontier < doc.first) doc.frontier = doc.first; 7735 }
2452 if (doc.frontier >= cm.display.viewTo) return; 7736
2453 var end = +new Date + cm.options.workTime; 7737 function disableBrowserMagic(field, spellcheck) {
2454 var state = copyState(doc.mode, getStateBefore(cm, doc.frontier)); 7738 field.setAttribute("autocorrect", "off")
2455 var changedLines = []; 7739 field.setAttribute("autocapitalize", "off")
2456 7740 field.setAttribute("spellcheck", !!spellcheck)
2457 doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.viewTo + 50 0), function(line) { 7741 }
2458 if (doc.frontier >= cm.display.viewFrom) { // Visible 7742
2459 var oldStyles = line.styles, tooLong = line.text.length > cm.options.max HighlightLength; 7743 function hiddenTextarea() {
2460 var highlighted = highlightLine(cm, line, tooLong ? copyState(doc.mode, state) : state, true); 7744 var te = elt("textarea", null, null, "position: absolute; bottom: -1em; paddin g: 0; width: 1px; height: 1em; outline: none")
2461 line.styles = highlighted.styles; 7745 var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;")
2462 var oldCls = line.styleClasses, newCls = highlighted.classes; 7746 // The textarea is kept positioned near the cursor to prevent the
2463 if (newCls) line.styleClasses = newCls; 7747 // fact that it'll be scrolled into view on input from scrolling
2464 else if (oldCls) line.styleClasses = null; 7748 // our fake cursor out of view. On webkit, when wrap=off, paste is
2465 var ischange = !oldStyles || oldStyles.length != line.styles.length || 7749 // very slow. So make the area wide instead.
2466 oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bg Class || oldCls.textClass != newCls.textClass); 7750 if (webkit) { te.style.width = "1000px" }
2467 for (var i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldSt yles[i] != line.styles[i]; 7751 else { te.setAttribute("wrap", "off") }
2468 if (ischange) changedLines.push(doc.frontier); 7752 // If border: 0; -- iOS fails to open keyboard (issue #1287)
2469 line.stateAfter = tooLong ? state : copyState(doc.mode, state); 7753 if (ios) { te.style.border = "1px solid black" }
2470 } else { 7754 disableBrowserMagic(te)
2471 if (line.text.length <= cm.options.maxHighlightLength) 7755 return div
2472 processLine(cm, line.text, state); 7756 }
2473 line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : n ull; 7757
2474 } 7758 // The publicly visible API. Note that methodOp(f) means
2475 ++doc.frontier; 7759 // 'wrap f in an operation, performed on its `this` parameter'.
2476 if (+new Date > end) { 7760
2477 startWorker(cm, cm.options.workDelay); 7761 // This is not the complete set of editor methods. Most of the
2478 return true; 7762 // methods defined on the Doc type are also injected into
2479 } 7763 // CodeMirror.prototype, for backwards compatibility and
2480 }); 7764 // convenience.
2481 if (changedLines.length) runInOp(cm, function() { 7765
2482 for (var i = 0; i < changedLines.length; i++) 7766 function addEditorMethods(CodeMirror) {
2483 regLineChange(cm, changedLines[i], "text"); 7767 var optionHandlers = CodeMirror.optionHandlers
2484 }); 7768
2485 } 7769 var helpers = CodeMirror.helpers = {}
2486
2487 // Finds the line to start with when starting a parse. Tries to
2488 // find a line with a stateAfter, so that it can start with a
2489 // valid state. If that fails, it returns the line with the
2490 // smallest indentation, which tends to need the least context to
2491 // parse correctly.
2492 function findStartLine(cm, n, precise) {
2493 var minindent, minline, doc = cm.doc;
2494 var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100);
2495 for (var search = n; search > lim; --search) {
2496 if (search <= doc.first) return doc.first;
2497 var line = getLine(doc, search - 1);
2498 if (line.stateAfter && (!precise || search <= doc.frontier)) return search ;
2499 var indented = countColumn(line.text, null, cm.options.tabSize);
2500 if (minline == null || minindent > indented) {
2501 minline = search - 1;
2502 minindent = indented;
2503 }
2504 }
2505 return minline;
2506 }
2507
2508 function getStateBefore(cm, n, precise) {
2509 var doc = cm.doc, display = cm.display;
2510 if (!doc.mode.startState) return true;
2511 var pos = findStartLine(cm, n, precise), state = pos > doc.first && getLine( doc, pos-1).stateAfter;
2512 if (!state) state = startState(doc.mode);
2513 else state = copyState(doc.mode, state);
2514 doc.iter(pos, n, function(line) {
2515 processLine(cm, line.text, state);
2516 var save = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo;
2517 line.stateAfter = save ? copyState(doc.mode, state) : null;
2518 ++pos;
2519 });
2520 if (precise) doc.frontier = pos;
2521 return state;
2522 }
2523
2524 // POSITION MEASUREMENT
2525
2526 function paddingTop(display) {return display.lineSpace.offsetTop;}
2527 function paddingVert(display) {return display.mover.offsetHeight - display.lin eSpace.offsetHeight;}
2528 function paddingH(display) {
2529 if (display.cachedPaddingH) return display.cachedPaddingH;
2530 var e = removeChildrenAndAdd(display.measure, elt("pre", "x"));
2531 var style = window.getComputedStyle ? window.getComputedStyle(e) : e.current Style;
2532 var data = {left: parseInt(style.paddingLeft), right: parseInt(style.padding Right)};
2533 if (!isNaN(data.left) && !isNaN(data.right)) display.cachedPaddingH = data;
2534 return data;
2535 }
2536
2537 function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth; }
2538 function displayWidth(cm) {
2539 return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth ;
2540 }
2541 function displayHeight(cm) {
2542 return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeig ht;
2543 }
2544
2545 // Ensure the lineView.wrapping.heights array is populated. This is
2546 // an array of bottom offsets for the lines that make up a drawn
2547 // line. When lineWrapping is on, there might be more than one
2548 // height.
2549 function ensureLineHeights(cm, lineView, rect) {
2550 var wrapping = cm.options.lineWrapping;
2551 var curWidth = wrapping && displayWidth(cm);
2552 if (!lineView.measure.heights || wrapping && lineView.measure.width != curWi dth) {
2553 var heights = lineView.measure.heights = [];
2554 if (wrapping) {
2555 lineView.measure.width = curWidth;
2556 var rects = lineView.text.firstChild.getClientRects();
2557 for (var i = 0; i < rects.length - 1; i++) {
2558 var cur = rects[i], next = rects[i + 1];
2559 if (Math.abs(cur.bottom - next.bottom) > 2)
2560 heights.push((cur.bottom + next.top) / 2 - rect.top);
2561 }
2562 }
2563 heights.push(rect.bottom - rect.top);
2564 }
2565 }
2566
2567 // Find a line map (mapping character offsets to text nodes) and a
2568 // measurement cache for the given line number. (A line view might
2569 // contain multiple lines when collapsed ranges are present.)
2570 function mapFromLineView(lineView, line, lineN) {
2571 if (lineView.line == line)
2572 return {map: lineView.measure.map, cache: lineView.measure.cache};
2573 for (var i = 0; i < lineView.rest.length; i++)
2574 if (lineView.rest[i] == line)
2575 return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i] };
2576 for (var i = 0; i < lineView.rest.length; i++)
2577 if (lineNo(lineView.rest[i]) > lineN)
2578 return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i] , before: true};
2579 }
2580
2581 // Render a line into the hidden node display.externalMeasured. Used
2582 // when measurement is needed for a line that's not in the viewport.
2583 function updateExternalMeasurement(cm, line) {
2584 line = visualLine(line);
2585 var lineN = lineNo(line);
2586 var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN);
2587 view.lineN = lineN;
2588 var built = view.built = buildLineContent(cm, view);
2589 view.text = built.pre;
2590 removeChildrenAndAdd(cm.display.lineMeasure, built.pre);
2591 return view;
2592 }
2593
2594 // Get a {top, bottom, left, right} box (in line-local coordinates)
2595 // for a given character.
2596 function measureChar(cm, line, ch, bias) {
2597 return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias);
2598 }
2599
2600 // Find a line view that corresponds to the given line number.
2601 function findViewForLine(cm, lineN) {
2602 if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo)
2603 return cm.display.view[findViewIndex(cm, lineN)];
2604 var ext = cm.display.externalMeasured;
2605 if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size)
2606 return ext;
2607 }
2608
2609 // Measurement can be split in two steps, the set-up work that
2610 // applies to the whole line, and the measurement of the actual
2611 // character. Functions like coordsChar, that need to do a lot of
2612 // measurements in a row, can thus ensure that the set-up work is
2613 // only done once.
2614 function prepareMeasureForLine(cm, line) {
2615 var lineN = lineNo(line);
2616 var view = findViewForLine(cm, lineN);
2617 if (view && !view.text) {
2618 view = null;
2619 } else if (view && view.changes) {
2620 updateLineForChanges(cm, view, lineN, getDimensions(cm));
2621 cm.curOp.forceUpdate = true;
2622 }
2623 if (!view)
2624 view = updateExternalMeasurement(cm, line);
2625
2626 var info = mapFromLineView(view, line, lineN);
2627 return {
2628 line: line, view: view, rect: null,
2629 map: info.map, cache: info.cache, before: info.before,
2630 hasHeights: false
2631 };
2632 }
2633
2634 // Given a prepared measurement object, measures the position of an
2635 // actual character (or fetches it from the cache).
2636 function measureCharPrepared(cm, prepared, ch, bias, varHeight) {
2637 if (prepared.before) ch = -1;
2638 var key = ch + (bias || ""), found;
2639 if (prepared.cache.hasOwnProperty(key)) {
2640 found = prepared.cache[key];
2641 } else {
2642 if (!prepared.rect)
2643 prepared.rect = prepared.view.text.getBoundingClientRect();
2644 if (!prepared.hasHeights) {
2645 ensureLineHeights(cm, prepared.view, prepared.rect);
2646 prepared.hasHeights = true;
2647 }
2648 found = measureCharInner(cm, prepared, ch, bias);
2649 if (!found.bogus) prepared.cache[key] = found;
2650 }
2651 return {left: found.left, right: found.right,
2652 top: varHeight ? found.rtop : found.top,
2653 bottom: varHeight ? found.rbottom : found.bottom};
2654 }
2655
2656 var nullRect = {left: 0, right: 0, top: 0, bottom: 0};
2657
2658 function nodeAndOffsetInLineMap(map, ch, bias) {
2659 var node, start, end, collapse;
2660 // First, search the line map for the text node corresponding to,
2661 // or closest to, the target character.
2662 for (var i = 0; i < map.length; i += 3) {
2663 var mStart = map[i], mEnd = map[i + 1];
2664 if (ch < mStart) {
2665 start = 0; end = 1;
2666 collapse = "left";
2667 } else if (ch < mEnd) {
2668 start = ch - mStart;
2669 end = start + 1;
2670 } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) {
2671 end = mEnd - mStart;
2672 start = end - 1;
2673 if (ch >= mEnd) collapse = "right";
2674 }
2675 if (start != null) {
2676 node = map[i + 2];
2677 if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right"))
2678 collapse = bias;
2679 if (bias == "left" && start == 0)
2680 while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) {
2681 node = map[(i -= 3) + 2];
2682 collapse = "left";
2683 }
2684 if (bias == "right" && start == mEnd - mStart)
2685 while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].i nsertLeft) {
2686 node = map[(i += 3) + 2];
2687 collapse = "right";
2688 }
2689 break;
2690 }
2691 }
2692 return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd};
2693 }
2694
2695 function getUsefulRect(rects, bias) {
2696 var rect = nullRect
2697 if (bias == "left") for (var i = 0; i < rects.length; i++) {
2698 if ((rect = rects[i]).left != rect.right) break
2699 } else for (var i = rects.length - 1; i >= 0; i--) {
2700 if ((rect = rects[i]).left != rect.right) break
2701 }
2702 return rect
2703 }
2704
2705 function measureCharInner(cm, prepared, ch, bias) {
2706 var place = nodeAndOffsetInLineMap(prepared.map, ch, bias);
2707 var node = place.node, start = place.start, end = place.end, collapse = plac e.collapse;
2708
2709 var rect;
2710 if (node.nodeType == 3) { // If it is a text node, use a range to retrieve t he coordinates.
2711 for (var i = 0; i < 4; i++) { // Retry a maximum of 4 times when nonsense rectangles are returned
2712 while (start && isExtendingChar(prepared.line.text.charAt(place.coverSta rt + start))) --start;
2713 while (place.coverStart + end < place.coverEnd && isExtendingChar(prepar ed.line.text.charAt(place.coverStart + end))) ++end;
2714 if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place. coverStart)
2715 rect = node.parentNode.getBoundingClientRect();
2716 else
2717 rect = getUsefulRect(range(node, start, end).getClientRects(), bias)
2718 if (rect.left || rect.right || start == 0) break;
2719 end = start;
2720 start = start - 1;
2721 collapse = "right";
2722 }
2723 if (ie && ie_version < 11) rect = maybeUpdateRectForZooming(cm.display.mea sure, rect);
2724 } else { // If it is a widget, simply get the box for the whole widget.
2725 if (start > 0) collapse = bias = "right";
2726 var rects;
2727 if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)
2728 rect = rects[bias == "right" ? rects.length - 1 : 0];
2729 else
2730 rect = node.getBoundingClientRect();
2731 }
2732 if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {
2733 var rSpan = node.parentNode.getClientRects()[0];
2734 if (rSpan)
2735 rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top : rSpan.top, bottom: rSpan.bottom};
2736 else
2737 rect = nullRect;
2738 }
2739
2740 var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect. top;
2741 var mid = (rtop + rbot) / 2;
2742 var heights = prepared.view.measure.heights;
2743 for (var i = 0; i < heights.length - 1; i++)
2744 if (mid < heights[i]) break;
2745 var top = i ? heights[i - 1] : 0, bot = heights[i];
2746 var result = {left: (collapse == "right" ? rect.right : rect.left) - prepare d.rect.left,
2747 right: (collapse == "left" ? rect.left : rect.right) - prepare d.rect.left,
2748 top: top, bottom: bot};
2749 if (!rect.left && !rect.right) result.bogus = true;
2750 if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbot tom = rbot; }
2751
2752 return result;
2753 }
2754
2755 // Work around problem with bounding client rects on ranges being
2756 // returned incorrectly when zoomed on IE10 and below.
2757 function maybeUpdateRectForZooming(measure, rect) {
2758 if (!window.screen || screen.logicalXDPI == null ||
2759 screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))
2760 return rect;
2761 var scaleX = screen.logicalXDPI / screen.deviceXDPI;
2762 var scaleY = screen.logicalYDPI / screen.deviceYDPI;
2763 return {left: rect.left * scaleX, right: rect.right * scaleX,
2764 top: rect.top * scaleY, bottom: rect.bottom * scaleY};
2765 }
2766
2767 function clearLineMeasurementCacheFor(lineView) {
2768 if (lineView.measure) {
2769 lineView.measure.cache = {};
2770 lineView.measure.heights = null;
2771 if (lineView.rest) for (var i = 0; i < lineView.rest.length; i++)
2772 lineView.measure.caches[i] = {};
2773 }
2774 }
2775
2776 function clearLineMeasurementCache(cm) {
2777 cm.display.externalMeasure = null;
2778 removeChildren(cm.display.lineMeasure);
2779 for (var i = 0; i < cm.display.view.length; i++)
2780 clearLineMeasurementCacheFor(cm.display.view[i]);
2781 }
2782
2783 function clearCaches(cm) {
2784 clearLineMeasurementCache(cm);
2785 cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cached PaddingH = null;
2786 if (!cm.options.lineWrapping) cm.display.maxLineChanged = true;
2787 cm.display.lineNumChars = null;
2788 }
2789
2790 function pageScrollX() { return window.pageXOffset || (document.documentElemen t || document.body).scrollLeft; }
2791 function pageScrollY() { return window.pageYOffset || (document.documentElemen t || document.body).scrollTop; }
2792
2793 // Converts a {top, bottom, left, right} box from line-local
2794 // coordinates into another coordinate system. Context may be one of
2795 // "line", "div" (display.lineDiv), "local"/null (editor), "window",
2796 // or "page".
2797 function intoCoordSystem(cm, lineObj, rect, context) {
2798 if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (li neObj.widgets[i].above) {
2799 var size = widgetHeight(lineObj.widgets[i]);
2800 rect.top += size; rect.bottom += size;
2801 }
2802 if (context == "line") return rect;
2803 if (!context) context = "local";
2804 var yOff = heightAtLine(lineObj);
2805 if (context == "local") yOff += paddingTop(cm.display);
2806 else yOff -= cm.display.viewOffset;
2807 if (context == "page" || context == "window") {
2808 var lOff = cm.display.lineSpace.getBoundingClientRect();
2809 yOff += lOff.top + (context == "window" ? 0 : pageScrollY());
2810 var xOff = lOff.left + (context == "window" ? 0 : pageScrollX());
2811 rect.left += xOff; rect.right += xOff;
2812 }
2813 rect.top += yOff; rect.bottom += yOff;
2814 return rect;
2815 }
2816
2817 // Coverts a box from "div" coords to another coordinate system.
2818 // Context may be "window", "page", "div", or "local"/null.
2819 function fromCoordSystem(cm, coords, context) {
2820 if (context == "div") return coords;
2821 var left = coords.left, top = coords.top;
2822 // First move into "page" coordinate system
2823 if (context == "page") {
2824 left -= pageScrollX();
2825 top -= pageScrollY();
2826 } else if (context == "local" || !context) {
2827 var localBox = cm.display.sizer.getBoundingClientRect();
2828 left += localBox.left;
2829 top += localBox.top;
2830 }
2831
2832 var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect();
2833 return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top};
2834 }
2835
2836 function charCoords(cm, pos, context, lineObj, bias) {
2837 if (!lineObj) lineObj = getLine(cm.doc, pos.line);
2838 return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context);
2839 }
2840
2841 // Returns a box for a given cursor position, which may have an
2842 // 'other' property containing the position of the secondary cursor
2843 // on a bidi boundary.
2844 function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
2845 lineObj = lineObj || getLine(cm.doc, pos.line);
2846 if (!preparedMeasure) preparedMeasure = prepareMeasureForLine(cm, lineObj);
2847 function get(ch, right) {
2848 var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "le ft", varHeight);
2849 if (right) m.left = m.right; else m.right = m.left;
2850 return intoCoordSystem(cm, lineObj, m, context);
2851 }
2852 function getBidi(ch, partPos) {
2853 var part = order[partPos], right = part.level % 2;
2854 if (ch == bidiLeft(part) && partPos && part.level < order[partPos - 1].lev el) {
2855 part = order[--partPos];
2856 ch = bidiRight(part) - (part.level % 2 ? 0 : 1);
2857 right = true;
2858 } else if (ch == bidiRight(part) && partPos < order.length - 1 && part.lev el < order[partPos + 1].level) {
2859 part = order[++partPos];
2860 ch = bidiLeft(part) - part.level % 2;
2861 right = false;
2862 }
2863 if (right && ch == part.to && ch > part.from) return get(ch - 1);
2864 return get(ch, right);
2865 }
2866 var order = getOrder(lineObj), ch = pos.ch;
2867 if (!order) return get(ch);
2868 var partPos = getBidiPartAt(order, ch);
2869 var val = getBidi(ch, partPos);
2870 if (bidiOther != null) val.other = getBidi(ch, bidiOther);
2871 return val;
2872 }
2873
2874 // Used to cheaply estimate the coordinates for a position. Used for
2875 // intermediate scroll updates.
2876 function estimateCoords(cm, pos) {
2877 var left = 0, pos = clipPos(cm.doc, pos);
2878 if (!cm.options.lineWrapping) left = charWidth(cm.display) * pos.ch;
2879 var lineObj = getLine(cm.doc, pos.line);
2880 var top = heightAtLine(lineObj) + paddingTop(cm.display);
2881 return {left: left, right: left, top: top, bottom: top + lineObj.height};
2882 }
2883
2884 // Positions returned by coordsChar contain some extra information.
2885 // xRel is the relative x position of the input coordinates compared
2886 // to the found position (so xRel > 0 means the coordinates are to
2887 // the right of the character position, for example). When outside
2888 // is true, that means the coordinates lie outside the line's
2889 // vertical range.
2890 function PosWithInfo(line, ch, outside, xRel) {
2891 var pos = Pos(line, ch);
2892 pos.xRel = xRel;
2893 if (outside) pos.outside = true;
2894 return pos;
2895 }
2896
2897 // Compute the character position closest to the given coordinates.
2898 // Input must be lineSpace-local ("div" coordinate system).
2899 function coordsChar(cm, x, y) {
2900 var doc = cm.doc;
2901 y += cm.display.viewOffset;
2902 if (y < 0) return PosWithInfo(doc.first, 0, true, -1);
2903 var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
2904 if (lineN > last)
2905 return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.lengt h, true, 1);
2906 if (x < 0) x = 0;
2907
2908 var lineObj = getLine(doc, lineN);
2909 for (;;) {
2910 var found = coordsCharInner(cm, lineObj, lineN, x, y);
2911 var merged = collapsedSpanAtEnd(lineObj);
2912 var mergedPos = merged && merged.find(0, true);
2913 if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from. ch && found.xRel > 0))
2914 lineN = lineNo(lineObj = mergedPos.to.line);
2915 else
2916 return found;
2917 }
2918 }
2919
2920 function coordsCharInner(cm, lineObj, lineNo, x, y) {
2921 var innerOff = y - heightAtLine(lineObj);
2922 var wrongLine = false, adjust = 2 * cm.display.wrapper.clientWidth;
2923 var preparedMeasure = prepareMeasureForLine(cm, lineObj);
2924
2925 function getX(ch) {
2926 var sp = cursorCoords(cm, Pos(lineNo, ch), "line", lineObj, preparedMeasur e);
2927 wrongLine = true;
2928 if (innerOff > sp.bottom) return sp.left - adjust;
2929 else if (innerOff < sp.top) return sp.left + adjust;
2930 else wrongLine = false;
2931 return sp.left;
2932 }
2933
2934 var bidi = getOrder(lineObj), dist = lineObj.text.length;
2935 var from = lineLeft(lineObj), to = lineRight(lineObj);
2936 var fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine;
2937
2938 if (x > toX) return PosWithInfo(lineNo, to, toOutside, 1);
2939 // Do a binary search between these bounds.
2940 for (;;) {
2941 if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
2942 var ch = x < fromX || x - fromX <= toX - x ? from : to;
2943 var outside = ch == from ? fromOutside : toOutside
2944 var xDiff = x - (ch == from ? fromX : toX);
2945 // This is a kludge to handle the case where the coordinates
2946 // are after a line-wrapped line. We should replace it with a
2947 // more general handling of cursor positions around line
2948 // breaks. (Issue #4078)
2949 if (toOutside && !bidi && !/\s/.test(lineObj.text.charAt(ch)) && xDiff > 0 &&
2950 ch < lineObj.text.length && preparedMeasure.view.measure.heights.len gth > 1) {
2951 var charSize = measureCharPrepared(cm, preparedMeasure, ch, "right");
2952 if (innerOff <= charSize.bottom && innerOff >= charSize.top && Math.ab s(x - charSize.right) < xDiff) {
2953 outside = false
2954 ch++
2955 xDiff = x - charSize.right
2956 }
2957 }
2958 while (isExtendingChar(lineObj.text.charAt(ch))) ++ch;
2959 var pos = PosWithInfo(lineNo, ch, outside, xDiff < -1 ? -1 : xDiff > 1 ? 1 : 0);
2960 return pos;
2961 }
2962 var step = Math.ceil(dist / 2), middle = from + step;
2963 if (bidi) {
2964 middle = from;
2965 for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1) ;
2966 }
2967 var middleX = getX(middle);
2968 if (middleX > x) {to = middle; toX = middleX; if (toOutside = wrongLine) t oX += 1000; dist = step;}
2969 else {from = middle; fromX = middleX; fromOutside = wrongLine; dist -= ste p;}
2970 }
2971 }
2972
2973 var measureText;
2974 // Compute the default text height.
2975 function textHeight(display) {
2976 if (display.cachedTextHeight != null) return display.cachedTextHeight;
2977 if (measureText == null) {
2978 measureText = elt("pre");
2979 // Measure a bunch of lines, for browsers that compute
2980 // fractional heights.
2981 for (var i = 0; i < 49; ++i) {
2982 measureText.appendChild(document.createTextNode("x"));
2983 measureText.appendChild(elt("br"));
2984 }
2985 measureText.appendChild(document.createTextNode("x"));
2986 }
2987 removeChildrenAndAdd(display.measure, measureText);
2988 var height = measureText.offsetHeight / 50;
2989 if (height > 3) display.cachedTextHeight = height;
2990 removeChildren(display.measure);
2991 return height || 1;
2992 }
2993
2994 // Compute the default character width.
2995 function charWidth(display) {
2996 if (display.cachedCharWidth != null) return display.cachedCharWidth;
2997 var anchor = elt("span", "xxxxxxxxxx");
2998 var pre = elt("pre", [anchor]);
2999 removeChildrenAndAdd(display.measure, pre);
3000 var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10;
3001 if (width > 2) display.cachedCharWidth = width;
3002 return width || 10;
3003 }
3004
3005 // OPERATIONS
3006
3007 // Operations are used to wrap a series of changes to the editor
3008 // state in such a way that each change won't have to update the
3009 // cursor and display (which would be awkward, slow, and
3010 // error-prone). Instead, display updates are batched and then all
3011 // combined and executed at once.
3012
3013 var operationGroup = null;
3014
3015 var nextOpId = 0;
3016 // Start a new operation.
3017 function startOperation(cm) {
3018 cm.curOp = {
3019 cm: cm,
3020 viewChanged: false, // Flag that indicates that lines might need to b e redrawn
3021 startHeight: cm.doc.height, // Used to detect need to update scrollbar
3022 forceUpdate: false, // Used to force a redraw
3023 updateInput: null, // Whether to reset the input textarea
3024 typing: false, // Whether this reset should be careful to leave existing text (for compositing)
3025 changeObjs: null, // Accumulated changes, for firing change events
3026 cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on
3027 cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
3028 selectionChanged: false, // Whether the selection needs to be redrawn
3029 updateMaxLine: false, // Set when the widest line needs to be determine d anew
3030 scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pu shed to DOM yet
3031 scrollToPos: null, // Used to scroll to a specific position
3032 focus: false,
3033 id: ++nextOpId // Unique ID
3034 };
3035 if (operationGroup) {
3036 operationGroup.ops.push(cm.curOp);
3037 } else {
3038 cm.curOp.ownsGroup = operationGroup = {
3039 ops: [cm.curOp],
3040 delayedCallbacks: []
3041 };
3042 }
3043 }
3044
3045 function fireCallbacksForOps(group) {
3046 // Calls delayed callbacks and cursorActivity handlers until no
3047 // new ones appear
3048 var callbacks = group.delayedCallbacks, i = 0;
3049 do {
3050 for (; i < callbacks.length; i++)
3051 callbacks[i].call(null);
3052 for (var j = 0; j < group.ops.length; j++) {
3053 var op = group.ops[j];
3054 if (op.cursorActivityHandlers)
3055 while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
3056 op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.c m);
3057 }
3058 } while (i < callbacks.length);
3059 }
3060
3061 // Finish an operation, updating the display and signalling delayed events
3062 function endOperation(cm) {
3063 var op = cm.curOp, group = op.ownsGroup;
3064 if (!group) return;
3065
3066 try { fireCallbacksForOps(group); }
3067 finally {
3068 operationGroup = null;
3069 for (var i = 0; i < group.ops.length; i++)
3070 group.ops[i].cm.curOp = null;
3071 endOperations(group);
3072 }
3073 }
3074
3075 // The DOM updates done when an operation finishes are batched so
3076 // that the minimum number of relayouts are required.
3077 function endOperations(group) {
3078 var ops = group.ops;
3079 for (var i = 0; i < ops.length; i++) // Read DOM
3080 endOperation_R1(ops[i]);
3081 for (var i = 0; i < ops.length; i++) // Write DOM (maybe)
3082 endOperation_W1(ops[i]);
3083 for (var i = 0; i < ops.length; i++) // Read DOM
3084 endOperation_R2(ops[i]);
3085 for (var i = 0; i < ops.length; i++) // Write DOM (maybe)
3086 endOperation_W2(ops[i]);
3087 for (var i = 0; i < ops.length; i++) // Read DOM
3088 endOperation_finish(ops[i]);
3089 }
3090
3091 function endOperation_R1(op) {
3092 var cm = op.cm, display = cm.display;
3093 maybeClipScrollbars(cm);
3094 if (op.updateMaxLine) findMaxLine(cm);
3095
3096 op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||
3097 op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
3098 op.scrollToPos.to.line >= display.viewTo) ||
3099 display.maxLineChanged && cm.options.lineWrapping;
3100 op.update = op.mustUpdate &&
3101 new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scro llToPos}, op.forceUpdate);
3102 }
3103
3104 function endOperation_W1(op) {
3105 op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update) ;
3106 }
3107
3108 function endOperation_R2(op) {
3109 var cm = op.cm, display = cm.display;
3110 if (op.updatedDisplay) updateHeightsInViewport(cm);
3111
3112 op.barMeasure = measureForScrollbars(cm);
3113
3114 // If the max line changed since it was last measured, measure it,
3115 // and ensure the document's width matches it.
3116 // updateDisplay_W2 will use these properties to do the actual resizing
3117 if (display.maxLineChanged && !cm.options.lineWrapping) {
3118 op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.l ength).left + 3;
3119 cm.display.sizerWidth = op.adjustWidthTo;
3120 op.barMeasure.scrollWidth =
3121 Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adj ustWidthTo + scrollGap(cm) + cm.display.barWidth);
3122 op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm));
3123 }
3124
3125 if (op.updatedDisplay || op.selectionChanged)
3126 op.preparedSelection = display.input.prepareSelection(op.focus);
3127 }
3128
3129 function endOperation_W2(op) {
3130 var cm = op.cm;
3131
3132 if (op.adjustWidthTo != null) {
3133 cm.display.sizer.style.minWidth = op.adjustWidthTo + "px";
3134 if (op.maxScrollLeft < cm.doc.scrollLeft)
3135 setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollL eft), true);
3136 cm.display.maxLineChanged = false;
3137 }
3138
3139 var takeFocus = op.focus && op.focus == activeElt() && (!document.hasFocus | | document.hasFocus())
3140 if (op.preparedSelection)
3141 cm.display.input.showSelection(op.preparedSelection, takeFocus);
3142 if (op.updatedDisplay || op.startHeight != cm.doc.height)
3143 updateScrollbars(cm, op.barMeasure);
3144 if (op.updatedDisplay)
3145 setDocumentHeight(cm, op.barMeasure);
3146
3147 if (op.selectionChanged) restartBlink(cm);
3148
3149 if (cm.state.focused && op.updateInput)
3150 cm.display.input.reset(op.typing);
3151 if (takeFocus) ensureFocus(op.cm);
3152 }
3153
3154 function endOperation_finish(op) {
3155 var cm = op.cm, display = cm.display, doc = cm.doc;
3156
3157 if (op.updatedDisplay) postUpdateDisplay(cm, op.update);
3158
3159 // Abort mouse wheel delta measurement, when scrolling explicitly
3160 if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))
3161 display.wheelStartX = display.wheelStartY = null;
3162
3163 // Propagate the scroll position to the actual DOM scroller
3164 if (op.scrollTop != null && (display.scroller.scrollTop != op.scrollTop || o p.forceScroll)) {
3165 doc.scrollTop = Math.max(0, Math.min(display.scroller.scrollHeight - displ ay.scroller.clientHeight, op.scrollTop));
3166 display.scrollbars.setScrollTop(doc.scrollTop);
3167 display.scroller.scrollTop = doc.scrollTop;
3168 }
3169 if (op.scrollLeft != null && (display.scroller.scrollLeft != op.scrollLeft | | op.forceScroll)) {
3170 doc.scrollLeft = Math.max(0, Math.min(display.scroller.scrollWidth - displ ay.scroller.clientWidth, op.scrollLeft));
3171 display.scrollbars.setScrollLeft(doc.scrollLeft);
3172 display.scroller.scrollLeft = doc.scrollLeft;
3173 alignHorizontally(cm);
3174 }
3175 // If we need to scroll a specific position into view, do so.
3176 if (op.scrollToPos) {
3177 var coords = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
3178 clipPos(doc, op.scrollToPos.to), op.scrollT oPos.margin);
3179 if (op.scrollToPos.isCursor && cm.state.focused) maybeScrollWindow(cm, coo rds);
3180 }
3181
3182 // Fire events for markers that are hidden/unidden by editing or
3183 // undoing
3184 var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
3185 if (hidden) for (var i = 0; i < hidden.length; ++i)
3186 if (!hidden[i].lines.length) signal(hidden[i], "hide");
3187 if (unhidden) for (var i = 0; i < unhidden.length; ++i)
3188 if (unhidden[i].lines.length) signal(unhidden[i], "unhide");
3189
3190 if (display.wrapper.offsetHeight)
3191 doc.scrollTop = cm.display.scroller.scrollTop;
3192
3193 // Fire change events, and delayed event handlers
3194 if (op.changeObjs)
3195 signal(cm, "changes", cm, op.changeObjs);
3196 if (op.update)
3197 op.update.finish();
3198 }
3199
3200 // Run the given function in an operation
3201 function runInOp(cm, f) {
3202 if (cm.curOp) return f();
3203 startOperation(cm);
3204 try { return f(); }
3205 finally { endOperation(cm); }
3206 }
3207 // Wraps a function in an operation. Returns the wrapped function.
3208 function operation(cm, f) {
3209 return function() {
3210 if (cm.curOp) return f.apply(cm, arguments);
3211 startOperation(cm);
3212 try { return f.apply(cm, arguments); }
3213 finally { endOperation(cm); }
3214 };
3215 }
3216 // Used to add methods to editor and doc instances, wrapping them in
3217 // operations.
3218 function methodOp(f) {
3219 return function() {
3220 if (this.curOp) return f.apply(this, arguments);
3221 startOperation(this);
3222 try { return f.apply(this, arguments); }
3223 finally { endOperation(this); }
3224 };
3225 }
3226 function docMethodOp(f) {
3227 return function() {
3228 var cm = this.cm;
3229 if (!cm || cm.curOp) return f.apply(this, arguments);
3230 startOperation(cm);
3231 try { return f.apply(this, arguments); }
3232 finally { endOperation(cm); }
3233 };
3234 }
3235
3236 // VIEW TRACKING
3237
3238 // These objects are used to represent the visible (currently drawn)
3239 // part of the document. A LineView may correspond to multiple
3240 // logical lines, if those are connected by collapsed ranges.
3241 function LineView(doc, line, lineN) {
3242 // The starting line
3243 this.line = line;
3244 // Continuing lines, if any
3245 this.rest = visualLineContinued(line);
3246 // Number of logical lines in this visual line
3247 this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1;
3248 this.node = this.text = null;
3249 this.hidden = lineIsHidden(doc, line);
3250 }
3251
3252 // Create a range of LineView objects for the given lines.
3253 function buildViewArray(cm, from, to) {
3254 var array = [], nextPos;
3255 for (var pos = from; pos < to; pos = nextPos) {
3256 var view = new LineView(cm.doc, getLine(cm.doc, pos), pos);
3257 nextPos = pos + view.size;
3258 array.push(view);
3259 }
3260 return array;
3261 }
3262
3263 // Updates the display.view data structure for a given change to the
3264 // document. From and to are in pre-change coordinates. Lendiff is
3265 // the amount of lines added or subtracted by the change. This is
3266 // used for changes that span multiple lines, or change the way
3267 // lines are divided into visual lines. regLineChange (below)
3268 // registers single-line changes.
3269 function regChange(cm, from, to, lendiff) {
3270 if (from == null) from = cm.doc.first;
3271 if (to == null) to = cm.doc.first + cm.doc.size;
3272 if (!lendiff) lendiff = 0;
3273
3274 var display = cm.display;
3275 if (lendiff && to < display.viewTo &&
3276 (display.updateLineNumbers == null || display.updateLineNumbers > from))
3277 display.updateLineNumbers = from;
3278
3279 cm.curOp.viewChanged = true;
3280
3281 if (from >= display.viewTo) { // Change after
3282 if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)
3283 resetView(cm);
3284 } else if (to <= display.viewFrom) { // Change before
3285 if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.v iewFrom) {
3286 resetView(cm);
3287 } else {
3288 display.viewFrom += lendiff;
3289 display.viewTo += lendiff;
3290 }
3291 } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overl ap
3292 resetView(cm);
3293 } else if (from <= display.viewFrom) { // Top overlap
3294 var cut = viewCuttingPoint(cm, to, to + lendiff, 1);
3295 if (cut) {
3296 display.view = display.view.slice(cut.index);
3297 display.viewFrom = cut.lineN;
3298 display.viewTo += lendiff;
3299 } else {
3300 resetView(cm);
3301 }
3302 } else if (to >= display.viewTo) { // Bottom overlap
3303 var cut = viewCuttingPoint(cm, from, from, -1);
3304 if (cut) {
3305 display.view = display.view.slice(0, cut.index);
3306 display.viewTo = cut.lineN;
3307 } else {
3308 resetView(cm);
3309 }
3310 } else { // Gap in the middle
3311 var cutTop = viewCuttingPoint(cm, from, from, -1);
3312 var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1);
3313 if (cutTop && cutBot) {
3314 display.view = display.view.slice(0, cutTop.index)
3315 .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))
3316 .concat(display.view.slice(cutBot.index));
3317 display.viewTo += lendiff;
3318 } else {
3319 resetView(cm);
3320 }
3321 }
3322
3323 var ext = display.externalMeasured;
3324 if (ext) {
3325 if (to < ext.lineN)
3326 ext.lineN += lendiff;
3327 else if (from < ext.lineN + ext.size)
3328 display.externalMeasured = null;
3329 }
3330 }
3331
3332 // Register a change to a single line. Type must be one of "text",
3333 // "gutter", "class", "widget"
3334 function regLineChange(cm, line, type) {
3335 cm.curOp.viewChanged = true;
3336 var display = cm.display, ext = cm.display.externalMeasured;
3337 if (ext && line >= ext.lineN && line < ext.lineN + ext.size)
3338 display.externalMeasured = null;
3339
3340 if (line < display.viewFrom || line >= display.viewTo) return;
3341 var lineView = display.view[findViewIndex(cm, line)];
3342 if (lineView.node == null) return;
3343 var arr = lineView.changes || (lineView.changes = []);
3344 if (indexOf(arr, type) == -1) arr.push(type);
3345 }
3346
3347 // Clear the view.
3348 function resetView(cm) {
3349 cm.display.viewFrom = cm.display.viewTo = cm.doc.first;
3350 cm.display.view = [];
3351 cm.display.viewOffset = 0;
3352 }
3353
3354 // Find the view element corresponding to a given line. Return null
3355 // when the line isn't visible.
3356 function findViewIndex(cm, n) {
3357 if (n >= cm.display.viewTo) return null;
3358 n -= cm.display.viewFrom;
3359 if (n < 0) return null;
3360 var view = cm.display.view;
3361 for (var i = 0; i < view.length; i++) {
3362 n -= view[i].size;
3363 if (n < 0) return i;
3364 }
3365 }
3366
3367 function viewCuttingPoint(cm, oldN, newN, dir) {
3368 var index = findViewIndex(cm, oldN), diff, view = cm.display.view;
3369 if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)
3370 return {index: index, lineN: newN};
3371 for (var i = 0, n = cm.display.viewFrom; i < index; i++)
3372 n += view[i].size;
3373 if (n != oldN) {
3374 if (dir > 0) {
3375 if (index == view.length - 1) return null;
3376 diff = (n + view[index].size) - oldN;
3377 index++;
3378 } else {
3379 diff = n - oldN;
3380 }
3381 oldN += diff; newN += diff;
3382 }
3383 while (visualLineNo(cm.doc, newN) != newN) {
3384 if (index == (dir < 0 ? 0 : view.length - 1)) return null;
3385 newN += dir * view[index - (dir < 0 ? 1 : 0)].size;
3386 index += dir;
3387 }
3388 return {index: index, lineN: newN};
3389 }
3390
3391 // Force the view to cover a given range, adding empty view element
3392 // or clipping off existing ones as needed.
3393 function adjustView(cm, from, to) {
3394 var display = cm.display, view = display.view;
3395 if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {
3396 display.view = buildViewArray(cm, from, to);
3397 display.viewFrom = from;
3398 } else {
3399 if (display.viewFrom > from)
3400 display.view = buildViewArray(cm, from, display.viewFrom).concat(display .view);
3401 else if (display.viewFrom < from)
3402 display.view = display.view.slice(findViewIndex(cm, from));
3403 display.viewFrom = from;
3404 if (display.viewTo < to)
3405 display.view = display.view.concat(buildViewArray(cm, display.viewTo, to ));
3406 else if (display.viewTo > to)
3407 display.view = display.view.slice(0, findViewIndex(cm, to));
3408 }
3409 display.viewTo = to;
3410 }
3411
3412 // Count the number of lines in the view whose DOM representation is
3413 // out of date (or nonexistent).
3414 function countDirtyView(cm) {
3415 var view = cm.display.view, dirty = 0;
3416 for (var i = 0; i < view.length; i++) {
3417 var lineView = view[i];
3418 if (!lineView.hidden && (!lineView.node || lineView.changes)) ++dirty;
3419 }
3420 return dirty;
3421 }
3422
3423 // EVENT HANDLERS
3424
3425 // Attach the necessary event handlers when initializing the editor
3426 function registerEventHandlers(cm) {
3427 var d = cm.display;
3428 on(d.scroller, "mousedown", operation(cm, onMouseDown));
3429 // Older IE's will not fire a second mousedown for a double click
3430 if (ie && ie_version < 11)
3431 on(d.scroller, "dblclick", operation(cm, function(e) {
3432 if (signalDOMEvent(cm, e)) return;
3433 var pos = posFromMouse(cm, e);
3434 if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) return ;
3435 e_preventDefault(e);
3436 var word = cm.findWordAt(pos);
3437 extendSelection(cm.doc, word.anchor, word.head);
3438 }));
3439 else
3440 on(d.scroller, "dblclick", function(e) { signalDOMEvent(cm, e) || e_preven tDefault(e); });
3441 // Some browsers fire contextmenu *after* opening the menu, at
3442 // which point we can't mess with it anymore. Context menu is
3443 // handled in onMouseDown for these browsers.
3444 if (!captureRightClick) on(d.scroller, "contextmenu", function(e) {onContext Menu(cm, e);});
3445
3446 // Used to suppress mouse event handling when a touch happens
3447 var touchFinished, prevTouch = {end: 0};
3448 function finishTouch() {
3449 if (d.activeTouch) {
3450 touchFinished = setTimeout(function() {d.activeTouch = null;}, 1000);
3451 prevTouch = d.activeTouch;
3452 prevTouch.end = +new Date;
3453 }
3454 };
3455 function isMouseLikeTouchEvent(e) {
3456 if (e.touches.length != 1) return false;
3457 var touch = e.touches[0];
3458 return touch.radiusX <= 1 && touch.radiusY <= 1;
3459 }
3460 function farAway(touch, other) {
3461 if (other.left == null) return true;
3462 var dx = other.left - touch.left, dy = other.top - touch.top;
3463 return dx * dx + dy * dy > 20 * 20;
3464 }
3465 on(d.scroller, "touchstart", function(e) {
3466 if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e)) {
3467 clearTimeout(touchFinished);
3468 var now = +new Date;
3469 d.activeTouch = {start: now, moved: false,
3470 prev: now - prevTouch.end <= 300 ? prevTouch : null};
3471 if (e.touches.length == 1) {
3472 d.activeTouch.left = e.touches[0].pageX;
3473 d.activeTouch.top = e.touches[0].pageY;
3474 }
3475 }
3476 });
3477 on(d.scroller, "touchmove", function() {
3478 if (d.activeTouch) d.activeTouch.moved = true;
3479 });
3480 on(d.scroller, "touchend", function(e) {
3481 var touch = d.activeTouch;
3482 if (touch && !eventInWidget(d, e) && touch.left != null &&
3483 !touch.moved && new Date - touch.start < 300) {
3484 var pos = cm.coordsChar(d.activeTouch, "page"), range;
3485 if (!touch.prev || farAway(touch, touch.prev)) // Single tap
3486 range = new Range(pos, pos);
3487 else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap
3488 range = cm.findWordAt(pos);
3489 else // Triple tap
3490 range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0)));
3491 cm.setSelection(range.anchor, range.head);
3492 cm.focus();
3493 e_preventDefault(e);
3494 }
3495 finishTouch();
3496 });
3497 on(d.scroller, "touchcancel", finishTouch);
3498
3499 // Sync scrolling between fake scrollbars and real scrollable
3500 // area, ensure viewport is updated when scrolling.
3501 on(d.scroller, "scroll", function() {
3502 if (d.scroller.clientHeight) {
3503 setScrollTop(cm, d.scroller.scrollTop);
3504 setScrollLeft(cm, d.scroller.scrollLeft, true);
3505 signal(cm, "scroll", cm);
3506 }
3507 });
3508
3509 // Listen to wheel events in order to try and update the viewport on time.
3510 on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);});
3511 on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);});
3512
3513 // Prevent wrapper from ever scrolling
3514 on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollL eft = 0; });
3515
3516 d.dragFunctions = {
3517 enter: function(e) {if (!signalDOMEvent(cm, e)) e_stop(e);},
3518 over: function(e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop (e); }},
3519 start: function(e){onDragStart(cm, e);},
3520 drop: operation(cm, onDrop),
3521 leave: function(e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm); }}
3522 };
3523
3524 var inp = d.input.getField();
3525 on(inp, "keyup", function(e) { onKeyUp.call(cm, e); });
3526 on(inp, "keydown", operation(cm, onKeyDown));
3527 on(inp, "keypress", operation(cm, onKeyPress));
3528 on(inp, "focus", bind(onFocus, cm));
3529 on(inp, "blur", bind(onBlur, cm));
3530 }
3531
3532 function dragDropChanged(cm, value, old) {
3533 var wasOn = old && old != CodeMirror.Init;
3534 if (!value != !wasOn) {
3535 var funcs = cm.display.dragFunctions;
3536 var toggle = value ? on : off;
3537 toggle(cm.display.scroller, "dragstart", funcs.start);
3538 toggle(cm.display.scroller, "dragenter", funcs.enter);
3539 toggle(cm.display.scroller, "dragover", funcs.over);
3540 toggle(cm.display.scroller, "dragleave", funcs.leave);
3541 toggle(cm.display.scroller, "drop", funcs.drop);
3542 }
3543 }
3544
3545 // Called when the window resizes
3546 function onResize(cm) {
3547 var d = cm.display;
3548 if (d.lastWrapHeight == d.wrapper.clientHeight && d.lastWrapWidth == d.wrapp er.clientWidth)
3549 return;
3550 // Might be a text scaling operation, clear size caches.
3551 d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
3552 d.scrollbarsClipped = false;
3553 cm.setSize();
3554 }
3555
3556 // MOUSE EVENTS
3557
3558 // Return true when the given mouse event happened in a widget
3559 function eventInWidget(display, e) {
3560 for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
3561 if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true" ) ||
3562 (n.parentNode == display.sizer && n != display.mover))
3563 return true;
3564 }
3565 }
3566
3567 // Given a mouse event, find the corresponding position. If liberal
3568 // is false, it checks whether a gutter or scrollbar was clicked,
3569 // and returns null if it was. forRect is used by rectangular
3570 // selections, and tries to estimate a character position even for
3571 // coordinates beyond the right of the text.
3572 function posFromMouse(cm, e, liberal, forRect) {
3573 var display = cm.display;
3574 if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") return null;
3575
3576 var x, y, space = display.lineSpace.getBoundingClientRect();
3577 // Fails unpredictably on IE[67] when mouse is dragged around quickly.
3578 try { x = e.clientX - space.left; y = e.clientY - space.top; }
3579 catch (e) { return null; }
3580 var coords = coordsChar(cm, x, y), line;
3581 if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text ).length == coords.ch) {
3582 var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.le ngth;
3583 coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display) .left) / charWidth(cm.display)) - colDiff));
3584 }
3585 return coords;
3586 }
3587
3588 // A mouse down can be a single click, double click, triple click,
3589 // start of selection drag, start of text drag, new cursor
3590 // (ctrl-click), rectangle drag (alt-drag), or xwin
3591 // middle-click-paste. Or it might be a click on something we should
3592 // not interfere with, such as a scrollbar or widget.
3593 function onMouseDown(e) {
3594 var cm = this, display = cm.display;
3595 if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTo uch()) return;
3596 display.shift = e.shiftKey;
3597
3598 if (eventInWidget(display, e)) {
3599 if (!webkit) {
3600 // Briefly turn off draggability, to allow widgets to do
3601 // normal dragging things.
3602 display.scroller.draggable = false;
3603 setTimeout(function(){display.scroller.draggable = true;}, 100);
3604 }
3605 return;
3606 }
3607 if (clickInGutter(cm, e)) return;
3608 var start = posFromMouse(cm, e);
3609 window.focus();
3610
3611 switch (e_button(e)) {
3612 case 1:
3613 // #3261: make sure, that we're not starting a second selection
3614 if (cm.state.selectingText)
3615 cm.state.selectingText(e);
3616 else if (start)
3617 leftButtonDown(cm, e, start);
3618 else if (e_target(e) == display.scroller)
3619 e_preventDefault(e);
3620 break;
3621 case 2:
3622 if (webkit) cm.state.lastMiddleDown = +new Date;
3623 if (start) extendSelection(cm.doc, start);
3624 setTimeout(function() {display.input.focus();}, 20);
3625 e_preventDefault(e);
3626 break;
3627 case 3:
3628 if (captureRightClick) onContextMenu(cm, e);
3629 else delayBlurEvent(cm);
3630 break;
3631 }
3632 }
3633
3634 var lastClick, lastDoubleClick;
3635 function leftButtonDown(cm, e, start) {
3636 if (ie) setTimeout(bind(ensureFocus, cm), 0);
3637 else cm.curOp.focus = activeElt();
3638
3639 var now = +new Date, type;
3640 if (lastDoubleClick && lastDoubleClick.time > now - 400 && cmp(lastDoubleCli ck.pos, start) == 0) {
3641 type = "triple";
3642 } else if (lastClick && lastClick.time > now - 400 && cmp(lastClick.pos, sta rt) == 0) {
3643 type = "double";
3644 lastDoubleClick = {time: now, pos: start};
3645 } else {
3646 type = "single";
3647 lastClick = {time: now, pos: start};
3648 }
3649
3650 var sel = cm.doc.sel, modifier = mac ? e.metaKey : e.ctrlKey, contained;
3651 if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() &&
3652 type == "single" && (contained = sel.contains(start)) > -1 &&
3653 (cmp((contained = sel.ranges[contained]).from(), start) < 0 || start.xRe l > 0) &&
3654 (cmp(contained.to(), start) > 0 || start.xRel < 0))
3655 leftButtonStartDrag(cm, e, start, modifier);
3656 else
3657 leftButtonSelect(cm, e, start, type, modifier);
3658 }
3659
3660 // Start a text drag. When it ends, see if any dragging actually
3661 // happen, and treat as a click if it didn't.
3662 function leftButtonStartDrag(cm, e, start, modifier) {
3663 var display = cm.display, startTime = +new Date;
3664 var dragEnd = operation(cm, function(e2) {
3665 if (webkit) display.scroller.draggable = false;
3666 cm.state.draggingText = false;
3667 off(document, "mouseup", dragEnd);
3668 off(display.scroller, "drop", dragEnd);
3669 if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
3670 e_preventDefault(e2);
3671 if (!modifier && +new Date - 200 < startTime)
3672 extendSelection(cm.doc, start);
3673 // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3 081)
3674 if (webkit || ie && ie_version == 9)
3675 setTimeout(function() {document.body.focus(); display.input.focus();}, 20);
3676 else
3677 display.input.focus();
3678 }
3679 });
3680 // Let the drag handler handle this.
3681 if (webkit) display.scroller.draggable = true;
3682 cm.state.draggingText = dragEnd;
3683 dragEnd.copy = mac ? e.altKey : e.ctrlKey
3684 // IE's approach to draggable
3685 if (display.scroller.dragDrop) display.scroller.dragDrop();
3686 on(document, "mouseup", dragEnd);
3687 on(display.scroller, "drop", dragEnd);
3688 }
3689
3690 // Normal selection, as opposed to text dragging.
3691 function leftButtonSelect(cm, e, start, type, addNew) {
3692 var display = cm.display, doc = cm.doc;
3693 e_preventDefault(e);
3694
3695 var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges;
3696 if (addNew && !e.shiftKey) {
3697 ourIndex = doc.sel.contains(start);
3698 if (ourIndex > -1)
3699 ourRange = ranges[ourIndex];
3700 else
3701 ourRange = new Range(start, start);
3702 } else {
3703 ourRange = doc.sel.primary();
3704 ourIndex = doc.sel.primIndex;
3705 }
3706
3707 if (chromeOS ? e.shiftKey && e.metaKey : e.altKey) {
3708 type = "rect";
3709 if (!addNew) ourRange = new Range(start, start);
3710 start = posFromMouse(cm, e, true, true);
3711 ourIndex = -1;
3712 } else if (type == "double") {
3713 var word = cm.findWordAt(start);
3714 if (cm.display.shift || doc.extend)
3715 ourRange = extendRange(doc, ourRange, word.anchor, word.head);
3716 else
3717 ourRange = word;
3718 } else if (type == "triple") {
3719 var line = new Range(Pos(start.line, 0), clipPos(doc, Pos(start.line + 1, 0)));
3720 if (cm.display.shift || doc.extend)
3721 ourRange = extendRange(doc, ourRange, line.anchor, line.head);
3722 else
3723 ourRange = line;
3724 } else {
3725 ourRange = extendRange(doc, ourRange, start);
3726 }
3727
3728 if (!addNew) {
3729 ourIndex = 0;
3730 setSelection(doc, new Selection([ourRange], 0), sel_mouse);
3731 startSel = doc.sel;
3732 } else if (ourIndex == -1) {
3733 ourIndex = ranges.length;
3734 setSelection(doc, normalizeSelection(ranges.concat([ourRange]), ourIndex),
3735 {scroll: false, origin: "*mouse"});
3736 } else if (ranges.length > 1 && ranges[ourIndex].empty() && type == "single" && !e.shiftKey) {
3737 setSelection(doc, normalizeSelection(ranges.slice(0, ourIndex).concat(rang es.slice(ourIndex + 1)), 0),
3738 {scroll: false, origin: "*mouse"});
3739 startSel = doc.sel;
3740 } else {
3741 replaceOneSelection(doc, ourIndex, ourRange, sel_mouse);
3742 }
3743
3744 var lastPos = start;
3745 function extendTo(pos) {
3746 if (cmp(lastPos, pos) == 0) return;
3747 lastPos = pos;
3748
3749 if (type == "rect") {
3750 var ranges = [], tabSize = cm.options.tabSize;
3751 var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabS ize);
3752 var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize);
3753 var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol );
3754 for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLi ne(), Math.max(start.line, pos.line));
3755 line <= end; line++) {
3756 var text = getLine(doc, line).text, leftPos = findColumn(text, left, t abSize);
3757 if (left == right)
3758 ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos)));
3759 else if (text.length > leftPos)
3760 ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize))));
3761 }
3762 if (!ranges.length) ranges.push(new Range(start, start));
3763 setSelection(doc, normalizeSelection(startSel.ranges.slice(0, ourIndex). concat(ranges), ourIndex),
3764 {origin: "*mouse", scroll: false});
3765 cm.scrollIntoView(pos);
3766 } else {
3767 var oldRange = ourRange;
3768 var anchor = oldRange.anchor, head = pos;
3769 if (type != "single") {
3770 if (type == "double")
3771 var range = cm.findWordAt(pos);
3772 else
3773 var range = new Range(Pos(pos.line, 0), clipPos(doc, Pos(pos.line + 1, 0)));
3774 if (cmp(range.anchor, anchor) > 0) {
3775 head = range.head;
3776 anchor = minPos(oldRange.from(), range.anchor);
3777 } else {
3778 head = range.anchor;
3779 anchor = maxPos(oldRange.to(), range.head);
3780 }
3781 }
3782 var ranges = startSel.ranges.slice(0);
3783 ranges[ourIndex] = new Range(clipPos(doc, anchor), head);
3784 setSelection(doc, normalizeSelection(ranges, ourIndex), sel_mouse);
3785 }
3786 }
3787
3788 var editorSize = display.wrapper.getBoundingClientRect();
3789 // Used to ensure timeout re-tries don't fire when another extend
3790 // happened in the meantime (clearTimeout isn't reliable -- at
3791 // least on Chrome, the timeouts still happen even when cleared,
3792 // if the clear happens after their scheduled firing time).
3793 var counter = 0;
3794
3795 function extend(e) {
3796 var curCount = ++counter;
3797 var cur = posFromMouse(cm, e, true, type == "rect");
3798 if (!cur) return;
3799 if (cmp(cur, lastPos) != 0) {
3800 cm.curOp.focus = activeElt();
3801 extendTo(cur);
3802 var visible = visibleLines(display, doc);
3803 if (cur.line >= visible.to || cur.line < visible.from)
3804 setTimeout(operation(cm, function(){if (counter == curCount) extend(e) ;}), 150);
3805 } else {
3806 var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize. bottom ? 20 : 0;
3807 if (outside) setTimeout(operation(cm, function() {
3808 if (counter != curCount) return;
3809 display.scroller.scrollTop += outside;
3810 extend(e);
3811 }), 50);
3812 }
3813 }
3814
3815 function done(e) {
3816 cm.state.selectingText = false;
3817 counter = Infinity;
3818 e_preventDefault(e);
3819 display.input.focus();
3820 off(document, "mousemove", move);
3821 off(document, "mouseup", up);
3822 doc.history.lastSelOrigin = null;
3823 }
3824
3825 var move = operation(cm, function(e) {
3826 if (!e_button(e)) done(e);
3827 else extend(e);
3828 });
3829 var up = operation(cm, done);
3830 cm.state.selectingText = up;
3831 on(document, "mousemove", move);
3832 on(document, "mouseup", up);
3833 }
3834
3835 // Determines whether an event happened in the gutter, and fires the
3836 // handlers for the corresponding event.
3837 function gutterEvent(cm, e, type, prevent) {
3838 try { var mX = e.clientX, mY = e.clientY; }
3839 catch(e) { return false; }
3840 if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) retu rn false;
3841 if (prevent) e_preventDefault(e);
3842
3843 var display = cm.display;
3844 var lineBox = display.lineDiv.getBoundingClientRect();
3845
3846 if (mY > lineBox.bottom || !hasHandler(cm, type)) return e_defaultPrevented( e);
3847 mY -= lineBox.top - display.viewOffset;
3848
3849 for (var i = 0; i < cm.options.gutters.length; ++i) {
3850 var g = display.gutters.childNodes[i];
3851 if (g && g.getBoundingClientRect().right >= mX) {
3852 var line = lineAtHeight(cm.doc, mY);
3853 var gutter = cm.options.gutters[i];
3854 signal(cm, type, cm, line, gutter, e);
3855 return e_defaultPrevented(e);
3856 }
3857 }
3858 }
3859
3860 function clickInGutter(cm, e) {
3861 return gutterEvent(cm, e, "gutterClick", true);
3862 }
3863
3864 // Kludge to work around strange IE behavior where it'll sometimes
3865 // re-fire a series of drag-related events right after the drop (#1551)
3866 var lastDrop = 0;
3867
3868 function onDrop(e) {
3869 var cm = this;
3870 clearDragCursor(cm);
3871 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))
3872 return;
3873 e_preventDefault(e);
3874 if (ie) lastDrop = +new Date;
3875 var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
3876 if (!pos || cm.isReadOnly()) return;
3877 // Might be a file drop, in which case we simply extract the text
3878 // and insert it.
3879 if (files && files.length && window.FileReader && window.File) {
3880 var n = files.length, text = Array(n), read = 0;
3881 var loadFile = function(file, i) {
3882 if (cm.options.allowDropFileTypes &&
3883 indexOf(cm.options.allowDropFileTypes, file.type) == -1)
3884 return;
3885
3886 var reader = new FileReader;
3887 reader.onload = operation(cm, function() {
3888 var content = reader.result;
3889 if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) content = "";
3890 text[i] = content;
3891 if (++read == n) {
3892 pos = clipPos(cm.doc, pos);
3893 var change = {from: pos, to: pos,
3894 text: cm.doc.splitLines(text.join(cm.doc.lineSeparator ())),
3895 origin: "paste"};
3896 makeChange(cm.doc, change);
3897 setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(ch ange)));
3898 }
3899 });
3900 reader.readAsText(file);
3901 };
3902 for (var i = 0; i < n; ++i) loadFile(files[i], i);
3903 } else { // Normal drop
3904 // Don't do a replace if the drop happened inside of the selected text.
3905 if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {
3906 cm.state.draggingText(e);
3907 // Ensure the editor is re-focused
3908 setTimeout(function() {cm.display.input.focus();}, 20);
3909 return;
3910 }
3911 try {
3912 var text = e.dataTransfer.getData("Text");
3913 if (text) {
3914 if (cm.state.draggingText && !cm.state.draggingText.copy)
3915 var selected = cm.listSelections();
3916 setSelectionNoUndo(cm.doc, simpleSelection(pos, pos));
3917 if (selected) for (var i = 0; i < selected.length; ++i)
3918 replaceRange(cm.doc, "", selected[i].anchor, selected[i].head, "drag ");
3919 cm.replaceSelection(text, "around", "paste");
3920 cm.display.input.focus();
3921 }
3922 }
3923 catch(e){}
3924 }
3925 }
3926
3927 function onDragStart(cm, e) {
3928 if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e ); return; }
3929 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) return;
3930
3931 e.dataTransfer.setData("Text", cm.getSelection());
3932 e.dataTransfer.effectAllowed = "copyMove"
3933
3934 // Use dummy image instead of default browsers image.
3935 // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
3936 if (e.dataTransfer.setDragImage && !safari) {
3937 var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
3938 img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAA AICTAEAOw==";
3939 if (presto) {
3940 img.width = img.height = 1;
3941 cm.display.wrapper.appendChild(img);
3942 // Force a relayout, or Opera won't use our image for some obscure reaso n
3943 img._top = img.offsetTop;
3944 }
3945 e.dataTransfer.setDragImage(img, 0, 0);
3946 if (presto) img.parentNode.removeChild(img);
3947 }
3948 }
3949
3950 function onDragOver(cm, e) {
3951 var pos = posFromMouse(cm, e);
3952 if (!pos) return;
3953 var frag = document.createDocumentFragment();
3954 drawSelectionCursor(cm, pos, frag);
3955 if (!cm.display.dragCursor) {
3956 cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dr agcursors");
3957 cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursor Div);
3958 }
3959 removeChildrenAndAdd(cm.display.dragCursor, frag);
3960 }
3961
3962 function clearDragCursor(cm) {
3963 if (cm.display.dragCursor) {
3964 cm.display.lineSpace.removeChild(cm.display.dragCursor);
3965 cm.display.dragCursor = null;
3966 }
3967 }
3968
3969 // SCROLL EVENTS
3970
3971 // Sync the scrollable area and scrollbars, ensure the viewport
3972 // covers the visible area.
3973 function setScrollTop(cm, val) {
3974 if (Math.abs(cm.doc.scrollTop - val) < 2) return;
3975 cm.doc.scrollTop = val;
3976 if (!gecko) updateDisplaySimple(cm, {top: val});
3977 if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = va l;
3978 cm.display.scrollbars.setScrollTop(val);
3979 if (gecko) updateDisplaySimple(cm);
3980 startWorker(cm, 100);
3981 }
3982 // Sync scroller and scrollbar, ensure the gutter elements are
3983 // aligned.
3984 function setScrollLeft(cm, val, isScroller) {
3985 if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val ) < 2) return;
3986 val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.cl ientWidth);
3987 cm.doc.scrollLeft = val;
3988 alignHorizontally(cm);
3989 if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val;
3990 cm.display.scrollbars.setScrollLeft(val);
3991 }
3992
3993 // Since the delta values reported on mouse wheel events are
3994 // unstandardized between browsers and even browser versions, and
3995 // generally horribly unpredictable, this code starts by measuring
3996 // the scroll effect that the first few mouse wheel events have,
3997 // and, from that, detects the way it can convert deltas to pixel
3998 // offsets afterwards.
3999 //
4000 // The reason we want to know the amount a wheel event will scroll
4001 // is that it gives us a chance to update the display before the
4002 // actual scrolling happens, reducing flickering.
4003
4004 var wheelSamples = 0, wheelPixelsPerUnit = null;
4005 // Fill in a browser-detected starting value on browsers where we
4006 // know one. These don't have to be accurate -- the result of them
4007 // being wrong would just be a slight flicker on the first wheel
4008 // scroll (if it is large enough).
4009 if (ie) wheelPixelsPerUnit = -.53;
4010 else if (gecko) wheelPixelsPerUnit = 15;
4011 else if (chrome) wheelPixelsPerUnit = -.7;
4012 else if (safari) wheelPixelsPerUnit = -1/3;
4013
4014 var wheelEventDelta = function(e) {
4015 var dx = e.wheelDeltaX, dy = e.wheelDeltaY;
4016 if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) dx = e.detail;
4017 if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) dy = e.detail;
4018 else if (dy == null) dy = e.wheelDelta;
4019 return {x: dx, y: dy};
4020 };
4021 CodeMirror.wheelEventPixels = function(e) {
4022 var delta = wheelEventDelta(e);
4023 delta.x *= wheelPixelsPerUnit;
4024 delta.y *= wheelPixelsPerUnit;
4025 return delta;
4026 };
4027
4028 function onScrollWheel(cm, e) {
4029 var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y;
4030
4031 var display = cm.display, scroll = display.scroller;
4032 // Quit if there's nothing to scroll here
4033 var canScrollX = scroll.scrollWidth > scroll.clientWidth;
4034 var canScrollY = scroll.scrollHeight > scroll.clientHeight;
4035 if (!(dx && canScrollX || dy && canScrollY)) return;
4036
4037 // Webkit browsers on OS X abort momentum scrolls when the target
4038 // of the scroll event is removed from the scrollable element.
4039 // This hack (see related code in patchDisplay) makes sure the
4040 // element is kept around.
4041 if (dy && mac && webkit) {
4042 outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {
4043 for (var i = 0; i < view.length; i++) {
4044 if (view[i].node == cur) {
4045 cm.display.currentWheelTarget = cur;
4046 break outer;
4047 }
4048 }
4049 }
4050 }
4051
4052 // On some browsers, horizontal scrolling will cause redraws to
4053 // happen before the gutter has been realigned, causing it to
4054 // wriggle around in a most unseemly way. When we have an
4055 // estimated pixels/delta value, we just handle horizontal
4056 // scrolling entirely here. It'll be slightly off from native, but
4057 // better than glitching out.
4058 if (dx && !gecko && !presto && wheelPixelsPerUnit != null) {
4059 if (dy && canScrollY)
4060 setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixel sPerUnit, scroll.scrollHeight - scroll.clientHeight)));
4061 setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixel sPerUnit, scroll.scrollWidth - scroll.clientWidth)));
4062 // Only prevent default scrolling if vertical scrolling is
4063 // actually possible. Otherwise, it causes vertical scroll
4064 // jitter on OSX trackpads when deltaX is small and deltaY
4065 // is large (issue #3579)
4066 if (!dy || (dy && canScrollY))
4067 e_preventDefault(e);
4068 display.wheelStartX = null; // Abort measurement, if in progress
4069 return;
4070 }
4071
4072 // 'Project' the visible viewport to cover the area that is being
4073 // scrolled into view (if we know enough to estimate it).
4074 if (dy && wheelPixelsPerUnit != null) {
4075 var pixels = dy * wheelPixelsPerUnit;
4076 var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
4077 if (pixels < 0) top = Math.max(0, top + pixels - 50);
4078 else bot = Math.min(cm.doc.height, bot + pixels + 50);
4079 updateDisplaySimple(cm, {top: top, bottom: bot});
4080 }
4081
4082 if (wheelSamples < 20) {
4083 if (display.wheelStartX == null) {
4084 display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.sc rollTop;
4085 display.wheelDX = dx; display.wheelDY = dy;
4086 setTimeout(function() {
4087 if (display.wheelStartX == null) return;
4088 var movedX = scroll.scrollLeft - display.wheelStartX;
4089 var movedY = scroll.scrollTop - display.wheelStartY;
4090 var sample = (movedY && display.wheelDY && movedY / display.wheelDY) | |
4091 (movedX && display.wheelDX && movedX / display.wheelDX);
4092 display.wheelStartX = display.wheelStartY = null;
4093 if (!sample) return;
4094 wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (w heelSamples + 1);
4095 ++wheelSamples;
4096 }, 200);
4097 } else {
4098 display.wheelDX += dx; display.wheelDY += dy;
4099 }
4100 }
4101 }
4102
4103 // KEY EVENTS
4104
4105 // Run a handler that was bound to a key.
4106 function doHandleBinding(cm, bound, dropShift) {
4107 if (typeof bound == "string") {
4108 bound = commands[bound];
4109 if (!bound) return false;
4110 }
4111 // Ensure previous input has been read, so that the handler sees a
4112 // consistent view of the document
4113 cm.display.input.ensurePolled();
4114 var prevShift = cm.display.shift, done = false;
4115 try {
4116 if (cm.isReadOnly()) cm.state.suppressEdits = true;
4117 if (dropShift) cm.display.shift = false;
4118 done = bound(cm) != Pass;
4119 } finally {
4120 cm.display.shift = prevShift;
4121 cm.state.suppressEdits = false;
4122 }
4123 return done;
4124 }
4125
4126 function lookupKeyForEditor(cm, name, handle) {
4127 for (var i = 0; i < cm.state.keyMaps.length; i++) {
4128 var result = lookupKey(name, cm.state.keyMaps[i], handle, cm);
4129 if (result) return result;
4130 }
4131 return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle , cm))
4132 || lookupKey(name, cm.options.keyMap, handle, cm);
4133 }
4134
4135 var stopSeq = new Delayed;
4136 function dispatchKey(cm, name, e, handle) {
4137 var seq = cm.state.keySeq;
4138 if (seq) {
4139 if (isModifierKey(name)) return "handled";
4140 stopSeq.set(50, function() {
4141 if (cm.state.keySeq == seq) {
4142 cm.state.keySeq = null;
4143 cm.display.input.reset();
4144 }
4145 });
4146 name = seq + " " + name;
4147 }
4148 var result = lookupKeyForEditor(cm, name, handle);
4149
4150 if (result == "multi")
4151 cm.state.keySeq = name;
4152 if (result == "handled")
4153 signalLater(cm, "keyHandled", cm, name, e);
4154
4155 if (result == "handled" || result == "multi") {
4156 e_preventDefault(e);
4157 restartBlink(cm);
4158 }
4159
4160 if (seq && !result && /\'$/.test(name)) {
4161 e_preventDefault(e);
4162 return true;
4163 }
4164 return !!result;
4165 }
4166
4167 // Handle a key from the keydown event.
4168 function handleKeyBinding(cm, e) {
4169 var name = keyName(e, true);
4170 if (!name) return false;
4171
4172 if (e.shiftKey && !cm.state.keySeq) {
4173 // First try to resolve full name (including 'Shift-'). Failing
4174 // that, see if there is a cursor-motion command (starting with
4175 // 'go') bound to the keyname without 'Shift-'.
4176 return dispatchKey(cm, "Shift-" + name, e, function(b) {return doHandleBin ding(cm, b, true);})
4177 || dispatchKey(cm, name, e, function(b) {
4178 if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
4179 return doHandleBinding(cm, b);
4180 });
4181 } else {
4182 return dispatchKey(cm, name, e, function(b) { return doHandleBinding(cm, b ); });
4183 }
4184 }
4185
4186 // Handle a key from the keypress event
4187 function handleCharBinding(cm, e, ch) {
4188 return dispatchKey(cm, "'" + ch + "'", e,
4189 function(b) { return doHandleBinding(cm, b, true); });
4190 }
4191
4192 var lastStoppedKey = null;
4193 function onKeyDown(e) {
4194 var cm = this;
4195 cm.curOp.focus = activeElt();
4196 if (signalDOMEvent(cm, e)) return;
4197 // IE does strange things with escape.
4198 if (ie && ie_version < 11 && e.keyCode == 27) e.returnValue = false;
4199 var code = e.keyCode;
4200 cm.display.shift = code == 16 || e.shiftKey;
4201 var handled = handleKeyBinding(cm, e);
4202 if (presto) {
4203 lastStoppedKey = handled ? code : null;
4204 // Opera has no cut event... we try to at least catch the key combo
4205 if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKe y))
4206 cm.replaceSelection("", null, "cut");
4207 }
4208
4209 // Turn mouse into crosshair when Alt is held on Mac.
4210 if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.classN ame))
4211 showCrossHair(cm);
4212 }
4213
4214 function showCrossHair(cm) {
4215 var lineDiv = cm.display.lineDiv;
4216 addClass(lineDiv, "CodeMirror-crosshair");
4217
4218 function up(e) {
4219 if (e.keyCode == 18 || !e.altKey) {
4220 rmClass(lineDiv, "CodeMirror-crosshair");
4221 off(document, "keyup", up);
4222 off(document, "mouseover", up);
4223 }
4224 }
4225 on(document, "keyup", up);
4226 on(document, "mouseover", up);
4227 }
4228
4229 function onKeyUp(e) {
4230 if (e.keyCode == 16) this.doc.sel.shift = false;
4231 signalDOMEvent(this, e);
4232 }
4233
4234 function onKeyPress(e) {
4235 var cm = this;
4236 if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e .altKey || mac && e.metaKey) return;
4237 var keyCode = e.keyCode, charCode = e.charCode;
4238 if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDe fault(e); return;}
4239 if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) retur n;
4240 var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
4241 if (handleCharBinding(cm, e, ch)) return;
4242 cm.display.input.onKeyPress(e);
4243 }
4244
4245 // FOCUS/BLUR EVENTS
4246
4247 function delayBlurEvent(cm) {
4248 cm.state.delayingBlurEvent = true;
4249 setTimeout(function() {
4250 if (cm.state.delayingBlurEvent) {
4251 cm.state.delayingBlurEvent = false;
4252 onBlur(cm);
4253 }
4254 }, 100);
4255 }
4256
4257 function onFocus(cm) {
4258 if (cm.state.delayingBlurEvent) cm.state.delayingBlurEvent = false;
4259
4260 if (cm.options.readOnly == "nocursor") return;
4261 if (!cm.state.focused) {
4262 signal(cm, "focus", cm);
4263 cm.state.focused = true;
4264 addClass(cm.display.wrapper, "CodeMirror-focused");
4265 // This test prevents this from firing when a context
4266 // menu is closed (since the input reset would kill the
4267 // select-all detection hack)
4268 if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {
4269 cm.display.input.reset();
4270 if (webkit) setTimeout(function() { cm.display.input.reset(true); }, 20) ; // Issue #1730
4271 }
4272 cm.display.input.receivedFocus();
4273 }
4274 restartBlink(cm);
4275 }
4276 function onBlur(cm) {
4277 if (cm.state.delayingBlurEvent) return;
4278
4279 if (cm.state.focused) {
4280 signal(cm, "blur", cm);
4281 cm.state.focused = false;
4282 rmClass(cm.display.wrapper, "CodeMirror-focused");
4283 }
4284 clearInterval(cm.display.blinker);
4285 setTimeout(function() {if (!cm.state.focused) cm.display.shift = false;}, 15 0);
4286 }
4287
4288 // CONTEXT MENU HANDLING
4289
4290 // To make the context menu work, we need to briefly unhide the
4291 // textarea (making it as unobtrusive as possible) to let the
4292 // right-click take effect on it.
4293 function onContextMenu(cm, e) {
4294 if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) return;
4295 if (signalDOMEvent(cm, e, "contextmenu")) return;
4296 cm.display.input.onContextMenu(e);
4297 }
4298
4299 function contextMenuInGutter(cm, e) {
4300 if (!hasHandler(cm, "gutterContextMenu")) return false;
4301 return gutterEvent(cm, e, "gutterContextMenu", false);
4302 }
4303
4304 // UPDATING
4305
4306 // Compute the position of the end of a change (its 'to' property
4307 // refers to the pre-change end).
4308 var changeEnd = CodeMirror.changeEnd = function(change) {
4309 if (!change.text) return change.to;
4310 return Pos(change.from.line + change.text.length - 1,
4311 lst(change.text).length + (change.text.length == 1 ? change.from. ch : 0));
4312 };
4313
4314 // Adjust a position to refer to the post-change position of the
4315 // same text, or the end of the change if the change covers it.
4316 function adjustForChange(pos, change) {
4317 if (cmp(pos, change.from) < 0) return pos;
4318 if (cmp(pos, change.to) <= 0) return changeEnd(change);
4319
4320 var line = pos.line + change.text.length - (change.to.line - change.from.lin e) - 1, ch = pos.ch;
4321 if (pos.line == change.to.line) ch += changeEnd(change).ch - change.to.ch;
4322 return Pos(line, ch);
4323 }
4324
4325 function computeSelAfterChange(doc, change) {
4326 var out = [];
4327 for (var i = 0; i < doc.sel.ranges.length; i++) {
4328 var range = doc.sel.ranges[i];
4329 out.push(new Range(adjustForChange(range.anchor, change),
4330 adjustForChange(range.head, change)));
4331 }
4332 return normalizeSelection(out, doc.sel.primIndex);
4333 }
4334
4335 function offsetPos(pos, old, nw) {
4336 if (pos.line == old.line)
4337 return Pos(nw.line, pos.ch - old.ch + nw.ch);
4338 else
4339 return Pos(nw.line + (pos.line - old.line), pos.ch);
4340 }
4341
4342 // Used by replaceSelections to allow moving the selection to the
4343 // start or around the replaced test. Hint may be "start" or "around".
4344 function computeReplacedSel(doc, changes, hint) {
4345 var out = [];
4346 var oldPrev = Pos(doc.first, 0), newPrev = oldPrev;
4347 for (var i = 0; i < changes.length; i++) {
4348 var change = changes[i];
4349 var from = offsetPos(change.from, oldPrev, newPrev);
4350 var to = offsetPos(changeEnd(change), oldPrev, newPrev);
4351 oldPrev = change.to;
4352 newPrev = to;
4353 if (hint == "around") {
4354 var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0;
4355 out[i] = new Range(inv ? to : from, inv ? from : to);
4356 } else {
4357 out[i] = new Range(from, from);
4358 }
4359 }
4360 return new Selection(out, doc.sel.primIndex);
4361 }
4362
4363 // Allow "beforeChange" event handlers to influence a change
4364 function filterChange(doc, change, update) {
4365 var obj = {
4366 canceled: false,
4367 from: change.from,
4368 to: change.to,
4369 text: change.text,
4370 origin: change.origin,
4371 cancel: function() { this.canceled = true; }
4372 };
4373 if (update) obj.update = function(from, to, text, origin) {
4374 if (from) this.from = clipPos(doc, from);
4375 if (to) this.to = clipPos(doc, to);
4376 if (text) this.text = text;
4377 if (origin !== undefined) this.origin = origin;
4378 };
4379 signal(doc, "beforeChange", doc, obj);
4380 if (doc.cm) signal(doc.cm, "beforeChange", doc.cm, obj);
4381
4382 if (obj.canceled) return null;
4383 return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin};
4384 }
4385
4386 // Apply a change to a document, and add it to the document's
4387 // history, and propagating it to all linked documents.
4388 function makeChange(doc, change, ignoreReadOnly) {
4389 if (doc.cm) {
4390 if (!doc.cm.curOp) return operation(doc.cm, makeChange)(doc, change, ignor eReadOnly);
4391 if (doc.cm.state.suppressEdits) return;
4392 }
4393
4394 if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeC hange")) {
4395 change = filterChange(doc, change, true);
4396 if (!change) return;
4397 }
4398
4399 // Possibly split or suppress the update based on the presence
4400 // of read-only spans in its range.
4401 var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);
4402 if (split) {
4403 for (var i = split.length - 1; i >= 0; --i)
4404 makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [" "] : change.text});
4405 } else {
4406 makeChangeInner(doc, change);
4407 }
4408 }
4409
4410 function makeChangeInner(doc, change) {
4411 if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, chan ge.to) == 0) return;
4412 var selAfter = computeSelAfterChange(doc, change);
4413 addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);
4414
4415 makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, chang e));
4416 var rebased = [];
4417
4418 linkedDocs(doc, function(doc, sharedHist) {
4419 if (!sharedHist && indexOf(rebased, doc.history) == -1) {
4420 rebaseHist(doc.history, change);
4421 rebased.push(doc.history);
4422 }
4423 makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change) );
4424 });
4425 }
4426
4427 // Revert a change stored in a document's history.
4428 function makeChangeFromHistory(doc, type, allowSelectionOnly) {
4429 if (doc.cm && doc.cm.state.suppressEdits && !allowSelectionOnly) return;
4430
4431 var hist = doc.history, event, selAfter = doc.sel;
4432 var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done;
4433
4434 // Verify that there is a useable event (so that ctrl-z won't
4435 // needlessly clear selection events)
4436 for (var i = 0; i < source.length; i++) {
4437 event = source[i];
4438 if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.r anges)
4439 break;
4440 }
4441 if (i == source.length) return;
4442 hist.lastOrigin = hist.lastSelOrigin = null;
4443
4444 for (;;) {
4445 event = source.pop();
4446 if (event.ranges) {
4447 pushSelectionToHistory(event, dest);
4448 if (allowSelectionOnly && !event.equals(doc.sel)) {
4449 setSelection(doc, event, {clearRedo: false});
4450 return;
4451 }
4452 selAfter = event;
4453 }
4454 else break;
4455 }
4456
4457 // Build up a reverse change object to add to the opposite history
4458 // stack (redo when undoing, and vice versa).
4459 var antiChanges = [];
4460 pushSelectionToHistory(selAfter, dest);
4461 dest.push({changes: antiChanges, generation: hist.generation});
4462 hist.generation = event.generation || ++hist.maxGeneration;
4463
4464 var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange");
4465
4466 for (var i = event.changes.length - 1; i >= 0; --i) {
4467 var change = event.changes[i];
4468 change.origin = type;
4469 if (filter && !filterChange(doc, change, false)) {
4470 source.length = 0;
4471 return;
4472 }
4473
4474 antiChanges.push(historyChangeFromChange(doc, change));
4475
4476 var after = i ? computeSelAfterChange(doc, change) : lst(source);
4477 makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
4478 if (!i && doc.cm) doc.cm.scrollIntoView({from: change.from, to: changeEnd( change)});
4479 var rebased = [];
4480
4481 // Propagate to the linked documents
4482 linkedDocs(doc, function(doc, sharedHist) {
4483 if (!sharedHist && indexOf(rebased, doc.history) == -1) {
4484 rebaseHist(doc.history, change);
4485 rebased.push(doc.history);
4486 }
4487 makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change));
4488 });
4489 }
4490 }
4491
4492 // Sub-views need their line numbers shifted when text is added
4493 // above or below them in the parent document.
4494 function shiftDoc(doc, distance) {
4495 if (distance == 0) return;
4496 doc.first += distance;
4497 doc.sel = new Selection(map(doc.sel.ranges, function(range) {
4498 return new Range(Pos(range.anchor.line + distance, range.anchor.ch),
4499 Pos(range.head.line + distance, range.head.ch));
4500 }), doc.sel.primIndex);
4501 if (doc.cm) {
4502 regChange(doc.cm, doc.first, doc.first - distance, distance);
4503 for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++)
4504 regLineChange(doc.cm, l, "gutter");
4505 }
4506 }
4507
4508 // More lower-level change function, handling only a single document
4509 // (not linked ones).
4510 function makeChangeSingleDoc(doc, change, selAfter, spans) {
4511 if (doc.cm && !doc.cm.curOp)
4512 return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans );
4513
4514 if (change.to.line < doc.first) {
4515 shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line) );
4516 return;
4517 }
4518 if (change.from.line > doc.lastLine()) return;
4519
4520 // Clip the change to the size of this doc
4521 if (change.from.line < doc.first) {
4522 var shift = change.text.length - 1 - (doc.first - change.from.line);
4523 shiftDoc(doc, shift);
4524 change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change. to.ch),
4525 text: [lst(change.text)], origin: change.origin};
4526 }
4527 var last = doc.lastLine();
4528 if (change.to.line > last) {
4529 change = {from: change.from, to: Pos(last, getLine(doc, last).text.length) ,
4530 text: [change.text[0]], origin: change.origin};
4531 }
4532
4533 change.removed = getBetween(doc, change.from, change.to);
4534
4535 if (!selAfter) selAfter = computeSelAfterChange(doc, change);
4536 if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans);
4537 else updateDoc(doc, change, spans);
4538 setSelectionNoUndo(doc, selAfter, sel_dontScroll);
4539 }
4540
4541 // Handle the interaction of a change to a document with the editor
4542 // that this document is part of.
4543 function makeChangeSingleDocInEditor(cm, change, spans) {
4544 var doc = cm.doc, display = cm.display, from = change.from, to = change.to;
4545
4546 var recomputeMaxLength = false, checkWidthStart = from.line;
4547 if (!cm.options.lineWrapping) {
4548 checkWidthStart = lineNo(visualLine(getLine(doc, from.line)));
4549 doc.iter(checkWidthStart, to.line + 1, function(line) {
4550 if (line == display.maxLine) {
4551 recomputeMaxLength = true;
4552 return true;
4553 }
4554 });
4555 }
4556
4557 if (doc.sel.contains(change.from, change.to) > -1)
4558 signalCursorActivity(cm);
4559
4560 updateDoc(doc, change, spans, estimateHeight(cm));
4561
4562 if (!cm.options.lineWrapping) {
4563 doc.iter(checkWidthStart, from.line + change.text.length, function(line) {
4564 var len = lineLength(line);
4565 if (len > display.maxLineLength) {
4566 display.maxLine = line;
4567 display.maxLineLength = len;
4568 display.maxLineChanged = true;
4569 recomputeMaxLength = false;
4570 }
4571 });
4572 if (recomputeMaxLength) cm.curOp.updateMaxLine = true;
4573 }
4574
4575 // Adjust frontier, schedule worker
4576 doc.frontier = Math.min(doc.frontier, from.line);
4577 startWorker(cm, 400);
4578
4579 var lendiff = change.text.length - (to.line - from.line) - 1;
4580 // Remember that these lines changed, for updating the display
4581 if (change.full)
4582 regChange(cm);
4583 else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpda te(cm.doc, change))
4584 regLineChange(cm, from.line, "text");
4585 else
4586 regChange(cm, from.line, to.line + 1, lendiff);
4587
4588 var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(c m, "change");
4589 if (changeHandler || changesHandler) {
4590 var obj = {
4591 from: from, to: to,
4592 text: change.text,
4593 removed: change.removed,
4594 origin: change.origin
4595 };
4596 if (changeHandler) signalLater(cm, "change", cm, obj);
4597 if (changesHandler) (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).pu sh(obj);
4598 }
4599 cm.display.selForContextMenu = null;
4600 }
4601
4602 function replaceRange(doc, code, from, to, origin) {
4603 if (!to) to = from;
4604 if (cmp(to, from) < 0) { var tmp = to; to = from; from = tmp; }
4605 if (typeof code == "string") code = doc.splitLines(code);
4606 makeChange(doc, {from: from, to: to, text: code, origin: origin});
4607 }
4608
4609 // SCROLLING THINGS INTO VIEW
4610
4611 // If an editor sits on the top or bottom of the window, partially
4612 // scrolled out of view, this ensures that the cursor is visible.
4613 function maybeScrollWindow(cm, coords) {
4614 if (signalDOMEvent(cm, "scrollCursorIntoView")) return;
4615
4616 var display = cm.display, box = display.sizer.getBoundingClientRect(), doScr oll = null;
4617 if (coords.top + box.top < 0) doScroll = true;
4618 else if (coords.bottom + box.top > (window.innerHeight || document.documentE lement.clientHeight)) doScroll = false;
4619 if (doScroll != null && !phantom) {
4620 var scrollNode = elt("div", "\u200b", null, "position: absolute; top: " +
4621 (coords.top - display.viewOffset - paddingTop(cm.disp lay)) + "px; height: " +
4622 (coords.bottom - coords.top + scrollGap(cm) + display .barHeight) + "px; left: " +
4623 coords.left + "px; width: 2px;");
4624 cm.display.lineSpace.appendChild(scrollNode);
4625 scrollNode.scrollIntoView(doScroll);
4626 cm.display.lineSpace.removeChild(scrollNode);
4627 }
4628 }
4629
4630 // Scroll a given position into view (immediately), verifying that
4631 // it actually became visible (as line heights are accurately
4632 // measured, the position of something may 'drift' during drawing).
4633 function scrollPosIntoView(cm, pos, end, margin) {
4634 if (margin == null) margin = 0;
4635 for (var limit = 0; limit < 5; limit++) {
4636 var changed = false, coords = cursorCoords(cm, pos);
4637 var endCoords = !end || end == pos ? coords : cursorCoords(cm, end);
4638 var scrollPos = calculateScrollPos(cm, Math.min(coords.left, endCoords.lef t),
4639 Math.min(coords.top, endCoords.top) - m argin,
4640 Math.max(coords.left, endCoords.left),
4641 Math.max(coords.bottom, endCoords.botto m) + margin);
4642 var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
4643 if (scrollPos.scrollTop != null) {
4644 setScrollTop(cm, scrollPos.scrollTop);
4645 if (Math.abs(cm.doc.scrollTop - startTop) > 1) changed = true;
4646 }
4647 if (scrollPos.scrollLeft != null) {
4648 setScrollLeft(cm, scrollPos.scrollLeft);
4649 if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) changed = true;
4650 }
4651 if (!changed) break;
4652 }
4653 return coords;
4654 }
4655
4656 // Scroll a given set of coordinates into view (immediately).
4657 function scrollIntoView(cm, x1, y1, x2, y2) {
4658 var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2);
4659 if (scrollPos.scrollTop != null) setScrollTop(cm, scrollPos.scrollTop);
4660 if (scrollPos.scrollLeft != null) setScrollLeft(cm, scrollPos.scrollLeft);
4661 }
4662
4663 // Calculate a new scroll position needed to scroll the given
4664 // rectangle into view. Returns an object with scrollTop and
4665 // scrollLeft properties. When these are undefined, the
4666 // vertical/horizontal position does not need to be adjusted.
4667 function calculateScrollPos(cm, x1, y1, x2, y2) {
4668 var display = cm.display, snapMargin = textHeight(cm.display);
4669 if (y1 < 0) y1 = 0;
4670 var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop;
4671 var screen = displayHeight(cm), result = {};
4672 if (y2 - y1 > screen) y2 = y1 + screen;
4673 var docBottom = cm.doc.height + paddingVert(display);
4674 var atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin;
4675 if (y1 < screentop) {
4676 result.scrollTop = atTop ? 0 : y1;
4677 } else if (y2 > screentop + screen) {
4678 var newTop = Math.min(y1, (atBottom ? docBottom : y2) - screen);
4679 if (newTop != screentop) result.scrollTop = newTop;
4680 }
4681
4682 var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLe ft : display.scroller.scrollLeft;
4683 var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.o ffsetWidth : 0);
4684 var tooWide = x2 - x1 > screenw;
4685 if (tooWide) x2 = x1 + screenw;
4686 if (x1 < 10)
4687 result.scrollLeft = 0;
4688 else if (x1 < screenleft)
4689 result.scrollLeft = Math.max(0, x1 - (tooWide ? 0 : 10));
4690 else if (x2 > screenw + screenleft - 3)
4691 result.scrollLeft = x2 + (tooWide ? 0 : 10) - screenw;
4692 return result;
4693 }
4694
4695 // Store a relative adjustment to the scroll position in the current
4696 // operation (to be applied when the operation finishes).
4697 function addToScrollPos(cm, left, top) {
4698 if (left != null || top != null) resolveScrollToPos(cm);
4699 if (left != null)
4700 cm.curOp.scrollLeft = (cm.curOp.scrollLeft == null ? cm.doc.scrollLeft : c m.curOp.scrollLeft) + left;
4701 if (top != null)
4702 cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.c urOp.scrollTop) + top;
4703 }
4704
4705 // Make sure that at the end of the operation the current cursor is
4706 // shown.
4707 function ensureCursorVisible(cm) {
4708 resolveScrollToPos(cm);
4709 var cur = cm.getCursor(), from = cur, to = cur;
4710 if (!cm.options.lineWrapping) {
4711 from = cur.ch ? Pos(cur.line, cur.ch - 1) : cur;
4712 to = Pos(cur.line, cur.ch + 1);
4713 }
4714 cm.curOp.scrollToPos = {from: from, to: to, margin: cm.options.cursorScrollM argin, isCursor: true};
4715 }
4716
4717 // When an operation has its scrollToPos property set, and another
4718 // scroll action is applied before the end of the operation, this
4719 // 'simulates' scrolling that position into view in a cheap way, so
4720 // that the effect of intermediate scroll commands is not ignored.
4721 function resolveScrollToPos(cm) {
4722 var range = cm.curOp.scrollToPos;
4723 if (range) {
4724 cm.curOp.scrollToPos = null;
4725 var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.t o);
4726 var sPos = calculateScrollPos(cm, Math.min(from.left, to.left),
4727 Math.min(from.top, to.top) - range.margin,
4728 Math.max(from.right, to.right),
4729 Math.max(from.bottom, to.bottom) + range.mar gin);
4730 cm.scrollTo(sPos.scrollLeft, sPos.scrollTop);
4731 }
4732 }
4733
4734 // API UTILITIES
4735
4736 // Indent the given line. The how parameter can be "smart",
4737 // "add"/null, "subtract", or "prev". When aggressive is false
4738 // (typically set to true for forced single-line indents), empty
4739 // lines are not indented, and places where the mode returns Pass
4740 // are left alone.
4741 function indentLine(cm, n, how, aggressive) {
4742 var doc = cm.doc, state;
4743 if (how == null) how = "add";
4744 if (how == "smart") {
4745 // Fall back to "prev" when the mode doesn't have an indentation
4746 // method.
4747 if (!doc.mode.indent) how = "prev";
4748 else state = getStateBefore(cm, n);
4749 }
4750
4751 var tabSize = cm.options.tabSize;
4752 var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize) ;
4753 if (line.stateAfter) line.stateAfter = null;
4754 var curSpaceString = line.text.match(/^\s*/)[0], indentation;
4755 if (!aggressive && !/\S/.test(line.text)) {
4756 indentation = 0;
4757 how = "not";
4758 } else if (how == "smart") {
4759 indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length ), line.text);
4760 if (indentation == Pass || indentation > 150) {
4761 if (!aggressive) return;
4762 how = "prev";
4763 }
4764 }
4765 if (how == "prev") {
4766 if (n > doc.first) indentation = countColumn(getLine(doc, n-1).text, null, tabSize);
4767 else indentation = 0;
4768 } else if (how == "add") {
4769 indentation = curSpace + cm.options.indentUnit;
4770 } else if (how == "subtract") {
4771 indentation = curSpace - cm.options.indentUnit;
4772 } else if (typeof how == "number") {
4773 indentation = curSpace + how;
4774 }
4775 indentation = Math.max(0, indentation);
4776
4777 var indentString = "", pos = 0;
4778 if (cm.options.indentWithTabs)
4779 for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; i ndentString += "\t";}
4780 if (pos < indentation) indentString += spaceStr(indentation - pos);
4781
4782 if (indentString != curSpaceString) {
4783 replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
4784 line.stateAfter = null;
4785 return true;
4786 } else {
4787 // Ensure that, if the cursor was in the whitespace at the start
4788 // of the line, it is moved to the end of that space.
4789 for (var i = 0; i < doc.sel.ranges.length; i++) {
4790 var range = doc.sel.ranges[i];
4791 if (range.head.line == n && range.head.ch < curSpaceString.length) {
4792 var pos = Pos(n, curSpaceString.length);
4793 replaceOneSelection(doc, i, new Range(pos, pos));
4794 break;
4795 }
4796 }
4797 }
4798 }
4799
4800 // Utility for applying a change to a line by handle or number,
4801 // returning the number and optionally registering the line as
4802 // changed.
4803 function changeLine(doc, handle, changeType, op) {
4804 var no = handle, line = handle;
4805 if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle));
4806 else no = lineNo(handle);
4807 if (no == null) return null;
4808 if (op(line, no) && doc.cm) regLineChange(doc.cm, no, changeType);
4809 return line;
4810 }
4811
4812 // Helper for deleting text near the selection(s), used to implement
4813 // backspace, delete, and similar functionality.
4814 function deleteNearSelection(cm, compute) {
4815 var ranges = cm.doc.sel.ranges, kill = [];
4816 // Build up a set of ranges to kill first, merging overlapping
4817 // ranges.
4818 for (var i = 0; i < ranges.length; i++) {
4819 var toKill = compute(ranges[i]);
4820 while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {
4821 var replaced = kill.pop();
4822 if (cmp(replaced.from, toKill.from) < 0) {
4823 toKill.from = replaced.from;
4824 break;
4825 }
4826 }
4827 kill.push(toKill);
4828 }
4829 // Next, remove those actual ranges.
4830 runInOp(cm, function() {
4831 for (var i = kill.length - 1; i >= 0; i--)
4832 replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete");
4833 ensureCursorVisible(cm);
4834 });
4835 }
4836
4837 // Used for horizontal relative motion. Dir is -1 or 1 (left or
4838 // right), unit can be "char", "column" (like char, but doesn't
4839 // cross line boundaries), "word" (across next word), or "group" (to
4840 // the start of next group of word or non-word-non-whitespace
4841 // chars). The visually param controls whether, in right-to-left
4842 // text, direction 1 means to move towards the next index in the
4843 // string, or towards the character to the right of the current
4844 // position. The resulting position will have a hitSide=true
4845 // property if it reached the end of the document.
4846 function findPosH(doc, pos, dir, unit, visually) {
4847 var line = pos.line, ch = pos.ch, origDir = dir;
4848 var lineObj = getLine(doc, line);
4849 function findNextLine() {
4850 var l = line + dir;
4851 if (l < doc.first || l >= doc.first + doc.size) return false
4852 line = l;
4853 return lineObj = getLine(doc, l);
4854 }
4855 function moveOnce(boundToLine) {
4856 var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, tru e);
4857 if (next == null) {
4858 if (!boundToLine && findNextLine()) {
4859 if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj);
4860 else ch = dir < 0 ? lineObj.text.length : 0;
4861 } else return false
4862 } else ch = next;
4863 return true;
4864 }
4865
4866 if (unit == "char") {
4867 moveOnce()
4868 } else if (unit == "column") {
4869 moveOnce(true)
4870 } else if (unit == "word" || unit == "group") {
4871 var sawType = null, group = unit == "group";
4872 var helper = doc.cm && doc.cm.getHelper(pos, "wordChars");
4873 for (var first = true;; first = false) {
4874 if (dir < 0 && !moveOnce(!first)) break;
4875 var cur = lineObj.text.charAt(ch) || "\n";
4876 var type = isWordChar(cur, helper) ? "w"
4877 : group && cur == "\n" ? "n"
4878 : !group || /\s/.test(cur) ? null
4879 : "p";
4880 if (group && !first && !type) type = "s";
4881 if (sawType && sawType != type) {
4882 if (dir < 0) {dir = 1; moveOnce();}
4883 break;
4884 }
4885
4886 if (type) sawType = type;
4887 if (dir > 0 && !moveOnce(!first)) break;
4888 }
4889 }
4890 var result = skipAtomic(doc, Pos(line, ch), pos, origDir, true);
4891 if (!cmp(pos, result)) result.hitSide = true;
4892 return result;
4893 }
4894
4895 // For relative vertical movement. Dir may be -1 or 1. Unit can be
4896 // "page" or "line". The resulting position will have a hitSide=true
4897 // property if it reached the end of the document.
4898 function findPosV(cm, pos, dir, unit) {
4899 var doc = cm.doc, x = pos.left, y;
4900 if (unit == "page") {
4901 var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeigh t || document.documentElement.clientHeight);
4902 y = pos.top + dir * (pageSize - (dir < 0 ? 1.5 : .5) * textHeight(cm.displ ay));
4903 } else if (unit == "line") {
4904 y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
4905 }
4906 for (;;) {
4907 var target = coordsChar(cm, x, y);
4908 if (!target.outside) break;
4909 if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break; }
4910 y += dir * 5;
4911 }
4912 return target;
4913 }
4914
4915 // EDITOR METHODS
4916
4917 // The publicly visible API. Note that methodOp(f) means
4918 // 'wrap f in an operation, performed on its `this` parameter'.
4919
4920 // This is not the complete set of editor methods. Most of the
4921 // methods defined on the Doc type are also injected into
4922 // CodeMirror.prototype, for backwards compatibility and
4923 // convenience.
4924 7770
4925 CodeMirror.prototype = { 7771 CodeMirror.prototype = {
4926 constructor: CodeMirror, 7772 constructor: CodeMirror,
4927 focus: function(){window.focus(); this.display.input.focus();}, 7773 focus: function(){window.focus(); this.display.input.focus()},
4928 7774
4929 setOption: function(option, value) { 7775 setOption: function(option, value) {
4930 var options = this.options, old = options[option]; 7776 var options = this.options, old = options[option]
4931 if (options[option] == value && option != "mode") return; 7777 if (options[option] == value && option != "mode") { return }
4932 options[option] = value; 7778 options[option] = value
4933 if (optionHandlers.hasOwnProperty(option)) 7779 if (optionHandlers.hasOwnProperty(option))
4934 operation(this, optionHandlers[option])(this, value, old); 7780 { operation(this, optionHandlers[option])(this, value, old) }
7781 signal(this, "optionChange", this, option)
4935 }, 7782 },
4936 7783
4937 getOption: function(option) {return this.options[option];}, 7784 getOption: function(option) {return this.options[option]},
4938 getDoc: function() {return this.doc;}, 7785 getDoc: function() {return this.doc},
4939 7786
4940 addKeyMap: function(map, bottom) { 7787 addKeyMap: function(map, bottom) {
4941 this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map)); 7788 this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map))
4942 }, 7789 },
4943 removeKeyMap: function(map) { 7790 removeKeyMap: function(map) {
4944 var maps = this.state.keyMaps; 7791 var maps = this.state.keyMaps
4945 for (var i = 0; i < maps.length; ++i) 7792 for (var i = 0; i < maps.length; ++i)
4946 if (maps[i] == map || maps[i].name == map) { 7793 { if (maps[i] == map || maps[i].name == map) {
4947 maps.splice(i, 1); 7794 maps.splice(i, 1)
4948 return true; 7795 return true
4949 } 7796 } }
4950 }, 7797 },
4951 7798
4952 addOverlay: methodOp(function(spec, options) { 7799 addOverlay: methodOp(function(spec, options) {
4953 var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec); 7800 var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec)
4954 if (mode.startState) throw new Error("Overlays may not be stateful."); 7801 if (mode.startState) { throw new Error("Overlays may not be stateful.") }
4955 this.state.overlays.push({mode: mode, modeSpec: spec, opaque: options && o ptions.opaque}); 7802 insertSorted(this.state.overlays,
4956 this.state.modeGen++; 7803 {mode: mode, modeSpec: spec, opaque: options && options.opaqu e,
4957 regChange(this); 7804 priority: (options && options.priority) || 0},
7805 function (overlay) { return overlay.priority; })
7806 this.state.modeGen++
7807 regChange(this)
4958 }), 7808 }),
4959 removeOverlay: methodOp(function(spec) { 7809 removeOverlay: methodOp(function(spec) {
4960 var overlays = this.state.overlays; 7810 var this$1 = this;
7811
7812 var overlays = this.state.overlays
4961 for (var i = 0; i < overlays.length; ++i) { 7813 for (var i = 0; i < overlays.length; ++i) {
4962 var cur = overlays[i].modeSpec; 7814 var cur = overlays[i].modeSpec
4963 if (cur == spec || typeof spec == "string" && cur.name == spec) { 7815 if (cur == spec || typeof spec == "string" && cur.name == spec) {
4964 overlays.splice(i, 1); 7816 overlays.splice(i, 1)
4965 this.state.modeGen++; 7817 this$1.state.modeGen++
4966 regChange(this); 7818 regChange(this$1)
4967 return; 7819 return
4968 } 7820 }
4969 } 7821 }
4970 }), 7822 }),
4971 7823
4972 indentLine: methodOp(function(n, dir, aggressive) { 7824 indentLine: methodOp(function(n, dir, aggressive) {
4973 if (typeof dir != "string" && typeof dir != "number") { 7825 if (typeof dir != "string" && typeof dir != "number") {
4974 if (dir == null) dir = this.options.smartIndent ? "smart" : "prev"; 7826 if (dir == null) { dir = this.options.smartIndent ? "smart" : "prev" }
4975 else dir = dir ? "add" : "subtract"; 7827 else { dir = dir ? "add" : "subtract" }
4976 } 7828 }
4977 if (isLine(this.doc, n)) indentLine(this, n, dir, aggressive); 7829 if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive) }
4978 }), 7830 }),
4979 indentSelection: methodOp(function(how) { 7831 indentSelection: methodOp(function(how) {
4980 var ranges = this.doc.sel.ranges, end = -1; 7832 var this$1 = this;
7833
7834 var ranges = this.doc.sel.ranges, end = -1
4981 for (var i = 0; i < ranges.length; i++) { 7835 for (var i = 0; i < ranges.length; i++) {
4982 var range = ranges[i]; 7836 var range = ranges[i]
4983 if (!range.empty()) { 7837 if (!range.empty()) {
4984 var from = range.from(), to = range.to(); 7838 var from = range.from(), to = range.to()
4985 var start = Math.max(end, from.line); 7839 var start = Math.max(end, from.line)
4986 end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1; 7840 end = Math.min(this$1.lastLine(), to.line - (to.ch ? 0 : 1)) + 1
4987 for (var j = start; j < end; ++j) 7841 for (var j = start; j < end; ++j)
4988 indentLine(this, j, how); 7842 { indentLine(this$1, j, how) }
4989 var newRanges = this.doc.sel.ranges; 7843 var newRanges = this$1.doc.sel.ranges
4990 if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i]. from().ch > 0) 7844 if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i]. from().ch > 0)
4991 replaceOneSelection(this.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll); 7845 { replaceOneSelection(this$1.doc, i, new Range(from, newRanges[i].to ()), sel_dontScroll) }
4992 } else if (range.head.line > end) { 7846 } else if (range.head.line > end) {
4993 indentLine(this, range.head.line, how, true); 7847 indentLine(this$1, range.head.line, how, true)
4994 end = range.head.line; 7848 end = range.head.line
4995 if (i == this.doc.sel.primIndex) ensureCursorVisible(this); 7849 if (i == this$1.doc.sel.primIndex) { ensureCursorVisible(this$1) }
4996 } 7850 }
4997 } 7851 }
4998 }), 7852 }),
4999 7853
5000 // Fetch the parser token for a given character. Useful for hacks 7854 // Fetch the parser token for a given character. Useful for hacks
5001 // that want to inspect the mode state (say, for completion). 7855 // that want to inspect the mode state (say, for completion).
5002 getTokenAt: function(pos, precise) { 7856 getTokenAt: function(pos, precise) {
5003 return takeToken(this, pos, precise); 7857 return takeToken(this, pos, precise)
5004 }, 7858 },
5005 7859
5006 getLineTokens: function(line, precise) { 7860 getLineTokens: function(line, precise) {
5007 return takeToken(this, Pos(line), precise, true); 7861 return takeToken(this, Pos(line), precise, true)
5008 }, 7862 },
5009 7863
5010 getTokenTypeAt: function(pos) { 7864 getTokenTypeAt: function(pos) {
5011 pos = clipPos(this.doc, pos); 7865 pos = clipPos(this.doc, pos)
5012 var styles = getLineStyles(this, getLine(this.doc, pos.line)); 7866 var styles = getLineStyles(this, getLine(this.doc, pos.line))
5013 var before = 0, after = (styles.length - 1) / 2, ch = pos.ch; 7867 var before = 0, after = (styles.length - 1) / 2, ch = pos.ch
5014 var type; 7868 var type
5015 if (ch == 0) type = styles[2]; 7869 if (ch == 0) { type = styles[2] }
5016 else for (;;) { 7870 else { for (;;) {
5017 var mid = (before + after) >> 1; 7871 var mid = (before + after) >> 1
5018 if ((mid ? styles[mid * 2 - 1] : 0) >= ch) after = mid; 7872 if ((mid ? styles[mid * 2 - 1] : 0) >= ch) { after = mid }
5019 else if (styles[mid * 2 + 1] < ch) before = mid + 1; 7873 else if (styles[mid * 2 + 1] < ch) { before = mid + 1 }
5020 else { type = styles[mid * 2 + 2]; break; } 7874 else { type = styles[mid * 2 + 2]; break }
5021 } 7875 } }
5022 var cut = type ? type.indexOf("cm-overlay ") : -1; 7876 var cut = type ? type.indexOf("overlay ") : -1
5023 return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1); 7877 return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1)
5024 }, 7878 },
5025 7879
5026 getModeAt: function(pos) { 7880 getModeAt: function(pos) {
5027 var mode = this.doc.mode; 7881 var mode = this.doc.mode
5028 if (!mode.innerMode) return mode; 7882 if (!mode.innerMode) { return mode }
5029 return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode; 7883 return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode
5030 }, 7884 },
5031 7885
5032 getHelper: function(pos, type) { 7886 getHelper: function(pos, type) {
5033 return this.getHelpers(pos, type)[0]; 7887 return this.getHelpers(pos, type)[0]
5034 }, 7888 },
5035 7889
5036 getHelpers: function(pos, type) { 7890 getHelpers: function(pos, type) {
5037 var found = []; 7891 var this$1 = this;
5038 if (!helpers.hasOwnProperty(type)) return found; 7892
5039 var help = helpers[type], mode = this.getModeAt(pos); 7893 var found = []
7894 if (!helpers.hasOwnProperty(type)) { return found }
7895 var help = helpers[type], mode = this.getModeAt(pos)
5040 if (typeof mode[type] == "string") { 7896 if (typeof mode[type] == "string") {
5041 if (help[mode[type]]) found.push(help[mode[type]]); 7897 if (help[mode[type]]) { found.push(help[mode[type]]) }
5042 } else if (mode[type]) { 7898 } else if (mode[type]) {
5043 for (var i = 0; i < mode[type].length; i++) { 7899 for (var i = 0; i < mode[type].length; i++) {
5044 var val = help[mode[type][i]]; 7900 var val = help[mode[type][i]]
5045 if (val) found.push(val); 7901 if (val) { found.push(val) }
5046 } 7902 }
5047 } else if (mode.helperType && help[mode.helperType]) { 7903 } else if (mode.helperType && help[mode.helperType]) {
5048 found.push(help[mode.helperType]); 7904 found.push(help[mode.helperType])
5049 } else if (help[mode.name]) { 7905 } else if (help[mode.name]) {
5050 found.push(help[mode.name]); 7906 found.push(help[mode.name])
5051 } 7907 }
5052 for (var i = 0; i < help._global.length; i++) { 7908 for (var i$1 = 0; i$1 < help._global.length; i$1++) {
5053 var cur = help._global[i]; 7909 var cur = help._global[i$1]
5054 if (cur.pred(mode, this) && indexOf(found, cur.val) == -1) 7910 if (cur.pred(mode, this$1) && indexOf(found, cur.val) == -1)
5055 found.push(cur.val); 7911 { found.push(cur.val) }
5056 } 7912 }
5057 return found; 7913 return found
5058 }, 7914 },
5059 7915
5060 getStateAfter: function(line, precise) { 7916 getStateAfter: function(line, precise) {
5061 var doc = this.doc; 7917 var doc = this.doc
5062 line = clipLine(doc, line == null ? doc.first + doc.size - 1: line); 7918 line = clipLine(doc, line == null ? doc.first + doc.size - 1: line)
5063 return getStateBefore(this, line + 1, precise); 7919 return getStateBefore(this, line + 1, precise)
5064 }, 7920 },
5065 7921
5066 cursorCoords: function(start, mode) { 7922 cursorCoords: function(start, mode) {
5067 var pos, range = this.doc.sel.primary(); 7923 var pos, range = this.doc.sel.primary()
5068 if (start == null) pos = range.head; 7924 if (start == null) { pos = range.head }
5069 else if (typeof start == "object") pos = clipPos(this.doc, start); 7925 else if (typeof start == "object") { pos = clipPos(this.doc, start) }
5070 else pos = start ? range.from() : range.to(); 7926 else { pos = start ? range.from() : range.to() }
5071 return cursorCoords(this, pos, mode || "page"); 7927 return cursorCoords(this, pos, mode || "page")
5072 }, 7928 },
5073 7929
5074 charCoords: function(pos, mode) { 7930 charCoords: function(pos, mode) {
5075 return charCoords(this, clipPos(this.doc, pos), mode || "page"); 7931 return charCoords(this, clipPos(this.doc, pos), mode || "page")
5076 }, 7932 },
5077 7933
5078 coordsChar: function(coords, mode) { 7934 coordsChar: function(coords, mode) {
5079 coords = fromCoordSystem(this, coords, mode || "page"); 7935 coords = fromCoordSystem(this, coords, mode || "page")
5080 return coordsChar(this, coords.left, coords.top); 7936 return coordsChar(this, coords.left, coords.top)
5081 }, 7937 },
5082 7938
5083 lineAtHeight: function(height, mode) { 7939 lineAtHeight: function(height, mode) {
5084 height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top ; 7940 height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top
5085 return lineAtHeight(this.doc, height + this.display.viewOffset); 7941 return lineAtHeight(this.doc, height + this.display.viewOffset)
5086 }, 7942 },
5087 heightAtLine: function(line, mode) { 7943 heightAtLine: function(line, mode, includeWidgets) {
5088 var end = false, lineObj; 7944 var end = false, lineObj
5089 if (typeof line == "number") { 7945 if (typeof line == "number") {
5090 var last = this.doc.first + this.doc.size - 1; 7946 var last = this.doc.first + this.doc.size - 1
5091 if (line < this.doc.first) line = this.doc.first; 7947 if (line < this.doc.first) { line = this.doc.first }
5092 else if (line > last) { line = last; end = true; } 7948 else if (line > last) { line = last; end = true }
5093 lineObj = getLine(this.doc, line); 7949 lineObj = getLine(this.doc, line)
5094 } else { 7950 } else {
5095 lineObj = line; 7951 lineObj = line
5096 } 7952 }
5097 return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page").t op + 7953 return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page", i ncludeWidgets || end).top +
5098 (end ? this.doc.height - heightAtLine(lineObj) : 0); 7954 (end ? this.doc.height - heightAtLine(lineObj) : 0)
5099 }, 7955 },
5100 7956
5101 defaultTextHeight: function() { return textHeight(this.display); }, 7957 defaultTextHeight: function() { return textHeight(this.display) },
5102 defaultCharWidth: function() { return charWidth(this.display); }, 7958 defaultCharWidth: function() { return charWidth(this.display) },
5103 7959
5104 setGutterMarker: methodOp(function(line, gutterID, value) { 7960 getViewport: function() { return {from: this.display.viewFrom, to: this.disp lay.viewTo}},
5105 return changeLine(this.doc, line, "gutter", function(line) {
5106 var markers = line.gutterMarkers || (line.gutterMarkers = {});
5107 markers[gutterID] = value;
5108 if (!value && isEmpty(markers)) line.gutterMarkers = null;
5109 return true;
5110 });
5111 }),
5112
5113 clearGutter: methodOp(function(gutterID) {
5114 var cm = this, doc = cm.doc, i = doc.first;
5115 doc.iter(function(line) {
5116 if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
5117 line.gutterMarkers[gutterID] = null;
5118 regLineChange(cm, i, "gutter");
5119 if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null;
5120 }
5121 ++i;
5122 });
5123 }),
5124
5125 lineInfo: function(line) {
5126 if (typeof line == "number") {
5127 if (!isLine(this.doc, line)) return null;
5128 var n = line;
5129 line = getLine(this.doc, line);
5130 if (!line) return null;
5131 } else {
5132 var n = lineNo(line);
5133 if (n == null) return null;
5134 }
5135 return {line: n, handle: line, text: line.text, gutterMarkers: line.gutter Markers,
5136 textClass: line.textClass, bgClass: line.bgClass, wrapClass: line. wrapClass,
5137 widgets: line.widgets};
5138 },
5139
5140 getViewport: function() { return {from: this.display.viewFrom, to: this.disp lay.viewTo};},
5141 7961
5142 addWidget: function(pos, node, scroll, vert, horiz) { 7962 addWidget: function(pos, node, scroll, vert, horiz) {
5143 var display = this.display; 7963 var display = this.display
5144 pos = cursorCoords(this, clipPos(this.doc, pos)); 7964 pos = cursorCoords(this, clipPos(this.doc, pos))
5145 var top = pos.bottom, left = pos.left; 7965 var top = pos.bottom, left = pos.left
5146 node.style.position = "absolute"; 7966 node.style.position = "absolute"
5147 node.setAttribute("cm-ignore-events", "true"); 7967 node.setAttribute("cm-ignore-events", "true")
5148 this.display.input.setUneditable(node); 7968 this.display.input.setUneditable(node)
5149 display.sizer.appendChild(node); 7969 display.sizer.appendChild(node)
5150 if (vert == "over") { 7970 if (vert == "over") {
5151 top = pos.top; 7971 top = pos.top
5152 } else if (vert == "above" || vert == "near") { 7972 } else if (vert == "above" || vert == "near") {
5153 var vspace = Math.max(display.wrapper.clientHeight, this.doc.height), 7973 var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
5154 hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWid th); 7974 hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWid th)
5155 // Default to positioning above (if specified and possible); otherwise d efault to positioning below 7975 // Default to positioning above (if specified and possible); otherwise d efault to positioning below
5156 if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos. top > node.offsetHeight) 7976 if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos. top > node.offsetHeight)
5157 top = pos.top - node.offsetHeight; 7977 { top = pos.top - node.offsetHeight }
5158 else if (pos.bottom + node.offsetHeight <= vspace) 7978 else if (pos.bottom + node.offsetHeight <= vspace)
5159 top = pos.bottom; 7979 { top = pos.bottom }
5160 if (left + node.offsetWidth > hspace) 7980 if (left + node.offsetWidth > hspace)
5161 left = hspace - node.offsetWidth; 7981 { left = hspace - node.offsetWidth }
5162 } 7982 }
5163 node.style.top = top + "px"; 7983 node.style.top = top + "px"
5164 node.style.left = node.style.right = ""; 7984 node.style.left = node.style.right = ""
5165 if (horiz == "right") { 7985 if (horiz == "right") {
5166 left = display.sizer.clientWidth - node.offsetWidth; 7986 left = display.sizer.clientWidth - node.offsetWidth
5167 node.style.right = "0px"; 7987 node.style.right = "0px"
5168 } else { 7988 } else {
5169 if (horiz == "left") left = 0; 7989 if (horiz == "left") { left = 0 }
5170 else if (horiz == "middle") left = (display.sizer.clientWidth - node.off setWidth) / 2; 7990 else if (horiz == "middle") { left = (display.sizer.clientWidth - node.o ffsetWidth) / 2 }
5171 node.style.left = left + "px"; 7991 node.style.left = left + "px"
5172 } 7992 }
5173 if (scroll) 7993 if (scroll)
5174 scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offs etHeight); 7994 { scrollIntoView(this, {left: left, top: top, right: left + node.offsetW idth, bottom: top + node.offsetHeight}) }
5175 }, 7995 },
5176 7996
5177 triggerOnKeyDown: methodOp(onKeyDown), 7997 triggerOnKeyDown: methodOp(onKeyDown),
5178 triggerOnKeyPress: methodOp(onKeyPress), 7998 triggerOnKeyPress: methodOp(onKeyPress),
5179 triggerOnKeyUp: onKeyUp, 7999 triggerOnKeyUp: onKeyUp,
5180 8000
5181 execCommand: function(cmd) { 8001 execCommand: function(cmd) {
5182 if (commands.hasOwnProperty(cmd)) 8002 if (commands.hasOwnProperty(cmd))
5183 return commands[cmd].call(null, this); 8003 { return commands[cmd].call(null, this) }
5184 }, 8004 },
5185 8005
5186 triggerElectric: methodOp(function(text) { triggerElectric(this, text); }), 8006 triggerElectric: methodOp(function(text) { triggerElectric(this, text) }),
5187 8007
5188 findPosH: function(from, amount, unit, visually) { 8008 findPosH: function(from, amount, unit, visually) {
5189 var dir = 1; 8009 var this$1 = this;
5190 if (amount < 0) { dir = -1; amount = -amount; } 8010
5191 for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) { 8011 var dir = 1
5192 cur = findPosH(this.doc, cur, dir, unit, visually); 8012 if (amount < 0) { dir = -1; amount = -amount }
5193 if (cur.hitSide) break; 8013 var cur = clipPos(this.doc, from)
8014 for (var i = 0; i < amount; ++i) {
8015 cur = findPosH(this$1.doc, cur, dir, unit, visually)
8016 if (cur.hitSide) { break }
5194 } 8017 }
5195 return cur; 8018 return cur
5196 }, 8019 },
5197 8020
5198 moveH: methodOp(function(dir, unit) { 8021 moveH: methodOp(function(dir, unit) {
5199 var cm = this; 8022 var this$1 = this;
5200 cm.extendSelectionsBy(function(range) { 8023
5201 if (cm.display.shift || cm.doc.extend || range.empty()) 8024 this.extendSelectionsBy(function (range) {
5202 return findPosH(cm.doc, range.head, dir, unit, cm.options.rtlMoveVisua lly); 8025 if (this$1.display.shift || this$1.doc.extend || range.empty())
8026 { return findPosH(this$1.doc, range.head, dir, unit, this$1.options.rt lMoveVisually) }
5203 else 8027 else
5204 return dir < 0 ? range.from() : range.to(); 8028 { return dir < 0 ? range.from() : range.to() }
5205 }, sel_move); 8029 }, sel_move)
5206 }), 8030 }),
5207 8031
5208 deleteH: methodOp(function(dir, unit) { 8032 deleteH: methodOp(function(dir, unit) {
5209 var sel = this.doc.sel, doc = this.doc; 8033 var sel = this.doc.sel, doc = this.doc
5210 if (sel.somethingSelected()) 8034 if (sel.somethingSelected())
5211 doc.replaceSelection("", null, "+delete"); 8035 { doc.replaceSelection("", null, "+delete") }
5212 else 8036 else
5213 deleteNearSelection(this, function(range) { 8037 { deleteNearSelection(this, function (range) {
5214 var other = findPosH(doc, range.head, dir, unit, false); 8038 var other = findPosH(doc, range.head, dir, unit, false)
5215 return dir < 0 ? {from: other, to: range.head} : {from: range.head, to : other}; 8039 return dir < 0 ? {from: other, to: range.head} : {from: range.head, to : other}
5216 }); 8040 }) }
5217 }), 8041 }),
5218 8042
5219 findPosV: function(from, amount, unit, goalColumn) { 8043 findPosV: function(from, amount, unit, goalColumn) {
5220 var dir = 1, x = goalColumn; 8044 var this$1 = this;
5221 if (amount < 0) { dir = -1; amount = -amount; } 8045
5222 for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) { 8046 var dir = 1, x = goalColumn
5223 var coords = cursorCoords(this, cur, "div"); 8047 if (amount < 0) { dir = -1; amount = -amount }
5224 if (x == null) x = coords.left; 8048 var cur = clipPos(this.doc, from)
5225 else coords.left = x; 8049 for (var i = 0; i < amount; ++i) {
5226 cur = findPosV(this, coords, dir, unit); 8050 var coords = cursorCoords(this$1, cur, "div")
5227 if (cur.hitSide) break; 8051 if (x == null) { x = coords.left }
8052 else { coords.left = x }
8053 cur = findPosV(this$1, coords, dir, unit)
8054 if (cur.hitSide) { break }
5228 } 8055 }
5229 return cur; 8056 return cur
5230 }, 8057 },
5231 8058
5232 moveV: methodOp(function(dir, unit) { 8059 moveV: methodOp(function(dir, unit) {
5233 var cm = this, doc = this.doc, goals = []; 8060 var this$1 = this;
5234 var collapse = !cm.display.shift && !doc.extend && doc.sel.somethingSelect ed(); 8061
5235 doc.extendSelectionsBy(function(range) { 8062 var doc = this.doc, goals = []
8063 var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSele cted()
8064 doc.extendSelectionsBy(function (range) {
5236 if (collapse) 8065 if (collapse)
5237 return dir < 0 ? range.from() : range.to(); 8066 { return dir < 0 ? range.from() : range.to() }
5238 var headPos = cursorCoords(cm, range.head, "div"); 8067 var headPos = cursorCoords(this$1, range.head, "div")
5239 if (range.goalColumn != null) headPos.left = range.goalColumn; 8068 if (range.goalColumn != null) { headPos.left = range.goalColumn }
5240 goals.push(headPos.left); 8069 goals.push(headPos.left)
5241 var pos = findPosV(cm, headPos, dir, unit); 8070 var pos = findPosV(this$1, headPos, dir, unit)
5242 if (unit == "page" && range == doc.sel.primary()) 8071 if (unit == "page" && range == doc.sel.primary())
5243 addToScrollPos(cm, null, charCoords(cm, pos, "div").top - headPos.top) ; 8072 { addToScrollPos(this$1, null, charCoords(this$1, pos, "div").top - he adPos.top) }
5244 return pos; 8073 return pos
5245 }, sel_move); 8074 }, sel_move)
5246 if (goals.length) for (var i = 0; i < doc.sel.ranges.length; i++) 8075 if (goals.length) { for (var i = 0; i < doc.sel.ranges.length; i++)
5247 doc.sel.ranges[i].goalColumn = goals[i]; 8076 { doc.sel.ranges[i].goalColumn = goals[i] } }
5248 }), 8077 }),
5249 8078
5250 // Find the word at the given position (as returned by coordsChar). 8079 // Find the word at the given position (as returned by coordsChar).
5251 findWordAt: function(pos) { 8080 findWordAt: function(pos) {
5252 var doc = this.doc, line = getLine(doc, pos.line).text; 8081 var doc = this.doc, line = getLine(doc, pos.line).text
5253 var start = pos.ch, end = pos.ch; 8082 var start = pos.ch, end = pos.ch
5254 if (line) { 8083 if (line) {
5255 var helper = this.getHelper(pos, "wordChars"); 8084 var helper = this.getHelper(pos, "wordChars")
5256 if ((pos.xRel < 0 || end == line.length) && start) --start; else ++end; 8085 if ((pos.sticky == "before" || end == line.length) && start) { --start; } else { ++end }
5257 var startChar = line.charAt(start); 8086 var startChar = line.charAt(start)
5258 var check = isWordChar(startChar, helper) 8087 var check = isWordChar(startChar, helper)
5259 ? function(ch) { return isWordChar(ch, helper); } 8088 ? function (ch) { return isWordChar(ch, helper); }
5260 : /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);} 8089 : /\s/.test(startChar) ? function (ch) { return /\s/.test(ch); }
5261 : function(ch) {return !/\s/.test(ch) && !isWordChar(ch);}; 8090 : function (ch) { return (!/\s/.test(ch) && !isWordChar(ch)); }
5262 while (start > 0 && check(line.charAt(start - 1))) --start; 8091 while (start > 0 && check(line.charAt(start - 1))) { --start }
5263 while (end < line.length && check(line.charAt(end))) ++end; 8092 while (end < line.length && check(line.charAt(end))) { ++end }
5264 } 8093 }
5265 return new Range(Pos(pos.line, start), Pos(pos.line, end)); 8094 return new Range(Pos(pos.line, start), Pos(pos.line, end))
5266 }, 8095 },
5267 8096
5268 toggleOverwrite: function(value) { 8097 toggleOverwrite: function(value) {
5269 if (value != null && value == this.state.overwrite) return; 8098 if (value != null && value == this.state.overwrite) { return }
5270 if (this.state.overwrite = !this.state.overwrite) 8099 if (this.state.overwrite = !this.state.overwrite)
5271 addClass(this.display.cursorDiv, "CodeMirror-overwrite"); 8100 { addClass(this.display.cursorDiv, "CodeMirror-overwrite") }
5272 else 8101 else
5273 rmClass(this.display.cursorDiv, "CodeMirror-overwrite"); 8102 { rmClass(this.display.cursorDiv, "CodeMirror-overwrite") }
5274 8103
5275 signal(this, "overwriteToggle", this, this.state.overwrite); 8104 signal(this, "overwriteToggle", this, this.state.overwrite)
5276 }, 8105 },
5277 hasFocus: function() { return this.display.input.getField() == activeElt(); }, 8106 hasFocus: function() { return this.display.input.getField() == activeElt() } ,
5278 isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdi t); }, 8107 isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdi t) },
5279 8108
5280 scrollTo: methodOp(function(x, y) { 8109 scrollTo: methodOp(function(x, y) {
5281 if (x != null || y != null) resolveScrollToPos(this); 8110 if (x != null || y != null) { resolveScrollToPos(this) }
5282 if (x != null) this.curOp.scrollLeft = x; 8111 if (x != null) { this.curOp.scrollLeft = x }
5283 if (y != null) this.curOp.scrollTop = y; 8112 if (y != null) { this.curOp.scrollTop = y }
5284 }), 8113 }),
5285 getScrollInfo: function() { 8114 getScrollInfo: function() {
5286 var scroller = this.display.scroller; 8115 var scroller = this.display.scroller
5287 return {left: scroller.scrollLeft, top: scroller.scrollTop, 8116 return {left: scroller.scrollLeft, top: scroller.scrollTop,
5288 height: scroller.scrollHeight - scrollGap(this) - this.display.bar Height, 8117 height: scroller.scrollHeight - scrollGap(this) - this.display.bar Height,
5289 width: scroller.scrollWidth - scrollGap(this) - this.display.barWi dth, 8118 width: scroller.scrollWidth - scrollGap(this) - this.display.barWi dth,
5290 clientHeight: displayHeight(this), clientWidth: displayWidth(this) }; 8119 clientHeight: displayHeight(this), clientWidth: displayWidth(this) }
5291 }, 8120 },
5292 8121
5293 scrollIntoView: methodOp(function(range, margin) { 8122 scrollIntoView: methodOp(function(range, margin) {
5294 if (range == null) { 8123 if (range == null) {
5295 range = {from: this.doc.sel.primary().head, to: null}; 8124 range = {from: this.doc.sel.primary().head, to: null}
5296 if (margin == null) margin = this.options.cursorScrollMargin; 8125 if (margin == null) { margin = this.options.cursorScrollMargin }
5297 } else if (typeof range == "number") { 8126 } else if (typeof range == "number") {
5298 range = {from: Pos(range, 0), to: null}; 8127 range = {from: Pos(range, 0), to: null}
5299 } else if (range.from == null) { 8128 } else if (range.from == null) {
5300 range = {from: range, to: null}; 8129 range = {from: range, to: null}
5301 } 8130 }
5302 if (!range.to) range.to = range.from; 8131 if (!range.to) { range.to = range.from }
5303 range.margin = margin || 0; 8132 range.margin = margin || 0
5304 8133
5305 if (range.from.line != null) { 8134 if (range.from.line != null) {
5306 resolveScrollToPos(this); 8135 resolveScrollToPos(this)
5307 this.curOp.scrollToPos = range; 8136 this.curOp.scrollToPos = range
5308 } else { 8137 } else {
5309 var sPos = calculateScrollPos(this, Math.min(range.from.left, range.to.l eft), 8138 var sPos = calculateScrollPos(this, {
5310 Math.min(range.from.top, range.to.top) - r ange.margin, 8139 left: Math.min(range.from.left, range.to.left),
5311 Math.max(range.from.right, range.to.right) , 8140 top: Math.min(range.from.top, range.to.top) - range.margin,
5312 Math.max(range.from.bottom, range.to.botto m) + range.margin); 8141 right: Math.max(range.from.right, range.to.right),
5313 this.scrollTo(sPos.scrollLeft, sPos.scrollTop); 8142 bottom: Math.max(range.from.bottom, range.to.bottom) + range.margin
8143 })
8144 this.scrollTo(sPos.scrollLeft, sPos.scrollTop)
5314 } 8145 }
5315 }), 8146 }),
5316 8147
5317 setSize: methodOp(function(width, height) { 8148 setSize: methodOp(function(width, height) {
5318 var cm = this; 8149 var this$1 = this;
5319 function interpret(val) { 8150
5320 return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; 8151 var interpret = function (val) { return typeof val == "number" || /^\d+$/. test(String(val)) ? val + "px" : val; }
8152 if (width != null) { this.display.wrapper.style.width = interpret(width) }
8153 if (height != null) { this.display.wrapper.style.height = interpret(height ) }
8154 if (this.options.lineWrapping) { clearLineMeasurementCache(this) }
8155 var lineNo = this.display.viewFrom
8156 this.doc.iter(lineNo, this.display.viewTo, function (line) {
8157 if (line.widgets) { for (var i = 0; i < line.widgets.length; i++)
8158 { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo, "widg et"); break } } }
8159 ++lineNo
8160 })
8161 this.curOp.forceUpdate = true
8162 signal(this, "refresh", this)
8163 }),
8164
8165 operation: function(f){return runInOp(this, f)},
8166
8167 refresh: methodOp(function() {
8168 var oldHeight = this.display.cachedTextHeight
8169 regChange(this)
8170 this.curOp.forceUpdate = true
8171 clearCaches(this)
8172 this.scrollTo(this.doc.scrollLeft, this.doc.scrollTop)
8173 updateGutterSpace(this)
8174 if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5)
8175 { estimateLineHeights(this) }
8176 signal(this, "refresh", this)
8177 }),
8178
8179 swapDoc: methodOp(function(doc) {
8180 var old = this.doc
8181 old.cm = null
8182 attachDoc(this, doc)
8183 clearCaches(this)
8184 this.display.input.reset()
8185 this.scrollTo(doc.scrollLeft, doc.scrollTop)
8186 this.curOp.forceScroll = true
8187 signalLater(this, "swapDoc", this, old)
8188 return old
8189 }),
8190
8191 getInputField: function(){return this.display.input.getField()},
8192 getWrapperElement: function(){return this.display.wrapper},
8193 getScrollerElement: function(){return this.display.scroller},
8194 getGutterElement: function(){return this.display.gutters}
8195 }
8196 eventMixin(CodeMirror)
8197
8198 CodeMirror.registerHelper = function(type, name, value) {
8199 if (!helpers.hasOwnProperty(type)) { helpers[type] = CodeMirror[type] = {_gl obal: []} }
8200 helpers[type][name] = value
8201 }
8202 CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {
8203 CodeMirror.registerHelper(type, name, value)
8204 helpers[type]._global.push({pred: predicate, val: value})
8205 }
8206 }
8207
8208 // Used for horizontal relative motion. Dir is -1 or 1 (left or
8209 // right), unit can be "char", "column" (like char, but doesn't
8210 // cross line boundaries), "word" (across next word), or "group" (to
8211 // the start of next group of word or non-word-non-whitespace
8212 // chars). The visually param controls whether, in right-to-left
8213 // text, direction 1 means to move towards the next index in the
8214 // string, or towards the character to the right of the current
8215 // position. The resulting position will have a hitSide=true
8216 // property if it reached the end of the document.
8217 function findPosH(doc, pos, dir, unit, visually) {
8218 var oldPos = pos
8219 var origDir = dir
8220 var lineObj = getLine(doc, pos.line)
8221 function findNextLine() {
8222 var l = pos.line + dir
8223 if (l < doc.first || l >= doc.first + doc.size) { return false }
8224 pos = new Pos(l, pos.ch, pos.sticky)
8225 return lineObj = getLine(doc, l)
8226 }
8227 function moveOnce(boundToLine) {
8228 var next
8229 if (visually) {
8230 next = moveVisually(doc.cm, lineObj, pos, dir)
8231 } else {
8232 next = moveLogically(lineObj, pos, dir)
8233 }
8234 if (next == null) {
8235 if (!boundToLine && findNextLine())
8236 { pos = endOfLine(visually, doc.cm, lineObj, pos.line, dir) }
8237 else
8238 { return false }
8239 } else {
8240 pos = next
8241 }
8242 return true
8243 }
8244
8245 if (unit == "char") {
8246 moveOnce()
8247 } else if (unit == "column") {
8248 moveOnce(true)
8249 } else if (unit == "word" || unit == "group") {
8250 var sawType = null, group = unit == "group"
8251 var helper = doc.cm && doc.cm.getHelper(pos, "wordChars")
8252 for (var first = true;; first = false) {
8253 if (dir < 0 && !moveOnce(!first)) { break }
8254 var cur = lineObj.text.charAt(pos.ch) || "\n"
8255 var type = isWordChar(cur, helper) ? "w"
8256 : group && cur == "\n" ? "n"
8257 : !group || /\s/.test(cur) ? null
8258 : "p"
8259 if (group && !first && !type) { type = "s" }
8260 if (sawType && sawType != type) {
8261 if (dir < 0) {dir = 1; moveOnce(); pos.sticky = "after"}
8262 break
5321 } 8263 }
5322 if (width != null) cm.display.wrapper.style.width = interpret(width); 8264
5323 if (height != null) cm.display.wrapper.style.height = interpret(height); 8265 if (type) { sawType = type }
5324 if (cm.options.lineWrapping) clearLineMeasurementCache(this); 8266 if (dir > 0 && !moveOnce(!first)) { break }
5325 var lineNo = cm.display.viewFrom; 8267 }
5326 cm.doc.iter(lineNo, cm.display.viewTo, function(line) { 8268 }
5327 if (line.widgets) for (var i = 0; i < line.widgets.length; i++) 8269 var result = skipAtomic(doc, pos, oldPos, origDir, true)
5328 if (line.widgets[i].noHScroll) { regLineChange(cm, lineNo, "widget"); break; } 8270 if (equalCursorPos(oldPos, result)) { result.hitSide = true }
5329 ++lineNo; 8271 return result
5330 }); 8272 }
5331 cm.curOp.forceUpdate = true; 8273
5332 signal(cm, "refresh", this); 8274 // For relative vertical movement. Dir may be -1 or 1. Unit can be
5333 }), 8275 // "page" or "line". The resulting position will have a hitSide=true
5334 8276 // property if it reached the end of the document.
5335 operation: function(f){return runInOp(this, f);}, 8277 function findPosV(cm, pos, dir, unit) {
5336 8278 var doc = cm.doc, x = pos.left, y
5337 refresh: methodOp(function() { 8279 if (unit == "page") {
5338 var oldHeight = this.display.cachedTextHeight; 8280 var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight)
5339 regChange(this); 8281 var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3)
5340 this.curOp.forceUpdate = true; 8282 y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount
5341 clearCaches(this); 8283
5342 this.scrollTo(this.doc.scrollLeft, this.doc.scrollTop); 8284 } else if (unit == "line") {
5343 updateGutterSpace(this); 8285 y = dir > 0 ? pos.bottom + 3 : pos.top - 3
5344 if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5) 8286 }
5345 estimateLineHeights(this); 8287 var target
5346 signal(this, "refresh", this); 8288 for (;;) {
5347 }), 8289 target = coordsChar(cm, x, y)
5348 8290 if (!target.outside) { break }
5349 swapDoc: methodOp(function(doc) { 8291 if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break }
5350 var old = this.doc; 8292 y += dir * 5
5351 old.cm = null; 8293 }
5352 attachDoc(this, doc); 8294 return target
5353 clearCaches(this); 8295 }
5354 this.display.input.reset(); 8296
5355 this.scrollTo(doc.scrollLeft, doc.scrollTop); 8297 // CONTENTEDITABLE INPUT STYLE
5356 this.curOp.forceScroll = true; 8298
5357 signalLater(this, "swapDoc", this, old); 8299 var ContentEditableInput = function ContentEditableInput(cm) {
5358 return old; 8300 this.cm = cm
5359 }), 8301 this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFo cusOffset = null
5360 8302 this.polling = new Delayed()
5361 getInputField: function(){return this.display.input.getField();}, 8303 this.composing = null
5362 getWrapperElement: function(){return this.display.wrapper;}, 8304 this.gracePeriod = false
5363 getScrollerElement: function(){return this.display.scroller;}, 8305 this.readDOMTimeout = null
5364 getGutterElement: function(){return this.display.gutters;} 8306 };
5365 }; 8307
5366 eventMixin(CodeMirror); 8308 ContentEditableInput.prototype.init = function init (display) {
5367 8309 var this$1 = this;
5368 // OPTION DEFAULTS 8310
5369 8311 var input = this, cm = input.cm
5370 // The default configuration options. 8312 var div = input.div = display.lineDiv
5371 var defaults = CodeMirror.defaults = {}; 8313 disableBrowserMagic(div, cm.options.spellcheck)
5372 // Functions to run when options are changed. 8314
5373 var optionHandlers = CodeMirror.optionHandlers = {}; 8315 on(div, "paste", function (e) {
5374 8316 if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
5375 function option(name, deflt, handle, notOnInit) { 8317 // IE doesn't fire input events, so we schedule a read for the pasted conten t in this way
5376 CodeMirror.defaults[name] = deflt; 8318 if (ie_version <= 11) { setTimeout(operation(cm, function () { return this$1 .updateFromDOM(); }), 20) }
5377 if (handle) optionHandlers[name] = 8319 })
5378 notOnInit ? function(cm, val, old) {if (old != Init) handle(cm, val, old); } : handle; 8320
5379 } 8321 on(div, "compositionstart", function (e) {
5380 8322 this$1.composing = {data: e.data, done: false}
5381 // Passed to option handlers when there is no old value. 8323 })
5382 var Init = CodeMirror.Init = {toString: function(){return "CodeMirror.Init";}} ; 8324 on(div, "compositionupdate", function (e) {
5383 8325 if (!this$1.composing) { this$1.composing = {data: e.data, done: false} }
5384 // These two are, on init, called from the constructor because they 8326 })
5385 // have to be initialized before the editor can start at all. 8327 on(div, "compositionend", function (e) {
5386 option("value", "", function(cm, val) { 8328 if (this$1.composing) {
5387 cm.setValue(val); 8329 if (e.data != this$1.composing.data) { this$1.readFromDOMSoon() }
5388 }, true); 8330 this$1.composing.done = true
5389 option("mode", null, function(cm, val) { 8331 }
5390 cm.doc.modeOption = val; 8332 })
5391 loadMode(cm); 8333
5392 }, true); 8334 on(div, "touchstart", function () { return input.forceCompositionEnd(); })
5393 8335
5394 option("indentUnit", 2, loadMode, true); 8336 on(div, "input", function () {
5395 option("indentWithTabs", false); 8337 if (!this$1.composing) { this$1.readFromDOMSoon() }
5396 option("smartIndent", true); 8338 })
5397 option("tabSize", 4, function(cm) { 8339
5398 resetModeState(cm); 8340 function onCopyCut(e) {
5399 clearCaches(cm); 8341 if (signalDOMEvent(cm, e)) { return }
5400 regChange(cm); 8342 if (cm.somethingSelected()) {
5401 }, true); 8343 setLastCopied({lineWise: false, text: cm.getSelections()})
5402 option("lineSeparator", null, function(cm, val) { 8344 if (e.type == "cut") { cm.replaceSelection("", null, "cut") }
5403 cm.doc.lineSep = val; 8345 } else if (!cm.options.lineWiseCopyCut) {
5404 if (!val) return; 8346 return
5405 var newBreaks = [], lineNo = cm.doc.first; 8347 } else {
5406 cm.doc.iter(function(line) { 8348 var ranges = copyableRanges(cm)
5407 for (var pos = 0;;) { 8349 setLastCopied({lineWise: true, text: ranges.text})
5408 var found = line.text.indexOf(val, pos); 8350 if (e.type == "cut") {
5409 if (found == -1) break; 8351 cm.operation(function () {
5410 pos = found + val.length; 8352 cm.setSelections(ranges.ranges, 0, sel_dontScroll)
5411 newBreaks.push(Pos(lineNo, found)); 8353 cm.replaceSelection("", null, "cut")
8354 })
5412 } 8355 }
5413 lineNo++; 8356 }
5414 }); 8357 if (e.clipboardData) {
5415 for (var i = newBreaks.length - 1; i >= 0; i--) 8358 e.clipboardData.clearData()
5416 replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i ].ch + val.length)) 8359 var content = lastCopied.text.join("\n")
5417 }); 8360 // iOS exposes the clipboard API, but seems to discard content inserted in to it
5418 option("specialChars", /[\u0000-\u001f\u007f\u00ad\u200b-\u200f\u2028\u2029\uf eff]/g, function(cm, val, old) { 8361 e.clipboardData.setData("Text", content)
5419 cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t" ), "g"); 8362 if (e.clipboardData.getData("Text") == content) {
5420 if (old != CodeMirror.Init) cm.refresh(); 8363 e.preventDefault()
5421 }); 8364 return
5422 option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function(cm) { cm.refresh();}, true); 8365 }
5423 option("electricChars", true); 8366 }
5424 option("inputStyle", mobile ? "contenteditable" : "textarea", function() { 8367 // Old-fashioned briefly-focus-a-textarea hack
5425 throw new Error("inputStyle can not (yet) be changed in a running editor"); // FIXME 8368 var kludge = hiddenTextarea(), te = kludge.firstChild
5426 }, true); 8369 cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild)
5427 option("rtlMoveVisually", !windows); 8370 te.value = lastCopied.text.join("\n")
5428 option("wholeLineUpdateBefore", true); 8371 var hadFocus = document.activeElement
5429 8372 selectInput(te)
5430 option("theme", "default", function(cm) { 8373 setTimeout(function () {
5431 themeChanged(cm); 8374 cm.display.lineSpace.removeChild(kludge)
5432 guttersChanged(cm); 8375 hadFocus.focus()
5433 }, true); 8376 if (hadFocus == div) { input.showPrimarySelection() }
5434 option("keyMap", "default", function(cm, val, old) { 8377 }, 50)
5435 var next = getKeyMap(val); 8378 }
5436 var prev = old != CodeMirror.Init && getKeyMap(old); 8379 on(div, "copy", onCopyCut)
5437 if (prev && prev.detach) prev.detach(cm, next); 8380 on(div, "cut", onCopyCut)
5438 if (next.attach) next.attach(cm, prev || null); 8381 };
5439 }); 8382
5440 option("extraKeys", null); 8383 ContentEditableInput.prototype.prepareSelection = function prepareSelection$1 () {
5441 8384 var result = prepareSelection(this.cm, false)
5442 option("lineWrapping", false, wrappingChanged, true); 8385 result.focus = this.cm.state.focused
5443 option("gutters", [], function(cm) { 8386 return result
5444 setGuttersForLineNumbers(cm.options); 8387 };
5445 guttersChanged(cm); 8388
5446 }, true); 8389 ContentEditableInput.prototype.showSelection = function showSelection (info, tak eFocus) {
5447 option("fixedGutter", true, function(cm, val) { 8390 if (!info || !this.cm.display.view.length) { return }
5448 cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px " : "0"; 8391 if (info.focus || takeFocus) { this.showPrimarySelection() }
5449 cm.refresh(); 8392 this.showMultipleSelections(info)
5450 }, true); 8393 };
5451 option("coverGutterNextToScrollbar", false, function(cm) {updateScrollbars(cm) ;}, true); 8394
5452 option("scrollbarStyle", "native", function(cm) { 8395 ContentEditableInput.prototype.showPrimarySelection = function showPrimarySelect ion () {
5453 initScrollbars(cm); 8396 var sel = window.getSelection(), prim = this.cm.doc.sel.primary()
5454 updateScrollbars(cm); 8397 var curAnchor = domToPos(this.cm, sel.anchorNode, sel.anchorOffset)
5455 cm.display.scrollbars.setScrollTop(cm.doc.scrollTop); 8398 var curFocus = domToPos(this.cm, sel.focusNode, sel.focusOffset)
5456 cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft); 8399 if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad &&
5457 }, true); 8400 cmp(minPos(curAnchor, curFocus), prim.from()) == 0 &&
5458 option("lineNumbers", false, function(cm) { 8401 cmp(maxPos(curAnchor, curFocus), prim.to()) == 0)
5459 setGuttersForLineNumbers(cm.options); 8402 { return }
5460 guttersChanged(cm); 8403
5461 }, true); 8404 var start = posToDOM(this.cm, prim.from())
5462 option("firstLineNumber", 1, guttersChanged, true); 8405 var end = posToDOM(this.cm, prim.to())
5463 option("lineNumberFormatter", function(integer) {return integer;}, guttersChan ged, true); 8406 if (!start && !end) {
5464 option("showCursorWhenSelecting", false, updateSelection, true); 8407 sel.removeAllRanges()
5465 8408 return
5466 option("resetSelectionOnContextMenu", true); 8409 }
5467 option("lineWiseCopyCut", true); 8410
5468 8411 var view = this.cm.display.view
5469 option("readOnly", false, function(cm, val) { 8412 var old = sel.rangeCount && sel.getRangeAt(0)
5470 if (val == "nocursor") { 8413 if (!start) {
5471 onBlur(cm); 8414 start = {node: view[0].measure.map[2], offset: 0}
5472 cm.display.input.blur(); 8415 } else if (!end) { // FIXME dangerously hacky
5473 cm.display.disabled = true; 8416 var measure = view[view.length - 1].measure
8417 var map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map
8418 end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map.leng th - 3]}
8419 }
8420
8421 var rng
8422 try { rng = range(start.node, start.offset, end.offset, end.node) }
8423 catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible
8424 if (rng) {
8425 if (!gecko && this.cm.state.focused) {
8426 sel.collapse(start.node, start.offset)
8427 if (!rng.collapsed) {
8428 sel.removeAllRanges()
8429 sel.addRange(rng)
8430 }
5474 } else { 8431 } else {
5475 cm.display.disabled = false; 8432 sel.removeAllRanges()
5476 } 8433 sel.addRange(rng)
5477 cm.display.input.readOnlyChanged(val) 8434 }
5478 }); 8435 if (old && sel.anchorNode == null) { sel.addRange(old) }
5479 option("disableInput", false, function(cm, val) {if (!val) cm.display.input.re set();}, true); 8436 else if (gecko) { this.startGracePeriod() }
5480 option("dragDrop", true, dragDropChanged); 8437 }
5481 option("allowDropFileTypes", null); 8438 this.rememberSelection()
5482 8439 };
5483 option("cursorBlinkRate", 530); 8440
5484 option("cursorScrollMargin", 0); 8441 ContentEditableInput.prototype.startGracePeriod = function startGracePeriod () {
5485 option("cursorHeight", 1, updateSelection, true); 8442 var this$1 = this;
5486 option("singleCursorHeightPerLine", true, updateSelection, true); 8443
5487 option("workTime", 100); 8444 clearTimeout(this.gracePeriod)
5488 option("workDelay", 100); 8445 this.gracePeriod = setTimeout(function () {
5489 option("flattenSpans", true, resetModeState, true); 8446 this$1.gracePeriod = false
5490 option("addModeClass", false, resetModeState, true); 8447 if (this$1.selectionChanged())
5491 option("pollInterval", 100); 8448 { this$1.cm.operation(function () { return this$1.cm.curOp.selectionChange d = true; }) }
5492 option("undoDepth", 200, function(cm, val){cm.doc.history.undoDepth = val;}); 8449 }, 20)
5493 option("historyEventDelay", 1250); 8450 };
5494 option("viewportMargin", 10, function(cm){cm.refresh();}, true); 8451
5495 option("maxHighlightLength", 10000, resetModeState, true); 8452 ContentEditableInput.prototype.showMultipleSelections = function showMultipleSel ections (info) {
5496 option("moveInputWithCursor", true, function(cm, val) { 8453 removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors)
5497 if (!val) cm.display.input.resetPosition(); 8454 removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection)
5498 }); 8455 };
5499 8456
5500 option("tabindex", null, function(cm, val) { 8457 ContentEditableInput.prototype.rememberSelection = function rememberSelection () {
5501 cm.display.input.getField().tabIndex = val || ""; 8458 var sel = window.getSelection()
5502 }); 8459 this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset
5503 option("autofocus", null); 8460 this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset
5504 8461 };
5505 // MODE DEFINITION AND QUERYING 8462
5506 8463 ContentEditableInput.prototype.selectionInEditor = function selectionInEditor () {
5507 // Known modes, by name and by MIME 8464 var sel = window.getSelection()
5508 var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {}; 8465 if (!sel.rangeCount) { return false }
5509 8466 var node = sel.getRangeAt(0).commonAncestorContainer
5510 // Extra arguments are stored as the mode's dependencies, which is 8467 return contains(this.div, node)
5511 // used by (legacy) mechanisms like loadmode.js to automatically 8468 };
5512 // load a mode. (Preferred mechanism is the require/define calls.) 8469
5513 CodeMirror.defineMode = function(name, mode) { 8470 ContentEditableInput.prototype.focus = function focus () {
5514 if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name; 8471 if (this.cm.options.readOnly != "nocursor") {
5515 if (arguments.length > 2) 8472 if (!this.selectionInEditor())
5516 mode.dependencies = Array.prototype.slice.call(arguments, 2); 8473 { this.showSelection(this.prepareSelection(), true) }
5517 modes[name] = mode; 8474 this.div.focus()
5518 }; 8475 }
5519 8476 };
5520 CodeMirror.defineMIME = function(mime, spec) { 8477 ContentEditableInput.prototype.blur = function blur () { this.div.blur() };
5521 mimeModes[mime] = spec; 8478 ContentEditableInput.prototype.getField = function getField () { return this.div };
5522 }; 8479
5523 8480 ContentEditableInput.prototype.supportsTouch = function supportsTouch () { retur n true };
5524 // Given a MIME type, a {name, ...options} config object, or a name 8481
5525 // string, return a mode config object. 8482 ContentEditableInput.prototype.receivedFocus = function receivedFocus () {
5526 CodeMirror.resolveMode = function(spec) { 8483 var input = this
5527 if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) { 8484 if (this.selectionInEditor())
5528 spec = mimeModes[spec]; 8485 { this.pollSelection() }
5529 } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty( spec.name)) { 8486 else
5530 var found = mimeModes[spec.name]; 8487 { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = tr ue; }) }
5531 if (typeof found == "string") found = {name: found}; 8488
5532 spec = createObj(found, spec); 8489 function poll() {
5533 spec.name = found.name; 8490 if (input.cm.state.focused) {
5534 } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) { 8491 input.pollSelection()
5535 return CodeMirror.resolveMode("application/xml"); 8492 input.polling.set(input.cm.options.pollInterval, poll)
5536 } 8493 }
5537 if (typeof spec == "string") return {name: spec}; 8494 }
5538 else return spec || {name: "null"}; 8495 this.polling.set(this.cm.options.pollInterval, poll)
5539 }; 8496 };
5540 8497
5541 // Given a mode spec (anything that resolveMode accepts), find and 8498 ContentEditableInput.prototype.selectionChanged = function selectionChanged () {
5542 // initialize an actual mode object. 8499 var sel = window.getSelection()
5543 CodeMirror.getMode = function(options, spec) { 8500 return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastA nchorOffset ||
5544 var spec = CodeMirror.resolveMode(spec); 8501 sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffs et
5545 var mfactory = modes[spec.name]; 8502 };
5546 if (!mfactory) return CodeMirror.getMode(options, "text/plain"); 8503
5547 var modeObj = mfactory(options, spec); 8504 ContentEditableInput.prototype.pollSelection = function pollSelection () {
5548 if (modeExtensions.hasOwnProperty(spec.name)) { 8505 if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged( )) { return }
5549 var exts = modeExtensions[spec.name]; 8506 var sel = window.getSelection(), cm = this.cm
5550 for (var prop in exts) { 8507 // On Android Chrome (version 56, at least), backspacing into an
5551 if (!exts.hasOwnProperty(prop)) continue; 8508 // uneditable block element will put the cursor in that element,
5552 if (modeObj.hasOwnProperty(prop)) modeObj["_" + prop] = modeObj[prop]; 8509 // and then, because it's not editable, hide the virtual keyboard.
5553 modeObj[prop] = exts[prop]; 8510 // Because Android doesn't allow us to actually detect backspace
8511 // presses in a sane way, this code checks for when that happens
8512 // and simulates a backspace press in this case.
8513 if (android && chrome && this.cm.options.gutters.length && isInGutter(sel.anch orNode)) {
8514 this.cm.triggerOnKeyDown({type: "keydown", keyCode: 8, preventDefault: Math. abs})
8515 this.blur()
8516 this.focus()
8517 return
8518 }
8519 if (this.composing) { return }
8520 this.rememberSelection()
8521 var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset)
8522 var head = domToPos(cm, sel.focusNode, sel.focusOffset)
8523 if (anchor && head) { runInOp(cm, function () {
8524 setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll)
8525 if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true }
8526 }) }
8527 };
8528
8529 ContentEditableInput.prototype.pollContent = function pollContent () {
8530 if (this.readDOMTimeout != null) {
8531 clearTimeout(this.readDOMTimeout)
8532 this.readDOMTimeout = null
8533 }
8534
8535 var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary()
8536 var from = sel.from(), to = sel.to()
8537 if (from.ch == 0 && from.line > cm.firstLine())
8538 { from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length) }
8539 if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine())
8540 { to = Pos(to.line + 1, 0) }
8541 if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return fal se }
8542
8543 var fromIndex, fromLine, fromNode
8544 if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line) ) == 0) {
8545 fromLine = lineNo(display.view[0].line)
8546 fromNode = display.view[0].node
8547 } else {
8548 fromLine = lineNo(display.view[fromIndex].line)
8549 fromNode = display.view[fromIndex - 1].node.nextSibling
8550 }
8551 var toIndex = findViewIndex(cm, to.line)
8552 var toLine, toNode
8553 if (toIndex == display.view.length - 1) {
8554 toLine = display.viewTo - 1
8555 toNode = display.lineDiv.lastChild
8556 } else {
8557 toLine = lineNo(display.view[toIndex + 1].line) - 1
8558 toNode = display.view[toIndex + 1].node.previousSibling
8559 }
8560
8561 if (!fromNode) { return false }
8562 var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine))
8563 var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length))
8564 while (newText.length > 1 && oldText.length > 1) {
8565 if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine-- }
8566 else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromL ine++ }
8567 else { break }
8568 }
8569
8570 var cutFront = 0, cutEnd = 0
8571 var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.le ngth, oldTop.length)
8572 while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCod eAt(cutFront))
8573 { ++cutFront }
8574 var newBot = lst(newText), oldBot = lst(oldText)
8575 var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0),
8576 oldBot.length - (oldText.length == 1 ? cutFront : 0))
8577 while (cutEnd < maxCutEnd &&
8578 newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldB ot.length - cutEnd - 1))
8579 { ++cutEnd }
8580 // Try to move start of change to start of selection if ambiguous
8581 if (newText.length == 1 && oldText.length == 1 && fromLine == from.line) {
8582 while (cutFront && cutFront > from.ch &&
8583 newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(ol dBot.length - cutEnd - 1)) {
8584 cutFront--
8585 cutEnd++
8586 }
8587 }
8588
8589 newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace( /^\u200b+/, "")
8590 newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, "")
8591
8592 var chFrom = Pos(fromLine, cutFront)
8593 var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0)
8594 if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {
8595 replaceRange(cm.doc, newText, chFrom, chTo, "+input")
8596 return true
8597 }
8598 };
8599
8600 ContentEditableInput.prototype.ensurePolled = function ensurePolled () {
8601 this.forceCompositionEnd()
8602 };
8603 ContentEditableInput.prototype.reset = function reset () {
8604 this.forceCompositionEnd()
8605 };
8606 ContentEditableInput.prototype.forceCompositionEnd = function forceCompositionEn d () {
8607 if (!this.composing) { return }
8608 clearTimeout(this.readDOMTimeout)
8609 this.composing = null
8610 this.updateFromDOM()
8611 this.div.blur()
8612 this.div.focus()
8613 };
8614 ContentEditableInput.prototype.readFromDOMSoon = function readFromDOMSoon () {
8615 var this$1 = this;
8616
8617 if (this.readDOMTimeout != null) { return }
8618 this.readDOMTimeout = setTimeout(function () {
8619 this$1.readDOMTimeout = null
8620 if (this$1.composing) {
8621 if (this$1.composing.done) { this$1.composing = null }
8622 else { return }
8623 }
8624 this$1.updateFromDOM()
8625 }, 80)
8626 };
8627
8628 ContentEditableInput.prototype.updateFromDOM = function updateFromDOM () {
8629 var this$1 = this;
8630
8631 if (this.cm.isReadOnly() || !this.pollContent())
8632 { runInOp(this.cm, function () { return regChange(this$1.cm); }) }
8633 };
8634
8635 ContentEditableInput.prototype.setUneditable = function setUneditable (node) {
8636 node.contentEditable = "false"
8637 };
8638
8639 ContentEditableInput.prototype.onKeyPress = function onKeyPress (e) {
8640 if (e.charCode == 0) { return }
8641 e.preventDefault()
8642 if (!this.cm.isReadOnly())
8643 { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0) }
8644 };
8645
8646 ContentEditableInput.prototype.readOnlyChanged = function readOnlyChanged (val) {
8647 this.div.contentEditable = String(val != "nocursor")
8648 };
8649
8650 ContentEditableInput.prototype.onContextMenu = function onContextMenu () {};
8651 ContentEditableInput.prototype.resetPosition = function resetPosition () {};
8652
8653 ContentEditableInput.prototype.needsContentAttribute = true
8654
8655 function posToDOM(cm, pos) {
8656 var view = findViewForLine(cm, pos.line)
8657 if (!view || view.hidden) { return null }
8658 var line = getLine(cm.doc, pos.line)
8659 var info = mapFromLineView(view, line, pos.line)
8660
8661 var order = getOrder(line, cm.doc.direction), side = "left"
8662 if (order) {
8663 var partPos = getBidiPartAt(order, pos.ch)
8664 side = partPos % 2 ? "right" : "left"
8665 }
8666 var result = nodeAndOffsetInLineMap(info.map, pos.ch, side)
8667 result.offset = result.collapse == "right" ? result.end : result.start
8668 return result
8669 }
8670
8671 function isInGutter(node) {
8672 for (var scan = node; scan; scan = scan.parentNode)
8673 { if (/CodeMirror-gutter-wrapper/.test(scan.className)) { return true } }
8674 return false
8675 }
8676
8677 function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos }
8678
8679 function domTextBetween(cm, from, to, fromLine, toLine) {
8680 var text = "", closing = false, lineSep = cm.doc.lineSeparator()
8681 function recognizeMarker(id) { return function (marker) { return marker.id == id; } }
8682 function close() {
8683 if (closing) {
8684 text += lineSep
8685 closing = false
8686 }
8687 }
8688 function addText(str) {
8689 if (str) {
8690 close()
8691 text += str
8692 }
8693 }
8694 function walk(node) {
8695 if (node.nodeType == 1) {
8696 var cmText = node.getAttribute("cm-text")
8697 if (cmText != null) {
8698 addText(cmText || node.textContent.replace(/\u200b/g, ""))
8699 return
5554 } 8700 }
5555 } 8701 var markerID = node.getAttribute("cm-marker"), range
5556 modeObj.name = spec.name; 8702 if (markerID) {
5557 if (spec.helperType) modeObj.helperType = spec.helperType; 8703 var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognize Marker(+markerID))
5558 if (spec.modeProps) for (var prop in spec.modeProps) 8704 if (found.length && (range = found[0].find()))
5559 modeObj[prop] = spec.modeProps[prop]; 8705 { addText(getBetween(cm.doc, range.from, range.to).join(lineSep)) }
5560 8706 return
5561 return modeObj;
5562 };
5563
5564 // Minimal default mode.
5565 CodeMirror.defineMode("null", function() {
5566 return {token: function(stream) {stream.skipToEnd();}};
5567 });
5568 CodeMirror.defineMIME("text/plain", "null");
5569
5570 // This can be used to attach properties to mode objects from
5571 // outside the actual mode definition.
5572 var modeExtensions = CodeMirror.modeExtensions = {};
5573 CodeMirror.extendMode = function(mode, properties) {
5574 var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (mod eExtensions[mode] = {});
5575 copyObj(properties, exts);
5576 };
5577
5578 // EXTENSIONS
5579
5580 CodeMirror.defineExtension = function(name, func) {
5581 CodeMirror.prototype[name] = func;
5582 };
5583 CodeMirror.defineDocExtension = function(name, func) {
5584 Doc.prototype[name] = func;
5585 };
5586 CodeMirror.defineOption = option;
5587
5588 var initHooks = [];
5589 CodeMirror.defineInitHook = function(f) {initHooks.push(f);};
5590
5591 var helpers = CodeMirror.helpers = {};
5592 CodeMirror.registerHelper = function(type, name, value) {
5593 if (!helpers.hasOwnProperty(type)) helpers[type] = CodeMirror[type] = {_glob al: []};
5594 helpers[type][name] = value;
5595 };
5596 CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {
5597 CodeMirror.registerHelper(type, name, value);
5598 helpers[type]._global.push({pred: predicate, val: value});
5599 };
5600
5601 // MODE STATE HANDLING
5602
5603 // Utility functions for working with state. Exported because nested
5604 // modes need to do this for their inner modes.
5605
5606 var copyState = CodeMirror.copyState = function(mode, state) {
5607 if (state === true) return state;
5608 if (mode.copyState) return mode.copyState(state);
5609 var nstate = {};
5610 for (var n in state) {
5611 var val = state[n];
5612 if (val instanceof Array) val = val.concat([]);
5613 nstate[n] = val;
5614 }
5615 return nstate;
5616 };
5617
5618 var startState = CodeMirror.startState = function(mode, a1, a2) {
5619 return mode.startState ? mode.startState(a1, a2) : true;
5620 };
5621
5622 // Given a mode and a state (for that mode), find the inner mode and
5623 // state at the position that the state refers to.
5624 CodeMirror.innerMode = function(mode, state) {
5625 while (mode.innerMode) {
5626 var info = mode.innerMode(state);
5627 if (!info || info.mode == mode) break;
5628 state = info.state;
5629 mode = info.mode;
5630 }
5631 return info || {mode: mode, state: state};
5632 };
5633
5634 // STANDARD COMMANDS
5635
5636 // Commands are parameter-less actions that can be performed on an
5637 // editor, mostly used for keybindings.
5638 var commands = CodeMirror.commands = {
5639 selectAll: function(cm) {cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.last Line()), sel_dontScroll);},
5640 singleSelection: function(cm) {
5641 cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScro ll);
5642 },
5643 killLine: function(cm) {
5644 deleteNearSelection(cm, function(range) {
5645 if (range.empty()) {
5646 var len = getLine(cm.doc, range.head.line).text.length;
5647 if (range.head.ch == len && range.head.line < cm.lastLine())
5648 return {from: range.head, to: Pos(range.head.line + 1, 0)};
5649 else
5650 return {from: range.head, to: Pos(range.head.line, len)};
5651 } else {
5652 return {from: range.from(), to: range.to()};
5653 }
5654 });
5655 },
5656 deleteLine: function(cm) {
5657 deleteNearSelection(cm, function(range) {
5658 return {from: Pos(range.from().line, 0),
5659 to: clipPos(cm.doc, Pos(range.to().line + 1, 0))};
5660 });
5661 },
5662 delLineLeft: function(cm) {
5663 deleteNearSelection(cm, function(range) {
5664 return {from: Pos(range.from().line, 0), to: range.from()};
5665 });
5666 },
5667 delWrappedLineLeft: function(cm) {
5668 deleteNearSelection(cm, function(range) {
5669 var top = cm.charCoords(range.head, "div").top + 5;
5670 var leftPos = cm.coordsChar({left: 0, top: top}, "div");
5671 return {from: leftPos, to: range.from()};
5672 });
5673 },
5674 delWrappedLineRight: function(cm) {
5675 deleteNearSelection(cm, function(range) {
5676 var top = cm.charCoords(range.head, "div").top + 5;
5677 var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100 , top: top}, "div");
5678 return {from: range.from(), to: rightPos };
5679 });
5680 },
5681 undo: function(cm) {cm.undo();},
5682 redo: function(cm) {cm.redo();},
5683 undoSelection: function(cm) {cm.undoSelection();},
5684 redoSelection: function(cm) {cm.redoSelection();},
5685 goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));},
5686 goDocEnd: function(cm) {cm.extendSelection(Pos(cm.lastLine()));},
5687 goLineStart: function(cm) {
5688 cm.extendSelectionsBy(function(range) { return lineStart(cm, range.head.li ne); },
5689 {origin: "+move", bias: 1});
5690 },
5691 goLineStartSmart: function(cm) {
5692 cm.extendSelectionsBy(function(range) {
5693 return lineStartSmart(cm, range.head);
5694 }, {origin: "+move", bias: 1});
5695 },
5696 goLineEnd: function(cm) {
5697 cm.extendSelectionsBy(function(range) { return lineEnd(cm, range.head.line ); },
5698 {origin: "+move", bias: -1});
5699 },
5700 goLineRight: function(cm) {
5701 cm.extendSelectionsBy(function(range) {
5702 var top = cm.charCoords(range.head, "div").top + 5;
5703 return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: t op}, "div");
5704 }, sel_move);
5705 },
5706 goLineLeft: function(cm) {
5707 cm.extendSelectionsBy(function(range) {
5708 var top = cm.charCoords(range.head, "div").top + 5;
5709 return cm.coordsChar({left: 0, top: top}, "div");
5710 }, sel_move);
5711 },
5712 goLineLeftSmart: function(cm) {
5713 cm.extendSelectionsBy(function(range) {
5714 var top = cm.charCoords(range.head, "div").top + 5;
5715 var pos = cm.coordsChar({left: 0, top: top}, "div");
5716 if (pos.ch < cm.getLine(pos.line).search(/\S/)) return lineStartSmart(cm , range.head);
5717 return pos;
5718 }, sel_move);
5719 },
5720 goLineUp: function(cm) {cm.moveV(-1, "line");},
5721 goLineDown: function(cm) {cm.moveV(1, "line");},
5722 goPageUp: function(cm) {cm.moveV(-1, "page");},
5723 goPageDown: function(cm) {cm.moveV(1, "page");},
5724 goCharLeft: function(cm) {cm.moveH(-1, "char");},
5725 goCharRight: function(cm) {cm.moveH(1, "char");},
5726 goColumnLeft: function(cm) {cm.moveH(-1, "column");},
5727 goColumnRight: function(cm) {cm.moveH(1, "column");},
5728 goWordLeft: function(cm) {cm.moveH(-1, "word");},
5729 goGroupRight: function(cm) {cm.moveH(1, "group");},
5730 goGroupLeft: function(cm) {cm.moveH(-1, "group");},
5731 goWordRight: function(cm) {cm.moveH(1, "word");},
5732 delCharBefore: function(cm) {cm.deleteH(-1, "char");},
5733 delCharAfter: function(cm) {cm.deleteH(1, "char");},
5734 delWordBefore: function(cm) {cm.deleteH(-1, "word");},
5735 delWordAfter: function(cm) {cm.deleteH(1, "word");},
5736 delGroupBefore: function(cm) {cm.deleteH(-1, "group");},
5737 delGroupAfter: function(cm) {cm.deleteH(1, "group");},
5738 indentAuto: function(cm) {cm.indentSelection("smart");},
5739 indentMore: function(cm) {cm.indentSelection("add");},
5740 indentLess: function(cm) {cm.indentSelection("subtract");},
5741 insertTab: function(cm) {cm.replaceSelection("\t");},
5742 insertSoftTab: function(cm) {
5743 var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSiz e;
5744 for (var i = 0; i < ranges.length; i++) {
5745 var pos = ranges[i].from();
5746 var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize);
5747 spaces.push(spaceStr(tabSize - col % tabSize));
5748 } 8707 }
5749 cm.replaceSelections(spaces); 8708 if (node.getAttribute("contenteditable") == "false") { return }
5750 }, 8709 var isBlock = /^(pre|div|p)$/i.test(node.nodeName)
5751 defaultTab: function(cm) { 8710 if (isBlock) { close() }
5752 if (cm.somethingSelected()) cm.indentSelection("add"); 8711 for (var i = 0; i < node.childNodes.length; i++)
5753 else cm.execCommand("insertTab"); 8712 { walk(node.childNodes[i]) }
5754 }, 8713 if (isBlock) { closing = true }
5755 transposeChars: function(cm) { 8714 } else if (node.nodeType == 3) {
5756 runInOp(cm, function() { 8715 addText(node.nodeValue)
5757 var ranges = cm.listSelections(), newSel = []; 8716 }
5758 for (var i = 0; i < ranges.length; i++) { 8717 }
5759 var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text; 8718 for (;;) {
5760 if (line) { 8719 walk(from)
5761 if (cur.ch == line.length) cur = new Pos(cur.line, cur.ch - 1); 8720 if (from == to) { break }
5762 if (cur.ch > 0) { 8721 from = from.nextSibling
5763 cur = new Pos(cur.line, cur.ch + 1); 8722 }
5764 cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2), 8723 return text
5765 Pos(cur.line, cur.ch - 2), cur, "+transpose"); 8724 }
5766 } else if (cur.line > cm.doc.first) { 8725
5767 var prev = getLine(cm.doc, cur.line - 1).text; 8726 function domToPos(cm, node, offset) {
5768 if (prev) 8727 var lineNode
5769 cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() + 8728 if (node == cm.display.lineDiv) {
5770 prev.charAt(prev.length - 1), 8729 lineNode = cm.display.lineDiv.childNodes[offset]
5771 Pos(cur.line - 1, prev.length - 1), Pos(cur.line , 1), "+transpose"); 8730 if (!lineNode) { return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) }
5772 } 8731 node = null; offset = 0
5773 } 8732 } else {
5774 newSel.push(new Range(cur, cur)); 8733 for (lineNode = node;; lineNode = lineNode.parentNode) {
5775 } 8734 if (!lineNode || lineNode == cm.display.lineDiv) { return null }
5776 cm.setSelections(newSel); 8735 if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) { br eak }
5777 }); 8736 }
5778 }, 8737 }
5779 newlineAndIndent: function(cm) { 8738 for (var i = 0; i < cm.display.view.length; i++) {
5780 runInOp(cm, function() { 8739 var lineView = cm.display.view[i]
5781 var len = cm.listSelections().length; 8740 if (lineView.node == lineNode)
5782 for (var i = 0; i < len; i++) { 8741 { return locateNodeInLineView(lineView, node, offset) }
5783 var range = cm.listSelections()[i]; 8742 }
5784 cm.replaceRange(cm.doc.lineSeparator(), range.anchor, range.head, "+in put"); 8743 }
5785 cm.indentLine(range.from().line + 1, null, true); 8744
5786 } 8745 function locateNodeInLineView(lineView, node, offset) {
5787 ensureCursorVisible(cm); 8746 var wrapper = lineView.text.firstChild, bad = false
5788 }); 8747 if (!node || !contains(wrapper, node)) { return badPos(Pos(lineNo(lineView.lin e), 0), true) }
5789 }, 8748 if (node == wrapper) {
5790 openLine: function(cm) {cm.replaceSelection("\n", "start")}, 8749 bad = true
5791 toggleOverwrite: function(cm) {cm.toggleOverwrite();} 8750 node = wrapper.childNodes[offset]
5792 }; 8751 offset = 0
5793 8752 if (!node) {
5794 8753 var line = lineView.rest ? lst(lineView.rest) : lineView.line
5795 // STANDARD KEYMAPS 8754 return badPos(Pos(lineNo(line), line.text.length), bad)
5796 8755 }
5797 var keyMap = CodeMirror.keyMap = {}; 8756 }
5798 8757
5799 keyMap.basic = { 8758 var textNode = node.nodeType == 3 ? node : null, topNode = node
5800 "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goL ineDown", 8759 if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {
5801 "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageD own": "goPageDown", 8760 textNode = node.firstChild
5802 "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": " delCharBefore", 8761 if (offset) { offset = textNode.nodeValue.length }
5803 "Tab": "defaultTab", "Shift-Tab": "indentAuto", 8762 }
5804 "Enter": "newlineAndIndent", "Insert": "toggleOverwrite", 8763 while (topNode.parentNode != wrapper) { topNode = topNode.parentNode }
5805 "Esc": "singleSelection" 8764 var measure = lineView.measure, maps = measure.maps
5806 }; 8765
5807 // Note that the save and find-related commands aren't defined by 8766 function find(textNode, topNode, offset) {
5808 // default. User code or addons can define them. Unknown commands 8767 for (var i = -1; i < (maps ? maps.length : 0); i++) {
5809 // are simply ignored. 8768 var map = i < 0 ? measure.map : maps[i]
5810 keyMap.pcDefault = { 8769 for (var j = 0; j < map.length; j += 3) {
5811 "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl -Z": "redo", "Ctrl-Y": "redo", 8770 var curNode = map[j + 2]
5812 "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "C trl-Down": "goLineDown", 8771 if (curNode == textNode || curNode == topNode) {
5813 "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLin eStart", "Alt-Right": "goLineEnd", 8772 var line = lineNo(i < 0 ? lineView.line : lineView.rest[i])
5814 "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S" : "save", "Ctrl-F": "find", 8773 var ch = map[j] + offset
5815 "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll", 8774 if (offset < 0 || curNode != textNode) { ch = map[j + (offset ? 1 : 0) ] }
5816 "Ctrl-[": "indentLess", "Ctrl-]": "indentMore", 8775 return Pos(line, ch)
5817 "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSe lection",
5818 fallthrough: "basic"
5819 };
5820 // Very basic readline/emacs-style bindings, which are standard on Mac.
5821 keyMap.emacsy = {
5822 "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl -N": "goLineDown",
5823 "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctr l-E": "goLineEnd",
5824 "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter" , "Ctrl-H": "delCharBefore",
5825 "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLi ne", "Ctrl-T": "transposeChars",
5826 "Ctrl-O": "openLine"
5827 };
5828 keyMap.macDefault = {
5829 "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
5830 "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cm d-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
5831 "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineR ight", "Alt-Backspace": "delGroupBefore",
5832 "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S ": "save", "Cmd-F": "find",
5833 "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shi ft-Cmd-Alt-F": "replaceAll",
5834 "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLi neLeft", "Cmd-Delete": "delWrappedLineRight",
5835 "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocS tart", "Ctrl-Down": "goDocEnd",
5836 fallthrough: ["basic", "emacsy"]
5837 };
5838 keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
5839
5840 // KEYMAP DISPATCH
5841
5842 function normalizeKeyName(name) {
5843 var parts = name.split(/-(?!$)/), name = parts[parts.length - 1];
5844 var alt, ctrl, shift, cmd;
5845 for (var i = 0; i < parts.length - 1; i++) {
5846 var mod = parts[i];
5847 if (/^(cmd|meta|m)$/i.test(mod)) cmd = true;
5848 else if (/^a(lt)?$/i.test(mod)) alt = true;
5849 else if (/^(c|ctrl|control)$/i.test(mod)) ctrl = true;
5850 else if (/^s(hift)$/i.test(mod)) shift = true;
5851 else throw new Error("Unrecognized modifier name: " + mod);
5852 }
5853 if (alt) name = "Alt-" + name;
5854 if (ctrl) name = "Ctrl-" + name;
5855 if (cmd) name = "Cmd-" + name;
5856 if (shift) name = "Shift-" + name;
5857 return name;
5858 }
5859
5860 // This is a kludge to keep keymaps mostly working as raw objects
5861 // (backwards compatibility) while at the same time support features
5862 // like normalization and multi-stroke key bindings. It compiles a
5863 // new normalized keymap, and then updates the old object to reflect
5864 // this.
5865 CodeMirror.normalizeKeyMap = function(keymap) {
5866 var copy = {};
5867 for (var keyname in keymap) if (keymap.hasOwnProperty(keyname)) {
5868 var value = keymap[keyname];
5869 if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) continue;
5870 if (value == "...") { delete keymap[keyname]; continue; }
5871
5872 var keys = map(keyname.split(" "), normalizeKeyName);
5873 for (var i = 0; i < keys.length; i++) {
5874 var val, name;
5875 if (i == keys.length - 1) {
5876 name = keys.join(" ");
5877 val = value;
5878 } else {
5879 name = keys.slice(0, i + 1).join(" ");
5880 val = "...";
5881 }
5882 var prev = copy[name];
5883 if (!prev) copy[name] = val;
5884 else if (prev != val) throw new Error("Inconsistent bindings for " + nam e);
5885 }
5886 delete keymap[keyname];
5887 }
5888 for (var prop in copy) keymap[prop] = copy[prop];
5889 return keymap;
5890 };
5891
5892 var lookupKey = CodeMirror.lookupKey = function(key, map, handle, context) {
5893 map = getKeyMap(map);
5894 var found = map.call ? map.call(key, context) : map[key];
5895 if (found === false) return "nothing";
5896 if (found === "...") return "multi";
5897 if (found != null && handle(found)) return "handled";
5898
5899 if (map.fallthrough) {
5900 if (Object.prototype.toString.call(map.fallthrough) != "[object Array]")
5901 return lookupKey(key, map.fallthrough, handle, context);
5902 for (var i = 0; i < map.fallthrough.length; i++) {
5903 var result = lookupKey(key, map.fallthrough[i], handle, context);
5904 if (result) return result;
5905 }
5906 }
5907 };
5908
5909 // Modifier key presses don't count as 'real' key presses for the
5910 // purpose of keymap fallthrough.
5911 var isModifierKey = CodeMirror.isModifierKey = function(value) {
5912 var name = typeof value == "string" ? value : keyNames[value.keyCode];
5913 return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
5914 };
5915
5916 // Look up the name of a key as indicated by an event object.
5917 var keyName = CodeMirror.keyName = function(event, noShift) {
5918 if (presto && event.keyCode == 34 && event["char"]) return false;
5919 var base = keyNames[event.keyCode], name = base;
5920 if (name == null || event.altGraphKey) return false;
5921 if (event.altKey && base != "Alt") name = "Alt-" + name;
5922 if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") name = "Ctrl-" + name;
5923 if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") name = " Cmd-" + name;
5924 if (!noShift && event.shiftKey && base != "Shift") name = "Shift-" + name;
5925 return name;
5926 };
5927
5928 function getKeyMap(val) {
5929 return typeof val == "string" ? keyMap[val] : val;
5930 }
5931
5932 // FROMTEXTAREA
5933
5934 CodeMirror.fromTextArea = function(textarea, options) {
5935 options = options ? copyObj(options) : {};
5936 options.value = textarea.value;
5937 if (!options.tabindex && textarea.tabIndex)
5938 options.tabindex = textarea.tabIndex;
5939 if (!options.placeholder && textarea.placeholder)
5940 options.placeholder = textarea.placeholder;
5941 // Set autofocus to true if this textarea is focused, or if it has
5942 // autofocus and no other element is focused.
5943 if (options.autofocus == null) {
5944 var hasFocus = activeElt();
5945 options.autofocus = hasFocus == textarea ||
5946 textarea.getAttribute("autofocus") != null && hasFocus == document.body;
5947 }
5948
5949 function save() {textarea.value = cm.getValue();}
5950 if (textarea.form) {
5951 on(textarea.form, "submit", save);
5952 // Deplorable hack to make the submit method do the right thing.
5953 if (!options.leaveSubmitMethodAlone) {
5954 var form = textarea.form, realSubmit = form.submit;
5955 try {
5956 var wrappedSubmit = form.submit = function() {
5957 save();
5958 form.submit = realSubmit;
5959 form.submit();
5960 form.submit = wrappedSubmit;
5961 };
5962 } catch(e) {}
5963 }
5964 }
5965
5966 options.finishInit = function(cm) {
5967 cm.save = save;
5968 cm.getTextArea = function() { return textarea; };
5969 cm.toTextArea = function() {
5970 cm.toTextArea = isNaN; // Prevent this from being ran twice
5971 save();
5972 textarea.parentNode.removeChild(cm.getWrapperElement());
5973 textarea.style.display = "";
5974 if (textarea.form) {
5975 off(textarea.form, "submit", save);
5976 if (typeof textarea.form.submit == "function")
5977 textarea.form.submit = realSubmit;
5978 }
5979 };
5980 };
5981
5982 textarea.style.display = "none";
5983 var cm = CodeMirror(function(node) {
5984 textarea.parentNode.insertBefore(node, textarea.nextSibling);
5985 }, options);
5986 return cm;
5987 };
5988
5989 // STRING STREAM
5990
5991 // Fed to the mode parsers, provides helper functions to make
5992 // parsers more succinct.
5993
5994 var StringStream = CodeMirror.StringStream = function(string, tabSize) {
5995 this.pos = this.start = 0;
5996 this.string = string;
5997 this.tabSize = tabSize || 8;
5998 this.lastColumnPos = this.lastColumnValue = 0;
5999 this.lineStart = 0;
6000 };
6001
6002 StringStream.prototype = {
6003 eol: function() {return this.pos >= this.string.length;},
6004 sol: function() {return this.pos == this.lineStart;},
6005 peek: function() {return this.string.charAt(this.pos) || undefined;},
6006 next: function() {
6007 if (this.pos < this.string.length)
6008 return this.string.charAt(this.pos++);
6009 },
6010 eat: function(match) {
6011 var ch = this.string.charAt(this.pos);
6012 if (typeof match == "string") var ok = ch == match;
6013 else var ok = ch && (match.test ? match.test(ch) : match(ch));
6014 if (ok) {++this.pos; return ch;}
6015 },
6016 eatWhile: function(match) {
6017 var start = this.pos;
6018 while (this.eat(match)){}
6019 return this.pos > start;
6020 },
6021 eatSpace: function() {
6022 var start = this.pos;
6023 while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
6024 return this.pos > start;
6025 },
6026 skipToEnd: function() {this.pos = this.string.length;},
6027 skipTo: function(ch) {
6028 var found = this.string.indexOf(ch, this.pos);
6029 if (found > -1) {this.pos = found; return true;}
6030 },
6031 backUp: function(n) {this.pos -= n;},
6032 column: function() {
6033 if (this.lastColumnPos < this.start) {
6034 this.lastColumnValue = countColumn(this.string, this.start, this.tabSize , this.lastColumnPos, this.lastColumnValue);
6035 this.lastColumnPos = this.start;
6036 }
6037 return this.lastColumnValue - (this.lineStart ? countColumn(this.string, t his.lineStart, this.tabSize) : 0);
6038 },
6039 indentation: function() {
6040 return countColumn(this.string, null, this.tabSize) -
6041 (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0);
6042 },
6043 match: function(pattern, consume, caseInsensitive) {
6044 if (typeof pattern == "string") {
6045 var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
6046 var substr = this.string.substr(this.pos, pattern.length);
6047 if (cased(substr) == cased(pattern)) {
6048 if (consume !== false) this.pos += pattern.length;
6049 return true;
6050 }
6051 } else {
6052 var match = this.string.slice(this.pos).match(pattern);
6053 if (match && match.index > 0) return null;
6054 if (match && consume !== false) this.pos += match[0].length;
6055 return match;
6056 }
6057 },
6058 current: function(){return this.string.slice(this.start, this.pos);},
6059 hideFirstChars: function(n, inner) {
6060 this.lineStart += n;
6061 try { return inner(); }
6062 finally { this.lineStart -= n; }
6063 }
6064 };
6065
6066 // TEXTMARKERS
6067
6068 // Created with markText and setBookmark methods. A TextMarker is a
6069 // handle that can be used to clear or find a marked position in the
6070 // document. Line objects hold arrays (markedSpans) containing
6071 // {from, to, marker} object pointing to such marker objects, and
6072 // indicating that such a marker is present on that line. Multiple
6073 // lines may point to the same marker when it spans across lines.
6074 // The spans will have null for their from/to properties when the
6075 // marker continues beyond the start/end of the line. Markers have
6076 // links back to the lines they currently touch.
6077
6078 var nextMarkerId = 0;
6079
6080 var TextMarker = CodeMirror.TextMarker = function(doc, type) {
6081 this.lines = [];
6082 this.type = type;
6083 this.doc = doc;
6084 this.id = ++nextMarkerId;
6085 };
6086 eventMixin(TextMarker);
6087
6088 // Clear the marker.
6089 TextMarker.prototype.clear = function() {
6090 if (this.explicitlyCleared) return;
6091 var cm = this.doc.cm, withOp = cm && !cm.curOp;
6092 if (withOp) startOperation(cm);
6093 if (hasHandler(this, "clear")) {
6094 var found = this.find();
6095 if (found) signalLater(this, "clear", found.from, found.to);
6096 }
6097 var min = null, max = null;
6098 for (var i = 0; i < this.lines.length; ++i) {
6099 var line = this.lines[i];
6100 var span = getMarkedSpanFor(line.markedSpans, this);
6101 if (cm && !this.collapsed) regLineChange(cm, lineNo(line), "text");
6102 else if (cm) {
6103 if (span.to != null) max = lineNo(line);
6104 if (span.from != null) min = lineNo(line);
6105 }
6106 line.markedSpans = removeMarkedSpan(line.markedSpans, span);
6107 if (span.from == null && this.collapsed && !lineIsHidden(this.doc, line) & & cm)
6108 updateLineHeight(line, textHeight(cm.display));
6109 }
6110 if (cm && this.collapsed && !cm.options.lineWrapping) for (var i = 0; i < th is.lines.length; ++i) {
6111 var visual = visualLine(this.lines[i]), len = lineLength(visual);
6112 if (len > cm.display.maxLineLength) {
6113 cm.display.maxLine = visual;
6114 cm.display.maxLineLength = len;
6115 cm.display.maxLineChanged = true;
6116 }
6117 }
6118
6119 if (min != null && cm && this.collapsed) regChange(cm, min, max + 1);
6120 this.lines.length = 0;
6121 this.explicitlyCleared = true;
6122 if (this.atomic && this.doc.cantEdit) {
6123 this.doc.cantEdit = false;
6124 if (cm) reCheckSelection(cm.doc);
6125 }
6126 if (cm) signalLater(cm, "markerCleared", cm, this);
6127 if (withOp) endOperation(cm);
6128 if (this.parent) this.parent.clear();
6129 };
6130
6131 // Find the position of the marker in the document. Returns a {from,
6132 // to} object by default. Side can be passed to get a specific side
6133 // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the
6134 // Pos objects returned contain a line object, rather than a line
6135 // number (used to prevent looking up the same line twice).
6136 TextMarker.prototype.find = function(side, lineObj) {
6137 if (side == null && this.type == "bookmark") side = 1;
6138 var from, to;
6139 for (var i = 0; i < this.lines.length; ++i) {
6140 var line = this.lines[i];
6141 var span = getMarkedSpanFor(line.markedSpans, this);
6142 if (span.from != null) {
6143 from = Pos(lineObj ? line : lineNo(line), span.from);
6144 if (side == -1) return from;
6145 }
6146 if (span.to != null) {
6147 to = Pos(lineObj ? line : lineNo(line), span.to);
6148 if (side == 1) return to;
6149 }
6150 }
6151 return from && {from: from, to: to};
6152 };
6153
6154 // Signals that the marker's widget changed, and surrounding layout
6155 // should be recomputed.
6156 TextMarker.prototype.changed = function() {
6157 var pos = this.find(-1, true), widget = this, cm = this.doc.cm;
6158 if (!pos || !cm) return;
6159 runInOp(cm, function() {
6160 var line = pos.line, lineN = lineNo(pos.line);
6161 var view = findViewForLine(cm, lineN);
6162 if (view) {
6163 clearLineMeasurementCacheFor(view);
6164 cm.curOp.selectionChanged = cm.curOp.forceUpdate = true;
6165 }
6166 cm.curOp.updateMaxLine = true;
6167 if (!lineIsHidden(widget.doc, line) && widget.height != null) {
6168 var oldHeight = widget.height;
6169 widget.height = null;
6170 var dHeight = widgetHeight(widget) - oldHeight;
6171 if (dHeight)
6172 updateLineHeight(line, line.height + dHeight);
6173 }
6174 });
6175 };
6176
6177 TextMarker.prototype.attachLine = function(line) {
6178 if (!this.lines.length && this.doc.cm) {
6179 var op = this.doc.cm.curOp;
6180 if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
6181 (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this);
6182 }
6183 this.lines.push(line);
6184 };
6185 TextMarker.prototype.detachLine = function(line) {
6186 this.lines.splice(indexOf(this.lines, line), 1);
6187 if (!this.lines.length && this.doc.cm) {
6188 var op = this.doc.cm.curOp;
6189 (op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this);
6190 }
6191 };
6192
6193 // Collapsed markers have unique ids, in order to be able to order
6194 // them, which is needed for uniquely determining an outer marker
6195 // when they overlap (they may nest, but not partially overlap).
6196 var nextMarkerId = 0;
6197
6198 // Create a marker, wire it up to the right lines, and
6199 function markText(doc, from, to, options, type) {
6200 // Shared markers (across linked documents) are handled separately
6201 // (markTextShared will call out to this again, once per
6202 // document).
6203 if (options && options.shared) return markTextShared(doc, from, to, options, type);
6204 // Ensure we are in an operation.
6205 if (doc.cm && !doc.cm.curOp) return operation(doc.cm, markText)(doc, from, t o, options, type);
6206
6207 var marker = new TextMarker(doc, type), diff = cmp(from, to);
6208 if (options) copyObj(options, marker, false);
6209 // Don't connect empty markers unless clearWhenEmpty is false
6210 if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false)
6211 return marker;
6212 if (marker.replacedWith) {
6213 // Showing up as a widget implies collapsed (widget replaces text)
6214 marker.collapsed = true;
6215 marker.widgetNode = elt("span", [marker.replacedWith], "CodeMirror-widget" );
6216 if (!options.handleMouseEvents) marker.widgetNode.setAttribute("cm-ignore- events", "true");
6217 if (options.insertLeft) marker.widgetNode.insertLeft = true;
6218 }
6219 if (marker.collapsed) {
6220 if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||
6221 from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker))
6222 throw new Error("Inserting collapsed marker partially overlapping an exi sting one");
6223 sawCollapsedSpans = true;
6224 }
6225
6226 if (marker.addToHistory)
6227 addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN);
6228
6229 var curLine = from.line, cm = doc.cm, updateMaxLine;
6230 doc.iter(curLine, to.line + 1, function(line) {
6231 if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine)
6232 updateMaxLine = true;
6233 if (marker.collapsed && curLine != from.line) updateLineHeight(line, 0);
6234 addMarkedSpan(line, new MarkedSpan(marker,
6235 curLine == from.line ? from.ch : null,
6236 curLine == to.line ? to.ch : null));
6237 ++curLine;
6238 });
6239 // lineIsHidden depends on the presence of the spans, so needs a second pass
6240 if (marker.collapsed) doc.iter(from.line, to.line + 1, function(line) {
6241 if (lineIsHidden(doc, line)) updateLineHeight(line, 0);
6242 });
6243
6244 if (marker.clearOnEnter) on(marker, "beforeCursorEnter", function() { marker .clear(); });
6245
6246 if (marker.readOnly) {
6247 sawReadOnlySpans = true;
6248 if (doc.history.done.length || doc.history.undone.length)
6249 doc.clearHistory();
6250 }
6251 if (marker.collapsed) {
6252 marker.id = ++nextMarkerId;
6253 marker.atomic = true;
6254 }
6255 if (cm) {
6256 // Sync editor state
6257 if (updateMaxLine) cm.curOp.updateMaxLine = true;
6258 if (marker.collapsed)
6259 regChange(cm, from.line, to.line + 1);
6260 else if (marker.className || marker.title || marker.startStyle || marker.e ndStyle || marker.css)
6261 for (var i = from.line; i <= to.line; i++) regLineChange(cm, i, "text");
6262 if (marker.atomic) reCheckSelection(cm.doc);
6263 signalLater(cm, "markerAdded", cm, marker);
6264 }
6265 return marker;
6266 }
6267
6268 // SHARED TEXTMARKERS
6269
6270 // A shared marker spans multiple linked documents. It is
6271 // implemented as a meta-marker-object controlling multiple normal
6272 // markers.
6273 var SharedTextMarker = CodeMirror.SharedTextMarker = function(markers, primary ) {
6274 this.markers = markers;
6275 this.primary = primary;
6276 for (var i = 0; i < markers.length; ++i)
6277 markers[i].parent = this;
6278 };
6279 eventMixin(SharedTextMarker);
6280
6281 SharedTextMarker.prototype.clear = function() {
6282 if (this.explicitlyCleared) return;
6283 this.explicitlyCleared = true;
6284 for (var i = 0; i < this.markers.length; ++i)
6285 this.markers[i].clear();
6286 signalLater(this, "clear");
6287 };
6288 SharedTextMarker.prototype.find = function(side, lineObj) {
6289 return this.primary.find(side, lineObj);
6290 };
6291
6292 function markTextShared(doc, from, to, options, type) {
6293 options = copyObj(options);
6294 options.shared = false;
6295 var markers = [markText(doc, from, to, options, type)], primary = markers[0] ;
6296 var widget = options.widgetNode;
6297 linkedDocs(doc, function(doc) {
6298 if (widget) options.widgetNode = widget.cloneNode(true);
6299 markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));
6300 for (var i = 0; i < doc.linked.length; ++i)
6301 if (doc.linked[i].isParent) return;
6302 primary = lst(markers);
6303 });
6304 return new SharedTextMarker(markers, primary);
6305 }
6306
6307 function findSharedMarkers(doc) {
6308 return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())),
6309 function(m) { return m.parent; });
6310 }
6311
6312 function copySharedMarkers(doc, markers) {
6313 for (var i = 0; i < markers.length; i++) {
6314 var marker = markers[i], pos = marker.find();
6315 var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to);
6316 if (cmp(mFrom, mTo)) {
6317 var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.t ype);
6318 marker.markers.push(subMark);
6319 subMark.parent = marker;
6320 }
6321 }
6322 }
6323
6324 function detachSharedMarkers(markers) {
6325 for (var i = 0; i < markers.length; i++) {
6326 var marker = markers[i], linked = [marker.primary.doc];;
6327 linkedDocs(marker.primary.doc, function(d) { linked.push(d); });
6328 for (var j = 0; j < marker.markers.length; j++) {
6329 var subMarker = marker.markers[j];
6330 if (indexOf(linked, subMarker.doc) == -1) {
6331 subMarker.parent = null;
6332 marker.markers.splice(j--, 1);
6333 } 8776 }
6334 } 8777 }
6335 } 8778 }
6336 } 8779 }
6337 8780 var found = find(textNode, topNode, offset)
6338 // TEXTMARKER SPANS 8781 if (found) { return badPos(found, bad) }
6339 8782
6340 function MarkedSpan(marker, from, to) { 8783 // FIXME this is all really shaky. might handle the few cases it needs to hand le, but likely to cause problems
6341 this.marker = marker; 8784 for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.len gth - offset : 0; after; after = after.nextSibling) {
6342 this.from = from; this.to = to; 8785 found = find(after, after.firstChild, 0)
6343 } 8786 if (found)
6344 8787 { return badPos(Pos(found.line, found.ch - dist), bad) }
6345 // Search an array of spans for a span matching the given marker. 8788 else
6346 function getMarkedSpanFor(spans, marker) { 8789 { dist += after.textContent.length }
6347 if (spans) for (var i = 0; i < spans.length; ++i) { 8790 }
6348 var span = spans[i]; 8791 for (var before = topNode.previousSibling, dist$1 = offset; before; before = b efore.previousSibling) {
6349 if (span.marker == marker) return span; 8792 found = find(before, before.firstChild, -1)
6350 } 8793 if (found)
6351 } 8794 { return badPos(Pos(found.line, found.ch + dist$1), bad) }
6352 // Remove a span from an array, returning undefined if no spans are 8795 else
6353 // left (we don't store arrays for lines without spans). 8796 { dist$1 += before.textContent.length }
6354 function removeMarkedSpan(spans, span) { 8797 }
6355 for (var r, i = 0; i < spans.length; ++i) 8798 }
6356 if (spans[i] != span) (r || (r = [])).push(spans[i]); 8799
6357 return r; 8800 // TEXTAREA INPUT STYLE
6358 } 8801
6359 // Add a span to a line. 8802 var TextareaInput = function TextareaInput(cm) {
6360 function addMarkedSpan(line, span) { 8803 this.cm = cm
6361 line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [spa n]; 8804 // See input.poll and input.reset
6362 span.marker.attachLine(line); 8805 this.prevInput = ""
6363 } 8806
6364 8807 // Flag that indicates whether we expect input to appear real soon
6365 // Used for the algorithm that adjusts markers for a change in the 8808 // now (after some event like 'keypress' or 'input') and are
6366 // document. These functions cut an array of spans at a given 8809 // polling intensively.
6367 // character position, returning an array of remaining chunks (or 8810 this.pollingFast = false
6368 // undefined if nothing remains). 8811 // Self-resetting timeout for the poller
6369 function markedSpansBefore(old, startCh, isInsert) { 8812 this.polling = new Delayed()
6370 if (old) for (var i = 0, nw; i < old.length; ++i) { 8813 // Tracks when input.reset has punted to just putting a short
6371 var span = old[i], marker = span.marker; 8814 // string into the textarea instead of the full selection.
6372 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh); 8815 this.inaccurateSelection = false
6373 if (startsBefore || span.from == startCh && marker.type == "bookmark" && ( !isInsert || !span.marker.insertLeft)) { 8816 // Used to work around IE issue with selection being forgotten when focus move s away from textarea
6374 var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= s tartCh : span.to > startCh); 8817 this.hasSelection = false
6375 (nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? nul l : span.to)); 8818 this.composing = null
8819 };
8820
8821 TextareaInput.prototype.init = function init (display) {
8822 var this$1 = this;
8823
8824 var input = this, cm = this.cm
8825
8826 // Wraps and hides input textarea
8827 var div = this.wrapper = hiddenTextarea()
8828 // The semihidden textarea that is focused when the editor is
8829 // focused, and receives input.
8830 var te = this.textarea = div.firstChild
8831 display.wrapper.insertBefore(div, display.wrapper.firstChild)
8832
8833 // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to w ork in iOS 8 anymore)
8834 if (ios) { te.style.width = "0px" }
8835
8836 on(te, "input", function () {
8837 if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = nu ll }
8838 input.poll()
8839 })
8840
8841 on(te, "paste", function (e) {
8842 if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
8843
8844 cm.state.pasteIncoming = true
8845 input.fastPoll()
8846 })
8847
8848 function prepareCopyCut(e) {
8849 if (signalDOMEvent(cm, e)) { return }
8850 if (cm.somethingSelected()) {
8851 setLastCopied({lineWise: false, text: cm.getSelections()})
8852 if (input.inaccurateSelection) {
8853 input.prevInput = ""
8854 input.inaccurateSelection = false
8855 te.value = lastCopied.text.join("\n")
8856 selectInput(te)
6376 } 8857 }
6377 } 8858 } else if (!cm.options.lineWiseCopyCut) {
6378 return nw; 8859 return
6379 } 8860 } else {
6380 function markedSpansAfter(old, endCh, isInsert) { 8861 var ranges = copyableRanges(cm)
6381 if (old) for (var i = 0, nw; i < old.length; ++i) { 8862 setLastCopied({lineWise: true, text: ranges.text})
6382 var span = old[i], marker = span.marker; 8863 if (e.type == "cut") {
6383 var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= end Ch : span.to > endCh); 8864 cm.setSelections(ranges.ranges, null, sel_dontScroll)
6384 if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isIn sert || span.marker.insertLeft)) { 8865 } else {
6385 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.fro m <= endCh : span.from < endCh); 8866 input.prevInput = ""
6386 (nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span .from - endCh, 8867 te.value = ranges.text.join("\n")
6387 span.to == null ? null : span.to - endCh)); 8868 selectInput(te)
6388 } 8869 }
6389 } 8870 }
6390 return nw; 8871 if (e.type == "cut") { cm.state.cutIncoming = true }
6391 } 8872 }
6392 8873 on(te, "cut", prepareCopyCut)
6393 // Given a change object, compute the new set of marker spans that 8874 on(te, "copy", prepareCopyCut)
6394 // cover the line in which the change took place. Removes spans 8875
6395 // entirely within the change, reconnects spans belonging to the 8876 on(display.scroller, "paste", function (e) {
6396 // same marker that appear on both sides of the change, and cuts off 8877 if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return }
6397 // spans partially within the change. Returns an array of span 8878 cm.state.pasteIncoming = true
6398 // arrays with one element for each line in (after) the change. 8879 input.focus()
6399 function stretchSpansOverChange(doc, change) { 8880 })
6400 if (change.full) return null; 8881
6401 var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.lin e).markedSpans; 8882 // Prevent normal selection in the editor (we handle our own)
6402 var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).ma rkedSpans; 8883 on(display.lineSpace, "selectstart", function (e) {
6403 if (!oldFirst && !oldLast) return null; 8884 if (!eventInWidget(display, e)) { e_preventDefault(e) }
6404 8885 })
6405 var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.fr om, change.to) == 0; 8886
6406 // Get the spans that 'stick out' on both sides 8887 on(te, "compositionstart", function () {
6407 var first = markedSpansBefore(oldFirst, startCh, isInsert); 8888 var start = cm.getCursor("from")
6408 var last = markedSpansAfter(oldLast, endCh, isInsert); 8889 if (input.composing) { input.composing.range.clear() }
6409 8890 input.composing = {
6410 // Next, merge those two ends 8891 start: start,
6411 var sameLine = change.text.length == 1, offset = lst(change.text).length + ( sameLine ? startCh : 0); 8892 range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-comp osing"})
6412 if (first) { 8893 }
6413 // Fix up .to properties of first 8894 })
6414 for (var i = 0; i < first.length; ++i) { 8895 on(te, "compositionend", function () {
6415 var span = first[i]; 8896 if (input.composing) {
6416 if (span.to == null) { 8897 input.poll()
6417 var found = getMarkedSpanFor(last, span.marker); 8898 input.composing.range.clear()
6418 if (!found) span.to = startCh; 8899 input.composing = null
6419 else if (sameLine) span.to = found.to == null ? null : found.to + offs et; 8900 }
8901 })
8902 };
8903
8904 TextareaInput.prototype.prepareSelection = function prepareSelection$1 () {
8905 // Redraw the selection and/or cursor
8906 var cm = this.cm, display = cm.display, doc = cm.doc
8907 var result = prepareSelection(cm)
8908
8909 // Move the hidden textarea near the cursor to prevent scrolling artifacts
8910 if (cm.options.moveInputWithCursor) {
8911 var headPos = cursorCoords(cm, doc.sel.primary().head, "div")
8912 var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lin eDiv.getBoundingClientRect()
8913 result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
8914 headPos.top + lineOff.top - wrapOff.top) )
8915 result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
8916 headPos.left + lineOff.left - wrapOff.l eft))
8917 }
8918
8919 return result
8920 };
8921
8922 TextareaInput.prototype.showSelection = function showSelection (drawn) {
8923 var cm = this.cm, display = cm.display
8924 removeChildrenAndAdd(display.cursorDiv, drawn.cursors)
8925 removeChildrenAndAdd(display.selectionDiv, drawn.selection)
8926 if (drawn.teTop != null) {
8927 this.wrapper.style.top = drawn.teTop + "px"
8928 this.wrapper.style.left = drawn.teLeft + "px"
8929 }
8930 };
8931
8932 // Reset the input to correspond to the selection (or to be empty,
8933 // when not typing and nothing is selected)
8934 TextareaInput.prototype.reset = function reset (typing) {
8935 if (this.contextMenuPending) { return }
8936 var minimal, selected, cm = this.cm, doc = cm.doc
8937 if (cm.somethingSelected()) {
8938 this.prevInput = ""
8939 var range = doc.sel.primary()
8940 minimal = hasCopyEvent &&
8941 (range.to().line - range.from().line > 100 || (selected = cm.getSelection( )).length > 1000)
8942 var content = minimal ? "-" : selected || cm.getSelection()
8943 this.textarea.value = content
8944 if (cm.state.focused) { selectInput(this.textarea) }
8945 if (ie && ie_version >= 9) { this.hasSelection = content }
8946 } else if (!typing) {
8947 this.prevInput = this.textarea.value = ""
8948 if (ie && ie_version >= 9) { this.hasSelection = null }
8949 }
8950 this.inaccurateSelection = minimal
8951 };
8952
8953 TextareaInput.prototype.getField = function getField () { return this.textarea } ;
8954
8955 TextareaInput.prototype.supportsTouch = function supportsTouch () { return false };
8956
8957 TextareaInput.prototype.focus = function focus () {
8958 if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this. textarea)) {
8959 try { this.textarea.focus() }
8960 catch (e) {} // IE8 will throw if the textarea is display: none or not in DO M
8961 }
8962 };
8963
8964 TextareaInput.prototype.blur = function blur () { this.textarea.blur() };
8965
8966 TextareaInput.prototype.resetPosition = function resetPosition () {
8967 this.wrapper.style.top = this.wrapper.style.left = 0
8968 };
8969
8970 TextareaInput.prototype.receivedFocus = function receivedFocus () { this.slowPol l() };
8971
8972 // Poll for input changes, using the normal rate of polling. This
8973 // runs as long as the editor is focused.
8974 TextareaInput.prototype.slowPoll = function slowPoll () {
8975 var this$1 = this;
8976
8977 if (this.pollingFast) { return }
8978 this.polling.set(this.cm.options.pollInterval, function () {
8979 this$1.poll()
8980 if (this$1.cm.state.focused) { this$1.slowPoll() }
8981 })
8982 };
8983
8984 // When an event has just come in that is likely to add or change
8985 // something in the input textarea, we poll faster, to ensure that
8986 // the change appears on the screen quickly.
8987 TextareaInput.prototype.fastPoll = function fastPoll () {
8988 var missed = false, input = this
8989 input.pollingFast = true
8990 function p() {
8991 var changed = input.poll()
8992 if (!changed && !missed) {missed = true; input.polling.set(60, p)}
8993 else {input.pollingFast = false; input.slowPoll()}
8994 }
8995 input.polling.set(20, p)
8996 };
8997
8998 // Read input from the textarea, and update the document to match.
8999 // When something is selected, it is present in the textarea, and
9000 // selected (unless it is huge, in which case a placeholder is
9001 // used). When nothing is selected, the cursor sits after previously
9002 // seen text (can be empty), which is stored in prevInput (we must
9003 // not reset the textarea when typing, because that breaks IME).
9004 TextareaInput.prototype.poll = function poll () {
9005 var this$1 = this;
9006
9007 var cm = this.cm, input = this.textarea, prevInput = this.prevInput
9008 // Since this is called a *lot*, try to bail out as cheaply as
9009 // possible when it is clear that nothing happened. hasSelection
9010 // will be the case when there is a lot of text in the textarea,
9011 // in which case reading its value would be expensive.
9012 if (this.contextMenuPending || !cm.state.focused ||
9013 (hasSelection(input) && !prevInput && !this.composing) ||
9014 cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq)
9015 { return false }
9016
9017 var text = input.value
9018 // If nothing changed, bail.
9019 if (text == prevInput && !cm.somethingSelected()) { return false }
9020 // Work around nonsensical selection resetting in IE9/10, and
9021 // inexplicable appearance of private area unicode characters on
9022 // some key combos in Mac (#2689).
9023 if (ie && ie_version >= 9 && this.hasSelection === text ||
9024 mac && /[\uf700-\uf7ff]/.test(text)) {
9025 cm.display.input.reset()
9026 return false
9027 }
9028
9029 if (cm.doc.sel == cm.display.selForContextMenu) {
9030 var first = text.charCodeAt(0)
9031 if (first == 0x200b && !prevInput) { prevInput = "\u200b" }
9032 if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") }
9033 }
9034 // Find the part of the input that is actually new
9035 var same = 0, l = Math.min(prevInput.length, text.length)
9036 while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++sa me }
9037
9038 runInOp(cm, function () {
9039 applyTextInput(cm, text.slice(same), prevInput.length - same,
9040 null, this$1.composing ? "*compose" : null)
9041
9042 // Don't leave long text in the textarea, since it makes further polling slo w
9043 if (text.length > 1000 || text.indexOf("\n") > -1) { input.value = this$1.pr evInput = "" }
9044 else { this$1.prevInput = text }
9045
9046 if (this$1.composing) {
9047 this$1.composing.range.clear()
9048 this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor( "to"),
9049 {className: "CodeMirror-composing"})
9050 }
9051 })
9052 return true
9053 };
9054
9055 TextareaInput.prototype.ensurePolled = function ensurePolled () {
9056 if (this.pollingFast && this.poll()) { this.pollingFast = false }
9057 };
9058
9059 TextareaInput.prototype.onKeyPress = function onKeyPress () {
9060 if (ie && ie_version >= 9) { this.hasSelection = null }
9061 this.fastPoll()
9062 };
9063
9064 TextareaInput.prototype.onContextMenu = function onContextMenu (e) {
9065 var input = this, cm = input.cm, display = cm.display, te = input.textarea
9066 var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop
9067 if (!pos || presto) { return } // Opera is difficult.
9068
9069 // Reset the current text selection only if the click is done outside of the s election
9070 // and 'resetSelectionOnContextMenu' option is true.
9071 var reset = cm.options.resetSelectionOnContextMenu
9072 if (reset && cm.doc.sel.contains(pos) == -1)
9073 { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll) }
9074
9075 var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText
9076 input.wrapper.style.cssText = "position: absolute"
9077 var wrapperBox = input.wrapper.getBoundingClientRect()
9078 te.style.cssText = "position: absolute; width: 30px; height: 30px;\n top: " + (e.clientY - wrapperBox.top - 5) + "px; left: " + (e.clientX - wrapperBox.l eft - 5) + "px;\n z-index: 1000; background: " + (ie ? "rgba(255, 255, 255, .05)" : "transparent") + ";\n outline: none; border-width: 0; outline: non e; overflow: hidden; opacity: .05; filter: alpha(opacity=5);"
9079 var oldScrollY
9080 if (webkit) { oldScrollY = window.scrollY } // Work around Chrome issue (#2712 )
9081 display.input.focus()
9082 if (webkit) { window.scrollTo(null, oldScrollY) }
9083 display.input.reset()
9084 // Adds "Select all" to context menu in FF
9085 if (!cm.somethingSelected()) { te.value = input.prevInput = " " }
9086 input.contextMenuPending = true
9087 display.selForContextMenu = cm.doc.sel
9088 clearTimeout(display.detectingSelectAll)
9089
9090 // Select-all will be greyed out if there's nothing to select, so
9091 // this adds a zero-width space so that we can later check whether
9092 // it got selected.
9093 function prepareSelectAllHack() {
9094 if (te.selectionStart != null) {
9095 var selected = cm.somethingSelected()
9096 var extval = "\u200b" + (selected ? te.value : "")
9097 te.value = "\u21da" // Used to catch context-menu undo
9098 te.value = extval
9099 input.prevInput = selected ? "" : "\u200b"
9100 te.selectionStart = 1; te.selectionEnd = extval.length
9101 // Re-set this, in case some other handler touched the
9102 // selection in the meantime.
9103 display.selForContextMenu = cm.doc.sel
9104 }
9105 }
9106 function rehide() {
9107 input.contextMenuPending = false
9108 input.wrapper.style.cssText = oldWrapperCSS
9109 te.style.cssText = oldCSS
9110 if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller .scrollTop = scrollPos) }
9111
9112 // Try to detect the user choosing select-all
9113 if (te.selectionStart != null) {
9114 if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack() }
9115 var i = 0, poll = function () {
9116 if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 &&
9117 te.selectionEnd > 0 && input.prevInput == "\u200b") {
9118 operation(cm, selectAll)(cm)
9119 } else if (i++ < 10) {
9120 display.detectingSelectAll = setTimeout(poll, 500)
9121 } else {
9122 display.selForContextMenu = null
9123 display.input.reset()
6420 } 9124 }
6421 } 9125 }
6422 } 9126 display.detectingSelectAll = setTimeout(poll, 200)
6423 if (last) { 9127 }
6424 // Fix up .from in last (or move them into first in case of sameLine) 9128 }
6425 for (var i = 0; i < last.length; ++i) { 9129
6426 var span = last[i]; 9130 if (ie && ie_version >= 9) { prepareSelectAllHack() }
6427 if (span.to != null) span.to += offset; 9131 if (captureRightClick) {
6428 if (span.from == null) { 9132 e_stop(e)
6429 var found = getMarkedSpanFor(first, span.marker); 9133 var mouseup = function () {
6430 if (!found) { 9134 off(window, "mouseup", mouseup)
6431 span.from = offset; 9135 setTimeout(rehide, 20)
6432 if (sameLine) (first || (first = [])).push(span); 9136 }
6433 } 9137 on(window, "mouseup", mouseup)
6434 } else { 9138 } else {
6435 span.from += offset; 9139 setTimeout(rehide, 50)
6436 if (sameLine) (first || (first = [])).push(span); 9140 }
9141 };
9142
9143 TextareaInput.prototype.readOnlyChanged = function readOnlyChanged (val) {
9144 if (!val) { this.reset() }
9145 };
9146
9147 TextareaInput.prototype.setUneditable = function setUneditable () {};
9148
9149 TextareaInput.prototype.needsContentAttribute = false
9150
9151 function fromTextArea(textarea, options) {
9152 options = options ? copyObj(options) : {}
9153 options.value = textarea.value
9154 if (!options.tabindex && textarea.tabIndex)
9155 { options.tabindex = textarea.tabIndex }
9156 if (!options.placeholder && textarea.placeholder)
9157 { options.placeholder = textarea.placeholder }
9158 // Set autofocus to true if this textarea is focused, or if it has
9159 // autofocus and no other element is focused.
9160 if (options.autofocus == null) {
9161 var hasFocus = activeElt()
9162 options.autofocus = hasFocus == textarea ||
9163 textarea.getAttribute("autofocus") != null && hasFocus == document.body
9164 }
9165
9166 function save() {textarea.value = cm.getValue()}
9167
9168 var realSubmit
9169 if (textarea.form) {
9170 on(textarea.form, "submit", save)
9171 // Deplorable hack to make the submit method do the right thing.
9172 if (!options.leaveSubmitMethodAlone) {
9173 var form = textarea.form
9174 realSubmit = form.submit
9175 try {
9176 var wrappedSubmit = form.submit = function () {
9177 save()
9178 form.submit = realSubmit
9179 form.submit()
9180 form.submit = wrappedSubmit
6437 } 9181 }
9182 } catch(e) {}
9183 }
9184 }
9185
9186 options.finishInit = function (cm) {
9187 cm.save = save
9188 cm.getTextArea = function () { return textarea; }
9189 cm.toTextArea = function () {
9190 cm.toTextArea = isNaN // Prevent this from being ran twice
9191 save()
9192 textarea.parentNode.removeChild(cm.getWrapperElement())
9193 textarea.style.display = ""
9194 if (textarea.form) {
9195 off(textarea.form, "submit", save)
9196 if (typeof textarea.form.submit == "function")
9197 { textarea.form.submit = realSubmit }
6438 } 9198 }
6439 } 9199 }
6440 // Make sure we didn't create any zero-length spans 9200 }
6441 if (first) first = clearEmptySpans(first); 9201
6442 if (last && last != first) last = clearEmptySpans(last); 9202 textarea.style.display = "none"
6443 9203 var cm = CodeMirror(function (node) { return textarea.parentNode.insertBefore( node, textarea.nextSibling); },
6444 var newMarkers = [first]; 9204 options)
6445 if (!sameLine) { 9205 return cm
6446 // Fill gap with whole-line-spans 9206 }
6447 var gap = change.text.length - 2, gapMarkers; 9207
6448 if (gap > 0 && first) 9208 function addLegacyProps(CodeMirror) {
6449 for (var i = 0; i < first.length; ++i) 9209 CodeMirror.off = off
6450 if (first[i].to == null) 9210 CodeMirror.on = on
6451 (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i].marke r, null, null)); 9211 CodeMirror.wheelEventPixels = wheelEventPixels
6452 for (var i = 0; i < gap; ++i) 9212 CodeMirror.Doc = Doc
6453 newMarkers.push(gapMarkers); 9213 CodeMirror.splitLines = splitLinesAuto
6454 newMarkers.push(last); 9214 CodeMirror.countColumn = countColumn
6455 } 9215 CodeMirror.findColumn = findColumn
6456 return newMarkers; 9216 CodeMirror.isWordChar = isWordCharBasic
6457 } 9217 CodeMirror.Pass = Pass
6458 9218 CodeMirror.signal = signal
6459 // Remove spans that are empty and don't have a clearWhenEmpty 9219 CodeMirror.Line = Line
6460 // option of false. 9220 CodeMirror.changeEnd = changeEnd
6461 function clearEmptySpans(spans) { 9221 CodeMirror.scrollbarModel = scrollbarModel
6462 for (var i = 0; i < spans.length; ++i) { 9222 CodeMirror.Pos = Pos
6463 var span = spans[i]; 9223 CodeMirror.cmpPos = cmp
6464 if (span.from != null && span.from == span.to && span.marker.clearWhenEmpt y !== false) 9224 CodeMirror.modes = modes
6465 spans.splice(i--, 1); 9225 CodeMirror.mimeModes = mimeModes
6466 } 9226 CodeMirror.resolveMode = resolveMode
6467 if (!spans.length) return null; 9227 CodeMirror.getMode = getMode
6468 return spans; 9228 CodeMirror.modeExtensions = modeExtensions
6469 } 9229 CodeMirror.extendMode = extendMode
6470 9230 CodeMirror.copyState = copyState
6471 // Used for un/re-doing changes from the history. Combines the 9231 CodeMirror.startState = startState
6472 // result of computing the existing spans with the set of spans that 9232 CodeMirror.innerMode = innerMode
6473 // existed in the history (so that deleting around a span and then 9233 CodeMirror.commands = commands
6474 // undoing brings back the span). 9234 CodeMirror.keyMap = keyMap
6475 function mergeOldSpans(doc, change) { 9235 CodeMirror.keyName = keyName
6476 var old = getOldSpans(doc, change); 9236 CodeMirror.isModifierKey = isModifierKey
6477 var stretched = stretchSpansOverChange(doc, change); 9237 CodeMirror.lookupKey = lookupKey
6478 if (!old) return stretched; 9238 CodeMirror.normalizeKeyMap = normalizeKeyMap
6479 if (!stretched) return old; 9239 CodeMirror.StringStream = StringStream
6480 9240 CodeMirror.SharedTextMarker = SharedTextMarker
6481 for (var i = 0; i < old.length; ++i) { 9241 CodeMirror.TextMarker = TextMarker
6482 var oldCur = old[i], stretchCur = stretched[i]; 9242 CodeMirror.LineWidget = LineWidget
6483 if (oldCur && stretchCur) { 9243 CodeMirror.e_preventDefault = e_preventDefault
6484 spans: for (var j = 0; j < stretchCur.length; ++j) { 9244 CodeMirror.e_stopPropagation = e_stopPropagation
6485 var span = stretchCur[j]; 9245 CodeMirror.e_stop = e_stop
6486 for (var k = 0; k < oldCur.length; ++k) 9246 CodeMirror.addClass = addClass
6487 if (oldCur[k].marker == span.marker) continue spans; 9247 CodeMirror.contains = contains
6488 oldCur.push(span); 9248 CodeMirror.rmClass = rmClass
6489 } 9249 CodeMirror.keyNames = keyNames
6490 } else if (stretchCur) { 9250 }
6491 old[i] = stretchCur; 9251
6492 } 9252 // EDITOR CONSTRUCTOR
6493 } 9253
6494 return old; 9254 defineOptions(CodeMirror)
6495 } 9255
6496 9256 addEditorMethods(CodeMirror)
6497 // Used to 'clip' out readOnly ranges when making a change. 9257
6498 function removeReadOnlyRanges(doc, from, to) { 9258 // Set up methods on CodeMirror's prototype to redirect to the editor's document .
6499 var markers = null; 9259 var dontDelegate = "iter insert remove copy getEditor constructor".split(" ")
6500 doc.iter(from.line, to.line + 1, function(line) { 9260 for (var prop in Doc.prototype) { if (Doc.prototype.hasOwnProperty(prop) && inde xOf(dontDelegate, prop) < 0)
6501 if (line.markedSpans) for (var i = 0; i < line.markedSpans.length; ++i) { 9261 { CodeMirror.prototype[prop] = (function(method) {
6502 var mark = line.markedSpans[i].marker; 9262 return function() {return method.apply(this.doc, arguments)}
6503 if (mark.readOnly && (!markers || indexOf(markers, mark) == -1)) 9263 })(Doc.prototype[prop]) } }
6504 (markers || (markers = [])).push(mark); 9264
6505 } 9265 eventMixin(Doc)
6506 }); 9266
6507 if (!markers) return null; 9267 // INPUT HANDLING
6508 var parts = [{from: from, to: to}]; 9268
6509 for (var i = 0; i < markers.length; ++i) { 9269 CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentE ditableInput}
6510 var mk = markers[i], m = mk.find(0); 9270
6511 for (var j = 0; j < parts.length; ++j) { 9271 // MODE DEFINITION AND QUERYING
6512 var p = parts[j]; 9272
6513 if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) continue; 9273 // Extra arguments are stored as the mode's dependencies, which is
6514 var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to ); 9274 // used by (legacy) mechanisms like loadmode.js to automatically
6515 if (dfrom < 0 || !mk.inclusiveLeft && !dfrom) 9275 // load a mode. (Preferred mechanism is the require/define calls.)
6516 newParts.push({from: p.from, to: m.from}); 9276 CodeMirror.defineMode = function(name/*, mode, …*/) {
6517 if (dto > 0 || !mk.inclusiveRight && !dto) 9277 if (!CodeMirror.defaults.mode && name != "null") { CodeMirror.defaults.mode = name }
6518 newParts.push({from: m.to, to: p.to}); 9278 defineMode.apply(this, arguments)
6519 parts.splice.apply(parts, newParts); 9279 }
6520 j += newParts.length - 1; 9280
6521 } 9281 CodeMirror.defineMIME = defineMIME
6522 } 9282
6523 return parts; 9283 // Minimal default mode.
6524 } 9284 CodeMirror.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); })
6525 9285 CodeMirror.defineMIME("text/plain", "null")
6526 // Connect or disconnect spans from a line. 9286
6527 function detachMarkedSpans(line) { 9287 // EXTENSIONS
6528 var spans = line.markedSpans; 9288
6529 if (!spans) return; 9289 CodeMirror.defineExtension = function (name, func) {
6530 for (var i = 0; i < spans.length; ++i) 9290 CodeMirror.prototype[name] = func
6531 spans[i].marker.detachLine(line); 9291 }
6532 line.markedSpans = null; 9292 CodeMirror.defineDocExtension = function (name, func) {
6533 } 9293 Doc.prototype[name] = func
6534 function attachMarkedSpans(line, spans) { 9294 }
6535 if (!spans) return; 9295
6536 for (var i = 0; i < spans.length; ++i) 9296 CodeMirror.fromTextArea = fromTextArea
6537 spans[i].marker.attachLine(line); 9297
6538 line.markedSpans = spans; 9298 addLegacyProps(CodeMirror)
6539 } 9299
6540 9300 CodeMirror.version = "5.25.1"
6541 // Helpers used when computing which overlapping collapsed span 9301
6542 // counts as the larger one. 9302 return CodeMirror;
6543 function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0; } 9303
6544 function extraRight(marker) { return marker.inclusiveRight ? 1 : 0; } 9304 })));
6545
6546 // Returns a number indicating which of two overlapping collapsed
6547 // spans is larger (and thus includes the other). Falls back to
6548 // comparing ids when the spans cover exactly the same range.
6549 function compareCollapsedMarkers(a, b) {
6550 var lenDiff = a.lines.length - b.lines.length;
6551 if (lenDiff != 0) return lenDiff;
6552 var aPos = a.find(), bPos = b.find();
6553 var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b);
6554 if (fromCmp) return -fromCmp;
6555 var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b);
6556 if (toCmp) return toCmp;
6557 return b.id - a.id;
6558 }
6559
6560 // Find out whether a line ends or starts in a collapsed span. If
6561 // so, return the marker for that span.
6562 function collapsedSpanAtSide(line, start) {
6563 var sps = sawCollapsedSpans && line.markedSpans, found;
6564 if (sps) for (var sp, i = 0; i < sps.length; ++i) {
6565 sp = sps[i];
6566 if (sp.marker.collapsed && (start ? sp.from : sp.to) == null &&
6567 (!found || compareCollapsedMarkers(found, sp.marker) < 0))
6568 found = sp.marker;
6569 }
6570 return found;
6571 }
6572 function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true); }
6573 function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false); }
6574
6575 // Test whether there exists a collapsed span that partially
6576 // overlaps (covers the start or end, but not both) of a new span.
6577 // Such overlap is not allowed.
6578 function conflictingCollapsedRange(doc, lineNo, from, to, marker) {
6579 var line = getLine(doc, lineNo);
6580 var sps = sawCollapsedSpans && line.markedSpans;
6581 if (sps) for (var i = 0; i < sps.length; ++i) {
6582 var sp = sps[i];
6583 if (!sp.marker.collapsed) continue;
6584 var found = sp.marker.find(0);
6585 var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(ma rker);
6586 var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker );
6587 if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) continue;
6588 if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cm p(found.to, from) >= 0 : cmp(found.to, from) > 0) ||
6589 fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cm p(found.from, to) <= 0 : cmp(found.from, to) < 0))
6590 return true;
6591 }
6592 }
6593
6594 // A visual line is a line as drawn on the screen. Folding, for
6595 // example, can cause multiple logical lines to appear on the same
6596 // visual line. This finds the start of the visual line that the
6597 // given line is part of (usually that is the line itself).
6598 function visualLine(line) {
6599 var merged;
6600 while (merged = collapsedSpanAtStart(line))
6601 line = merged.find(-1, true).line;
6602 return line;
6603 }
6604
6605 // Returns an array of logical lines that continue the visual line
6606 // started by the argument, or undefined if there are no such lines.
6607 function visualLineContinued(line) {
6608 var merged, lines;
6609 while (merged = collapsedSpanAtEnd(line)) {
6610 line = merged.find(1, true).line;
6611 (lines || (lines = [])).push(line);
6612 }
6613 return lines;
6614 }
6615
6616 // Get the line number of the start of the visual line that the
6617 // given line number is part of.
6618 function visualLineNo(doc, lineN) {
6619 var line = getLine(doc, lineN), vis = visualLine(line);
6620 if (line == vis) return lineN;
6621 return lineNo(vis);
6622 }
6623 // Get the line number of the start of the next visual line after
6624 // the given line.
6625 function visualLineEndNo(doc, lineN) {
6626 if (lineN > doc.lastLine()) return lineN;
6627 var line = getLine(doc, lineN), merged;
6628 if (!lineIsHidden(doc, line)) return lineN;
6629 while (merged = collapsedSpanAtEnd(line))
6630 line = merged.find(1, true).line;
6631 return lineNo(line) + 1;
6632 }
6633
6634 // Compute whether a line is hidden. Lines count as hidden when they
6635 // are part of a visual line that starts with another line, or when
6636 // they are entirely covered by collapsed, non-widget span.
6637 function lineIsHidden(doc, line) {
6638 var sps = sawCollapsedSpans && line.markedSpans;
6639 if (sps) for (var sp, i = 0; i < sps.length; ++i) {
6640 sp = sps[i];
6641 if (!sp.marker.collapsed) continue;
6642 if (sp.from == null) return true;
6643 if (sp.marker.widgetNode) continue;
6644 if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line , sp))
6645 return true;
6646 }
6647 }
6648 function lineIsHiddenInner(doc, line, span) {
6649 if (span.to == null) {
6650 var end = span.marker.find(1, true);
6651 return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSp ans, span.marker));
6652 }
6653 if (span.marker.inclusiveRight && span.to == line.text.length)
6654 return true;
6655 for (var sp, i = 0; i < line.markedSpans.length; ++i) {
6656 sp = line.markedSpans[i];
6657 if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to &&
6658 (sp.to == null || sp.to != span.from) &&
6659 (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
6660 lineIsHiddenInner(doc, line, sp)) return true;
6661 }
6662 }
6663
6664 // LINE WIDGETS
6665
6666 // Line widgets are block elements displayed above or below a line.
6667
6668 var LineWidget = CodeMirror.LineWidget = function(doc, node, options) {
6669 if (options) for (var opt in options) if (options.hasOwnProperty(opt))
6670 this[opt] = options[opt];
6671 this.doc = doc;
6672 this.node = node;
6673 };
6674 eventMixin(LineWidget);
6675
6676 function adjustScrollWhenAboveVisible(cm, line, diff) {
6677 if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollT op))
6678 addToScrollPos(cm, null, diff);
6679 }
6680
6681 LineWidget.prototype.clear = function() {
6682 var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo( line);
6683 if (no == null || !ws) return;
6684 for (var i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1);
6685 if (!ws.length) line.widgets = null;
6686 var height = widgetHeight(this);
6687 updateLineHeight(line, Math.max(0, line.height - height));
6688 if (cm) runInOp(cm, function() {
6689 adjustScrollWhenAboveVisible(cm, line, -height);
6690 regLineChange(cm, no, "widget");
6691 });
6692 };
6693 LineWidget.prototype.changed = function() {
6694 var oldH = this.height, cm = this.doc.cm, line = this.line;
6695 this.height = null;
6696 var diff = widgetHeight(this) - oldH;
6697 if (!diff) return;
6698 updateLineHeight(line, line.height + diff);
6699 if (cm) runInOp(cm, function() {
6700 cm.curOp.forceUpdate = true;
6701 adjustScrollWhenAboveVisible(cm, line, diff);
6702 });
6703 };
6704
6705 function widgetHeight(widget) {
6706 if (widget.height != null) return widget.height;
6707 var cm = widget.doc.cm;
6708 if (!cm) return 0;
6709 if (!contains(document.body, widget.node)) {
6710 var parentStyle = "position: relative;";
6711 if (widget.coverGutter)
6712 parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;" ;
6713 if (widget.noHScroll)
6714 parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;";
6715 removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, p arentStyle));
6716 }
6717 return widget.height = widget.node.parentNode.offsetHeight;
6718 }
6719
6720 function addLineWidget(doc, handle, node, options) {
6721 var widget = new LineWidget(doc, node, options);
6722 var cm = doc.cm;
6723 if (cm && widget.noHScroll) cm.display.alignWidgets = true;
6724 changeLine(doc, handle, "widget", function(line) {
6725 var widgets = line.widgets || (line.widgets = []);
6726 if (widget.insertAt == null) widgets.push(widget);
6727 else widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insert At)), 0, widget);
6728 widget.line = line;
6729 if (cm && !lineIsHidden(doc, line)) {
6730 var aboveVisible = heightAtLine(line) < doc.scrollTop;
6731 updateLineHeight(line, line.height + widgetHeight(widget));
6732 if (aboveVisible) addToScrollPos(cm, null, widget.height);
6733 cm.curOp.forceUpdate = true;
6734 }
6735 return true;
6736 });
6737 return widget;
6738 }
6739
6740 // LINE DATA STRUCTURE
6741
6742 // Line objects. These hold state related to a line, including
6743 // highlighting info (the styles array).
6744 var Line = CodeMirror.Line = function(text, markedSpans, estimateHeight) {
6745 this.text = text;
6746 attachMarkedSpans(this, markedSpans);
6747 this.height = estimateHeight ? estimateHeight(this) : 1;
6748 };
6749 eventMixin(Line);
6750 Line.prototype.lineNo = function() { return lineNo(this); };
6751
6752 // Change the content (text, markers) of a line. Automatically
6753 // invalidates cached information and tries to re-estimate the
6754 // line's height.
6755 function updateLine(line, text, markedSpans, estimateHeight) {
6756 line.text = text;
6757 if (line.stateAfter) line.stateAfter = null;
6758 if (line.styles) line.styles = null;
6759 if (line.order != null) line.order = null;
6760 detachMarkedSpans(line);
6761 attachMarkedSpans(line, markedSpans);
6762 var estHeight = estimateHeight ? estimateHeight(line) : 1;
6763 if (estHeight != line.height) updateLineHeight(line, estHeight);
6764 }
6765
6766 // Detach a line from the document tree and its markers.
6767 function cleanUpLine(line) {
6768 line.parent = null;
6769 detachMarkedSpans(line);
6770 }
6771
6772 function extractLineClasses(type, output) {
6773 if (type) for (;;) {
6774 var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/);
6775 if (!lineClass) break;
6776 type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineC lass[0].length);
6777 var prop = lineClass[1] ? "bgClass" : "textClass";
6778 if (output[prop] == null)
6779 output[prop] = lineClass[2];
6780 else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output [prop]))
6781 output[prop] += " " + lineClass[2];
6782 }
6783 return type;
6784 }
6785
6786 function callBlankLine(mode, state) {
6787 if (mode.blankLine) return mode.blankLine(state);
6788 if (!mode.innerMode) return;
6789 var inner = CodeMirror.innerMode(mode, state);
6790 if (inner.mode.blankLine) return inner.mode.blankLine(inner.state);
6791 }
6792
6793 function readToken(mode, stream, state, inner) {
6794 for (var i = 0; i < 10; i++) {
6795 if (inner) inner[0] = CodeMirror.innerMode(mode, state).mode;
6796 var style = mode.token(stream, state);
6797 if (stream.pos > stream.start) return style;
6798 }
6799 throw new Error("Mode " + mode.name + " failed to advance stream.");
6800 }
6801
6802 // Utility for getTokenAt and getLineTokens
6803 function takeToken(cm, pos, precise, asArray) {
6804 function getObj(copy) {
6805 return {start: stream.start, end: stream.pos,
6806 string: stream.current(),
6807 type: style || null,
6808 state: copy ? copyState(doc.mode, state) : state};
6809 }
6810
6811 var doc = cm.doc, mode = doc.mode, style;
6812 pos = clipPos(doc, pos);
6813 var line = getLine(doc, pos.line), state = getStateBefore(cm, pos.line, prec ise);
6814 var stream = new StringStream(line.text, cm.options.tabSize), tokens;
6815 if (asArray) tokens = [];
6816 while ((asArray || stream.pos < pos.ch) && !stream.eol()) {
6817 stream.start = stream.pos;
6818 style = readToken(mode, stream, state);
6819 if (asArray) tokens.push(getObj(true));
6820 }
6821 return asArray ? tokens : getObj();
6822 }
6823
6824 // Run the given mode's parser over a line, calling f for each token.
6825 function runMode(cm, text, mode, state, f, lineClasses, forceToEnd) {
6826 var flattenSpans = mode.flattenSpans;
6827 if (flattenSpans == null) flattenSpans = cm.options.flattenSpans;
6828 var curStart = 0, curStyle = null;
6829 var stream = new StringStream(text, cm.options.tabSize), style;
6830 var inner = cm.options.addModeClass && [null];
6831 if (text == "") extractLineClasses(callBlankLine(mode, state), lineClasses);
6832 while (!stream.eol()) {
6833 if (stream.pos > cm.options.maxHighlightLength) {
6834 flattenSpans = false;
6835 if (forceToEnd) processLine(cm, text, state, stream.pos);
6836 stream.pos = text.length;
6837 style = null;
6838 } else {
6839 style = extractLineClasses(readToken(mode, stream, state, inner), lineCl asses);
6840 }
6841 if (inner) {
6842 var mName = inner[0].name;
6843 if (mName) style = "m-" + (style ? mName + " " + style : mName);
6844 }
6845 if (!flattenSpans || curStyle != style) {
6846 while (curStart < stream.start) {
6847 curStart = Math.min(stream.start, curStart + 50000);
6848 f(curStart, curStyle);
6849 }
6850 curStyle = style;
6851 }
6852 stream.start = stream.pos;
6853 }
6854 while (curStart < stream.pos) {
6855 // Webkit seems to refuse to render text nodes longer than 57444 character s
6856 var pos = Math.min(stream.pos, curStart + 50000);
6857 f(pos, curStyle);
6858 curStart = pos;
6859 }
6860 }
6861
6862 // Compute a style array (an array starting with a mode generation
6863 // -- for invalidation -- followed by pairs of end positions and
6864 // style strings), which is used to highlight the tokens on the
6865 // line.
6866 function highlightLine(cm, line, state, forceToEnd) {
6867 // A styles array always starts with a number identifying the
6868 // mode/overlays that it is based on (for easy invalidation).
6869 var st = [cm.state.modeGen], lineClasses = {};
6870 // Compute the base array of styles
6871 runMode(cm, line.text, cm.doc.mode, state, function(end, style) {
6872 st.push(end, style);
6873 }, lineClasses, forceToEnd);
6874
6875 // Run overlays, adjust style array.
6876 for (var o = 0; o < cm.state.overlays.length; ++o) {
6877 var overlay = cm.state.overlays[o], i = 1, at = 0;
6878 runMode(cm, line.text, overlay.mode, true, function(end, style) {
6879 var start = i;
6880 // Ensure there's a token end at the current position, and that i points at it
6881 while (at < end) {
6882 var i_end = st[i];
6883 if (i_end > end)
6884 st.splice(i, 1, end, st[i+1], i_end);
6885 i += 2;
6886 at = Math.min(end, i_end);
6887 }
6888 if (!style) return;
6889 if (overlay.opaque) {
6890 st.splice(start, i - start, end, "cm-overlay " + style);
6891 i = start + 2;
6892 } else {
6893 for (; start < i; start += 2) {
6894 var cur = st[start+1];
6895 st[start+1] = (cur ? cur + " " : "") + "cm-overlay " + style;
6896 }
6897 }
6898 }, lineClasses);
6899 }
6900
6901 return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null};
6902 }
6903
6904 function getLineStyles(cm, line, updateFrontier) {
6905 if (!line.styles || line.styles[0] != cm.state.modeGen) {
6906 var state = getStateBefore(cm, lineNo(line));
6907 var result = highlightLine(cm, line, line.text.length > cm.options.maxHigh lightLength ? copyState(cm.doc.mode, state) : state);
6908 line.stateAfter = state;
6909 line.styles = result.styles;
6910 if (result.classes) line.styleClasses = result.classes;
6911 else if (line.styleClasses) line.styleClasses = null;
6912 if (updateFrontier === cm.doc.frontier) cm.doc.frontier++;
6913 }
6914 return line.styles;
6915 }
6916
6917 // Lightweight form of highlight -- proceed over this line and
6918 // update state, but don't save a style array. Used for lines that
6919 // aren't currently visible.
6920 function processLine(cm, text, state, startAt) {
6921 var mode = cm.doc.mode;
6922 var stream = new StringStream(text, cm.options.tabSize);
6923 stream.start = stream.pos = startAt || 0;
6924 if (text == "") callBlankLine(mode, state);
6925 while (!stream.eol()) {
6926 readToken(mode, stream, state);
6927 stream.start = stream.pos;
6928 }
6929 }
6930
6931 // Convert a style as returned by a mode (either null, or a string
6932 // containing one or more styles) to a CSS style. This is cached,
6933 // and also looks for line-wide styles.
6934 var styleToClassCache = {}, styleToClassCacheWithMode = {};
6935 function interpretTokenStyle(style, options) {
6936 if (!style || /^\s*$/.test(style)) return null;
6937 var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassC ache;
6938 return cache[style] ||
6939 (cache[style] = style.replace(/\S+/g, "cm-$&"));
6940 }
6941
6942 // Render the DOM representation of the text of a line. Also builds
6943 // up a 'line map', which points at the DOM nodes that represent
6944 // specific stretches of text, and is used by the measuring code.
6945 // The returned object contains the DOM node, this map, and
6946 // information about line-wide styles that were set by the mode.
6947 function buildLineContent(cm, lineView) {
6948 // The padding-right forces the element to have a 'border', which
6949 // is needed on Webkit to be able to get line-level bounding
6950 // rectangles for it (in measureChar).
6951 var content = elt("span", null, null, webkit ? "padding-right: .1px" : null) ;
6952 var builder = {pre: elt("pre", [content], "CodeMirror-line"), content: conte nt,
6953 col: 0, pos: 0, cm: cm,
6954 trailingSpace: false,
6955 splitSpaces: (ie || webkit) && cm.getOption("lineWrapping")};
6956 lineView.measure = {};
6957
6958 // Iterate over the logical lines that make up this visual line.
6959 for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {
6960 var line = i ? lineView.rest[i - 1] : lineView.line, order;
6961 builder.pos = 0;
6962 builder.addToken = buildToken;
6963 // Optionally wire in some hacks into the token-rendering
6964 // algorithm, to deal with browser quirks.
6965 if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line)))
6966 builder.addToken = buildTokenBadBidi(builder.addToken, order);
6967 builder.map = [];
6968 var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineN o(line);
6969 insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpda te));
6970 if (line.styleClasses) {
6971 if (line.styleClasses.bgClass)
6972 builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgCla ss || "");
6973 if (line.styleClasses.textClass)
6974 builder.textClass = joinClasses(line.styleClasses.textClass, builder.t extClass || "");
6975 }
6976
6977 // Ensure at least a single node is present, for measuring.
6978 if (builder.map.length == 0)
6979 builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.d isplay.measure)));
6980
6981 // Store the map and a cache object for the current logical line
6982 if (i == 0) {
6983 lineView.measure.map = builder.map;
6984 lineView.measure.cache = {};
6985 } else {
6986 (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map );
6987 (lineView.measure.caches || (lineView.measure.caches = [])).push({});
6988 }
6989 }
6990
6991 // See issue #2901
6992 if (webkit) {
6993 var last = builder.content.lastChild
6994 if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.query Selector(".cm-tab")))
6995 builder.content.className = "cm-tab-wrap-hack";
6996 }
6997
6998 signal(cm, "renderLine", cm, lineView.line, builder.pre);
6999 if (builder.pre.className)
7000 builder.textClass = joinClasses(builder.pre.className, builder.textClass | | "");
7001
7002 return builder;
7003 }
7004
7005 function defaultSpecialCharPlaceholder(ch) {
7006 var token = elt("span", "\u2022", "cm-invalidchar");
7007 token.title = "\\u" + ch.charCodeAt(0).toString(16);
7008 token.setAttribute("aria-label", token.title);
7009 return token;
7010 }
7011
7012 // Build up the DOM representation for a single token, and add it to
7013 // the line map. Takes care to render special characters separately.
7014 function buildToken(builder, text, style, startStyle, endStyle, title, css) {
7015 if (!text) return;
7016 var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSp ace) : text
7017 var special = builder.cm.state.specialChars, mustWrap = false;
7018 if (!special.test(text)) {
7019 builder.col += text.length;
7020 var content = document.createTextNode(displayText);
7021 builder.map.push(builder.pos, builder.pos + text.length, content);
7022 if (ie && ie_version < 9) mustWrap = true;
7023 builder.pos += text.length;
7024 } else {
7025 var content = document.createDocumentFragment(), pos = 0;
7026 while (true) {
7027 special.lastIndex = pos;
7028 var m = special.exec(text);
7029 var skipped = m ? m.index - pos : text.length - pos;
7030 if (skipped) {
7031 var txt = document.createTextNode(displayText.slice(pos, pos + skipped ));
7032 if (ie && ie_version < 9) content.appendChild(elt("span", [txt]));
7033 else content.appendChild(txt);
7034 builder.map.push(builder.pos, builder.pos + skipped, txt);
7035 builder.col += skipped;
7036 builder.pos += skipped;
7037 }
7038 if (!m) break;
7039 pos += skipped + 1;
7040 if (m[0] == "\t") {
7041 var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder .col % tabSize;
7042 var txt = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab" ));
7043 txt.setAttribute("role", "presentation");
7044 txt.setAttribute("cm-text", "\t");
7045 builder.col += tabWidth;
7046 } else if (m[0] == "\r" || m[0] == "\n") {
7047 var txt = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\ u2424", "cm-invalidchar"));
7048 txt.setAttribute("cm-text", m[0]);
7049 builder.col += 1;
7050 } else {
7051 var txt = builder.cm.options.specialCharPlaceholder(m[0]);
7052 txt.setAttribute("cm-text", m[0]);
7053 if (ie && ie_version < 9) content.appendChild(elt("span", [txt]));
7054 else content.appendChild(txt);
7055 builder.col += 1;
7056 }
7057 builder.map.push(builder.pos, builder.pos + 1, txt);
7058 builder.pos++;
7059 }
7060 }
7061 builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32
7062 if (style || startStyle || endStyle || mustWrap || css) {
7063 var fullStyle = style || "";
7064 if (startStyle) fullStyle += startStyle;
7065 if (endStyle) fullStyle += endStyle;
7066 var token = elt("span", [content], fullStyle, css);
7067 if (title) token.title = title;
7068 return builder.content.appendChild(token);
7069 }
7070 builder.content.appendChild(content);
7071 }
7072
7073 function splitSpaces(text, trailingBefore) {
7074 if (text.length > 1 && !/ /.test(text)) return text
7075 var spaceBefore = trailingBefore, result = ""
7076 for (var i = 0; i < text.length; i++) {
7077 var ch = text.charAt(i)
7078 if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32))
7079 ch = "\u00a0"
7080 result += ch
7081 spaceBefore = ch == " "
7082 }
7083 return result
7084 }
7085
7086 // Work around nonsense dimensions being reported for stretches of
7087 // right-to-left text.
7088 function buildTokenBadBidi(inner, order) {
7089 return function(builder, text, style, startStyle, endStyle, title, css) {
7090 style = style ? style + " cm-force-border" : "cm-force-border";
7091 var start = builder.pos, end = start + text.length;
7092 for (;;) {
7093 // Find the part that overlaps with the start of this text
7094 for (var i = 0; i < order.length; i++) {
7095 var part = order[i];
7096 if (part.to > start && part.from <= start) break;
7097 }
7098 if (part.to >= end) return inner(builder, text, style, startStyle, endSt yle, title, css);
7099 inner(builder, text.slice(0, part.to - start), style, startStyle, null, title, css);
7100 startStyle = null;
7101 text = text.slice(part.to - start);
7102 start = part.to;
7103 }
7104 };
7105 }
7106
7107 function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
7108 var widget = !ignoreWidget && marker.widgetNode;
7109 if (widget) builder.map.push(builder.pos, builder.pos + size, widget);
7110 if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {
7111 if (!widget)
7112 widget = builder.content.appendChild(document.createElement("span"));
7113 widget.setAttribute("cm-marker", marker.id);
7114 }
7115 if (widget) {
7116 builder.cm.display.input.setUneditable(widget);
7117 builder.content.appendChild(widget);
7118 }
7119 builder.pos += size;
7120 builder.trailingSpace = false
7121 }
7122
7123 // Outputs a number of spans to make up a line, taking highlighting
7124 // and marked text into account.
7125 function insertLineContent(line, builder, styles) {
7126 var spans = line.markedSpans, allText = line.text, at = 0;
7127 if (!spans) {
7128 for (var i = 1; i < styles.length; i+=2)
7129 builder.addToken(builder, allText.slice(at, at = styles[i]), interpretTo kenStyle(styles[i+1], builder.cm.options));
7130 return;
7131 }
7132
7133 var len = allText.length, pos = 0, i = 1, text = "", style, css;
7134 var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapse d;
7135 for (;;) {
7136 if (nextChange == pos) { // Update current marker set
7137 spanStyle = spanEndStyle = spanStartStyle = title = css = "";
7138 collapsed = null; nextChange = Infinity;
7139 var foundBookmarks = [], endStyles
7140 for (var j = 0; j < spans.length; ++j) {
7141 var sp = spans[j], m = sp.marker;
7142 if (m.type == "bookmark" && sp.from == pos && m.widgetNode) {
7143 foundBookmarks.push(m);
7144 } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collap sed && sp.to == pos && sp.from == pos)) {
7145 if (sp.to != null && sp.to != pos && nextChange > sp.to) {
7146 nextChange = sp.to;
7147 spanEndStyle = "";
7148 }
7149 if (m.className) spanStyle += " " + m.className;
7150 if (m.css) css = (css ? css + ";" : "") + m.css;
7151 if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startS tyle;
7152 if (m.endStyle && sp.to == nextChange) (endStyles || (endStyles = [] )).push(m.endStyle, sp.to)
7153 if (m.title && !title) title = m.title;
7154 if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed. marker, m) < 0))
7155 collapsed = sp;
7156 } else if (sp.from > pos && nextChange > sp.from) {
7157 nextChange = sp.from;
7158 }
7159 }
7160 if (endStyles) for (var j = 0; j < endStyles.length; j += 2)
7161 if (endStyles[j + 1] == nextChange) spanEndStyle += " " + endStyles[j]
7162
7163 if (!collapsed || collapsed.from == pos) for (var j = 0; j < foundBookma rks.length; ++j)
7164 buildCollapsedSpan(builder, 0, foundBookmarks[j]);
7165 if (collapsed && (collapsed.from || 0) == pos) {
7166 buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapse d.to) - pos,
7167 collapsed.marker, collapsed.from == null);
7168 if (collapsed.to == null) return;
7169 if (collapsed.to == pos) collapsed = false;
7170 }
7171 }
7172 if (pos >= len) break;
7173
7174 var upto = Math.min(len, nextChange);
7175 while (true) {
7176 if (text) {
7177 var end = pos + text.length;
7178 if (!collapsed) {
7179 var tokenText = end > upto ? text.slice(0, upto - pos) : text;
7180 builder.addToken(builder, tokenText, style ? style + spanStyle : spa nStyle,
7181 spanStartStyle, pos + tokenText.length == nextChang e ? spanEndStyle : "", title, css);
7182 }
7183 if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
7184 pos = end;
7185 spanStartStyle = "";
7186 }
7187 text = allText.slice(at, at = styles[i++]);
7188 style = interpretTokenStyle(styles[i++], builder.cm.options);
7189 }
7190 }
7191 }
7192
7193 // DOCUMENT DATA STRUCTURE
7194
7195 // By default, updates that start and end at the beginning of a line
7196 // are treated specially, in order to make the association of line
7197 // widgets and marker elements with the text behave more intuitive.
7198 function isWholeLineUpdate(doc, change) {
7199 return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" &&
7200 (!doc.cm || doc.cm.options.wholeLineUpdateBefore);
7201 }
7202
7203 // Perform a change on the document data structure.
7204 function updateDoc(doc, change, markedSpans, estimateHeight) {
7205 function spansFor(n) {return markedSpans ? markedSpans[n] : null;}
7206 function update(line, text, spans) {
7207 updateLine(line, text, spans, estimateHeight);
7208 signalLater(line, "change", line, change);
7209 }
7210 function linesFor(start, end) {
7211 for (var i = start, result = []; i < end; ++i)
7212 result.push(new Line(text[i], spansFor(i), estimateHeight));
7213 return result;
7214 }
7215
7216 var from = change.from, to = change.to, text = change.text;
7217 var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
7218 var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to .line - from.line;
7219
7220 // Adjust the line structure
7221 if (change.full) {
7222 doc.insert(0, linesFor(0, text.length));
7223 doc.remove(text.length, doc.size - text.length);
7224 } else if (isWholeLineUpdate(doc, change)) {
7225 // This is a whole-line replace. Treated specially to make
7226 // sure line objects move the way they are supposed to.
7227 var added = linesFor(0, text.length - 1);
7228 update(lastLine, lastLine.text, lastSpans);
7229 if (nlines) doc.remove(from.line, nlines);
7230 if (added.length) doc.insert(from.line, added);
7231 } else if (firstLine == lastLine) {
7232 if (text.length == 1) {
7233 update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLin e.text.slice(to.ch), lastSpans);
7234 } else {
7235 var added = linesFor(1, text.length - 1);
7236 added.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, e stimateHeight));
7237 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0 ));
7238 doc.insert(from.line + 1, added);
7239 }
7240 } else if (text.length == 1) {
7241 update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.te xt.slice(to.ch), spansFor(0));
7242 doc.remove(from.line + 1, nlines);
7243 } else {
7244 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)) ;
7245 update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
7246 var added = linesFor(1, text.length - 1);
7247 if (nlines > 1) doc.remove(from.line + 1, nlines - 1);
7248 doc.insert(from.line + 1, added);
7249 }
7250
7251 signalLater(doc, "change", doc, change);
7252 }
7253
7254 // The document is represented as a BTree consisting of leaves, with
7255 // chunk of lines in them, and branches, with up to ten leaves or
7256 // other branch nodes below them. The top node is always a branch
7257 // node, and is the document object itself (meaning it has
7258 // additional methods and properties).
7259 //
7260 // All nodes have parent links. The tree is used both to go from
7261 // line numbers to line objects, and to go from objects to numbers.
7262 // It also indexes by height, and is used to convert between height
7263 // and line object, and to find the total height of the document.
7264 //
7265 // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html
7266
7267 function LeafChunk(lines) {
7268 this.lines = lines;
7269 this.parent = null;
7270 for (var i = 0, height = 0; i < lines.length; ++i) {
7271 lines[i].parent = this;
7272 height += lines[i].height;
7273 }
7274 this.height = height;
7275 }
7276
7277 LeafChunk.prototype = {
7278 chunkSize: function() { return this.lines.length; },
7279 // Remove the n lines at offset 'at'.
7280 removeInner: function(at, n) {
7281 for (var i = at, e = at + n; i < e; ++i) {
7282 var line = this.lines[i];
7283 this.height -= line.height;
7284 cleanUpLine(line);
7285 signalLater(line, "delete");
7286 }
7287 this.lines.splice(at, n);
7288 },
7289 // Helper used to collapse a small branch into a single leaf.
7290 collapse: function(lines) {
7291 lines.push.apply(lines, this.lines);
7292 },
7293 // Insert the given array of lines at offset 'at', count them as
7294 // having the given height.
7295 insertInner: function(at, lines, height) {
7296 this.height += height;
7297 this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice (at));
7298 for (var i = 0; i < lines.length; ++i) lines[i].parent = this;
7299 },
7300 // Used to iterate over a part of the tree.
7301 iterN: function(at, n, op) {
7302 for (var e = at + n; at < e; ++at)
7303 if (op(this.lines[at])) return true;
7304 }
7305 };
7306
7307 function BranchChunk(children) {
7308 this.children = children;
7309 var size = 0, height = 0;
7310 for (var i = 0; i < children.length; ++i) {
7311 var ch = children[i];
7312 size += ch.chunkSize(); height += ch.height;
7313 ch.parent = this;
7314 }
7315 this.size = size;
7316 this.height = height;
7317 this.parent = null;
7318 }
7319
7320 BranchChunk.prototype = {
7321 chunkSize: function() { return this.size; },
7322 removeInner: function(at, n) {
7323 this.size -= n;
7324 for (var i = 0; i < this.children.length; ++i) {
7325 var child = this.children[i], sz = child.chunkSize();
7326 if (at < sz) {
7327 var rm = Math.min(n, sz - at), oldHeight = child.height;
7328 child.removeInner(at, rm);
7329 this.height -= oldHeight - child.height;
7330 if (sz == rm) { this.children.splice(i--, 1); child.parent = null; }
7331 if ((n -= rm) == 0) break;
7332 at = 0;
7333 } else at -= sz;
7334 }
7335 // If the result is smaller than 25 lines, ensure that it is a
7336 // single leaf node.
7337 if (this.size - n < 25 &&
7338 (this.children.length > 1 || !(this.children[0] instanceof LeafChunk)) ) {
7339 var lines = [];
7340 this.collapse(lines);
7341 this.children = [new LeafChunk(lines)];
7342 this.children[0].parent = this;
7343 }
7344 },
7345 collapse: function(lines) {
7346 for (var i = 0; i < this.children.length; ++i) this.children[i].collapse(l ines);
7347 },
7348 insertInner: function(at, lines, height) {
7349 this.size += lines.length;
7350 this.height += height;
7351 for (var i = 0; i < this.children.length; ++i) {
7352 var child = this.children[i], sz = child.chunkSize();
7353 if (at <= sz) {
7354 child.insertInner(at, lines, height);
7355 if (child.lines && child.lines.length > 50) {
7356 // To avoid memory thrashing when child.lines is huge (e.g. first vi ew of a large file), it's never spliced.
7357 // Instead, small slices are taken. They're taken in order because s equential memory accesses are fastest.
7358 var remaining = child.lines.length % 25 + 25
7359 for (var pos = remaining; pos < child.lines.length;) {
7360 var leaf = new LeafChunk(child.lines.slice(pos, pos += 25));
7361 child.height -= leaf.height;
7362 this.children.splice(++i, 0, leaf);
7363 leaf.parent = this;
7364 }
7365 child.lines = child.lines.slice(0, remaining);
7366 this.maybeSpill();
7367 }
7368 break;
7369 }
7370 at -= sz;
7371 }
7372 },
7373 // When a node has grown, check whether it should be split.
7374 maybeSpill: function() {
7375 if (this.children.length <= 10) return;
7376 var me = this;
7377 do {
7378 var spilled = me.children.splice(me.children.length - 5, 5);
7379 var sibling = new BranchChunk(spilled);
7380 if (!me.parent) { // Become the parent node
7381 var copy = new BranchChunk(me.children);
7382 copy.parent = me;
7383 me.children = [copy, sibling];
7384 me = copy;
7385 } else {
7386 me.size -= sibling.size;
7387 me.height -= sibling.height;
7388 var myIndex = indexOf(me.parent.children, me);
7389 me.parent.children.splice(myIndex + 1, 0, sibling);
7390 }
7391 sibling.parent = me.parent;
7392 } while (me.children.length > 10);
7393 me.parent.maybeSpill();
7394 },
7395 iterN: function(at, n, op) {
7396 for (var i = 0; i < this.children.length; ++i) {
7397 var child = this.children[i], sz = child.chunkSize();
7398 if (at < sz) {
7399 var used = Math.min(n, sz - at);
7400 if (child.iterN(at, used, op)) return true;
7401 if ((n -= used) == 0) break;
7402 at = 0;
7403 } else at -= sz;
7404 }
7405 }
7406 };
7407
7408 var nextDocId = 0;
7409 var Doc = CodeMirror.Doc = function(text, mode, firstLine, lineSep) {
7410 if (!(this instanceof Doc)) return new Doc(text, mode, firstLine, lineSep);
7411 if (firstLine == null) firstLine = 0;
7412
7413 BranchChunk.call(this, [new LeafChunk([new Line("", null)])]);
7414 this.first = firstLine;
7415 this.scrollTop = this.scrollLeft = 0;
7416 this.cantEdit = false;
7417 this.cleanGeneration = 1;
7418 this.frontier = firstLine;
7419 var start = Pos(firstLine, 0);
7420 this.sel = simpleSelection(start);
7421 this.history = new History(null);
7422 this.id = ++nextDocId;
7423 this.modeOption = mode;
7424 this.lineSep = lineSep;
7425 this.extend = false;
7426
7427 if (typeof text == "string") text = this.splitLines(text);
7428 updateDoc(this, {from: start, to: start, text: text});
7429 setSelection(this, simpleSelection(start), sel_dontScroll);
7430 };
7431
7432 Doc.prototype = createObj(BranchChunk.prototype, {
7433 constructor: Doc,
7434 // Iterate over the document. Supports two forms -- with only one
7435 // argument, it calls that for each line in the document. With
7436 // three, it iterates over the range given by the first two (with
7437 // the second being non-inclusive).
7438 iter: function(from, to, op) {
7439 if (op) this.iterN(from - this.first, to - from, op);
7440 else this.iterN(this.first, this.first + this.size, from);
7441 },
7442
7443 // Non-public interface for adding and removing lines.
7444 insert: function(at, lines) {
7445 var height = 0;
7446 for (var i = 0; i < lines.length; ++i) height += lines[i].height;
7447 this.insertInner(at - this.first, lines, height);
7448 },
7449 remove: function(at, n) { this.removeInner(at - this.first, n); },
7450
7451 // From here, the methods are part of the public interface. Most
7452 // are also available from CodeMirror (editor) instances.
7453
7454 getValue: function(lineSep) {
7455 var lines = getLines(this, this.first, this.first + this.size);
7456 if (lineSep === false) return lines;
7457 return lines.join(lineSep || this.lineSeparator());
7458 },
7459 setValue: docMethodOp(function(code) {
7460 var top = Pos(this.first, 0), last = this.first + this.size - 1;
7461 makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length ),
7462 text: this.splitLines(code), origin: "setValue", full: t rue}, true);
7463 setSelection(this, simpleSelection(top));
7464 }),
7465 replaceRange: function(code, from, to, origin) {
7466 from = clipPos(this, from);
7467 to = to ? clipPos(this, to) : from;
7468 replaceRange(this, code, from, to, origin);
7469 },
7470 getRange: function(from, to, lineSep) {
7471 var lines = getBetween(this, clipPos(this, from), clipPos(this, to));
7472 if (lineSep === false) return lines;
7473 return lines.join(lineSep || this.lineSeparator());
7474 },
7475
7476 getLine: function(line) {var l = this.getLineHandle(line); return l && l.tex t;},
7477
7478 getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line);},
7479 getLineNumber: function(line) {return lineNo(line);},
7480
7481 getLineHandleVisualStart: function(line) {
7482 if (typeof line == "number") line = getLine(this, line);
7483 return visualLine(line);
7484 },
7485
7486 lineCount: function() {return this.size;},
7487 firstLine: function() {return this.first;},
7488 lastLine: function() {return this.first + this.size - 1;},
7489
7490 clipPos: function(pos) {return clipPos(this, pos);},
7491
7492 getCursor: function(start) {
7493 var range = this.sel.primary(), pos;
7494 if (start == null || start == "head") pos = range.head;
7495 else if (start == "anchor") pos = range.anchor;
7496 else if (start == "end" || start == "to" || start === false) pos = range.t o();
7497 else pos = range.from();
7498 return pos;
7499 },
7500 listSelections: function() { return this.sel.ranges; },
7501 somethingSelected: function() {return this.sel.somethingSelected();},
7502
7503 setCursor: docMethodOp(function(line, ch, options) {
7504 setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options);
7505 }),
7506 setSelection: docMethodOp(function(anchor, head, options) {
7507 setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anch or), options);
7508 }),
7509 extendSelection: docMethodOp(function(head, other, options) {
7510 extendSelection(this, clipPos(this, head), other && clipPos(this, other), options);
7511 }),
7512 extendSelections: docMethodOp(function(heads, options) {
7513 extendSelections(this, clipPosArray(this, heads), options);
7514 }),
7515 extendSelectionsBy: docMethodOp(function(f, options) {
7516 var heads = map(this.sel.ranges, f);
7517 extendSelections(this, clipPosArray(this, heads), options);
7518 }),
7519 setSelections: docMethodOp(function(ranges, primary, options) {
7520 if (!ranges.length) return;
7521 for (var i = 0, out = []; i < ranges.length; i++)
7522 out[i] = new Range(clipPos(this, ranges[i].anchor),
7523 clipPos(this, ranges[i].head));
7524 if (primary == null) primary = Math.min(ranges.length - 1, this.sel.primIn dex);
7525 setSelection(this, normalizeSelection(out, primary), options);
7526 }),
7527 addSelection: docMethodOp(function(anchor, head, options) {
7528 var ranges = this.sel.ranges.slice(0);
7529 ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor) ));
7530 setSelection(this, normalizeSelection(ranges, ranges.length - 1), options) ;
7531 }),
7532
7533 getSelection: function(lineSep) {
7534 var ranges = this.sel.ranges, lines;
7535 for (var i = 0; i < ranges.length; i++) {
7536 var sel = getBetween(this, ranges[i].from(), ranges[i].to());
7537 lines = lines ? lines.concat(sel) : sel;
7538 }
7539 if (lineSep === false) return lines;
7540 else return lines.join(lineSep || this.lineSeparator());
7541 },
7542 getSelections: function(lineSep) {
7543 var parts = [], ranges = this.sel.ranges;
7544 for (var i = 0; i < ranges.length; i++) {
7545 var sel = getBetween(this, ranges[i].from(), ranges[i].to());
7546 if (lineSep !== false) sel = sel.join(lineSep || this.lineSeparator());
7547 parts[i] = sel;
7548 }
7549 return parts;
7550 },
7551 replaceSelection: function(code, collapse, origin) {
7552 var dup = [];
7553 for (var i = 0; i < this.sel.ranges.length; i++)
7554 dup[i] = code;
7555 this.replaceSelections(dup, collapse, origin || "+input");
7556 },
7557 replaceSelections: docMethodOp(function(code, collapse, origin) {
7558 var changes = [], sel = this.sel;
7559 for (var i = 0; i < sel.ranges.length; i++) {
7560 var range = sel.ranges[i];
7561 changes[i] = {from: range.from(), to: range.to(), text: this.splitLines( code[i]), origin: origin};
7562 }
7563 var newSel = collapse && collapse != "end" && computeReplacedSel(this, cha nges, collapse);
7564 for (var i = changes.length - 1; i >= 0; i--)
7565 makeChange(this, changes[i]);
7566 if (newSel) setSelectionReplaceHistory(this, newSel);
7567 else if (this.cm) ensureCursorVisible(this.cm);
7568 }),
7569 undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}),
7570 redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}),
7571 undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", t rue);}),
7572 redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", t rue);}),
7573
7574 setExtending: function(val) {this.extend = val;},
7575 getExtending: function() {return this.extend;},
7576
7577 historySize: function() {
7578 var hist = this.history, done = 0, undone = 0;
7579 for (var i = 0; i < hist.done.length; i++) if (!hist.done[i].ranges) ++don e;
7580 for (var i = 0; i < hist.undone.length; i++) if (!hist.undone[i].ranges) + +undone;
7581 return {undo: done, redo: undone};
7582 },
7583 clearHistory: function() {this.history = new History(this.history.maxGenerat ion);},
7584
7585 markClean: function() {
7586 this.cleanGeneration = this.changeGeneration(true);
7587 },
7588 changeGeneration: function(forceSplit) {
7589 if (forceSplit)
7590 this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null;
7591 return this.history.generation;
7592 },
7593 isClean: function (gen) {
7594 return this.history.generation == (gen || this.cleanGeneration);
7595 },
7596
7597 getHistory: function() {
7598 return {done: copyHistoryArray(this.history.done),
7599 undone: copyHistoryArray(this.history.undone)};
7600 },
7601 setHistory: function(histData) {
7602 var hist = this.history = new History(this.history.maxGeneration);
7603 hist.done = copyHistoryArray(histData.done.slice(0), null, true);
7604 hist.undone = copyHistoryArray(histData.undone.slice(0), null, true);
7605 },
7606
7607 addLineClass: docMethodOp(function(handle, where, cls) {
7608 return changeLine(this, handle, where == "gutter" ? "gutter" : "class", fu nction(line) {
7609 var prop = where == "text" ? "textClass"
7610 : where == "background" ? "bgClass"
7611 : where == "gutter" ? "gutterClass" : "wrapClass";
7612 if (!line[prop]) line[prop] = cls;
7613 else if (classTest(cls).test(line[prop])) return false;
7614 else line[prop] += " " + cls;
7615 return true;
7616 });
7617 }),
7618 removeLineClass: docMethodOp(function(handle, where, cls) {
7619 return changeLine(this, handle, where == "gutter" ? "gutter" : "class", fu nction(line) {
7620 var prop = where == "text" ? "textClass"
7621 : where == "background" ? "bgClass"
7622 : where == "gutter" ? "gutterClass" : "wrapClass";
7623 var cur = line[prop];
7624 if (!cur) return false;
7625 else if (cls == null) line[prop] = null;
7626 else {
7627 var found = cur.match(classTest(cls));
7628 if (!found) return false;
7629 var end = found.index + found[0].length;
7630 line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.l ength ? "" : " ") + cur.slice(end) || null;
7631 }
7632 return true;
7633 });
7634 }),
7635
7636 addLineWidget: docMethodOp(function(handle, node, options) {
7637 return addLineWidget(this, handle, node, options);
7638 }),
7639 removeLineWidget: function(widget) { widget.clear(); },
7640
7641 markText: function(from, to, options) {
7642 return markText(this, clipPos(this, from), clipPos(this, to), options, opt ions && options.type || "range");
7643 },
7644 setBookmark: function(pos, options) {
7645 var realOpts = {replacedWith: options && (options.nodeType == null ? optio ns.widget : options),
7646 insertLeft: options && options.insertLeft,
7647 clearWhenEmpty: false, shared: options && options.shared,
7648 handleMouseEvents: options && options.handleMouseEvents};
7649 pos = clipPos(this, pos);
7650 return markText(this, pos, pos, realOpts, "bookmark");
7651 },
7652 findMarksAt: function(pos) {
7653 pos = clipPos(this, pos);
7654 var markers = [], spans = getLine(this, pos.line).markedSpans;
7655 if (spans) for (var i = 0; i < spans.length; ++i) {
7656 var span = spans[i];
7657 if ((span.from == null || span.from <= pos.ch) &&
7658 (span.to == null || span.to >= pos.ch))
7659 markers.push(span.marker.parent || span.marker);
7660 }
7661 return markers;
7662 },
7663 findMarks: function(from, to, filter) {
7664 from = clipPos(this, from); to = clipPos(this, to);
7665 var found = [], lineNo = from.line;
7666 this.iter(from.line, to.line + 1, function(line) {
7667 var spans = line.markedSpans;
7668 if (spans) for (var i = 0; i < spans.length; i++) {
7669 var span = spans[i];
7670 if (!(span.to != null && lineNo == from.line && from.ch >= span.to ||
7671 span.from == null && lineNo != from.line ||
7672 span.from != null && lineNo == to.line && span.from >= to.ch) &&
7673 (!filter || filter(span.marker)))
7674 found.push(span.marker.parent || span.marker);
7675 }
7676 ++lineNo;
7677 });
7678 return found;
7679 },
7680 getAllMarks: function() {
7681 var markers = [];
7682 this.iter(function(line) {
7683 var sps = line.markedSpans;
7684 if (sps) for (var i = 0; i < sps.length; ++i)
7685 if (sps[i].from != null) markers.push(sps[i].marker);
7686 });
7687 return markers;
7688 },
7689
7690 posFromIndex: function(off) {
7691 var ch, lineNo = this.first, sepSize = this.lineSeparator().length;
7692 this.iter(function(line) {
7693 var sz = line.text.length + sepSize;
7694 if (sz > off) { ch = off; return true; }
7695 off -= sz;
7696 ++lineNo;
7697 });
7698 return clipPos(this, Pos(lineNo, ch));
7699 },
7700 indexFromPos: function (coords) {
7701 coords = clipPos(this, coords);
7702 var index = coords.ch;
7703 if (coords.line < this.first || coords.ch < 0) return 0;
7704 var sepSize = this.lineSeparator().length;
7705 this.iter(this.first, coords.line, function (line) {
7706 index += line.text.length + sepSize;
7707 });
7708 return index;
7709 },
7710
7711 copy: function(copyHistory) {
7712 var doc = new Doc(getLines(this, this.first, this.first + this.size),
7713 this.modeOption, this.first, this.lineSep);
7714 doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;
7715 doc.sel = this.sel;
7716 doc.extend = false;
7717 if (copyHistory) {
7718 doc.history.undoDepth = this.history.undoDepth;
7719 doc.setHistory(this.getHistory());
7720 }
7721 return doc;
7722 },
7723
7724 linkedDoc: function(options) {
7725 if (!options) options = {};
7726 var from = this.first, to = this.first + this.size;
7727 if (options.from != null && options.from > from) from = options.from;
7728 if (options.to != null && options.to < to) to = options.to;
7729 var copy = new Doc(getLines(this, from, to), options.mode || this.modeOpti on, from, this.lineSep);
7730 if (options.sharedHist) copy.history = this.history;
7731 (this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.s haredHist});
7732 copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist} ];
7733 copySharedMarkers(copy, findSharedMarkers(this));
7734 return copy;
7735 },
7736 unlinkDoc: function(other) {
7737 if (other instanceof CodeMirror) other = other.doc;
7738 if (this.linked) for (var i = 0; i < this.linked.length; ++i) {
7739 var link = this.linked[i];
7740 if (link.doc != other) continue;
7741 this.linked.splice(i, 1);
7742 other.unlinkDoc(this);
7743 detachSharedMarkers(findSharedMarkers(this));
7744 break;
7745 }
7746 // If the histories were shared, split them again
7747 if (other.history == this.history) {
7748 var splitIds = [other.id];
7749 linkedDocs(other, function(doc) {splitIds.push(doc.id);}, true);
7750 other.history = new History(null);
7751 other.history.done = copyHistoryArray(this.history.done, splitIds);
7752 other.history.undone = copyHistoryArray(this.history.undone, splitIds);
7753 }
7754 },
7755 iterLinkedDocs: function(f) {linkedDocs(this, f);},
7756
7757 getMode: function() {return this.mode;},
7758 getEditor: function() {return this.cm;},
7759
7760 splitLines: function(str) {
7761 if (this.lineSep) return str.split(this.lineSep);
7762 return splitLinesAuto(str);
7763 },
7764 lineSeparator: function() { return this.lineSep || "\n"; }
7765 });
7766
7767 // Public alias.
7768 Doc.prototype.eachLine = Doc.prototype.iter;
7769
7770 // Set up methods on CodeMirror's prototype to redirect to the editor's docume nt.
7771 var dontDelegate = "iter insert remove copy getEditor constructor".split(" ");
7772 for (var prop in Doc.prototype) if (Doc.prototype.hasOwnProperty(prop) && inde xOf(dontDelegate, prop) < 0)
7773 CodeMirror.prototype[prop] = (function(method) {
7774 return function() {return method.apply(this.doc, arguments);};
7775 })(Doc.prototype[prop]);
7776
7777 eventMixin(Doc);
7778
7779 // Call f for all linked documents.
7780 function linkedDocs(doc, f, sharedHistOnly) {
7781 function propagate(doc, skip, sharedHist) {
7782 if (doc.linked) for (var i = 0; i < doc.linked.length; ++i) {
7783 var rel = doc.linked[i];
7784 if (rel.doc == skip) continue;
7785 var shared = sharedHist && rel.sharedHist;
7786 if (sharedHistOnly && !shared) continue;
7787 f(rel.doc, shared);
7788 propagate(rel.doc, doc, shared);
7789 }
7790 }
7791 propagate(doc, null, true);
7792 }
7793
7794 // Attach a document to an editor.
7795 function attachDoc(cm, doc) {
7796 if (doc.cm) throw new Error("This document is already in use.");
7797 cm.doc = doc;
7798 doc.cm = cm;
7799 estimateLineHeights(cm);
7800 loadMode(cm);
7801 if (!cm.options.lineWrapping) findMaxLine(cm);
7802 cm.options.mode = doc.modeOption;
7803 regChange(cm);
7804 }
7805
7806 // LINE UTILITIES
7807
7808 // Find the line object corresponding to the given line number.
7809 function getLine(doc, n) {
7810 n -= doc.first;
7811 if (n < 0 || n >= doc.size) throw new Error("There is no line " + (n + doc.f irst) + " in the document.");
7812 for (var chunk = doc; !chunk.lines;) {
7813 for (var i = 0;; ++i) {
7814 var child = chunk.children[i], sz = child.chunkSize();
7815 if (n < sz) { chunk = child; break; }
7816 n -= sz;
7817 }
7818 }
7819 return chunk.lines[n];
7820 }
7821
7822 // Get the part of a document between two positions, as an array of
7823 // strings.
7824 function getBetween(doc, start, end) {
7825 var out = [], n = start.line;
7826 doc.iter(start.line, end.line + 1, function(line) {
7827 var text = line.text;
7828 if (n == end.line) text = text.slice(0, end.ch);
7829 if (n == start.line) text = text.slice(start.ch);
7830 out.push(text);
7831 ++n;
7832 });
7833 return out;
7834 }
7835 // Get the lines between from and to, as array of strings.
7836 function getLines(doc, from, to) {
7837 var out = [];
7838 doc.iter(from, to, function(line) { out.push(line.text); });
7839 return out;
7840 }
7841
7842 // Update the height of a line, propagating the height change
7843 // upwards to parent nodes.
7844 function updateLineHeight(line, height) {
7845 var diff = height - line.height;
7846 if (diff) for (var n = line; n; n = n.parent) n.height += diff;
7847 }
7848
7849 // Given a line object, find its line number by walking up through
7850 // its parent links.
7851 function lineNo(line) {
7852 if (line.parent == null) return null;
7853 var cur = line.parent, no = indexOf(cur.lines, line);
7854 for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
7855 for (var i = 0;; ++i) {
7856 if (chunk.children[i] == cur) break;
7857 no += chunk.children[i].chunkSize();
7858 }
7859 }
7860 return no + cur.first;
7861 }
7862
7863 // Find the line at the given vertical position, using the height
7864 // information in the document tree.
7865 function lineAtHeight(chunk, h) {
7866 var n = chunk.first;
7867 outer: do {
7868 for (var i = 0; i < chunk.children.length; ++i) {
7869 var child = chunk.children[i], ch = child.height;
7870 if (h < ch) { chunk = child; continue outer; }
7871 h -= ch;
7872 n += child.chunkSize();
7873 }
7874 return n;
7875 } while (!chunk.lines);
7876 for (var i = 0; i < chunk.lines.length; ++i) {
7877 var line = chunk.lines[i], lh = line.height;
7878 if (h < lh) break;
7879 h -= lh;
7880 }
7881 return n + i;
7882 }
7883
7884
7885 // Find the height above the given line.
7886 function heightAtLine(lineObj) {
7887 lineObj = visualLine(lineObj);
7888
7889 var h = 0, chunk = lineObj.parent;
7890 for (var i = 0; i < chunk.lines.length; ++i) {
7891 var line = chunk.lines[i];
7892 if (line == lineObj) break;
7893 else h += line.height;
7894 }
7895 for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
7896 for (var i = 0; i < p.children.length; ++i) {
7897 var cur = p.children[i];
7898 if (cur == chunk) break;
7899 else h += cur.height;
7900 }
7901 }
7902 return h;
7903 }
7904
7905 // Get the bidi ordering for the given line (and cache it). Returns
7906 // false for lines that are fully left-to-right, and an array of
7907 // BidiSpan objects otherwise.
7908 function getOrder(line) {
7909 var order = line.order;
7910 if (order == null) order = line.order = bidiOrdering(line.text);
7911 return order;
7912 }
7913
7914 // HISTORY
7915
7916 function History(startGen) {
7917 // Arrays of change events and selections. Doing something adds an
7918 // event to done and clears undo. Undoing moves events from done
7919 // to undone, redoing moves them in the other direction.
7920 this.done = []; this.undone = [];
7921 this.undoDepth = Infinity;
7922 // Used to track when changes can be merged into a single undo
7923 // event
7924 this.lastModTime = this.lastSelTime = 0;
7925 this.lastOp = this.lastSelOp = null;
7926 this.lastOrigin = this.lastSelOrigin = null;
7927 // Used by the isClean() method
7928 this.generation = this.maxGeneration = startGen || 1;
7929 }
7930
7931 // Create a history change event from an updateDoc-style change
7932 // object.
7933 function historyChangeFromChange(doc, change) {
7934 var histChange = {from: copyPos(change.from), to: changeEnd(change), text: g etBetween(doc, change.from, change.to)};
7935 attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
7936 linkedDocs(doc, function(doc) {attachLocalSpans(doc, histChange, change.from .line, change.to.line + 1);}, true);
7937 return histChange;
7938 }
7939
7940 // Pop all selection events off the end of a history array. Stop at
7941 // a change event.
7942 function clearSelectionEvents(array) {
7943 while (array.length) {
7944 var last = lst(array);
7945 if (last.ranges) array.pop();
7946 else break;
7947 }
7948 }
7949
7950 // Find the top change event in the history. Pop off selection
7951 // events that are in the way.
7952 function lastChangeEvent(hist, force) {
7953 if (force) {
7954 clearSelectionEvents(hist.done);
7955 return lst(hist.done);
7956 } else if (hist.done.length && !lst(hist.done).ranges) {
7957 return lst(hist.done);
7958 } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) {
7959 hist.done.pop();
7960 return lst(hist.done);
7961 }
7962 }
7963
7964 // Register a change in the history. Merges changes that are within
7965 // a single operation, ore are close together with an origin that
7966 // allows merging (starting with "+") into a single event.
7967 function addChangeToHistory(doc, change, selAfter, opId) {
7968 var hist = doc.history;
7969 hist.undone.length = 0;
7970 var time = +new Date, cur;
7971
7972 if ((hist.lastOp == opId ||
7973 hist.lastOrigin == change.origin && change.origin &&
7974 ((change.origin.charAt(0) == "+" && doc.cm && hist.lastModTime > time - doc.cm.options.historyEventDelay) ||
7975 change.origin.charAt(0) == "*")) &&
7976 (cur = lastChangeEvent(hist, hist.lastOp == opId))) {
7977 // Merge this change into the last event
7978 var last = lst(cur.changes);
7979 if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) {
7980 // Optimized case for simple insertion -- don't want to add
7981 // new changesets for every character typed
7982 last.to = changeEnd(change);
7983 } else {
7984 // Add new sub-event
7985 cur.changes.push(historyChangeFromChange(doc, change));
7986 }
7987 } else {
7988 // Can not be merged, start a new event.
7989 var before = lst(hist.done);
7990 if (!before || !before.ranges)
7991 pushSelectionToHistory(doc.sel, hist.done);
7992 cur = {changes: [historyChangeFromChange(doc, change)],
7993 generation: hist.generation};
7994 hist.done.push(cur);
7995 while (hist.done.length > hist.undoDepth) {
7996 hist.done.shift();
7997 if (!hist.done[0].ranges) hist.done.shift();
7998 }
7999 }
8000 hist.done.push(selAfter);
8001 hist.generation = ++hist.maxGeneration;
8002 hist.lastModTime = hist.lastSelTime = time;
8003 hist.lastOp = hist.lastSelOp = opId;
8004 hist.lastOrigin = hist.lastSelOrigin = change.origin;
8005
8006 if (!last) signal(doc, "historyAdded");
8007 }
8008
8009 function selectionEventCanBeMerged(doc, origin, prev, sel) {
8010 var ch = origin.charAt(0);
8011 return ch == "*" ||
8012 ch == "+" &&
8013 prev.ranges.length == sel.ranges.length &&
8014 prev.somethingSelected() == sel.somethingSelected() &&
8015 new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEven tDelay : 500);
8016 }
8017
8018 // Called whenever the selection changes, sets the new selection as
8019 // the pending selection in the history, and pushes the old pending
8020 // selection into the 'done' array when it was significantly
8021 // different (in number of selected ranges, emptiness, or time).
8022 function addSelectionToHistory(doc, sel, opId, options) {
8023 var hist = doc.history, origin = options && options.origin;
8024
8025 // A new event is started when the previous origin does not match
8026 // the current, or the origins don't allow matching. Origins
8027 // starting with * are always merged, those starting with + are
8028 // merged when similar and close together in time.
8029 if (opId == hist.lastSelOp ||
8030 (origin && hist.lastSelOrigin == origin &&
8031 (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||
8032 selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))
8033 hist.done[hist.done.length - 1] = sel;
8034 else
8035 pushSelectionToHistory(sel, hist.done);
8036
8037 hist.lastSelTime = +new Date;
8038 hist.lastSelOrigin = origin;
8039 hist.lastSelOp = opId;
8040 if (options && options.clearRedo !== false)
8041 clearSelectionEvents(hist.undone);
8042 }
8043
8044 function pushSelectionToHistory(sel, dest) {
8045 var top = lst(dest);
8046 if (!(top && top.ranges && top.equals(sel)))
8047 dest.push(sel);
8048 }
8049
8050 // Used to store marked span information in the history.
8051 function attachLocalSpans(doc, change, from, to) {
8052 var existing = change["spans_" + doc.id], n = 0;
8053 doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), func tion(line) {
8054 if (line.markedSpans)
8055 (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.mark edSpans;
8056 ++n;
8057 });
8058 }
8059
8060 // When un/re-doing restores text containing marked spans, those
8061 // that have been explicitly cleared should not be restored.
8062 function removeClearedSpans(spans) {
8063 if (!spans) return null;
8064 for (var i = 0, out; i < spans.length; ++i) {
8065 if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i) ; }
8066 else if (out) out.push(spans[i]);
8067 }
8068 return !out ? spans : out.length ? out : null;
8069 }
8070
8071 // Retrieve and filter the old marked spans stored in a change event.
8072 function getOldSpans(doc, change) {
8073 var found = change["spans_" + doc.id];
8074 if (!found) return null;
8075 for (var i = 0, nw = []; i < change.text.length; ++i)
8076 nw.push(removeClearedSpans(found[i]));
8077 return nw;
8078 }
8079
8080 // Used both to provide a JSON-safe object in .getHistory, and, when
8081 // detaching a document, to split the history in two
8082 function copyHistoryArray(events, newGroup, instantiateSel) {
8083 for (var i = 0, copy = []; i < events.length; ++i) {
8084 var event = events[i];
8085 if (event.ranges) {
8086 copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : ev ent);
8087 continue;
8088 }
8089 var changes = event.changes, newChanges = [];
8090 copy.push({changes: newChanges});
8091 for (var j = 0; j < changes.length; ++j) {
8092 var change = changes[j], m;
8093 newChanges.push({from: change.from, to: change.to, text: change.text});
8094 if (newGroup) for (var prop in change) if (m = prop.match(/^spans_(\d+)$ /)) {
8095 if (indexOf(newGroup, Number(m[1])) > -1) {
8096 lst(newChanges)[prop] = change[prop];
8097 delete change[prop];
8098 }
8099 }
8100 }
8101 }
8102 return copy;
8103 }
8104
8105 // Rebasing/resetting history to deal with externally-sourced changes
8106
8107 function rebaseHistSelSingle(pos, from, to, diff) {
8108 if (to < pos.line) {
8109 pos.line += diff;
8110 } else if (from < pos.line) {
8111 pos.line = from;
8112 pos.ch = 0;
8113 }
8114 }
8115
8116 // Tries to rebase an array of history events given a change in the
8117 // document. If the change touches the same lines as the event, the
8118 // event, and everything 'behind' it, is discarded. If the change is
8119 // before the event, the event's positions are updated. Uses a
8120 // copy-on-write scheme for the positions, to avoid having to
8121 // reallocate them all on every rebase, but also avoid problems with
8122 // shared position objects being unsafely updated.
8123 function rebaseHistArray(array, from, to, diff) {
8124 for (var i = 0; i < array.length; ++i) {
8125 var sub = array[i], ok = true;
8126 if (sub.ranges) {
8127 if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; }
8128 for (var j = 0; j < sub.ranges.length; j++) {
8129 rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff);
8130 rebaseHistSelSingle(sub.ranges[j].head, from, to, diff);
8131 }
8132 continue;
8133 }
8134 for (var j = 0; j < sub.changes.length; ++j) {
8135 var cur = sub.changes[j];
8136 if (to < cur.from.line) {
8137 cur.from = Pos(cur.from.line + diff, cur.from.ch);
8138 cur.to = Pos(cur.to.line + diff, cur.to.ch);
8139 } else if (from <= cur.to.line) {
8140 ok = false;
8141 break;
8142 }
8143 }
8144 if (!ok) {
8145 array.splice(0, i + 1);
8146 i = 0;
8147 }
8148 }
8149 }
8150
8151 function rebaseHist(hist, change) {
8152 var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1;
8153 rebaseHistArray(hist.done, from, to, diff);
8154 rebaseHistArray(hist.undone, from, to, diff);
8155 }
8156
8157 // EVENT UTILITIES
8158
8159 // Due to the fact that we still support jurassic IE versions, some
8160 // compatibility wrappers are needed.
8161
8162 var e_preventDefault = CodeMirror.e_preventDefault = function(e) {
8163 if (e.preventDefault) e.preventDefault();
8164 else e.returnValue = false;
8165 };
8166 var e_stopPropagation = CodeMirror.e_stopPropagation = function(e) {
8167 if (e.stopPropagation) e.stopPropagation();
8168 else e.cancelBubble = true;
8169 };
8170 function e_defaultPrevented(e) {
8171 return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == fa lse;
8172 }
8173 var e_stop = CodeMirror.e_stop = function(e) {e_preventDefault(e); e_stopPropa gation(e);};
8174
8175 function e_target(e) {return e.target || e.srcElement;}
8176 function e_button(e) {
8177 var b = e.which;
8178 if (b == null) {
8179 if (e.button & 1) b = 1;
8180 else if (e.button & 2) b = 3;
8181 else if (e.button & 4) b = 2;
8182 }
8183 if (mac && e.ctrlKey && b == 1) b = 3;
8184 return b;
8185 }
8186
8187 // EVENT HANDLING
8188
8189 // Lightweight event framework. on/off also work on DOM nodes,
8190 // registering native DOM handlers.
8191
8192 var on = CodeMirror.on = function(emitter, type, f) {
8193 if (emitter.addEventListener)
8194 emitter.addEventListener(type, f, false);
8195 else if (emitter.attachEvent)
8196 emitter.attachEvent("on" + type, f);
8197 else {
8198 var map = emitter._handlers || (emitter._handlers = {});
8199 var arr = map[type] || (map[type] = []);
8200 arr.push(f);
8201 }
8202 };
8203
8204 var noHandlers = []
8205 function getHandlers(emitter, type, copy) {
8206 var arr = emitter._handlers && emitter._handlers[type]
8207 if (copy) return arr && arr.length > 0 ? arr.slice() : noHandlers
8208 else return arr || noHandlers
8209 }
8210
8211 var off = CodeMirror.off = function(emitter, type, f) {
8212 if (emitter.removeEventListener)
8213 emitter.removeEventListener(type, f, false);
8214 else if (emitter.detachEvent)
8215 emitter.detachEvent("on" + type, f);
8216 else {
8217 var handlers = getHandlers(emitter, type, false)
8218 for (var i = 0; i < handlers.length; ++i)
8219 if (handlers[i] == f) { handlers.splice(i, 1); break; }
8220 }
8221 };
8222
8223 var signal = CodeMirror.signal = function(emitter, type /*, values...*/) {
8224 var handlers = getHandlers(emitter, type, true)
8225 if (!handlers.length) return;
8226 var args = Array.prototype.slice.call(arguments, 2);
8227 for (var i = 0; i < handlers.length; ++i) handlers[i].apply(null, args);
8228 };
8229
8230 var orphanDelayedCallbacks = null;
8231
8232 // Often, we want to signal events at a point where we are in the
8233 // middle of some work, but don't want the handler to start calling
8234 // other methods on the editor, which might be in an inconsistent
8235 // state or simply not expect any other events to happen.
8236 // signalLater looks whether there are any handlers, and schedules
8237 // them to be executed when the last operation ends, or, if no
8238 // operation is active, when a timeout fires.
8239 function signalLater(emitter, type /*, values...*/) {
8240 var arr = getHandlers(emitter, type, false)
8241 if (!arr.length) return;
8242 var args = Array.prototype.slice.call(arguments, 2), list;
8243 if (operationGroup) {
8244 list = operationGroup.delayedCallbacks;
8245 } else if (orphanDelayedCallbacks) {
8246 list = orphanDelayedCallbacks;
8247 } else {
8248 list = orphanDelayedCallbacks = [];
8249 setTimeout(fireOrphanDelayed, 0);
8250 }
8251 function bnd(f) {return function(){f.apply(null, args);};};
8252 for (var i = 0; i < arr.length; ++i)
8253 list.push(bnd(arr[i]));
8254 }
8255
8256 function fireOrphanDelayed() {
8257 var delayed = orphanDelayedCallbacks;
8258 orphanDelayedCallbacks = null;
8259 for (var i = 0; i < delayed.length; ++i) delayed[i]();
8260 }
8261
8262 // The DOM events that CodeMirror handles can be overridden by
8263 // registering a (non-DOM) handler on the editor for the event name,
8264 // and preventDefault-ing the event in that handler.
8265 function signalDOMEvent(cm, e, override) {
8266 if (typeof e == "string")
8267 e = {type: e, preventDefault: function() { this.defaultPrevented = true; } };
8268 signal(cm, override || e.type, cm, e);
8269 return e_defaultPrevented(e) || e.codemirrorIgnore;
8270 }
8271
8272 function signalCursorActivity(cm) {
8273 var arr = cm._handlers && cm._handlers.cursorActivity;
8274 if (!arr) return;
8275 var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandler s = []);
8276 for (var i = 0; i < arr.length; ++i) if (indexOf(set, arr[i]) == -1)
8277 set.push(arr[i]);
8278 }
8279
8280 function hasHandler(emitter, type) {
8281 return getHandlers(emitter, type).length > 0
8282 }
8283
8284 // Add on and off methods to a constructor's prototype, to make
8285 // registering events on such objects more convenient.
8286 function eventMixin(ctor) {
8287 ctor.prototype.on = function(type, f) {on(this, type, f);};
8288 ctor.prototype.off = function(type, f) {off(this, type, f);};
8289 }
8290
8291 // MISC UTILITIES
8292
8293 // Number of pixels added to scroller and sizer to hide scrollbar
8294 var scrollerGap = 30;
8295
8296 // Returned or thrown by various protocols to signal 'I'm not
8297 // handling this'.
8298 var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}} ;
8299
8300 // Reused option objects for setSelection & friends
8301 var sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"};
8302
8303 function Delayed() {this.id = null;}
8304 Delayed.prototype.set = function(ms, f) {
8305 clearTimeout(this.id);
8306 this.id = setTimeout(f, ms);
8307 };
8308
8309 // Counts the column offset in a string, taking tabs into account.
8310 // Used mostly to find indentation.
8311 var countColumn = CodeMirror.countColumn = function(string, end, tabSize, star tIndex, startValue) {
8312 if (end == null) {
8313 end = string.search(/[^\s\u00a0]/);
8314 if (end == -1) end = string.length;
8315 }
8316 for (var i = startIndex || 0, n = startValue || 0;;) {
8317 var nextTab = string.indexOf("\t", i);
8318 if (nextTab < 0 || nextTab >= end)
8319 return n + (end - i);
8320 n += nextTab - i;
8321 n += tabSize - (n % tabSize);
8322 i = nextTab + 1;
8323 }
8324 };
8325
8326 // The inverse of countColumn -- find the offset that corresponds to
8327 // a particular column.
8328 var findColumn = CodeMirror.findColumn = function(string, goal, tabSize) {
8329 for (var pos = 0, col = 0;;) {
8330 var nextTab = string.indexOf("\t", pos);
8331 if (nextTab == -1) nextTab = string.length;
8332 var skipped = nextTab - pos;
8333 if (nextTab == string.length || col + skipped >= goal)
8334 return pos + Math.min(skipped, goal - col);
8335 col += nextTab - pos;
8336 col += tabSize - (col % tabSize);
8337 pos = nextTab + 1;
8338 if (col >= goal) return pos;
8339 }
8340 }
8341
8342 var spaceStrs = [""];
8343 function spaceStr(n) {
8344 while (spaceStrs.length <= n)
8345 spaceStrs.push(lst(spaceStrs) + " ");
8346 return spaceStrs[n];
8347 }
8348
8349 function lst(arr) { return arr[arr.length-1]; }
8350
8351 var selectInput = function(node) { node.select(); };
8352 if (ios) // Mobile Safari apparently has a bug where select() is broken.
8353 selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; };
8354 else if (ie) // Suppress mysterious IE10 errors
8355 selectInput = function(node) { try { node.select(); } catch(_e) {} };
8356
8357 function indexOf(array, elt) {
8358 for (var i = 0; i < array.length; ++i)
8359 if (array[i] == elt) return i;
8360 return -1;
8361 }
8362 function map(array, f) {
8363 var out = [];
8364 for (var i = 0; i < array.length; i++) out[i] = f(array[i], i);
8365 return out;
8366 }
8367
8368 function nothing() {}
8369
8370 function createObj(base, props) {
8371 var inst;
8372 if (Object.create) {
8373 inst = Object.create(base);
8374 } else {
8375 nothing.prototype = base;
8376 inst = new nothing();
8377 }
8378 if (props) copyObj(props, inst);
8379 return inst;
8380 };
8381
8382 function copyObj(obj, target, overwrite) {
8383 if (!target) target = {};
8384 for (var prop in obj)
8385 if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProp erty(prop)))
8386 target[prop] = obj[prop];
8387 return target;
8388 }
8389
8390 function bind(f) {
8391 var args = Array.prototype.slice.call(arguments, 1);
8392 return function(){return f.apply(null, args);};
8393 }
8394
8395 var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u304 0-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
8396 var isWordCharBasic = CodeMirror.isWordChar = function(ch) {
8397 return /\w/.test(ch) || ch > "\x80" &&
8398 (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(c h));
8399 };
8400 function isWordChar(ch, helper) {
8401 if (!helper) return isWordCharBasic(ch);
8402 if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) return true;
8403 return helper.test(ch);
8404 }
8405
8406 function isEmpty(obj) {
8407 for (var n in obj) if (obj.hasOwnProperty(n) && obj[n]) return false;
8408 return true;
8409 }
8410
8411 // Extending unicode characters. A series of a non-extending char +
8412 // any number of extending chars is treated as a single unit as far
8413 // as editing and measuring is concerned. This is not fully correct,
8414 // since some scripts/fonts/browsers also treat other configurations
8415 // of code points as a group.
8416 var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05 c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u 06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u081 9\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u 0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u 0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0 a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b 3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3 e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0c c6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d6 3\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0e b4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0 f80-\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-\u17b d\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u19 39-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u 1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2- \u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1c e8\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\ua9b 3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab 2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe0 0-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/;
8417 function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChar s.test(ch); }
8418
8419 // DOM UTILITIES
8420
8421 function elt(tag, content, className, style) {
8422 var e = document.createElement(tag);
8423 if (className) e.className = className;
8424 if (style) e.style.cssText = style;
8425 if (typeof content == "string") e.appendChild(document.createTextNode(conten t));
8426 else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(con tent[i]);
8427 return e;
8428 }
8429
8430 var range;
8431 if (document.createRange) range = function(node, start, end, endNode) {
8432 var r = document.createRange();
8433 r.setEnd(endNode || node, end);
8434 r.setStart(node, start);
8435 return r;
8436 };
8437 else range = function(node, start, end) {
8438 var r = document.body.createTextRange();
8439 try { r.moveToElementText(node.parentNode); }
8440 catch(e) { return r; }
8441 r.collapse(true);
8442 r.moveEnd("character", end);
8443 r.moveStart("character", start);
8444 return r;
8445 };
8446
8447 function removeChildren(e) {
8448 for (var count = e.childNodes.length; count > 0; --count)
8449 e.removeChild(e.firstChild);
8450 return e;
8451 }
8452
8453 function removeChildrenAndAdd(parent, e) {
8454 return removeChildren(parent).appendChild(e);
8455 }
8456
8457 var contains = CodeMirror.contains = function(parent, child) {
8458 if (child.nodeType == 3) // Android browser always returns false when child is a textnode
8459 child = child.parentNode;
8460 if (parent.contains)
8461 return parent.contains(child);
8462 do {
8463 if (child.nodeType == 11) child = child.host;
8464 if (child == parent) return true;
8465 } while (child = child.parentNode);
8466 };
8467
8468 function activeElt() {
8469 var activeElement = document.activeElement;
8470 while (activeElement && activeElement.root && activeElement.root.activeEleme nt)
8471 activeElement = activeElement.root.activeElement;
8472 return activeElement;
8473 }
8474 // Older versions of IE throws unspecified error when touching
8475 // document.activeElement in some cases (during loading, in iframe)
8476 if (ie && ie_version < 11) activeElt = function() {
8477 try { return document.activeElement; }
8478 catch(e) { return document.body; }
8479 };
8480
8481 function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") ; }
8482 var rmClass = CodeMirror.rmClass = function(node, cls) {
8483 var current = node.className;
8484 var match = classTest(cls).exec(current);
8485 if (match) {
8486 var after = current.slice(match.index + match[0].length);
8487 node.className = current.slice(0, match.index) + (after ? match[1] + after : "");
8488 }
8489 };
8490 var addClass = CodeMirror.addClass = function(node, cls) {
8491 var current = node.className;
8492 if (!classTest(cls).test(current)) node.className += (current ? " " : "") + cls;
8493 };
8494 function joinClasses(a, b) {
8495 var as = a.split(" ");
8496 for (var i = 0; i < as.length; i++)
8497 if (as[i] && !classTest(as[i]).test(b)) b += " " + as[i];
8498 return b;
8499 }
8500
8501 // WINDOW-WIDE EVENTS
8502
8503 // These must be handled carefully, because naively registering a
8504 // handler for each editor will cause the editors to never be
8505 // garbage collected.
8506
8507 function forEachCodeMirror(f) {
8508 if (!document.body.getElementsByClassName) return;
8509 var byClass = document.body.getElementsByClassName("CodeMirror");
8510 for (var i = 0; i < byClass.length; i++) {
8511 var cm = byClass[i].CodeMirror;
8512 if (cm) f(cm);
8513 }
8514 }
8515
8516 var globalsRegistered = false;
8517 function ensureGlobalHandlers() {
8518 if (globalsRegistered) return;
8519 registerGlobalHandlers();
8520 globalsRegistered = true;
8521 }
8522 function registerGlobalHandlers() {
8523 // When the window resizes, we need to refresh active editors.
8524 var resizeTimer;
8525 on(window, "resize", function() {
8526 if (resizeTimer == null) resizeTimer = setTimeout(function() {
8527 resizeTimer = null;
8528 forEachCodeMirror(onResize);
8529 }, 100);
8530 });
8531 // When the window loses focus, we want to show the editor as blurred
8532 on(window, "blur", function() {
8533 forEachCodeMirror(onBlur);
8534 });
8535 }
8536
8537 // FEATURE DETECTION
8538
8539 // Detect drag-and-drop
8540 var dragAndDrop = function() {
8541 // There is *some* kind of drag-and-drop support in IE6-8, but I
8542 // couldn't get it to work yet.
8543 if (ie && ie_version < 9) return false;
8544 var div = elt('div');
8545 return "draggable" in div || "dragDrop" in div;
8546 }();
8547
8548 var zwspSupported;
8549 function zeroWidthElement(measure) {
8550 if (zwspSupported == null) {
8551 var test = elt("span", "\u200b");
8552 removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode(" x")]));
8553 if (measure.firstChild.offsetHeight != 0)
8554 zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie & & ie_version < 8);
8555 }
8556 var node = zwspSupported ? elt("span", "\u200b") :
8557 elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-rig ht: -1px");
8558 node.setAttribute("cm-text", "");
8559 return node;
8560 }
8561
8562 // Feature-detect IE's crummy client rect reporting for bidi text
8563 var badBidiRects;
8564 function hasBadBidiRects(measure) {
8565 if (badBidiRects != null) return badBidiRects;
8566 var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA")) ;
8567 var r0 = range(txt, 0, 1).getBoundingClientRect();
8568 var r1 = range(txt, 1, 2).getBoundingClientRect();
8569 removeChildren(measure);
8570 if (!r0 || r0.left == r0.right) return false; // Safari returns null in some cases (#2780)
8571 return badBidiRects = (r1.right - r0.right < 3);
8572 }
8573
8574 // See if "".split is the broken IE version, if so, provide an
8575 // alternative way to split lines.
8576 var splitLinesAuto = CodeMirror.splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) {
8577 var pos = 0, result = [], l = string.length;
8578 while (pos <= l) {
8579 var nl = string.indexOf("\n", pos);
8580 if (nl == -1) nl = string.length;
8581 var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
8582 var rt = line.indexOf("\r");
8583 if (rt != -1) {
8584 result.push(line.slice(0, rt));
8585 pos += rt + 1;
8586 } else {
8587 result.push(line);
8588 pos = nl + 1;
8589 }
8590 }
8591 return result;
8592 } : function(string){return string.split(/\r\n?|\n/);};
8593
8594 var hasSelection = window.getSelection ? function(te) {
8595 try { return te.selectionStart != te.selectionEnd; }
8596 catch(e) { return false; }
8597 } : function(te) {
8598 try {var range = te.ownerDocument.selection.createRange();}
8599 catch(e) {}
8600 if (!range || range.parentElement() != te) return false;
8601 return range.compareEndPoints("StartToEnd", range) != 0;
8602 };
8603
8604 var hasCopyEvent = (function() {
8605 var e = elt("div");
8606 if ("oncopy" in e) return true;
8607 e.setAttribute("oncopy", "return;");
8608 return typeof e.oncopy == "function";
8609 })();
8610
8611 var badZoomedRects = null;
8612 function hasBadZoomedRects(measure) {
8613 if (badZoomedRects != null) return badZoomedRects;
8614 var node = removeChildrenAndAdd(measure, elt("span", "x"));
8615 var normal = node.getBoundingClientRect();
8616 var fromRange = range(node, 0, 1).getBoundingClientRect();
8617 return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1;
8618 }
8619
8620 // KEY NAMES
8621
8622 var keyNames = CodeMirror.keyNames = {
8623 3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
8624 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "Page Down", 35: "End",
8625 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
8626 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod",
8627 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 127: "Delete",
8628 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: " `", 219: "[", 220: "\\",
8629 221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right ", 63272: "Delete",
8630 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Ins ert"
8631 };
8632 (function() {
8633 // Number keys
8634 for (var i = 0; i < 10; i++) keyNames[i + 48] = keyNames[i + 96] = String(i) ;
8635 // Alphabetic keys
8636 for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i);
8637 // Function keys
8638 for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i;
8639 })();
8640
8641 // BIDI HELPERS
8642
8643 function iterateBidiSections(order, from, to, f) {
8644 if (!order) return f(from, to, "ltr");
8645 var found = false;
8646 for (var i = 0; i < order.length; ++i) {
8647 var part = order[i];
8648 if (part.from < to && part.to > from || from == to && part.to == from) {
8649 f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "r tl" : "ltr");
8650 found = true;
8651 }
8652 }
8653 if (!found) f(from, to, "ltr");
8654 }
8655
8656 function bidiLeft(part) { return part.level % 2 ? part.to : part.from; }
8657 function bidiRight(part) { return part.level % 2 ? part.from : part.to; }
8658
8659 function lineLeft(line) { var order = getOrder(line); return order ? bidiLeft( order[0]) : 0; }
8660 function lineRight(line) {
8661 var order = getOrder(line);
8662 if (!order) return line.text.length;
8663 return bidiRight(lst(order));
8664 }
8665
8666 function lineStart(cm, lineN) {
8667 var line = getLine(cm.doc, lineN);
8668 var visual = visualLine(line);
8669 if (visual != line) lineN = lineNo(visual);
8670 var order = getOrder(visual);
8671 var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visu al);
8672 return Pos(lineN, ch);
8673 }
8674 function lineEnd(cm, lineN) {
8675 var merged, line = getLine(cm.doc, lineN);
8676 while (merged = collapsedSpanAtEnd(line)) {
8677 line = merged.find(1, true).line;
8678 lineN = null;
8679 }
8680 var order = getOrder(line);
8681 var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : l ineRight(line);
8682 return Pos(lineN == null ? lineNo(line) : lineN, ch);
8683 }
8684 function lineStartSmart(cm, pos) {
8685 var start = lineStart(cm, pos.line);
8686 var line = getLine(cm.doc, start.line);
8687 var order = getOrder(line);
8688 if (!order || order[0].level == 0) {
8689 var firstNonWS = Math.max(0, line.text.search(/\S/));
8690 var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch;
8691 return Pos(start.line, inWS ? 0 : firstNonWS);
8692 }
8693 return start;
8694 }
8695
8696 function compareBidiLevel(order, a, b) {
8697 var linedir = order[0].level;
8698 if (a == linedir) return true;
8699 if (b == linedir) return false;
8700 return a < b;
8701 }
8702 var bidiOther;
8703 function getBidiPartAt(order, pos) {
8704 bidiOther = null;
8705 for (var i = 0, found; i < order.length; ++i) {
8706 var cur = order[i];
8707 if (cur.from < pos && cur.to > pos) return i;
8708 if ((cur.from == pos || cur.to == pos)) {
8709 if (found == null) {
8710 found = i;
8711 } else if (compareBidiLevel(order, cur.level, order[found].level)) {
8712 if (cur.from != cur.to) bidiOther = found;
8713 return i;
8714 } else {
8715 if (cur.from != cur.to) bidiOther = i;
8716 return found;
8717 }
8718 }
8719 }
8720 return found;
8721 }
8722
8723 function moveInLine(line, pos, dir, byUnit) {
8724 if (!byUnit) return pos + dir;
8725 do pos += dir;
8726 while (pos > 0 && isExtendingChar(line.text.charAt(pos)));
8727 return pos;
8728 }
8729
8730 // This is needed in order to move 'visually' through bi-directional
8731 // text -- i.e., pressing left should make the cursor go left, even
8732 // when in RTL text. The tricky part is the 'jumps', where RTL and
8733 // LTR text touch each other. This often requires the cursor offset
8734 // to move more than one unit, in order to visually move one unit.
8735 function moveVisually(line, start, dir, byUnit) {
8736 var bidi = getOrder(line);
8737 if (!bidi) return moveLogically(line, start, dir, byUnit);
8738 var pos = getBidiPartAt(bidi, start), part = bidi[pos];
8739 var target = moveInLine(line, start, part.level % 2 ? -dir : dir, byUnit);
8740
8741 for (;;) {
8742 if (target > part.from && target < part.to) return target;
8743 if (target == part.from || target == part.to) {
8744 if (getBidiPartAt(bidi, target) == pos) return target;
8745 part = bidi[pos += dir];
8746 return (dir > 0) == part.level % 2 ? part.to : part.from;
8747 } else {
8748 part = bidi[pos += dir];
8749 if (!part) return null;
8750 if ((dir > 0) == part.level % 2)
8751 target = moveInLine(line, part.to, -1, byUnit);
8752 else
8753 target = moveInLine(line, part.from, 1, byUnit);
8754 }
8755 }
8756 }
8757
8758 function moveLogically(line, start, dir, byUnit) {
8759 var target = start + dir;
8760 if (byUnit) while (target > 0 && isExtendingChar(line.text.charAt(target))) target += dir;
8761 return target < 0 || target > line.text.length ? null : target;
8762 }
8763
8764 // Bidirectional ordering algorithm
8765 // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
8766 // that this (partially) implements.
8767
8768 // One-char codes used for character types:
8769 // L (L): Left-to-Right
8770 // R (R): Right-to-Left
8771 // r (AL): Right-to-Left Arabic
8772 // 1 (EN): European Number
8773 // + (ES): European Number Separator
8774 // % (ET): European Number Terminator
8775 // n (AN): Arabic Number
8776 // , (CS): Common Number Separator
8777 // m (NSM): Non-Spacing Mark
8778 // b (BN): Boundary Neutral
8779 // s (B): Paragraph Separator
8780 // t (S): Segment Separator
8781 // w (WS): Whitespace
8782 // N (ON): Other Neutrals
8783
8784 // Returns null if characters are ordered as they appear
8785 // (left-to-right), or an array of sections ({from, to, level}
8786 // objects) in the order in which they occur visually.
8787 var bidiOrdering = (function() {
8788 // Character types for codepoints 0 to 0xff
8789 var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NN NNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbb bbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLL LLLLLLLLLLLLLLLLLLLLLLLLLLLN";
8790 // Character types for codepoints 0x600 to 0x6ff
8791 var arabicTypes = "rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr rrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrr rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmm mmmmmmmmmmmmmmmmNmmmm";
8792 function charType(code) {
8793 if (code <= 0xf7) return lowTypes.charAt(code);
8794 else if (0x590 <= code && code <= 0x5f4) return "R";
8795 else if (0x600 <= code && code <= 0x6ed) return arabicTypes.charAt(code - 0x600);
8796 else if (0x6ee <= code && code <= 0x8ac) return "r";
8797 else if (0x2000 <= code && code <= 0x200b) return "w";
8798 else if (code == 0x200c) return "b";
8799 else return "L";
8800 }
8801
8802 var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
8803 var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, count sAsNum = /[1n]/;
8804 // Browsers seem to always treat the boundaries of block elements as being L .
8805 var outerType = "L";
8806
8807 function BidiSpan(level, from, to) {
8808 this.level = level;
8809 this.from = from; this.to = to;
8810 }
8811
8812 return function(str) {
8813 if (!bidiRE.test(str)) return false;
8814 var len = str.length, types = [];
8815 for (var i = 0, type; i < len; ++i)
8816 types.push(type = charType(str.charCodeAt(i)));
8817
8818 // W1. Examine each non-spacing mark (NSM) in the level run, and
8819 // change the type of the NSM to the type of the previous
8820 // character. If the NSM is at the start of the level run, it will
8821 // get the type of sor.
8822 for (var i = 0, prev = outerType; i < len; ++i) {
8823 var type = types[i];
8824 if (type == "m") types[i] = prev;
8825 else prev = type;
8826 }
8827
8828 // W2. Search backwards from each instance of a European number
8829 // until the first strong type (R, L, AL, or sor) is found. If an
8830 // AL is found, change the type of the European number to Arabic
8831 // number.
8832 // W3. Change all ALs to R.
8833 for (var i = 0, cur = outerType; i < len; ++i) {
8834 var type = types[i];
8835 if (type == "1" && cur == "r") types[i] = "n";
8836 else if (isStrong.test(type)) { cur = type; if (type == "r") types[i] = "R"; }
8837 }
8838
8839 // W4. A single European separator between two European numbers
8840 // changes to a European number. A single common separator between
8841 // two numbers of the same type changes to that type.
8842 for (var i = 1, prev = types[0]; i < len - 1; ++i) {
8843 var type = types[i];
8844 if (type == "+" && prev == "1" && types[i+1] == "1") types[i] = "1";
8845 else if (type == "," && prev == types[i+1] &&
8846 (prev == "1" || prev == "n")) types[i] = prev;
8847 prev = type;
8848 }
8849
8850 // W5. A sequence of European terminators adjacent to European
8851 // numbers changes to all European numbers.
8852 // W6. Otherwise, separators and terminators change to Other
8853 // Neutral.
8854 for (var i = 0; i < len; ++i) {
8855 var type = types[i];
8856 if (type == ",") types[i] = "N";
8857 else if (type == "%") {
8858 for (var end = i + 1; end < len && types[end] == "%"; ++end) {}
8859 var replace = (i && types[i-1] == "!") || (end < len && types[end] == "1") ? "1" : "N";
8860 for (var j = i; j < end; ++j) types[j] = replace;
8861 i = end - 1;
8862 }
8863 }
8864
8865 // W7. Search backwards from each instance of a European number
8866 // until the first strong type (R, L, or sor) is found. If an L is
8867 // found, then change the type of the European number to L.
8868 for (var i = 0, cur = outerType; i < len; ++i) {
8869 var type = types[i];
8870 if (cur == "L" && type == "1") types[i] = "L";
8871 else if (isStrong.test(type)) cur = type;
8872 }
8873
8874 // N1. A sequence of neutrals takes the direction of the
8875 // surrounding strong text if the text on both sides has the same
8876 // direction. European and Arabic numbers act as if they were R in
8877 // terms of their influence on neutrals. Start-of-level-run (sor)
8878 // and end-of-level-run (eor) are used at level run boundaries.
8879 // N2. Any remaining neutrals take the embedding direction.
8880 for (var i = 0; i < len; ++i) {
8881 if (isNeutral.test(types[i])) {
8882 for (var end = i + 1; end < len && isNeutral.test(types[end]); ++end) {}
8883 var before = (i ? types[i-1] : outerType) == "L";
8884 var after = (end < len ? types[end] : outerType) == "L";
8885 var replace = before || after ? "L" : "R";
8886 for (var j = i; j < end; ++j) types[j] = replace;
8887 i = end - 1;
8888 }
8889 }
8890
8891 // Here we depart from the documented algorithm, in order to avoid
8892 // building up an actual levels array. Since there are only three
8893 // levels (0, 1, 2) in an implementation that doesn't take
8894 // explicit embedding into account, we can build up the order on
8895 // the fly, without following the level-based algorithm.
8896 var order = [], m;
8897 for (var i = 0; i < len;) {
8898 if (countsAsLeft.test(types[i])) {
8899 var start = i;
8900 for (++i; i < len && countsAsLeft.test(types[i]); ++i) {}
8901 order.push(new BidiSpan(0, start, i));
8902 } else {
8903 var pos = i, at = order.length;
8904 for (++i; i < len && types[i] != "L"; ++i) {}
8905 for (var j = pos; j < i;) {
8906 if (countsAsNum.test(types[j])) {
8907 if (pos < j) order.splice(at, 0, new BidiSpan(1, pos, j));
8908 var nstart = j;
8909 for (++j; j < i && countsAsNum.test(types[j]); ++j) {}
8910 order.splice(at, 0, new BidiSpan(2, nstart, j));
8911 pos = j;
8912 } else ++j;
8913 }
8914 if (pos < i) order.splice(at, 0, new BidiSpan(1, pos, i));
8915 }
8916 }
8917 if (order[0].level == 1 && (m = str.match(/^\s+/))) {
8918 order[0].from = m[0].length;
8919 order.unshift(new BidiSpan(0, 0, m[0].length));
8920 }
8921 if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
8922 lst(order).to -= m[0].length;
8923 order.push(new BidiSpan(0, len - m[0].length, len));
8924 }
8925 if (order[0].level == 2)
8926 order.unshift(new BidiSpan(1, order[0].to, order[0].to));
8927 if (order[0].level != lst(order).level)
8928 order.push(new BidiSpan(order[0].level, len, len));
8929
8930 return order;
8931 };
8932 })();
8933
8934 // THE END
8935
8936 CodeMirror.version = "5.17.1";
8937
8938 return CodeMirror;
8939 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698