WP Add Custom CSS - Version 1.1.0

Version Description

  • Added CodeMirror text editor
Download this release

Release Info

Developer danieledesantis
Plugin Icon 128x128 WP Add Custom CSS
Version 1.1.0
Comparing to
See all releases

Code changes from version 1.0.1 to 1.1.0

js/scripts.js ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ (function ($) {
2
+ 'use strict';
3
+
4
+ $(document).ready(function(){
5
+ var targetId = "single_custom_css",
6
+ options = {
7
+ lineNumbers: true,
8
+ mode: "css",
9
+ tabSize: 2, // default 4
10
+ //theme: "tomorrow-night-bright"
11
+ };
12
+ if ( $('.wpacc_editor_dark').length > 0 )
13
+ options.theme = "tomorrow-night-bright";
14
+ if ( $('#main_custom_css').length > 0 )
15
+ targetId = "main_custom_css";
16
+ var cssCodeMirror = CodeMirror.fromTextArea(document.getElementById(targetId), options);
17
+ });
18
+
19
+ $(window).on('load', function() {
20
+
21
+ });
22
+
23
+ })(jQuery);
lib/codemirror/addon/hint/css-hint.js ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // CodeMirror, copyright (c) by Marijn Haverbeke and others
2
+ // Distributed under an MIT license: http://codemirror.net/LICENSE
3
+
4
+ (function(mod) {
5
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
6
+ mod(require("../../lib/codemirror"), require("../../mode/css/css"));
7
+ else if (typeof define == "function" && define.amd) // AMD
8
+ define(["../../lib/codemirror", "../../mode/css/css"], mod);
9
+ else // Plain browser env
10
+ mod(CodeMirror);
11
+ })(function(CodeMirror) {
12
+ "use strict";
13
+
14
+ var pseudoClasses = {link: 1, visited: 1, active: 1, hover: 1, focus: 1,
15
+ "first-letter": 1, "first-line": 1, "first-child": 1,
16
+ before: 1, after: 1, lang: 1};
17
+
18
+ CodeMirror.registerHelper("hint", "css", function(cm) {
19
+ var cur = cm.getCursor(), token = cm.getTokenAt(cur);
20
+ var inner = CodeMirror.innerMode(cm.getMode(), token.state);
21
+ if (inner.mode.name != "css") return;
22
+
23
+ if (token.type == "keyword" && "!important".indexOf(token.string) == 0)
24
+ return {list: ["!important"], from: CodeMirror.Pos(cur.line, token.start),
25
+ to: CodeMirror.Pos(cur.line, token.end)};
26
+
27
+ var start = token.start, end = cur.ch, word = token.string.slice(0, end - start);
28
+ if (/[^\w$_-]/.test(word)) {
29
+ word = ""; start = end = cur.ch;
30
+ }
31
+
32
+ var spec = CodeMirror.resolveMode("text/css");
33
+
34
+ var result = [];
35
+ function add(keywords) {
36
+ for (var name in keywords)
37
+ if (!word || name.lastIndexOf(word, 0) == 0)
38
+ result.push(name);
39
+ }
40
+
41
+ var st = inner.state.state;
42
+ if (st == "pseudo" || token.type == "variable-3") {
43
+ add(pseudoClasses);
44
+ } else if (st == "block" || st == "maybeprop") {
45
+ add(spec.propertyKeywords);
46
+ } else if (st == "prop" || st == "parens" || st == "at" || st == "params") {
47
+ add(spec.valueKeywords);
48
+ add(spec.colorKeywords);
49
+ } else if (st == "media" || st == "media_parens") {
50
+ add(spec.mediaTypes);
51
+ add(spec.mediaFeatures);
52
+ }
53
+
54
+ if (result.length) return {
55
+ list: result,
56
+ from: CodeMirror.Pos(cur.line, start),
57
+ to: CodeMirror.Pos(cur.line, end)
58
+ };
59
+ });
60
+ });
lib/codemirror/addon/hint/show-hint.css ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .CodeMirror-hints {
2
+ position: absolute;
3
+ z-index: 10;
4
+ overflow: hidden;
5
+ list-style: none;
6
+
7
+ margin: 0;
8
+ padding: 2px;
9
+
10
+ -webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
11
+ -moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
12
+ box-shadow: 2px 3px 5px rgba(0,0,0,.2);
13
+ border-radius: 3px;
14
+ border: 1px solid silver;
15
+
16
+ background: white;
17
+ font-size: 90%;
18
+ font-family: monospace;
19
+
20
+ max-height: 20em;
21
+ overflow-y: auto;
22
+ }
23
+
24
+ .CodeMirror-hint {
25
+ margin: 0;
26
+ padding: 0 4px;
27
+ border-radius: 2px;
28
+ white-space: pre;
29
+ color: black;
30
+ cursor: pointer;
31
+ }
32
+
33
+ li.CodeMirror-hint-active {
34
+ background: #08f;
35
+ color: white;
36
+ }
lib/codemirror/addon/hint/show-hint.js ADDED
@@ -0,0 +1,438 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // CodeMirror, copyright (c) by Marijn Haverbeke and others
2
+ // Distributed under an MIT license: http://codemirror.net/LICENSE
3
+
4
+ (function(mod) {
5
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
6
+ mod(require("../../lib/codemirror"));
7
+ else if (typeof define == "function" && define.amd) // AMD
8
+ define(["../../lib/codemirror"], mod);
9
+ else // Plain browser env
10
+ mod(CodeMirror);
11
+ })(function(CodeMirror) {
12
+ "use strict";
13
+
14
+ var HINT_ELEMENT_CLASS = "CodeMirror-hint";
15
+ var ACTIVE_HINT_ELEMENT_CLASS = "CodeMirror-hint-active";
16
+
17
+ // This is the old interface, kept around for now to stay
18
+ // backwards-compatible.
19
+ CodeMirror.showHint = function(cm, getHints, options) {
20
+ if (!getHints) return cm.showHint(options);
21
+ if (options && options.async) getHints.async = true;
22
+ var newOpts = {hint: getHints};
23
+ if (options) for (var prop in options) newOpts[prop] = options[prop];
24
+ return cm.showHint(newOpts);
25
+ };
26
+
27
+ CodeMirror.defineExtension("showHint", function(options) {
28
+ options = parseOptions(this, this.getCursor("start"), options);
29
+ var selections = this.listSelections()
30
+ if (selections.length > 1) return;
31
+ // By default, don't allow completion when something is selected.
32
+ // A hint function can have a `supportsSelection` property to
33
+ // indicate that it can handle selections.
34
+ if (this.somethingSelected()) {
35
+ if (!options.hint.supportsSelection) return;
36
+ // Don't try with cross-line selections
37
+ for (var i = 0; i < selections.length; i++)
38
+ if (selections[i].head.line != selections[i].anchor.line) return;
39
+ }
40
+
41
+ if (this.state.completionActive) this.state.completionActive.close();
42
+ var completion = this.state.completionActive = new Completion(this, options);
43
+ if (!completion.options.hint) return;
44
+
45
+ CodeMirror.signal(this, "startCompletion", this);
46
+ completion.update(true);
47
+ });
48
+
49
+ function Completion(cm, options) {
50
+ this.cm = cm;
51
+ this.options = options;
52
+ this.widget = null;
53
+ this.debounce = 0;
54
+ this.tick = 0;
55
+ this.startPos = this.cm.getCursor("start");
56
+ this.startLen = this.cm.getLine(this.startPos.line).length - this.cm.getSelection().length;
57
+
58
+ var self = this;
59
+ cm.on("cursorActivity", this.activityFunc = function() { self.cursorActivity(); });
60
+ }
61
+
62
+ var requestAnimationFrame = window.requestAnimationFrame || function(fn) {
63
+ return setTimeout(fn, 1000/60);
64
+ };
65
+ var cancelAnimationFrame = window.cancelAnimationFrame || clearTimeout;
66
+
67
+ Completion.prototype = {
68
+ close: function() {
69
+ if (!this.active()) return;
70
+ this.cm.state.completionActive = null;
71
+ this.tick = null;
72
+ this.cm.off("cursorActivity", this.activityFunc);
73
+
74
+ if (this.widget && this.data) CodeMirror.signal(this.data, "close");
75
+ if (this.widget) this.widget.close();
76
+ CodeMirror.signal(this.cm, "endCompletion", this.cm);
77
+ },
78
+
79
+ active: function() {
80
+ return this.cm.state.completionActive == this;
81
+ },
82
+
83
+ pick: function(data, i) {
84
+ var completion = data.list[i];
85
+ if (completion.hint) completion.hint(this.cm, data, completion);
86
+ else this.cm.replaceRange(getText(completion), completion.from || data.from,
87
+ completion.to || data.to, "complete");
88
+ CodeMirror.signal(data, "pick", completion);
89
+ this.close();
90
+ },
91
+
92
+ cursorActivity: function() {
93
+ if (this.debounce) {
94
+ cancelAnimationFrame(this.debounce);
95
+ this.debounce = 0;
96
+ }
97
+
98
+ var pos = this.cm.getCursor(), line = this.cm.getLine(pos.line);
99
+ if (pos.line != this.startPos.line || line.length - pos.ch != this.startLen - this.startPos.ch ||
100
+ pos.ch < this.startPos.ch || this.cm.somethingSelected() ||
101
+ (pos.ch && this.options.closeCharacters.test(line.charAt(pos.ch - 1)))) {
102
+ this.close();
103
+ } else {
104
+ var self = this;
105
+ this.debounce = requestAnimationFrame(function() {self.update();});
106
+ if (this.widget) this.widget.disable();
107
+ }
108
+ },
109
+
110
+ update: function(first) {
111
+ if (this.tick == null) return
112
+ var self = this, myTick = ++this.tick
113
+ fetchHints(this.options.hint, this.cm, this.options, function(data) {
114
+ if (self.tick == myTick) self.finishUpdate(data, first)
115
+ })
116
+ },
117
+
118
+ finishUpdate: function(data, first) {
119
+ if (this.data) CodeMirror.signal(this.data, "update");
120
+
121
+ var picked = (this.widget && this.widget.picked) || (first && this.options.completeSingle);
122
+ if (this.widget) this.widget.close();
123
+
124
+ if (data && this.data && isNewCompletion(this.data, data)) return;
125
+ this.data = data;
126
+
127
+ if (data && data.list.length) {
128
+ if (picked && data.list.length == 1) {
129
+ this.pick(data, 0);
130
+ } else {
131
+ this.widget = new Widget(this, data);
132
+ CodeMirror.signal(data, "shown");
133
+ }
134
+ }
135
+ }
136
+ };
137
+
138
+ function isNewCompletion(old, nw) {
139
+ var moved = CodeMirror.cmpPos(nw.from, old.from)
140
+ return moved > 0 && old.to.ch - old.from.ch != nw.to.ch - nw.from.ch
141
+ }
142
+
143
+ function parseOptions(cm, pos, options) {
144
+ var editor = cm.options.hintOptions;
145
+ var out = {};
146
+ for (var prop in defaultOptions) out[prop] = defaultOptions[prop];
147
+ if (editor) for (var prop in editor)
148
+ if (editor[prop] !== undefined) out[prop] = editor[prop];
149
+ if (options) for (var prop in options)
150
+ if (options[prop] !== undefined) out[prop] = options[prop];
151
+ if (out.hint.resolve) out.hint = out.hint.resolve(cm, pos)
152
+ return out;
153
+ }
154
+
155
+ function getText(completion) {
156
+ if (typeof completion == "string") return completion;
157
+ else return completion.text;
158
+ }
159
+
160
+ function buildKeyMap(completion, handle) {
161
+ var baseMap = {
162
+ Up: function() {handle.moveFocus(-1);},
163
+ Down: function() {handle.moveFocus(1);},
164
+ PageUp: function() {handle.moveFocus(-handle.menuSize() + 1, true);},
165
+ PageDown: function() {handle.moveFocus(handle.menuSize() - 1, true);},
166
+ Home: function() {handle.setFocus(0);},
167
+ End: function() {handle.setFocus(handle.length - 1);},
168
+ Enter: handle.pick,
169
+ Tab: handle.pick,
170
+ Esc: handle.close
171
+ };
172
+ var custom = completion.options.customKeys;
173
+ var ourMap = custom ? {} : baseMap;
174
+ function addBinding(key, val) {
175
+ var bound;
176
+ if (typeof val != "string")
177
+ bound = function(cm) { return val(cm, handle); };
178
+ // This mechanism is deprecated
179
+ else if (baseMap.hasOwnProperty(val))
180
+ bound = baseMap[val];
181
+ else
182
+ bound = val;
183
+ ourMap[key] = bound;
184
+ }
185
+ if (custom)
186
+ for (var key in custom) if (custom.hasOwnProperty(key))
187
+ addBinding(key, custom[key]);
188
+ var extra = completion.options.extraKeys;
189
+ if (extra)
190
+ for (var key in extra) if (extra.hasOwnProperty(key))
191
+ addBinding(key, extra[key]);
192
+ return ourMap;
193
+ }
194
+
195
+ function getHintElement(hintsElement, el) {
196
+ while (el && el != hintsElement) {
197
+ if (el.nodeName.toUpperCase() === "LI" && el.parentNode == hintsElement) return el;
198
+ el = el.parentNode;
199
+ }
200
+ }
201
+
202
+ function Widget(completion, data) {
203
+ this.completion = completion;
204
+ this.data = data;
205
+ this.picked = false;
206
+ var widget = this, cm = completion.cm;
207
+
208
+ var hints = this.hints = document.createElement("ul");
209
+ hints.className = "CodeMirror-hints";
210
+ this.selectedHint = data.selectedHint || 0;
211
+
212
+ var completions = data.list;
213
+ for (var i = 0; i < completions.length; ++i) {
214
+ var elt = hints.appendChild(document.createElement("li")), cur = completions[i];
215
+ var className = HINT_ELEMENT_CLASS + (i != this.selectedHint ? "" : " " + ACTIVE_HINT_ELEMENT_CLASS);
216
+ if (cur.className != null) className = cur.className + " " + className;
217
+ elt.className = className;
218
+ if (cur.render) cur.render(elt, data, cur);
219
+ else elt.appendChild(document.createTextNode(cur.displayText || getText(cur)));
220
+ elt.hintId = i;
221
+ }
222
+
223
+ var pos = cm.cursorCoords(completion.options.alignWithWord ? data.from : null);
224
+ var left = pos.left, top = pos.bottom, below = true;
225
+ hints.style.left = left + "px";
226
+ hints.style.top = top + "px";
227
+ // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor.
228
+ var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth);
229
+ var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight);
230
+ (completion.options.container || document.body).appendChild(hints);
231
+ var box = hints.getBoundingClientRect(), overlapY = box.bottom - winH;
232
+ var scrolls = hints.scrollHeight > hints.clientHeight + 1
233
+ var startScroll = cm.getScrollInfo();
234
+
235
+ if (overlapY > 0) {
236
+ var height = box.bottom - box.top, curTop = pos.top - (pos.bottom - box.top);
237
+ if (curTop - height > 0) { // Fits above cursor
238
+ hints.style.top = (top = pos.top - height) + "px";
239
+ below = false;
240
+ } else if (height > winH) {
241
+ hints.style.height = (winH - 5) + "px";
242
+ hints.style.top = (top = pos.bottom - box.top) + "px";
243
+ var cursor = cm.getCursor();
244
+ if (data.from.ch != cursor.ch) {
245
+ pos = cm.cursorCoords(cursor);
246
+ hints.style.left = (left = pos.left) + "px";
247
+ box = hints.getBoundingClientRect();
248
+ }
249
+ }
250
+ }
251
+ var overlapX = box.right - winW;
252
+ if (overlapX > 0) {
253
+ if (box.right - box.left > winW) {
254
+ hints.style.width = (winW - 5) + "px";
255
+ overlapX -= (box.right - box.left) - winW;
256
+ }
257
+ hints.style.left = (left = pos.left - overlapX) + "px";
258
+ }
259
+ if (scrolls) for (var node = hints.firstChild; node; node = node.nextSibling)
260
+ node.style.paddingRight = cm.display.nativeBarWidth + "px"
261
+
262
+ cm.addKeyMap(this.keyMap = buildKeyMap(completion, {
263
+ moveFocus: function(n, avoidWrap) { widget.changeActive(widget.selectedHint + n, avoidWrap); },
264
+ setFocus: function(n) { widget.changeActive(n); },
265
+ menuSize: function() { return widget.screenAmount(); },
266
+ length: completions.length,
267
+ close: function() { completion.close(); },
268
+ pick: function() { widget.pick(); },
269
+ data: data
270
+ }));
271
+
272
+ if (completion.options.closeOnUnfocus) {
273
+ var closingOnBlur;
274
+ cm.on("blur", this.onBlur = function() { closingOnBlur = setTimeout(function() { completion.close(); }, 100); });
275
+ cm.on("focus", this.onFocus = function() { clearTimeout(closingOnBlur); });
276
+ }
277
+
278
+ cm.on("scroll", this.onScroll = function() {
279
+ var curScroll = cm.getScrollInfo(), editor = cm.getWrapperElement().getBoundingClientRect();
280
+ var newTop = top + startScroll.top - curScroll.top;
281
+ var point = newTop - (window.pageYOffset || (document.documentElement || document.body).scrollTop);
282
+ if (!below) point += hints.offsetHeight;
283
+ if (point <= editor.top || point >= editor.bottom) return completion.close();
284
+ hints.style.top = newTop + "px";
285
+ hints.style.left = (left + startScroll.left - curScroll.left) + "px";
286
+ });
287
+
288
+ CodeMirror.on(hints, "dblclick", function(e) {
289
+ var t = getHintElement(hints, e.target || e.srcElement);
290
+ if (t && t.hintId != null) {widget.changeActive(t.hintId); widget.pick();}
291
+ });
292
+
293
+ CodeMirror.on(hints, "click", function(e) {
294
+ var t = getHintElement(hints, e.target || e.srcElement);
295
+ if (t && t.hintId != null) {
296
+ widget.changeActive(t.hintId);
297
+ if (completion.options.completeOnSingleClick) widget.pick();
298
+ }
299
+ });
300
+
301
+ CodeMirror.on(hints, "mousedown", function() {
302
+ setTimeout(function(){cm.focus();}, 20);
303
+ });
304
+
305
+ CodeMirror.signal(data, "select", completions[this.selectedHint], hints.childNodes[this.selectedHint]);
306
+ return true;
307
+ }
308
+
309
+ Widget.prototype = {
310
+ close: function() {
311
+ if (this.completion.widget != this) return;
312
+ this.completion.widget = null;
313
+ this.hints.parentNode.removeChild(this.hints);
314
+ this.completion.cm.removeKeyMap(this.keyMap);
315
+
316
+ var cm = this.completion.cm;
317
+ if (this.completion.options.closeOnUnfocus) {
318
+ cm.off("blur", this.onBlur);
319
+ cm.off("focus", this.onFocus);
320
+ }
321
+ cm.off("scroll", this.onScroll);
322
+ },
323
+
324
+ disable: function() {
325
+ this.completion.cm.removeKeyMap(this.keyMap);
326
+ var widget = this;
327
+ this.keyMap = {Enter: function() { widget.picked = true; }};
328
+ this.completion.cm.addKeyMap(this.keyMap);
329
+ },
330
+
331
+ pick: function() {
332
+ this.completion.pick(this.data, this.selectedHint);
333
+ },
334
+
335
+ changeActive: function(i, avoidWrap) {
336
+ if (i >= this.data.list.length)
337
+ i = avoidWrap ? this.data.list.length - 1 : 0;
338
+ else if (i < 0)
339
+ i = avoidWrap ? 0 : this.data.list.length - 1;
340
+ if (this.selectedHint == i) return;
341
+ var node = this.hints.childNodes[this.selectedHint];
342
+ node.className = node.className.replace(" " + ACTIVE_HINT_ELEMENT_CLASS, "");
343
+ node = this.hints.childNodes[this.selectedHint = i];
344
+ node.className += " " + ACTIVE_HINT_ELEMENT_CLASS;
345
+ if (node.offsetTop < this.hints.scrollTop)
346
+ this.hints.scrollTop = node.offsetTop - 3;
347
+ else if (node.offsetTop + node.offsetHeight > this.hints.scrollTop + this.hints.clientHeight)
348
+ this.hints.scrollTop = node.offsetTop + node.offsetHeight - this.hints.clientHeight + 3;
349
+ CodeMirror.signal(this.data, "select", this.data.list[this.selectedHint], node);
350
+ },
351
+
352
+ screenAmount: function() {
353
+ return Math.floor(this.hints.clientHeight / this.hints.firstChild.offsetHeight) || 1;
354
+ }
355
+ };
356
+
357
+ function applicableHelpers(cm, helpers) {
358
+ if (!cm.somethingSelected()) return helpers
359
+ var result = []
360
+ for (var i = 0; i < helpers.length; i++)
361
+ if (helpers[i].supportsSelection) result.push(helpers[i])
362
+ return result
363
+ }
364
+
365
+ function fetchHints(hint, cm, options, callback) {
366
+ if (hint.async) {
367
+ hint(cm, callback, options)
368
+ } else {
369
+ var result = hint(cm, options)
370
+ if (result && result.then) result.then(callback)
371
+ else callback(result)
372
+ }
373
+ }
374
+
375
+ function resolveAutoHints(cm, pos) {
376
+ var helpers = cm.getHelpers(pos, "hint"), words
377
+ if (helpers.length) {
378
+ var resolved = function(cm, callback, options) {
379
+ var app = applicableHelpers(cm, helpers);
380
+ function run(i) {
381
+ if (i == app.length) return callback(null)
382
+ fetchHints(app[i], cm, options, function(result) {
383
+ if (result && result.list.length > 0) callback(result)
384
+ else run(i + 1)
385
+ })
386
+ }
387
+ run(0)
388
+ }
389
+ resolved.async = true
390
+ resolved.supportsSelection = true
391
+ return resolved
392
+ } else if (words = cm.getHelper(cm.getCursor(), "hintWords")) {
393
+ return function(cm) { return CodeMirror.hint.fromList(cm, {words: words}) }
394
+ } else if (CodeMirror.hint.anyword) {
395
+ return function(cm, options) { return CodeMirror.hint.anyword(cm, options) }
396
+ } else {
397
+ return function() {}
398
+ }
399
+ }
400
+
401
+ CodeMirror.registerHelper("hint", "auto", {
402
+ resolve: resolveAutoHints
403
+ });
404
+
405
+ CodeMirror.registerHelper("hint", "fromList", function(cm, options) {
406
+ var cur = cm.getCursor(), token = cm.getTokenAt(cur);
407
+ var to = CodeMirror.Pos(cur.line, token.end);
408
+ if (token.string && /\w/.test(token.string[token.string.length - 1])) {
409
+ var term = token.string, from = CodeMirror.Pos(cur.line, token.start);
410
+ } else {
411
+ var term = "", from = to;
412
+ }
413
+ var found = [];
414
+ for (var i = 0; i < options.words.length; i++) {
415
+ var word = options.words[i];
416
+ if (word.slice(0, term.length) == term)
417
+ found.push(word);
418
+ }
419
+
420
+ if (found.length) return {list: found, from: from, to: to};
421
+ });
422
+
423
+ CodeMirror.commands.autocomplete = CodeMirror.showHint;
424
+
425
+ var defaultOptions = {
426
+ hint: CodeMirror.hint.auto,
427
+ completeSingle: true,
428
+ alignWithWord: true,
429
+ closeCharacters: /[\s()\[\]{};:>,]/,
430
+ closeOnUnfocus: true,
431
+ completeOnSingleClick: true,
432
+ container: null,
433
+ customKeys: null,
434
+ extraKeys: null
435
+ };
436
+
437
+ CodeMirror.defineOption("hintOptions", null);
438
+ });
lib/codemirror/codemirror.css ADDED
@@ -0,0 +1,346 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* BASICS */
2
+
3
+ .CodeMirror {
4
+ /* Set height, width, borders, and global font properties here */
5
+ font-family: monospace;
6
+ height: 300px;
7
+ color: black;
8
+ direction: ltr;
9
+ }
10
+
11
+ /* PADDING */
12
+
13
+ .CodeMirror-lines {
14
+ padding: 4px 0; /* Vertical padding around content */
15
+ }
16
+ .CodeMirror pre {
17
+ padding: 0 4px; /* Horizontal padding of content */
18
+ }
19
+
20
+ .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
21
+ background-color: white; /* The little square between H and V scrollbars */
22
+ }
23
+
24
+ /* GUTTER */
25
+
26
+ .CodeMirror-gutters {
27
+ border-right: 1px solid #ddd;
28
+ background-color: #f7f7f7;
29
+ white-space: nowrap;
30
+ }
31
+ .CodeMirror-linenumbers {}
32
+ .CodeMirror-linenumber {
33
+ padding: 0 3px 0 5px;
34
+ min-width: 20px;
35
+ text-align: right;
36
+ color: #999;
37
+ white-space: nowrap;
38
+ }
39
+
40
+ .CodeMirror-guttermarker { color: black; }
41
+ .CodeMirror-guttermarker-subtle { color: #999; }
42
+
43
+ /* CURSOR */
44
+
45
+ .CodeMirror-cursor {
46
+ border-left: 1px solid black;
47
+ border-right: none;
48
+ width: 0;
49
+ }
50
+ /* Shown when moving in bi-directional text */
51
+ .CodeMirror div.CodeMirror-secondarycursor {
52
+ border-left: 1px solid silver;
53
+ }
54
+ .cm-fat-cursor .CodeMirror-cursor {
55
+ width: auto;
56
+ border: 0 !important;
57
+ background: #7e7;
58
+ }
59
+ .cm-fat-cursor div.CodeMirror-cursors {
60
+ z-index: 1;
61
+ }
62
+ .cm-fat-cursor-mark {
63
+ background-color: rgba(20, 255, 20, 0.5);
64
+ -webkit-animation: blink 1.06s steps(1) infinite;
65
+ -moz-animation: blink 1.06s steps(1) infinite;
66
+ animation: blink 1.06s steps(1) infinite;
67
+ }
68
+ .cm-animate-fat-cursor {
69
+ width: auto;
70
+ border: 0;
71
+ -webkit-animation: blink 1.06s steps(1) infinite;
72
+ -moz-animation: blink 1.06s steps(1) infinite;
73
+ animation: blink 1.06s steps(1) infinite;
74
+ background-color: #7e7;
75
+ }
76
+ @-moz-keyframes blink {
77
+ 0% {}
78
+ 50% { background-color: transparent; }
79
+ 100% {}
80
+ }
81
+ @-webkit-keyframes blink {
82
+ 0% {}
83
+ 50% { background-color: transparent; }
84
+ 100% {}
85
+ }
86
+ @keyframes blink {
87
+ 0% {}
88
+ 50% { background-color: transparent; }
89
+ 100% {}
90
+ }
91
+
92
+ /* Can style cursor different in overwrite (non-insert) mode */
93
+ .CodeMirror-overwrite .CodeMirror-cursor {}
94
+
95
+ .cm-tab { display: inline-block; text-decoration: inherit; }
96
+
97
+ .CodeMirror-rulers {
98
+ position: absolute;
99
+ left: 0; right: 0; top: -50px; bottom: -20px;
100
+ overflow: hidden;
101
+ }
102
+ .CodeMirror-ruler {
103
+ border-left: 1px solid #ccc;
104
+ top: 0; bottom: 0;
105
+ position: absolute;
106
+ }
107
+
108
+ /* DEFAULT THEME */
109
+
110
+ .cm-s-default .cm-header {color: blue;}
111
+ .cm-s-default .cm-quote {color: #090;}
112
+ .cm-negative {color: #d44;}
113
+ .cm-positive {color: #292;}
114
+ .cm-header, .cm-strong {font-weight: bold;}
115
+ .cm-em {font-style: italic;}
116
+ .cm-link {text-decoration: underline;}
117
+ .cm-strikethrough {text-decoration: line-through;}
118
+
119
+ .cm-s-default .cm-keyword {color: #708;}
120
+ .cm-s-default .cm-atom {color: #219;}
121
+ .cm-s-default .cm-number {color: #164;}
122
+ .cm-s-default .cm-def {color: #00f;}
123
+ .cm-s-default .cm-variable,
124
+ .cm-s-default .cm-punctuation,
125
+ .cm-s-default .cm-property,
126
+ .cm-s-default .cm-operator {}
127
+ .cm-s-default .cm-variable-2 {color: #05a;}
128
+ .cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;}
129
+ .cm-s-default .cm-comment {color: #a50;}
130
+ .cm-s-default .cm-string {color: #a11;}
131
+ .cm-s-default .cm-string-2 {color: #f50;}
132
+ .cm-s-default .cm-meta {color: #555;}
133
+ .cm-s-default .cm-qualifier {color: #555;}
134
+ .cm-s-default .cm-builtin {color: #30a;}
135
+ .cm-s-default .cm-bracket {color: #997;}
136
+ .cm-s-default .cm-tag {color: #170;}
137
+ .cm-s-default .cm-attribute {color: #00c;}
138
+ .cm-s-default .cm-hr {color: #999;}
139
+ .cm-s-default .cm-link {color: #00c;}
140
+
141
+ .cm-s-default .cm-error {color: #f00;}
142
+ .cm-invalidchar {color: #f00;}
143
+
144
+ .CodeMirror-composing { border-bottom: 2px solid; }
145
+
146
+ /* Default styles for common addons */
147
+
148
+ div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
149
+ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
150
+ .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
151
+ .CodeMirror-activeline-background {background: #e8f2ff;}
152
+
153
+ /* STOP */
154
+
155
+ /* The rest of this file contains styles related to the mechanics of
156
+ the editor. You probably shouldn't touch them. */
157
+
158
+ .CodeMirror {
159
+ position: relative;
160
+ overflow: hidden;
161
+ background: white;
162
+ }
163
+
164
+ .CodeMirror-scroll {
165
+ overflow: scroll !important; /* Things will break if this is overridden */
166
+ /* 30px is the magic margin used to hide the element's real scrollbars */
167
+ /* See overflow: hidden in .CodeMirror */
168
+ margin-bottom: -30px; margin-right: -30px;
169
+ padding-bottom: 30px;
170
+ height: 100%;
171
+ outline: none; /* Prevent dragging from highlighting the element */
172
+ position: relative;
173
+ }
174
+ .CodeMirror-sizer {
175
+ position: relative;
176
+ border-right: 30px solid transparent;
177
+ }
178
+
179
+ /* The fake, visible scrollbars. Used to force redraw during scrolling
180
+ before actual scrolling happens, thus preventing shaking and
181
+ flickering artifacts. */
182
+ .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
183
+ position: absolute;
184
+ z-index: 6;
185
+ display: none;
186
+ }
187
+ .CodeMirror-vscrollbar {
188
+ right: 0; top: 0;
189
+ overflow-x: hidden;
190
+ overflow-y: scroll;
191
+ }
192
+ .CodeMirror-hscrollbar {
193
+ bottom: 0; left: 0;
194
+ overflow-y: hidden;
195
+ overflow-x: scroll;
196
+ }
197
+ .CodeMirror-scrollbar-filler {
198
+ right: 0; bottom: 0;
199
+ }
200
+ .CodeMirror-gutter-filler {
201
+ left: 0; bottom: 0;
202
+ }
203
+
204
+ .CodeMirror-gutters {
205
+ position: absolute; left: 0; top: 0;
206
+ min-height: 100%;
207
+ z-index: 3;
208
+ }
209
+ .CodeMirror-gutter {
210
+ white-space: normal;
211
+ height: 100%;
212
+ display: inline-block;
213
+ vertical-align: top;
214
+ margin-bottom: -30px;
215
+ }
216
+ .CodeMirror-gutter-wrapper {
217
+ position: absolute;
218
+ z-index: 4;
219
+ background: none !important;
220
+ border: none !important;
221
+ }
222
+ .CodeMirror-gutter-background {
223
+ position: absolute;
224
+ top: 0; bottom: 0;
225
+ z-index: 4;
226
+ }
227
+ .CodeMirror-gutter-elt {
228
+ position: absolute;
229
+ cursor: default;
230
+ z-index: 4;
231
+ }
232
+ .CodeMirror-gutter-wrapper ::selection { background-color: transparent }
233
+ .CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent }
234
+
235
+ .CodeMirror-lines {
236
+ cursor: text;
237
+ min-height: 1px; /* prevents collapsing before first draw */
238
+ }
239
+ .CodeMirror pre {
240
+ /* Reset some styles that the rest of the page might have set */
241
+ -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
242
+ border-width: 0;
243
+ background: transparent;
244
+ font-family: inherit;
245
+ font-size: inherit;
246
+ margin: 0;
247
+ white-space: pre;
248
+ word-wrap: normal;
249
+ line-height: inherit;
250
+ color: inherit;
251
+ z-index: 2;
252
+ position: relative;
253
+ overflow: visible;
254
+ -webkit-tap-highlight-color: transparent;
255
+ -webkit-font-variant-ligatures: contextual;
256
+ font-variant-ligatures: contextual;
257
+ }
258
+ .CodeMirror-wrap pre {
259
+ word-wrap: break-word;
260
+ white-space: pre-wrap;
261
+ word-break: normal;
262
+ }
263
+
264
+ .CodeMirror-linebackground {
265
+ position: absolute;
266
+ left: 0; right: 0; top: 0; bottom: 0;
267
+ z-index: 0;
268
+ }
269
+
270
+ .CodeMirror-linewidget {
271
+ position: relative;
272
+ z-index: 2;
273
+ overflow: auto;
274
+ }
275
+
276
+ .CodeMirror-widget {}
277
+
278
+ .CodeMirror-rtl pre { direction: rtl; }
279
+
280
+ .CodeMirror-code {
281
+ outline: none;
282
+ }
283
+
284
+ /* Force content-box sizing for the elements where we expect it */
285
+ .CodeMirror-scroll,
286
+ .CodeMirror-sizer,
287
+ .CodeMirror-gutter,
288
+ .CodeMirror-gutters,
289
+ .CodeMirror-linenumber {
290
+ -moz-box-sizing: content-box;
291
+ box-sizing: content-box;
292
+ }
293
+
294
+ .CodeMirror-measure {
295
+ position: absolute;
296
+ width: 100%;
297
+ height: 0;
298
+ overflow: hidden;
299
+ visibility: hidden;
300
+ }
301
+
302
+ .CodeMirror-cursor {
303
+ position: absolute;
304
+ pointer-events: none;
305
+ }
306
+ .CodeMirror-measure pre { position: static; }
307
+
308
+ div.CodeMirror-cursors {
309
+ visibility: hidden;
310
+ position: relative;
311
+ z-index: 3;
312
+ }
313
+ div.CodeMirror-dragcursors {
314
+ visibility: visible;
315
+ }
316
+
317
+ .CodeMirror-focused div.CodeMirror-cursors {
318
+ visibility: visible;
319
+ }
320
+
321
+ .CodeMirror-selected { background: #d9d9d9; }
322
+ .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
323
+ .CodeMirror-crosshair { cursor: crosshair; }
324
+ .CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }
325
+ .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }
326
+
327
+ .cm-searching {
328
+ background-color: #ffa;
329
+ background-color: rgba(255, 255, 0, .4);
330
+ }
331
+
332
+ /* Used to force a border model for a node */
333
+ .cm-force-border { padding-right: .1px; }
334
+
335
+ @media print {
336
+ /* Hide the cursor when printing */
337
+ .CodeMirror div.CodeMirror-cursors {
338
+ visibility: hidden;
339
+ }
340
+ }
341
+
342
+ /* See issue #2901 */
343
+ .cm-tab-wrap-hack:after { content: ''; }
344
+
345
+ /* Help users use markselection to safely style text background */
346
+ span.CodeMirror-selectedtext { background: none; }
lib/codemirror/codemirror.js ADDED
@@ -0,0 +1,9647 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // CodeMirror, copyright (c) by Marijn Haverbeke and others
2
+ // Distributed under an MIT license: http://codemirror.net/LICENSE
3
+
4
+ // This is CodeMirror (http://codemirror.net), a code editor
5
+ // implemented in JavaScript on top of the browser's DOM.
6
+ //
7
+ // You can find some technical background for some of the code below
8
+ // at http://marijnhaverbeke.nl/blog/#cm-internals .
9
+
10
+ (function (global, factory) {
11
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
12
+ typeof define === 'function' && define.amd ? define(factory) :
13
+ (global.CodeMirror = factory());
14
+ }(this, (function () { 'use strict';
15
+
16
+ // Kludges for bugs and behavior differences that can't be feature
17
+ // detected are enabled based on userAgent etc sniffing.
18
+ var userAgent = navigator.userAgent
19
+ var platform = navigator.platform
20
+
21
+ var gecko = /gecko\/\d/i.test(userAgent)
22
+ var ie_upto10 = /MSIE \d/.test(userAgent)
23
+ var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent)
24
+ var edge = /Edge\/(\d+)/.exec(userAgent)
25
+ var ie = ie_upto10 || ie_11up || edge
26
+ var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1])
27
+ var webkit = !edge && /WebKit\//.test(userAgent)
28
+ var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent)
29
+ var chrome = !edge && /Chrome\//.test(userAgent)
30
+ var presto = /Opera\//.test(userAgent)
31
+ var safari = /Apple Computer/.test(navigator.vendor)
32
+ var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent)
33
+ var phantom = /PhantomJS/.test(userAgent)
34
+
35
+ var ios = !edge && /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent)
36
+ var android = /Android/.test(userAgent)
37
+ // This is woefully incomplete. Suggestions for alternative methods welcome.
38
+ var mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent)
39
+ var mac = ios || /Mac/.test(platform)
40
+ var chromeOS = /\bCrOS\b/.test(userAgent)
41
+ var windows = /win/i.test(platform)
42
+
43
+ var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/)
44
+ if (presto_version) { presto_version = Number(presto_version[1]) }
45
+ if (presto_version && presto_version >= 15) { presto = false; webkit = true }
46
+ // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
47
+ var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11))
48
+ var captureRightClick = gecko || (ie && ie_version >= 9)
49
+
50
+ function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") }
51
+
52
+ var rmClass = function(node, cls) {
53
+ var current = node.className
54
+ var match = classTest(cls).exec(current)
55
+ if (match) {
56
+ var after = current.slice(match.index + match[0].length)
57
+ node.className = current.slice(0, match.index) + (after ? match[1] + after : "")
58
+ }
59
+ }
60
+
61
+ function removeChildren(e) {
62
+ for (var count = e.childNodes.length; count > 0; --count)
63
+ { e.removeChild(e.firstChild) }
64
+ return e
65
+ }
66
+
67
+ function removeChildrenAndAdd(parent, e) {
68
+ return removeChildren(parent).appendChild(e)
69
+ }
70
+
71
+ function elt(tag, content, className, style) {
72
+ var e = document.createElement(tag)
73
+ if (className) { e.className = className }
74
+ if (style) { e.style.cssText = style }
75
+ if (typeof content == "string") { e.appendChild(document.createTextNode(content)) }
76
+ else if (content) { for (var i = 0; i < content.length; ++i) { e.appendChild(content[i]) } }
77
+ return e
78
+ }
79
+ // wrapper for elt, which removes the elt from the accessibility tree
80
+ function eltP(tag, content, className, style) {
81
+ var e = elt(tag, content, className, style)
82
+ e.setAttribute("role", "presentation")
83
+ return e
84
+ }
85
+
86
+ var range
87
+ if (document.createRange) { range = function(node, start, end, endNode) {
88
+ var r = document.createRange()
89
+ r.setEnd(endNode || node, end)
90
+ r.setStart(node, start)
91
+ return r
92
+ } }
93
+ else { range = function(node, start, end) {
94
+ var r = document.body.createTextRange()
95
+ try { r.moveToElementText(node.parentNode) }
96
+ catch(e) { return r }
97
+ r.collapse(true)
98
+ r.moveEnd("character", end)
99
+ r.moveStart("character", start)
100
+ return r
101
+ } }
102
+
103
+ function contains(parent, child) {
104
+ if (child.nodeType == 3) // Android browser always returns false when child is a textnode
105
+ { child = child.parentNode }
106
+ if (parent.contains)
107
+ { return parent.contains(child) }
108
+ do {
109
+ if (child.nodeType == 11) { child = child.host }
110
+ if (child == parent) { return true }
111
+ } while (child = child.parentNode)
112
+ }
113
+
114
+ function activeElt() {
115
+ // IE and Edge may throw an "Unspecified Error" when accessing document.activeElement.
116
+ // IE < 10 will throw when accessed while the page is loading or in an iframe.
117
+ // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable.
118
+ var activeElement
119
+ try {
120
+ activeElement = document.activeElement
121
+ } catch(e) {
122
+ activeElement = document.body || null
123
+ }
124
+ while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement)
125
+ { activeElement = activeElement.shadowRoot.activeElement }
126
+ return activeElement
127
+ }
128
+
129
+ function addClass(node, cls) {
130
+ var current = node.className
131
+ if (!classTest(cls).test(current)) { node.className += (current ? " " : "") + cls }
132
+ }
133
+ function joinClasses(a, b) {
134
+ var as = a.split(" ")
135
+ for (var i = 0; i < as.length; i++)
136
+ { if (as[i] && !classTest(as[i]).test(b)) { b += " " + as[i] } }
137
+ return b
138
+ }
139
+
140
+ var selectInput = function(node) { node.select() }
141
+ if (ios) // Mobile Safari apparently has a bug where select() is broken.
142
+ { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length } }
143
+ else if (ie) // Suppress mysterious IE10 errors
144
+ { selectInput = function(node) { try { node.select() } catch(_e) {} } }
145
+
146
+ function bind(f) {
147
+ var args = Array.prototype.slice.call(arguments, 1)
148
+ return function(){return f.apply(null, args)}
149
+ }
150
+
151
+ function copyObj(obj, target, overwrite) {
152
+ if (!target) { target = {} }
153
+ for (var prop in obj)
154
+ { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
155
+ { target[prop] = obj[prop] } }
156
+ return target
157
+ }
158
+
159
+ // Counts the column offset in a string, taking tabs into account.
160
+ // Used mostly to find indentation.
161
+ function countColumn(string, end, tabSize, startIndex, startValue) {
162
+ if (end == null) {
163
+ end = string.search(/[^\s\u00a0]/)
164
+ if (end == -1) { end = string.length }
165
+ }
166
+ for (var i = startIndex || 0, n = startValue || 0;;) {
167
+ var nextTab = string.indexOf("\t", i)
168
+ if (nextTab < 0 || nextTab >= end)
169
+ { return n + (end - i) }
170
+ n += nextTab - i
171
+ n += tabSize - (n % tabSize)
172
+ i = nextTab + 1
173
+ }
174
+ }
175
+
176
+ var Delayed = function() {this.id = null};
177
+ Delayed.prototype.set = function (ms, f) {
178
+ clearTimeout(this.id)
179
+ this.id = setTimeout(f, ms)
180
+ };
181
+
182
+ function indexOf(array, elt) {
183
+ for (var i = 0; i < array.length; ++i)
184
+ { if (array[i] == elt) { return i } }
185
+ return -1
186
+ }
187
+
188
+ // Number of pixels added to scroller and sizer to hide scrollbar
189
+ var scrollerGap = 30
190
+
191
+ // Returned or thrown by various protocols to signal 'I'm not
192
+ // handling this'.
193
+ var Pass = {toString: function(){return "CodeMirror.Pass"}}
194
+
195
+ // Reused option objects for setSelection & friends
196
+ var sel_dontScroll = {scroll: false};
197
+ var sel_mouse = {origin: "*mouse"};
198
+ var sel_move = {origin: "+move"};
199
+ // The inverse of countColumn -- find the offset that corresponds to
200
+ // a particular column.
201
+ function findColumn(string, goal, tabSize) {
202
+ for (var pos = 0, col = 0;;) {
203
+ var nextTab = string.indexOf("\t", pos)
204
+ if (nextTab == -1) { nextTab = string.length }
205
+ var skipped = nextTab - pos
206
+ if (nextTab == string.length || col + skipped >= goal)
207
+ { return pos + Math.min(skipped, goal - col) }
208
+ col += nextTab - pos
209
+ col += tabSize - (col % tabSize)
210
+ pos = nextTab + 1
211
+ if (col >= goal) { return pos }
212
+ }
213
+ }
214
+
215
+ var spaceStrs = [""]
216
+ function spaceStr(n) {
217
+ while (spaceStrs.length <= n)
218
+ { spaceStrs.push(lst(spaceStrs) + " ") }
219
+ return spaceStrs[n]
220
+ }
221
+
222
+ function lst(arr) { return arr[arr.length-1] }
223
+
224
+ function map(array, f) {
225
+ var out = []
226
+ for (var i = 0; i < array.length; i++) { out[i] = f(array[i], i) }
227
+ return out
228
+ }
229
+
230
+ function insertSorted(array, value, score) {
231
+ var pos = 0, priority = score(value)
232
+ while (pos < array.length && score(array[pos]) <= priority) { pos++ }
233
+ array.splice(pos, 0, value)
234
+ }
235
+
236
+ function nothing() {}
237
+
238
+ function createObj(base, props) {
239
+ var inst
240
+ if (Object.create) {
241
+ inst = Object.create(base)
242
+ } else {
243
+ nothing.prototype = base
244
+ inst = new nothing()
245
+ }
246
+ if (props) { copyObj(props, inst) }
247
+ return inst
248
+ }
249
+
250
+ var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/
251
+ function isWordCharBasic(ch) {
252
+ return /\w/.test(ch) || ch > "\x80" &&
253
+ (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch))
254
+ }
255
+ function isWordChar(ch, helper) {
256
+ if (!helper) { return isWordCharBasic(ch) }
257
+ if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) { return true }
258
+ return helper.test(ch)
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\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/
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 that range.
275
+ function skipExtendingChars(str, pos, dir) {
276
+ while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(pos))) { 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`
282
+ // satisfies `pred`. Supports `from` being greater than `to`.
283
+ function findFirst(pred, from, to) {
284
+ // At any point we are certain `to` satisfies `pred`, don't know
285
+ // whether `from` does.
286
+ var dir = from > to ? -1 : 1
287
+ for (;;) {
288
+ if (from == to) { return from }
289
+ var midF = (from + to) / 2, mid = dir < 0 ? Math.ceil(midF) : Math.floor(midF)
290
+ if (mid == from) { return pred(mid) ? from : to }
291
+ if (pred(mid)) { to = mid }
292
+ else { from = mid + dir }
293
+ }
294
+ }
295
+
296
+ // The display handles the DOM integration, both for input reading
297
+ // and content drawing. It holds references to DOM nodes and
298
+ // display-related state.
299
+
300
+ function Display(place, doc, input) {
301
+ var d = this
302
+ this.input = input
303
+
304
+ // Covers bottom-right square when both scrollbars are present.
305
+ d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler")
306
+ d.scrollbarFiller.setAttribute("cm-not-content", "true")
307
+ // Covers bottom of gutter when coverGutterNextToScrollbar is on
308
+ // and h scrollbar is present.
309
+ d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler")
310
+ d.gutterFiller.setAttribute("cm-not-content", "true")
311
+ // Will contain the actual code, positioned to cover the viewport.
312
+ d.lineDiv = eltP("div", null, "CodeMirror-code")
313
+ // Elements are added to these to represent selection and cursors.
314
+ d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1")
315
+ d.cursorDiv = elt("div", null, "CodeMirror-cursors")
316
+ // A visibility: hidden element used to find the size of things.
317
+ d.measure = elt("div", null, "CodeMirror-measure")
318
+ // When lines outside of the viewport are measured, they are drawn in this.
319
+ d.lineMeasure = elt("div", null, "CodeMirror-measure")
320
+ // Wraps everything that needs to exist inside the vertically-padded coordinate system
321
+ d.lineSpace = eltP("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],
322
+ null, "position: relative; outline: none")
323
+ var lines = eltP("div", [d.lineSpace], "CodeMirror-lines")
324
+ // Moved around its parent to cover visible view.
325
+ d.mover = elt("div", [lines], null, "position: relative")
326
+ // Set to the height of the document, allowing scrolling.
327
+ d.sizer = elt("div", [d.mover], "CodeMirror-sizer")
328
+ d.sizerWidth = null
329
+ // Behavior of elts with overflow: auto and padding is
330
+ // inconsistent across browsers. This is used to ensure the
331
+ // scrollable area is big enough.
332
+ d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;")
333
+ // Will contain the gutters, if any.
334
+ d.gutters = elt("div", null, "CodeMirror-gutters")
335
+ d.lineGutter = null
336
+ // Actual scrollable element.
337
+ d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll")
338
+ d.scroller.setAttribute("tabIndex", "-1")
339
+ // The element in which the editor lives.
340
+ d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror")
341
+
342
+ // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)
343
+ if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0 }
344
+ if (!webkit && !(gecko && mobile)) { d.scroller.draggable = true }
345
+
346
+ if (place) {
347
+ if (place.appendChild) { place.appendChild(d.wrapper) }
348
+ else { place(d.wrapper) }
349
+ }
350
+
351
+ // Current rendered range (may be bigger than the view window).
352
+ d.viewFrom = d.viewTo = doc.first
353
+ d.reportedViewFrom = d.reportedViewTo = doc.first
354
+ // Information about the rendered lines.
355
+ d.view = []
356
+ d.renderedView = null
357
+ // Holds info about a single rendered line when it was rendered
358
+ // for measurement, while not in view.
359
+ d.externalMeasured = null
360
+ // Empty space (in pixels) above the view
361
+ d.viewOffset = 0
362
+ d.lastWrapHeight = d.lastWrapWidth = 0
363
+ d.updateLineNumbers = null
364
+
365
+ d.nativeBarWidth = d.barHeight = d.barWidth = 0
366
+ d.scrollbarsClipped = false
367
+
368
+ // Used to only resize the line number gutter when necessary (when
369
+ // the amount of lines crosses a boundary that makes its width change)
370
+ d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null
371
+ // Set to true when a non-horizontal-scrolling line widget is
372
+ // added. As an optimization, line widget aligning is skipped when
373
+ // this is false.
374
+ d.alignWidgets = false
375
+
376
+ d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null
377
+
378
+ // Tracks the maximum line length so that the horizontal scrollbar
379
+ // can be kept static when scrolling.
380
+ d.maxLine = null
381
+ d.maxLineLength = 0
382
+ d.maxLineChanged = false
383
+
384
+ // Used for measuring wheel scrolling granularity
385
+ d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null
386
+
387
+ // True when shift is held down.
388
+ d.shift = false
389
+
390
+ // Used to track whether anything happened since the context menu
391
+ // was opened.
392
+ d.selForContextMenu = null
393
+
394
+ d.activeTouch = null
395
+
396
+ input.init(d)
397
+ }
398
+
399
+ // Find the line object corresponding to the given line number.
400
+ function getLine(doc, n) {
401
+ n -= doc.first
402
+ if (n < 0 || n >= doc.size) { throw new Error("There is no line " + (n + doc.first) + " in the document.") }
403
+ var chunk = doc
404
+ while (!chunk.lines) {
405
+ for (var i = 0;; ++i) {
406
+ var child = chunk.children[i], sz = child.chunkSize()
407
+ if (n < sz) { chunk = child; break }
408
+ n -= sz
409
+ }
410
+ }
411
+ return chunk.lines[n]
412
+ }
413
+
414
+ // Get the part of a document between two positions, as an array of
415
+ // strings.
416
+ function getBetween(doc, start, end) {
417
+ var out = [], n = start.line
418
+ doc.iter(start.line, end.line + 1, function (line) {
419
+ var text = line.text
420
+ if (n == end.line) { text = text.slice(0, end.ch) }
421
+ if (n == start.line) { text = text.slice(start.ch) }
422
+ out.push(text)
423
+ ++n
424
+ })
425
+ return out
426
+ }
427
+ // Get the lines between from and to, as array of strings.
428
+ function getLines(doc, from, to) {
429
+ var out = []
430
+ doc.iter(from, to, function (line) { out.push(line.text) }) // iter aborts when callback returns truthy value
431
+ return out
432
+ }
433
+
434
+ // Update the height of a line, propagating the height change
435
+ // upwards to parent nodes.
436
+ function updateLineHeight(line, height) {
437
+ var diff = height - line.height
438
+ if (diff) { for (var n = line; n; n = n.parent) { n.height += diff } }
439
+ }
440
+
441
+ // Given a line object, find its line number by walking up through
442
+ // its parent links.
443
+ function lineNo(line) {
444
+ if (line.parent == null) { return null }
445
+ var cur = line.parent, no = indexOf(cur.lines, line)
446
+ for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
447
+ for (var i = 0;; ++i) {
448
+ if (chunk.children[i] == cur) { break }
449
+ no += chunk.children[i].chunkSize()
450
+ }
451
+ }
452
+ return no + cur.first
453
+ }
454
+
455
+ // Find the line at the given vertical position, using the height
456
+ // information in the document tree.
457
+ function lineAtHeight(chunk, h) {
458
+ var n = chunk.first
459
+ outer: do {
460
+ for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) {
461
+ var child = chunk.children[i$1], ch = child.height
462
+ if (h < ch) { chunk = child; continue outer }
463
+ h -= ch
464
+ n += child.chunkSize()
465
+ }
466
+ return n
467
+ } while (!chunk.lines)
468
+ var i = 0
469
+ for (; i < chunk.lines.length; ++i) {
470
+ var line = chunk.lines[i], lh = line.height
471
+ if (h < lh) { break }
472
+ h -= lh
473
+ }
474
+ return n + i
475
+ }
476
+
477
+ function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size}
478
+
479
+ function lineNumberFor(options, i) {
480
+ return String(options.lineNumberFormatter(i + options.firstLineNumber))
481
+ }
482
+
483
+ // A Pos instance represents a position within the text.
484
+ function Pos(line, ch, sticky) {
485
+ if ( sticky === void 0 ) sticky = null;
486
+
487
+ if (!(this instanceof Pos)) { return new Pos(line, ch, sticky) }
488
+ this.line = line
489
+ this.ch = ch
490
+ this.sticky = sticky
491
+ }
492
+
493
+ // Compare two positions, return 0 if they are the same, a negative
494
+ // number when a is less, and a positive number otherwise.
495
+ function cmp(a, b) { return a.line - b.line || a.ch - b.ch }
496
+
497
+ function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 }
498
+
499
+ function copyPos(x) {return Pos(x.line, x.ch)}
500
+ function maxPos(a, b) { return cmp(a, b) < 0 ? b : a }
501
+ function minPos(a, b) { return cmp(a, b) < 0 ? a : b }
502
+
503
+ // Most of the external API clips given positions to make sure they
504
+ // actually exist within the document.
505
+ function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))}
506
+ function clipPos(doc, pos) {
507
+ if (pos.line < doc.first) { return Pos(doc.first, 0) }
508
+ var last = doc.first + doc.size - 1
509
+ if (pos.line > last) { return Pos(last, getLine(doc, last).text.length) }
510
+ return clipToLen(pos, getLine(doc, pos.line).text.length)
511
+ }
512
+ function clipToLen(pos, linelen) {
513
+ var ch = pos.ch
514
+ if (ch == null || ch > linelen) { return Pos(pos.line, linelen) }
515
+ else if (ch < 0) { return Pos(pos.line, 0) }
516
+ else { return pos }
517
+ }
518
+ function clipPosArray(doc, array) {
519
+ var out = []
520
+ for (var i = 0; i < array.length; i++) { out[i] = clipPos(doc, array[i]) }
521
+ return out
522
+ }
523
+
524
+ // Optimize some code when these features are not used.
525
+ var sawReadOnlySpans = false;
526
+ var sawCollapsedSpans = false;
527
+ function seeReadOnlySpans() {
528
+ sawReadOnlySpans = true
529
+ }
530
+
531
+ function seeCollapsedSpans() {
532
+ sawCollapsedSpans = true
533
+ }
534
+
535
+ // TEXTMARKER SPANS
536
+
537
+ function MarkedSpan(marker, from, to) {
538
+ this.marker = marker
539
+ this.from = from; this.to = to
540
+ }
541
+
542
+ // Search an array of spans for a span matching the given marker.
543
+ function getMarkedSpanFor(spans, marker) {
544
+ if (spans) { for (var i = 0; i < spans.length; ++i) {
545
+ var span = spans[i]
546
+ if (span.marker == marker) { return span }
547
+ } }
548
+ }
549
+ // Remove a span from an array, returning undefined if no spans are
550
+ // left (we don't store arrays for lines without spans).
551
+ function removeMarkedSpan(spans, span) {
552
+ var r
553
+ for (var i = 0; i < spans.length; ++i)
554
+ { if (spans[i] != span) { (r || (r = [])).push(spans[i]) } }
555
+ return r
556
+ }
557
+ // Add a span to a line.
558
+ function addMarkedSpan(line, span) {
559
+ line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span]
560
+ span.marker.attachLine(line)
561
+ }
562
+
563
+ // Used for the algorithm that adjusts markers for a change in the
564
+ // document. These functions cut an array of spans at a given
565
+ // character position, returning an array of remaining chunks (or
566
+ // undefined if nothing remains).
567
+ function markedSpansBefore(old, startCh, isInsert) {
568
+ var nw
569
+ if (old) { for (var i = 0; i < old.length; ++i) {
570
+ var span = old[i], marker = span.marker
571
+ var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh)
572
+ if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) {
573
+ var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh)
574
+ ;(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to))
575
+ }
576
+ } }
577
+ return nw
578
+ }
579
+ function markedSpansAfter(old, endCh, isInsert) {
580
+ var nw
581
+ if (old) { for (var i = 0; i < old.length; ++i) {
582
+ var span = old[i], marker = span.marker
583
+ var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh)
584
+ if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) {
585
+ var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh)
586
+ ;(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh,
587
+ span.to == null ? null : span.to - endCh))
588
+ }
589
+ } }
590
+ return nw
591
+ }
592
+
593
+ // Given a change object, compute the new set of marker spans that
594
+ // cover the line in which the change took place. Removes spans
595
+ // entirely within the change, reconnects spans belonging to the
596
+ // same marker that appear on both sides of the change, and cuts off
597
+ // spans partially within the change. Returns an array of span
598
+ // arrays with one element for each line in (after) the change.
599
+ function stretchSpansOverChange(doc, change) {
600
+ if (change.full) { return null }
601
+ var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans
602
+ var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans
603
+ if (!oldFirst && !oldLast) { return null }
604
+
605
+ var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0
606
+ // Get the spans that 'stick out' on both sides
607
+ var first = markedSpansBefore(oldFirst, startCh, isInsert)
608
+ var last = markedSpansAfter(oldLast, endCh, isInsert)
609
+
610
+ // Next, merge those two ends
611
+ var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0)
612
+ if (first) {
613
+ // Fix up .to properties of first
614
+ for (var i = 0; i < first.length; ++i) {
615
+ var span = first[i]
616
+ if (span.to == null) {
617
+ var found = getMarkedSpanFor(last, span.marker)
618
+ if (!found) { span.to = startCh }
619
+ else if (sameLine) { span.to = found.to == null ? null : found.to + offset }
620
+ }
621
+ }
622
+ }
623
+ if (last) {
624
+ // Fix up .from in last (or move them into first in case of sameLine)
625
+ for (var i$1 = 0; i$1 < last.length; ++i$1) {
626
+ var span$1 = last[i$1]
627
+ if (span$1.to != null) { span$1.to += offset }
628
+ if (span$1.from == null) {
629
+ var found$1 = getMarkedSpanFor(first, span$1.marker)
630
+ if (!found$1) {
631
+ span$1.from = offset
632
+ if (sameLine) { (first || (first = [])).push(span$1) }
633
+ }
634
+ } else {
635
+ span$1.from += offset
636
+ if (sameLine) { (first || (first = [])).push(span$1) }
637
+ }
638
+ }
639
+ }
640
+ // Make sure we didn't create any zero-length spans
641
+ if (first) { first = clearEmptySpans(first) }
642
+ if (last && last != first) { last = clearEmptySpans(last) }
643
+
644
+ var newMarkers = [first]
645
+ if (!sameLine) {
646
+ // Fill gap with whole-line-spans
647
+ var gap = change.text.length - 2, gapMarkers
648
+ if (gap > 0 && first)
649
+ { for (var i$2 = 0; i$2 < first.length; ++i$2)
650
+ { if (first[i$2].to == null)
651
+ { (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i$2].marker, null, null)) } } }
652
+ for (var i$3 = 0; i$3 < gap; ++i$3)
653
+ { newMarkers.push(gapMarkers) }
654
+ newMarkers.push(last)
655
+ }
656
+ return newMarkers
657
+ }
658
+
659
+ // Remove spans that are empty and don't have a clearWhenEmpty
660
+ // option of false.
661
+ function clearEmptySpans(spans) {
662
+ for (var i = 0; i < spans.length; ++i) {
663
+ var span = spans[i]
664
+ if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)
665
+ { spans.splice(i--, 1) }
666
+ }
667
+ if (!spans.length) { return null }
668
+ return spans
669
+ }
670
+
671
+ // Used to 'clip' out readOnly ranges when making a change.
672
+ function removeReadOnlyRanges(doc, from, to) {
673
+ var markers = null
674
+ doc.iter(from.line, to.line + 1, function (line) {
675
+ if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
676
+ var mark = line.markedSpans[i].marker
677
+ if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
678
+ { (markers || (markers = [])).push(mark) }
679
+ } }
680
+ })
681
+ if (!markers) { return null }
682
+ var parts = [{from: from, to: to}]
683
+ for (var i = 0; i < markers.length; ++i) {
684
+ var mk = markers[i], m = mk.find(0)
685
+ for (var j = 0; j < parts.length; ++j) {
686
+ var p = parts[j]
687
+ if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) { continue }
688
+ var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to)
689
+ if (dfrom < 0 || !mk.inclusiveLeft && !dfrom)
690
+ { newParts.push({from: p.from, to: m.from}) }
691
+ if (dto > 0 || !mk.inclusiveRight && !dto)
692
+ { newParts.push({from: m.to, to: p.to}) }
693
+ parts.splice.apply(parts, newParts)
694
+ j += newParts.length - 3
695
+ }
696
+ }
697
+ return parts
698
+ }
699
+
700
+ // Connect or disconnect spans from a line.
701
+ function detachMarkedSpans(line) {
702
+ var spans = line.markedSpans
703
+ if (!spans) { return }
704
+ for (var i = 0; i < spans.length; ++i)
705
+ { spans[i].marker.detachLine(line) }
706
+ line.markedSpans = null
707
+ }
708
+ function attachMarkedSpans(line, spans) {
709
+ if (!spans) { return }
710
+ for (var i = 0; i < spans.length; ++i)
711
+ { spans[i].marker.attachLine(line) }
712
+ line.markedSpans = spans
713
+ }
714
+
715
+ // Helpers used when computing which overlapping collapsed span
716
+ // counts as the larger one.
717
+ function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 }
718
+ function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 }
719
+
720
+ // Returns a number indicating which of two overlapping collapsed
721
+ // spans is larger (and thus includes the other). Falls back to
722
+ // comparing ids when the spans cover exactly the same range.
723
+ function compareCollapsedMarkers(a, b) {
724
+ var lenDiff = a.lines.length - b.lines.length
725
+ if (lenDiff != 0) { return lenDiff }
726
+ var aPos = a.find(), bPos = b.find()
727
+ var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b)
728
+ if (fromCmp) { return -fromCmp }
729
+ var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b)
730
+ if (toCmp) { return toCmp }
731
+ return b.id - a.id
732
+ }
733
+
734
+ // Find out whether a line ends or starts in a collapsed span. If
735
+ // so, return the marker for that span.
736
+ function collapsedSpanAtSide(line, start) {
737
+ var sps = sawCollapsedSpans && line.markedSpans, found
738
+ if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
739
+ sp = sps[i]
740
+ if (sp.marker.collapsed && (start ? sp.from : sp.to) == null &&
741
+ (!found || compareCollapsedMarkers(found, sp.marker) < 0))
742
+ { found = sp.marker }
743
+ } }
744
+ return found
745
+ }
746
+ function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) }
747
+ function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) }
748
+
749
+ // Test whether there exists a collapsed span that partially
750
+ // overlaps (covers the start or end, but not both) of a new span.
751
+ // Such overlap is not allowed.
752
+ function conflictingCollapsedRange(doc, lineNo, from, to, marker) {
753
+ var line = getLine(doc, lineNo)
754
+ var sps = sawCollapsedSpans && line.markedSpans
755
+ if (sps) { for (var i = 0; i < sps.length; ++i) {
756
+ var sp = sps[i]
757
+ if (!sp.marker.collapsed) { continue }
758
+ var found = sp.marker.find(0)
759
+ var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker)
760
+ var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker)
761
+ if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue }
762
+ if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) ||
763
+ fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0))
764
+ { return true }
765
+ } }
766
+ }
767
+
768
+ // A visual line is a line as drawn on the screen. Folding, for
769
+ // example, can cause multiple logical lines to appear on the same
770
+ // visual line. This finds the start of the visual line that the
771
+ // given line is part of (usually that is the line itself).
772
+ function visualLine(line) {
773
+ var merged
774
+ while (merged = collapsedSpanAtStart(line))
775
+ { line = merged.find(-1, true).line }
776
+ return line
777
+ }
778
+
779
+ function visualLineEnd(line) {
780
+ var merged
781
+ while (merged = collapsedSpanAtEnd(line))
782
+ { line = merged.find(1, true).line }
783
+ return line
784
+ }
785
+
786
+ // Returns an array of logical lines that continue the visual line
787
+ // started by the argument, or undefined if there are no such lines.
788
+ function visualLineContinued(line) {
789
+ var merged, lines
790
+ while (merged = collapsedSpanAtEnd(line)) {
791
+ line = merged.find(1, true).line
792
+ ;(lines || (lines = [])).push(line)
793
+ }
794
+ return lines
795
+ }
796
+
797
+ // Get the line number of the start of the visual line that the
798
+ // given line number is part of.
799
+ function visualLineNo(doc, lineN) {
800
+ var line = getLine(doc, lineN), vis = visualLine(line)
801
+ if (line == vis) { return lineN }
802
+ return lineNo(vis)
803
+ }
804
+
805
+ // Get the line number of the start of the next visual line after
806
+ // the given line.
807
+ function visualLineEndNo(doc, lineN) {
808
+ if (lineN > doc.lastLine()) { return lineN }
809
+ var line = getLine(doc, lineN), merged
810
+ if (!lineIsHidden(doc, line)) { return lineN }
811
+ while (merged = collapsedSpanAtEnd(line))
812
+ { line = merged.find(1, true).line }
813
+ return lineNo(line) + 1
814
+ }
815
+
816
+ // Compute whether a line is hidden. Lines count as hidden when they
817
+ // are part of a visual line that starts with another line, or when
818
+ // they are entirely covered by collapsed, non-widget span.
819
+ function lineIsHidden(doc, line) {
820
+ var sps = sawCollapsedSpans && line.markedSpans
821
+ if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
822
+ sp = sps[i]
823
+ if (!sp.marker.collapsed) { continue }
824
+ if (sp.from == null) { return true }
825
+ if (sp.marker.widgetNode) { continue }
826
+ if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
827
+ { return true }
828
+ } }
829
+ }
830
+ function lineIsHiddenInner(doc, line, span) {
831
+ if (span.to == null) {
832
+ var end = span.marker.find(1, true)
833
+ return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker))
834
+ }
835
+ if (span.marker.inclusiveRight && span.to == line.text.length)
836
+ { return true }
837
+ for (var sp = (void 0), i = 0; i < line.markedSpans.length; ++i) {
838
+ sp = line.markedSpans[i]
839
+ if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to &&
840
+ (sp.to == null || sp.to != span.from) &&
841
+ (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
842
+ lineIsHiddenInner(doc, line, sp)) { return true }
843
+ }
844
+ }
845
+
846
+ // Find the height above the given line.
847
+ function heightAtLine(lineObj) {
848
+ lineObj = visualLine(lineObj)
849
+
850
+ var h = 0, chunk = lineObj.parent
851
+ for (var i = 0; i < chunk.lines.length; ++i) {
852
+ var line = chunk.lines[i]
853
+ if (line == lineObj) { break }
854
+ else { h += line.height }
855
+ }
856
+ for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
857
+ for (var i$1 = 0; i$1 < p.children.length; ++i$1) {
858
+ var cur = p.children[i$1]
859
+ if (cur == chunk) { break }
860
+ else { h += cur.height }
861
+ }
862
+ }
863
+ return h
864
+ }
865
+
866
+ // Compute the character length of a line, taking into account
867
+ // collapsed ranges (see markText) that might hide parts, and join
868
+ // other lines onto it.
869
+ function lineLength(line) {
870
+ if (line.height == 0) { return 0 }
871
+ var len = line.text.length, merged, cur = line
872
+ while (merged = collapsedSpanAtStart(cur)) {
873
+ var found = merged.find(0, true)
874
+ cur = found.from.line
875
+ len += found.from.ch - found.to.ch
876
+ }
877
+ cur = line
878
+ while (merged = collapsedSpanAtEnd(cur)) {
879
+ var found$1 = merged.find(0, true)
880
+ len -= cur.text.length - found$1.from.ch
881
+ cur = found$1.to.line
882
+ len += cur.text.length - found$1.to.ch
883
+ }
884
+ return len
885
+ }
886
+
887
+ // Find the longest line in the document.
888
+ function findMaxLine(cm) {
889
+ var d = cm.display, doc = cm.doc
890
+ d.maxLine = getLine(doc, doc.first)
891
+ d.maxLineLength = lineLength(d.maxLine)
892
+ d.maxLineChanged = true
893
+ doc.iter(function (line) {
894
+ var len = lineLength(line)
895
+ if (len > d.maxLineLength) {
896
+ d.maxLineLength = len
897
+ d.maxLine = line
898
+ }
899
+ })
900
+ }
901
+
902
+ // BIDI HELPERS
903
+
904
+ function iterateBidiSections(order, from, to, f) {
905
+ if (!order) { return f(from, to, "ltr", 0) }
906
+ var found = false
907
+ for (var i = 0; i < order.length; ++i) {
908
+ var part = order[i]
909
+ if (part.from < to && part.to > from || from == to && part.to == from) {
910
+ f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr", i)
911
+ found = true
912
+ }
913
+ }
914
+ if (!found) { f(from, to, "ltr") }
915
+ }
916
+
917
+ var bidiOther = null
918
+ function getBidiPartAt(order, ch, sticky) {
919
+ var found
920
+ bidiOther = null
921
+ for (var i = 0; i < order.length; ++i) {
922
+ var cur = order[i]
923
+ if (cur.from < ch && cur.to > ch) { return i }
924
+ if (cur.to == ch) {
925
+ if (cur.from != cur.to && sticky == "before") { found = i }
926
+ else { bidiOther = i }
927
+ }
928
+ if (cur.from == ch) {
929
+ if (cur.from != cur.to && sticky != "before") { found = i }
930
+ else { bidiOther = i }
931
+ }
932
+ }
933
+ return found != null ? found : bidiOther
934
+ }
935
+
936
+ // Bidirectional ordering algorithm
937
+ // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
938
+ // that this (partially) implements.
939
+
940
+ // One-char codes used for character types:
941
+ // L (L): Left-to-Right
942
+ // R (R): Right-to-Left
943
+ // r (AL): Right-to-Left Arabic
944
+ // 1 (EN): European Number
945
+ // + (ES): European Number Separator
946
+ // % (ET): European Number Terminator
947
+ // n (AN): Arabic Number
948
+ // , (CS): Common Number Separator
949
+ // m (NSM): Non-Spacing Mark
950
+ // b (BN): Boundary Neutral
951
+ // s (B): Paragraph Separator
952
+ // t (S): Segment Separator
953
+ // w (WS): Whitespace
954
+ // N (ON): Other Neutrals
955
+
956
+ // Returns null if characters are ordered as they appear
957
+ // (left-to-right), or an array of sections ({from, to, level}
958
+ // objects) in the order in which they occur visually.
959
+ var bidiOrdering = (function() {
960
+ // Character types for codepoints 0 to 0xff
961
+ var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN"
962
+ // Character types for codepoints 0x600 to 0x6f9
963
+ var arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111"
964
+ function charType(code) {
965
+ if (code <= 0xf7) { return lowTypes.charAt(code) }
966
+ else if (0x590 <= code && code <= 0x5f4) { return "R" }
967
+ else if (0x600 <= code && code <= 0x6f9) { return arabicTypes.charAt(code - 0x600) }
968
+ else if (0x6ee <= code && code <= 0x8ac) { return "r" }
969
+ else if (0x2000 <= code && code <= 0x200b) { return "w" }
970
+ else if (code == 0x200c) { return "b" }
971
+ else { return "L" }
972
+ }
973
+
974
+ var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/
975
+ var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/
976
+
977
+ function BidiSpan(level, from, to) {
978
+ this.level = level
979
+ this.from = from; this.to = to
980
+ }
981
+
982
+ return function(str, direction) {
983
+ var outerType = direction == "ltr" ? "L" : "R"
984
+
985
+ if (str.length == 0 || direction == "ltr" && !bidiRE.test(str)) { return false }
986
+ var len = str.length, types = []
987
+ for (var i = 0; i < len; ++i)
988
+ { types.push(charType(str.charCodeAt(i))) }
989
+
990
+ // W1. Examine each non-spacing mark (NSM) in the level run, and
991
+ // change the type of the NSM to the type of the previous
992
+ // character. If the NSM is at the start of the level run, it will
993
+ // get the type of sor.
994
+ for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) {
995
+ var type = types[i$1]
996
+ if (type == "m") { types[i$1] = prev }
997
+ else { prev = type }
998
+ }
999
+
1000
+ // W2. Search backwards from each instance of a European number
1001
+ // until the first strong type (R, L, AL, or sor) is found. If an
1002
+ // AL is found, change the type of the European number to Arabic
1003
+ // number.
1004
+ // W3. Change all ALs to R.
1005
+ for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) {
1006
+ var type$1 = types[i$2]
1007
+ if (type$1 == "1" && cur == "r") { types[i$2] = "n" }
1008
+ else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == "r") { types[i$2] = "R" } }
1009
+ }
1010
+
1011
+ // W4. A single European separator between two European numbers
1012
+ // changes to a European number. A single common separator between
1013
+ // two numbers of the same type changes to that type.
1014
+ for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) {
1015
+ var type$2 = types[i$3]
1016
+ if (type$2 == "+" && prev$1 == "1" && types[i$3+1] == "1") { types[i$3] = "1" }
1017
+ else if (type$2 == "," && prev$1 == types[i$3+1] &&
1018
+ (prev$1 == "1" || prev$1 == "n")) { types[i$3] = prev$1 }
1019
+ prev$1 = type$2
1020
+ }
1021
+
1022
+ // W5. A sequence of European terminators adjacent to European
1023
+ // numbers changes to all European numbers.
1024
+ // W6. Otherwise, separators and terminators change to Other
1025
+ // Neutral.
1026
+ for (var i$4 = 0; i$4 < len; ++i$4) {
1027
+ var type$3 = types[i$4]
1028
+ if (type$3 == ",") { types[i$4] = "N" }
1029
+ else if (type$3 == "%") {
1030
+ var end = (void 0)
1031
+ for (end = i$4 + 1; end < len && types[end] == "%"; ++end) {}
1032
+ var replace = (i$4 && types[i$4-1] == "!") || (end < len && types[end] == "1") ? "1" : "N"
1033
+ for (var j = i$4; j < end; ++j) { types[j] = replace }
1034
+ i$4 = end - 1
1035
+ }
1036
+ }
1037
+
1038
+ // W7. Search backwards from each instance of a European number
1039
+ // until the first strong type (R, L, or sor) is found. If an L is
1040
+ // found, then change the type of the European number to L.
1041
+ for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) {
1042
+ var type$4 = types[i$5]
1043
+ if (cur$1 == "L" && type$4 == "1") { types[i$5] = "L" }
1044
+ else if (isStrong.test(type$4)) { cur$1 = type$4 }
1045
+ }
1046
+
1047
+ // N1. A sequence of neutrals takes the direction of the
1048
+ // surrounding strong text if the text on both sides has the same
1049
+ // direction. European and Arabic numbers act as if they were R in
1050
+ // terms of their influence on neutrals. Start-of-level-run (sor)
1051
+ // and end-of-level-run (eor) are used at level run boundaries.
1052
+ // N2. Any remaining neutrals take the embedding direction.
1053
+ for (var i$6 = 0; i$6 < len; ++i$6) {
1054
+ if (isNeutral.test(types[i$6])) {
1055
+ var end$1 = (void 0)
1056
+ for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {}
1057
+ var before = (i$6 ? types[i$6-1] : outerType) == "L"
1058
+ var after = (end$1 < len ? types[end$1] : outerType) == "L"
1059
+ var replace$1 = before == after ? (before ? "L" : "R") : outerType
1060
+ for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1 }
1061
+ i$6 = end$1 - 1
1062
+ }
1063
+ }
1064
+
1065
+ // Here we depart from the documented algorithm, in order to avoid
1066
+ // building up an actual levels array. Since there are only three
1067
+ // levels (0, 1, 2) in an implementation that doesn't take
1068
+ // explicit embedding into account, we can build up the order on
1069
+ // the fly, without following the level-based algorithm.
1070
+ var order = [], m
1071
+ for (var i$7 = 0; i$7 < len;) {
1072
+ if (countsAsLeft.test(types[i$7])) {
1073
+ var start = i$7
1074
+ for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {}
1075
+ order.push(new BidiSpan(0, start, i$7))
1076
+ } else {
1077
+ var pos = i$7, at = order.length
1078
+ for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {}
1079
+ for (var j$2 = pos; j$2 < i$7;) {
1080
+ if (countsAsNum.test(types[j$2])) {
1081
+ if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)) }
1082
+ var nstart = j$2
1083
+ for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {}
1084
+ order.splice(at, 0, new BidiSpan(2, nstart, j$2))
1085
+ pos = j$2
1086
+ } else { ++j$2 }
1087
+ }
1088
+ if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)) }
1089
+ }
1090
+ }
1091
+ if (direction == "ltr") {
1092
+ if (order[0].level == 1 && (m = str.match(/^\s+/))) {
1093
+ order[0].from = m[0].length
1094
+ order.unshift(new BidiSpan(0, 0, m[0].length))
1095
+ }
1096
+ if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
1097
+ lst(order).to -= m[0].length
1098
+ order.push(new BidiSpan(0, len - m[0].length, len))
1099
+ }
1100
+ }
1101
+
1102
+ return direction == "rtl" ? order.reverse() : order
1103
+ }
1104
+ })()
1105
+
1106
+ // Get the bidi ordering for the given line (and cache it). Returns
1107
+ // false for lines that are fully left-to-right, and an array of
1108
+ // BidiSpan objects otherwise.
1109
+ function getOrder(line, direction) {
1110
+ var order = line.order
1111
+ if (order == null) { order = line.order = bidiOrdering(line.text, direction) }
1112
+ return order
1113
+ }
1114
+
1115
+ // EVENT HANDLING
1116
+
1117
+ // Lightweight event framework. on/off also work on DOM nodes,
1118
+ // registering native DOM handlers.
1119
+
1120
+ var noHandlers = []
1121
+
1122
+ var on = function(emitter, type, f) {
1123
+ if (emitter.addEventListener) {
1124
+ emitter.addEventListener(type, f, false)
1125
+ } else if (emitter.attachEvent) {
1126
+ emitter.attachEvent("on" + type, f)
1127
+ } else {
1128
+ var map = emitter._handlers || (emitter._handlers = {})
1129
+ map[type] = (map[type] || noHandlers).concat(f)
1130
+ }
1131
+ }
1132
+
1133
+ function getHandlers(emitter, type) {
1134
+ return emitter._handlers && emitter._handlers[type] || noHandlers
1135
+ }
1136
+
1137
+ function off(emitter, type, f) {
1138
+ if (emitter.removeEventListener) {
1139
+ emitter.removeEventListener(type, f, false)
1140
+ } else if (emitter.detachEvent) {
1141
+ emitter.detachEvent("on" + type, f)
1142
+ } else {
1143
+ var map = emitter._handlers, arr = map && map[type]
1144
+ if (arr) {
1145
+ var index = indexOf(arr, f)
1146
+ if (index > -1)
1147
+ { map[type] = arr.slice(0, index).concat(arr.slice(index + 1)) }
1148
+ }
1149
+ }
1150
+ }
1151
+
1152
+ function signal(emitter, type /*, values...*/) {
1153
+ var handlers = getHandlers(emitter, type)
1154
+ if (!handlers.length) { return }
1155
+ var args = Array.prototype.slice.call(arguments, 2)
1156
+ for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args) }
1157
+ }
1158
+
1159
+ // The DOM events that CodeMirror handles can be overridden by
1160
+ // registering a (non-DOM) handler on the editor for the event name,
1161
+ // and preventDefault-ing the event in that handler.
1162
+ function signalDOMEvent(cm, e, override) {
1163
+ if (typeof e == "string")
1164
+ { e = {type: e, preventDefault: function() { this.defaultPrevented = true }} }
1165
+ signal(cm, override || e.type, cm, e)
1166
+ return e_defaultPrevented(e) || e.codemirrorIgnore
1167
+ }
1168
+
1169
+ function signalCursorActivity(cm) {
1170
+ var arr = cm._handlers && cm._handlers.cursorActivity
1171
+ if (!arr) { return }
1172
+ var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = [])
1173
+ for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1)
1174
+ { set.push(arr[i]) } }
1175
+ }
1176
+
1177
+ function hasHandler(emitter, type) {
1178
+ return getHandlers(emitter, type).length > 0
1179
+ }
1180
+
1181
+ // Add on and off methods to a constructor's prototype, to make
1182
+ // registering events on such objects more convenient.
1183
+ function eventMixin(ctor) {
1184
+ ctor.prototype.on = function(type, f) {on(this, type, f)}
1185
+ ctor.prototype.off = function(type, f) {off(this, type, f)}
1186
+ }
1187
+
1188
+ // Due to the fact that we still support jurassic IE versions, some
1189
+ // compatibility wrappers are needed.
1190
+
1191
+ function e_preventDefault(e) {
1192
+ if (e.preventDefault) { e.preventDefault() }
1193
+ else { e.returnValue = false }
1194
+ }
1195
+ function e_stopPropagation(e) {
1196
+ if (e.stopPropagation) { e.stopPropagation() }
1197
+ else { e.cancelBubble = true }
1198
+ }
1199
+ function e_defaultPrevented(e) {
1200
+ return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false
1201
+ }
1202
+ function e_stop(e) {e_preventDefault(e); e_stopPropagation(e)}
1203
+
1204
+ function e_target(e) {return e.target || e.srcElement}
1205
+ function e_button(e) {
1206
+ var b = e.which
1207
+ if (b == null) {
1208
+ if (e.button & 1) { b = 1 }
1209
+ else if (e.button & 2) { b = 3 }
1210
+ else if (e.button & 4) { b = 2 }
1211
+ }
1212
+ if (mac && e.ctrlKey && b == 1) { b = 3 }
1213
+ return b
1214
+ }
1215
+
1216
+ // Detect drag-and-drop
1217
+ var dragAndDrop = function() {
1218
+ // There is *some* kind of drag-and-drop support in IE6-8, but I
1219
+ // couldn't get it to work yet.
1220
+ if (ie && ie_version < 9) { return false }
1221
+ var div = elt('div')
1222
+ return "draggable" in div || "dragDrop" in div
1223
+ }()
1224
+
1225
+ var zwspSupported
1226
+ function zeroWidthElement(measure) {
1227
+ if (zwspSupported == null) {
1228
+ var test = elt("span", "\u200b")
1229
+ removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]))
1230
+ if (measure.firstChild.offsetHeight != 0)
1231
+ { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8) }
1232
+ }
1233
+ var node = zwspSupported ? elt("span", "\u200b") :
1234
+ elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px")
1235
+ node.setAttribute("cm-text", "")
1236
+ return node
1237
+ }
1238
+
1239
+ // Feature-detect IE's crummy client rect reporting for bidi text
1240
+ var badBidiRects
1241
+ function hasBadBidiRects(measure) {
1242
+ if (badBidiRects != null) { return badBidiRects }
1243
+ var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA"))
1244
+ var r0 = range(txt, 0, 1).getBoundingClientRect()
1245
+ var r1 = range(txt, 1, 2).getBoundingClientRect()
1246
+ removeChildren(measure)
1247
+ if (!r0 || r0.left == r0.right) { return false } // Safari returns null in some cases (#2780)
1248
+ return badBidiRects = (r1.right - r0.right < 3)
1249
+ }
1250
+
1251
+ // See if "".split is the broken IE version, if so, provide an
1252
+ // alternative way to split lines.
1253
+ var splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function (string) {
1254
+ var pos = 0, result = [], l = string.length
1255
+ while (pos <= l) {
1256
+ var nl = string.indexOf("\n", pos)
1257
+ if (nl == -1) { nl = string.length }
1258
+ var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl)
1259
+ var rt = line.indexOf("\r")
1260
+ if (rt != -1) {
1261
+ result.push(line.slice(0, rt))
1262
+ pos += rt + 1
1263
+ } else {
1264
+ result.push(line)
1265
+ pos = nl + 1
1266
+ }
1267
+ }
1268
+ return result
1269
+ } : function (string) { return string.split(/\r\n?|\n/); }
1270
+
1271
+ var hasSelection = window.getSelection ? function (te) {
1272
+ try { return te.selectionStart != te.selectionEnd }
1273
+ catch(e) { return false }
1274
+ } : function (te) {
1275
+ var range
1276
+ try {range = te.ownerDocument.selection.createRange()}
1277
+ catch(e) {}
1278
+ if (!range || range.parentElement() != te) { return false }
1279
+ return range.compareEndPoints("StartToEnd", range) != 0
1280
+ }
1281
+
1282
+ var hasCopyEvent = (function () {
1283
+ var e = elt("div")
1284
+ if ("oncopy" in e) { return true }
1285
+ e.setAttribute("oncopy", "return;")
1286
+ return typeof e.oncopy == "function"
1287
+ })()
1288
+
1289
+ var badZoomedRects = null
1290
+ function hasBadZoomedRects(measure) {
1291
+ if (badZoomedRects != null) { return badZoomedRects }
1292
+ var node = removeChildrenAndAdd(measure, elt("span", "x"))
1293
+ var normal = node.getBoundingClientRect()
1294
+ var fromRange = range(node, 0, 1).getBoundingClientRect()
1295
+ return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1
1296
+ }
1297
+
1298
+ var modes = {};
1299
+ var mimeModes = {};
1300
+ // Extra arguments are stored as the mode's dependencies, which is
1301
+ // used by (legacy) mechanisms like loadmode.js to automatically
1302
+ // load a mode. (Preferred mechanism is the require/define calls.)
1303
+ function defineMode(name, mode) {
1304
+ if (arguments.length > 2)
1305
+ { mode.dependencies = Array.prototype.slice.call(arguments, 2) }
1306
+ modes[name] = mode
1307
+ }
1308
+
1309
+ function defineMIME(mime, spec) {
1310
+ mimeModes[mime] = spec
1311
+ }
1312
+
1313
+ // Given a MIME type, a {name, ...options} config object, or a name
1314
+ // string, return a mode config object.
1315
+ function resolveMode(spec) {
1316
+ if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
1317
+ spec = mimeModes[spec]
1318
+ } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
1319
+ var found = mimeModes[spec.name]
1320
+ if (typeof found == "string") { found = {name: found} }
1321
+ spec = createObj(found, spec)
1322
+ spec.name = found.name
1323
+ } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
1324
+ return resolveMode("application/xml")
1325
+ } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) {
1326
+ return resolveMode("application/json")
1327
+ }
1328
+ if (typeof spec == "string") { return {name: spec} }
1329
+ else { return spec || {name: "null"} }
1330
+ }
1331
+
1332
+ // Given a mode spec (anything that resolveMode accepts), find and
1333
+ // initialize an actual mode object.
1334
+ function getMode(options, spec) {
1335
+ spec = resolveMode(spec)
1336
+ var mfactory = modes[spec.name]
1337
+ if (!mfactory) { return getMode(options, "text/plain") }
1338
+ var modeObj = mfactory(options, spec)
1339
+ if (modeExtensions.hasOwnProperty(spec.name)) {
1340
+ var exts = modeExtensions[spec.name]
1341
+ for (var prop in exts) {
1342
+ if (!exts.hasOwnProperty(prop)) { continue }
1343
+ if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop] }
1344
+ modeObj[prop] = exts[prop]
1345
+ }
1346
+ }
1347
+ modeObj.name = spec.name
1348
+ if (spec.helperType) { modeObj.helperType = spec.helperType }
1349
+ if (spec.modeProps) { for (var prop$1 in spec.modeProps)
1350
+ { modeObj[prop$1] = spec.modeProps[prop$1] } }
1351
+
1352
+ return modeObj
1353
+ }
1354
+
1355
+ // This can be used to attach properties to mode objects from
1356
+ // outside the actual mode definition.
1357
+ var modeExtensions = {}
1358
+ function extendMode(mode, properties) {
1359
+ var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {})
1360
+ copyObj(properties, exts)
1361
+ }
1362
+
1363
+ function copyState(mode, state) {
1364
+ if (state === true) { return state }
1365
+ if (mode.copyState) { return mode.copyState(state) }
1366
+ var nstate = {}
1367
+ for (var n in state) {
1368
+ var val = state[n]
1369
+ if (val instanceof Array) { val = val.concat([]) }
1370
+ nstate[n] = val
1371
+ }
1372
+ return nstate
1373
+ }
1374
+
1375
+ // Given a mode and a state (for that mode), find the inner mode and
1376
+ // state at the position that the state refers to.
1377
+ function innerMode(mode, state) {
1378
+ var info
1379
+ while (mode.innerMode) {
1380
+ info = mode.innerMode(state)
1381
+ if (!info || info.mode == mode) { break }
1382
+ state = info.state
1383
+ mode = info.mode
1384
+ }
1385
+ return info || {mode: mode, state: state}
1386
+ }
1387
+
1388
+ function startState(mode, a1, a2) {
1389
+ return mode.startState ? mode.startState(a1, a2) : true
1390
+ }
1391
+
1392
+ // STRING STREAM
1393
+
1394
+ // Fed to the mode parsers, provides helper functions to make
1395
+ // parsers more succinct.
1396
+
1397
+ var StringStream = function(string, tabSize, lineOracle) {
1398
+ this.pos = this.start = 0
1399
+ this.string = string
1400
+ this.tabSize = tabSize || 8
1401
+ this.lastColumnPos = this.lastColumnValue = 0
1402
+ this.lineStart = 0
1403
+ this.lineOracle = lineOracle
1404
+ };
1405
+
1406
+ StringStream.prototype.eol = function () {return this.pos >= this.string.length};
1407
+ StringStream.prototype.sol = function () {return this.pos == this.lineStart};
1408
+ StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined};
1409
+ StringStream.prototype.next = function () {
1410
+ if (this.pos < this.string.length)
1411
+ { return this.string.charAt(this.pos++) }
1412
+ };
1413
+ StringStream.prototype.eat = function (match) {
1414
+ var ch = this.string.charAt(this.pos)
1415
+ var ok
1416
+ if (typeof match == "string") { ok = ch == match }
1417
+ else { ok = ch && (match.test ? match.test(ch) : match(ch)) }
1418
+ if (ok) {++this.pos; return ch}
1419
+ };
1420
+ StringStream.prototype.eatWhile = function (match) {
1421
+ var start = this.pos
1422
+ while (this.eat(match)){}
1423
+ return this.pos > start
1424
+ };
1425
+ StringStream.prototype.eatSpace = function () {
1426
+ var this$1 = this;
1427
+
1428
+ var start = this.pos
1429
+ while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this$1.pos }
1430
+ return this.pos > start
1431
+ };
1432
+ StringStream.prototype.skipToEnd = function () {this.pos = this.string.length};
1433
+ StringStream.prototype.skipTo = function (ch) {
1434
+ var found = this.string.indexOf(ch, this.pos)
1435
+ if (found > -1) {this.pos = found; return true}
1436
+ };
1437
+ StringStream.prototype.backUp = function (n) {this.pos -= n};
1438
+ StringStream.prototype.column = function () {
1439
+ if (this.lastColumnPos < this.start) {
1440
+ this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue)
1441
+ this.lastColumnPos = this.start
1442
+ }
1443
+ return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
1444
+ };
1445
+ StringStream.prototype.indentation = function () {
1446
+ return countColumn(this.string, null, this.tabSize) -
1447
+ (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
1448
+ };
1449
+ StringStream.prototype.match = function (pattern, consume, caseInsensitive) {
1450
+ if (typeof pattern == "string") {
1451
+ var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; }
1452
+ var substr = this.string.substr(this.pos, pattern.length)
1453
+ if (cased(substr) == cased(pattern)) {
1454
+ if (consume !== false) { this.pos += pattern.length }
1455
+ return true
1456
+ }
1457
+ } else {
1458
+ var match = this.string.slice(this.pos).match(pattern)
1459
+ if (match && match.index > 0) { return null }
1460
+ if (match && consume !== false) { this.pos += match[0].length }
1461
+ return match
1462
+ }
1463
+ };
1464
+ StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)};
1465
+ StringStream.prototype.hideFirstChars = function (n, inner) {
1466
+ this.lineStart += n
1467
+ try { return inner() }
1468
+ finally { this.lineStart -= n }
1469
+ };
1470
+ StringStream.prototype.lookAhead = function (n) {
1471
+ var oracle = this.lineOracle
1472
+ return oracle && oracle.lookAhead(n)
1473
+ };
1474
+ StringStream.prototype.baseToken = function () {
1475
+ var oracle = this.lineOracle
1476
+ return oracle && oracle.baseToken(this.pos)
1477
+ };
1478
+
1479
+ var SavedContext = function(state, lookAhead) {
1480
+ this.state = state
1481
+ this.lookAhead = lookAhead
1482
+ };
1483
+
1484
+ var Context = function(doc, state, line, lookAhead) {
1485
+ this.state = state
1486
+ this.doc = doc
1487
+ this.line = line
1488
+ this.maxLookAhead = lookAhead || 0
1489
+ this.baseTokens = null
1490
+ this.baseTokenPos = 1
1491
+ };
1492
+
1493
+ Context.prototype.lookAhead = function (n) {
1494
+ var line = this.doc.getLine(this.line + n)
1495
+ if (line != null && n > this.maxLookAhead) { this.maxLookAhead = n }
1496
+ return line
1497
+ };
1498
+
1499
+ Context.prototype.baseToken = function (n) {
1500
+ var this$1 = this;
1501
+
1502
+ if (!this.baseTokens) { return null }
1503
+ while (this.baseTokens[this.baseTokenPos] <= n)
1504
+ { this$1.baseTokenPos += 2 }
1505
+ var type = this.baseTokens[this.baseTokenPos + 1]
1506
+ return {type: type && type.replace(/( |^)overlay .*/, ""),
1507
+ size: this.baseTokens[this.baseTokenPos] - n}
1508
+ };
1509
+
1510
+ Context.prototype.nextLine = function () {
1511
+ this.line++
1512
+ if (this.maxLookAhead > 0) { this.maxLookAhead-- }
1513
+ };
1514
+
1515
+ Context.fromSaved = function (doc, saved, line) {
1516
+ if (saved instanceof SavedContext)
1517
+ { return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead) }
1518
+ else
1519
+ { return new Context(doc, copyState(doc.mode, saved), line) }
1520
+ };
1521
+
1522
+ Context.prototype.save = function (copy) {
1523
+ var state = copy !== false ? copyState(this.doc.mode, this.state) : this.state
1524
+ return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state
1525
+ };
1526
+
1527
+
1528
+ // Compute a style array (an array starting with a mode generation
1529
+ // -- for invalidation -- followed by pairs of end positions and
1530
+ // style strings), which is used to highlight the tokens on the
1531
+ // line.
1532
+ function highlightLine(cm, line, context, forceToEnd) {
1533
+ // A styles array always starts with a number identifying the
1534
+ // mode/overlays that it is based on (for easy invalidation).
1535
+ var st = [cm.state.modeGen], lineClasses = {}
1536
+ // Compute the base array of styles
1537
+ runMode(cm, line.text, cm.doc.mode, context, function (end, style) { return st.push(end, style); },
1538
+ lineClasses, forceToEnd)
1539
+ var state = context.state
1540
+
1541
+ // Run overlays, adjust style array.
1542
+ var loop = function ( o ) {
1543
+ context.baseTokens = st
1544
+ var overlay = cm.state.overlays[o], i = 1, at = 0
1545
+ context.state = true
1546
+ runMode(cm, line.text, overlay.mode, context, function (end, style) {
1547
+ var start = i
1548
+ // Ensure there's a token end at the current position, and that i points at it
1549
+ while (at < end) {
1550
+ var i_end = st[i]
1551
+ if (i_end > end)
1552
+ { st.splice(i, 1, end, st[i+1], i_end) }
1553
+ i += 2
1554
+ at = Math.min(end, i_end)
1555
+ }
1556
+ if (!style) { return }
1557
+ if (overlay.opaque) {
1558
+ st.splice(start, i - start, end, "overlay " + style)
1559
+ i = start + 2
1560
+ } else {
1561
+ for (; start < i; start += 2) {
1562
+ var cur = st[start+1]
1563
+ st[start+1] = (cur ? cur + " " : "") + "overlay " + style
1564
+ }
1565
+ }
1566
+ }, lineClasses)
1567
+ context.state = state
1568
+ context.baseTokens = null
1569
+ context.baseTokenPos = 1
1570
+ };
1571
+
1572
+ for (var o = 0; o < cm.state.overlays.length; ++o) loop( o );
1573
+
1574
+ return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null}
1575
+ }
1576
+
1577
+ function getLineStyles(cm, line, updateFrontier) {
1578
+ if (!line.styles || line.styles[0] != cm.state.modeGen) {
1579
+ var context = getContextBefore(cm, lineNo(line))
1580
+ var resetState = line.text.length > cm.options.maxHighlightLength && copyState(cm.doc.mode, context.state)
1581
+ var result = highlightLine(cm, line, context)
1582
+ if (resetState) { context.state = resetState }
1583
+ line.stateAfter = context.save(!resetState)
1584
+ line.styles = result.styles
1585
+ if (result.classes) { line.styleClasses = result.classes }
1586
+ else if (line.styleClasses) { line.styleClasses = null }
1587
+ if (updateFrontier === cm.doc.highlightFrontier)
1588
+ { cm.doc.modeFrontier = Math.max(cm.doc.modeFrontier, ++cm.doc.highlightFrontier) }
1589
+ }
1590
+ return line.styles
1591
+ }
1592
+
1593
+ function getContextBefore(cm, n, precise) {
1594
+ var doc = cm.doc, display = cm.display
1595
+ if (!doc.mode.startState) { return new Context(doc, true, n) }
1596
+ var start = findStartLine(cm, n, precise)
1597
+ var saved = start > doc.first && getLine(doc, start - 1).stateAfter
1598
+ var context = saved ? Context.fromSaved(doc, saved, start) : new Context(doc, startState(doc.mode), start)
1599
+
1600
+ doc.iter(start, n, function (line) {
1601
+ processLine(cm, line.text, context)
1602
+ var pos = context.line
1603
+ line.stateAfter = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo ? context.save() : null
1604
+ context.nextLine()
1605
+ })
1606
+ if (precise) { doc.modeFrontier = context.line }
1607
+ return context
1608
+ }
1609
+
1610
+ // Lightweight form of highlight -- proceed over this line and
1611
+ // update state, but don't save a style array. Used for lines that
1612
+ // aren't currently visible.
1613
+ function processLine(cm, text, context, startAt) {
1614
+ var mode = cm.doc.mode
1615
+ var stream = new StringStream(text, cm.options.tabSize, context)
1616
+ stream.start = stream.pos = startAt || 0
1617
+ if (text == "") { callBlankLine(mode, context.state) }
1618
+ while (!stream.eol()) {
1619
+ readToken(mode, stream, context.state)
1620
+ stream.start = stream.pos
1621
+ }
1622
+ }
1623
+
1624
+ function callBlankLine(mode, state) {
1625
+ if (mode.blankLine) { return mode.blankLine(state) }
1626
+ if (!mode.innerMode) { return }
1627
+ var inner = innerMode(mode, state)
1628
+ if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) }
1629
+ }
1630
+
1631
+ function readToken(mode, stream, state, inner) {
1632
+ for (var i = 0; i < 10; i++) {
1633
+ if (inner) { inner[0] = innerMode(mode, state).mode }
1634
+ var style = mode.token(stream, state)
1635
+ if (stream.pos > stream.start) { return style }
1636
+ }
1637
+ throw new Error("Mode " + mode.name + " failed to advance stream.")
1638
+ }
1639
+
1640
+ var Token = function(stream, type, state) {
1641
+ this.start = stream.start; this.end = stream.pos
1642
+ this.string = stream.current()
1643
+ this.type = type || null
1644
+ this.state = state
1645
+ };
1646
+
1647
+ // Utility for getTokenAt and getLineTokens
1648
+ function takeToken(cm, pos, precise, asArray) {
1649
+ var doc = cm.doc, mode = doc.mode, style
1650
+ pos = clipPos(doc, pos)
1651
+ var line = getLine(doc, pos.line), context = getContextBefore(cm, pos.line, precise)
1652
+ var stream = new StringStream(line.text, cm.options.tabSize, context), tokens
1653
+ if (asArray) { tokens = [] }
1654
+ while ((asArray || stream.pos < pos.ch) && !stream.eol()) {
1655
+ stream.start = stream.pos
1656
+ style = readToken(mode, stream, context.state)
1657
+ if (asArray) { tokens.push(new Token(stream, style, copyState(doc.mode, context.state))) }
1658
+ }
1659
+ return asArray ? tokens : new Token(stream, style, context.state)
1660
+ }
1661
+
1662
+ function extractLineClasses(type, output) {
1663
+ if (type) { for (;;) {
1664
+ var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/)
1665
+ if (!lineClass) { break }
1666
+ type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length)
1667
+ var prop = lineClass[1] ? "bgClass" : "textClass"
1668
+ if (output[prop] == null)
1669
+ { output[prop] = lineClass[2] }
1670
+ else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop]))
1671
+ { output[prop] += " " + lineClass[2] }
1672
+ } }
1673
+ return type
1674
+ }
1675
+
1676
+ // Run the given mode's parser over a line, calling f for each token.
1677
+ function runMode(cm, text, mode, context, f, lineClasses, forceToEnd) {
1678
+ var flattenSpans = mode.flattenSpans
1679
+ if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans }
1680
+ var curStart = 0, curStyle = null
1681
+ var stream = new StringStream(text, cm.options.tabSize, context), style
1682
+ var inner = cm.options.addModeClass && [null]
1683
+ if (text == "") { extractLineClasses(callBlankLine(mode, context.state), lineClasses) }
1684
+ while (!stream.eol()) {
1685
+ if (stream.pos > cm.options.maxHighlightLength) {
1686
+ flattenSpans = false
1687
+ if (forceToEnd) { processLine(cm, text, context, stream.pos) }
1688
+ stream.pos = text.length
1689
+ style = null
1690
+ } else {
1691
+ style = extractLineClasses(readToken(mode, stream, context.state, inner), lineClasses)
1692
+ }
1693
+ if (inner) {
1694
+ var mName = inner[0].name
1695
+ if (mName) { style = "m-" + (style ? mName + " " + style : mName) }
1696
+ }
1697
+ if (!flattenSpans || curStyle != style) {
1698
+ while (curStart < stream.start) {
1699
+ curStart = Math.min(stream.start, curStart + 5000)
1700
+ f(curStart, curStyle)
1701
+ }
1702
+ curStyle = style
1703
+ }
1704
+ stream.start = stream.pos
1705
+ }
1706
+ while (curStart < stream.pos) {
1707
+ // Webkit seems to refuse to render text nodes longer than 57444
1708
+ // characters, and returns inaccurate measurements in nodes
1709
+ // starting around 5000 chars.
1710
+ var pos = Math.min(stream.pos, curStart + 5000)
1711
+ f(pos, curStyle)
1712
+ curStart = pos
1713
+ }
1714
+ }
1715
+
1716
+ // Finds the line to start with when starting a parse. Tries to
1717
+ // find a line with a stateAfter, so that it can start with a
1718
+ // valid state. If that fails, it returns the line with the
1719
+ // smallest indentation, which tends to need the least context to
1720
+ // parse correctly.
1721
+ function findStartLine(cm, n, precise) {
1722
+ var minindent, minline, doc = cm.doc
1723
+ var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100)
1724
+ for (var search = n; search > lim; --search) {
1725
+ if (search <= doc.first) { return doc.first }
1726
+ var line = getLine(doc, search - 1), after = line.stateAfter
1727
+ if (after && (!precise || search + (after instanceof SavedContext ? after.lookAhead : 0) <= doc.modeFrontier))
1728
+ { return search }
1729
+ var indented = countColumn(line.text, null, cm.options.tabSize)
1730
+ if (minline == null || minindent > indented) {
1731
+ minline = search - 1
1732
+ minindent = indented
1733
+ }
1734
+ }
1735
+ return minline
1736
+ }
1737
+
1738
+ function retreatFrontier(doc, n) {
1739
+ doc.modeFrontier = Math.min(doc.modeFrontier, n)
1740
+ if (doc.highlightFrontier < n - 10) { return }
1741
+ var start = doc.first
1742
+ for (var line = n - 1; line > start; line--) {
1743
+ var saved = getLine(doc, line).stateAfter
1744
+ // change is on 3
1745
+ // state on line 1 looked ahead 2 -- so saw 3
1746
+ // test 1 + 2 < 3 should cover this
1747
+ if (saved && (!(saved instanceof SavedContext) || line + saved.lookAhead < n)) {
1748
+ start = line + 1
1749
+ break
1750
+ }
1751
+ }
1752
+ doc.highlightFrontier = Math.min(doc.highlightFrontier, start)
1753
+ }
1754
+
1755
+ // LINE DATA STRUCTURE
1756
+
1757
+ // Line objects. These hold state related to a line, including
1758
+ // highlighting info (the styles array).
1759
+ var Line = function(text, markedSpans, estimateHeight) {
1760
+ this.text = text
1761
+ attachMarkedSpans(this, markedSpans)
1762
+ this.height = estimateHeight ? estimateHeight(this) : 1
1763
+ };
1764
+
1765
+ Line.prototype.lineNo = function () { return lineNo(this) };
1766
+ eventMixin(Line)
1767
+
1768
+ // Change the content (text, markers) of a line. Automatically
1769
+ // invalidates cached information and tries to re-estimate the
1770
+ // line's height.
1771
+ function updateLine(line, text, markedSpans, estimateHeight) {
1772
+ line.text = text
1773
+ if (line.stateAfter) { line.stateAfter = null }
1774
+ if (line.styles) { line.styles = null }
1775
+ if (line.order != null) { line.order = null }
1776
+ detachMarkedSpans(line)
1777
+ attachMarkedSpans(line, markedSpans)
1778
+ var estHeight = estimateHeight ? estimateHeight(line) : 1
1779
+ if (estHeight != line.height) { updateLineHeight(line, estHeight) }
1780
+ }
1781
+
1782
+ // Detach a line from the document tree and its markers.
1783
+ function cleanUpLine(line) {
1784
+ line.parent = null
1785
+ detachMarkedSpans(line)
1786
+ }
1787
+
1788
+ // Convert a style as returned by a mode (either null, or a string
1789
+ // containing one or more styles) to a CSS style. This is cached,
1790
+ // and also looks for line-wide styles.
1791
+ var styleToClassCache = {};
1792
+ var styleToClassCacheWithMode = {};
1793
+ function interpretTokenStyle(style, options) {
1794
+ if (!style || /^\s*$/.test(style)) { return null }
1795
+ var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache
1796
+ return cache[style] ||
1797
+ (cache[style] = style.replace(/\S+/g, "cm-$&"))
1798
+ }
1799
+
1800
+ // Render the DOM representation of the text of a line. Also builds
1801
+ // up a 'line map', which points at the DOM nodes that represent
1802
+ // specific stretches of text, and is used by the measuring code.
1803
+ // The returned object contains the DOM node, this map, and
1804
+ // information about line-wide styles that were set by the mode.
1805
+ function buildLineContent(cm, lineView) {
1806
+ // The padding-right forces the element to have a 'border', which
1807
+ // is needed on Webkit to be able to get line-level bounding
1808
+ // rectangles for it (in measureChar).
1809
+ var content = eltP("span", null, null, webkit ? "padding-right: .1px" : null)
1810
+ var builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: content,
1811
+ col: 0, pos: 0, cm: cm,
1812
+ trailingSpace: false,
1813
+ splitSpaces: (ie || webkit) && cm.getOption("lineWrapping")}
1814
+ lineView.measure = {}
1815
+
1816
+ // Iterate over the logical lines that make up this visual line.
1817
+ for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {
1818
+ var line = i ? lineView.rest[i - 1] : lineView.line, order = (void 0)
1819
+ builder.pos = 0
1820
+ builder.addToken = buildToken
1821
+ // Optionally wire in some hacks into the token-rendering
1822
+ // algorithm, to deal with browser quirks.
1823
+ if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction)))
1824
+ { builder.addToken = buildTokenBadBidi(builder.addToken, order) }
1825
+ builder.map = []
1826
+ var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line)
1827
+ insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate))
1828
+ if (line.styleClasses) {
1829
+ if (line.styleClasses.bgClass)
1830
+ { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || "") }
1831
+ if (line.styleClasses.textClass)
1832
+ { builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || "") }
1833
+ }
1834
+
1835
+ // Ensure at least a single node is present, for measuring.
1836
+ if (builder.map.length == 0)
1837
+ { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))) }
1838
+
1839
+ // Store the map and a cache object for the current logical line
1840
+ if (i == 0) {
1841
+ lineView.measure.map = builder.map
1842
+ lineView.measure.cache = {}
1843
+ } else {
1844
+ ;(lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map)
1845
+ ;(lineView.measure.caches || (lineView.measure.caches = [])).push({})
1846
+ }
1847
+ }
1848
+
1849
+ // See issue #2901
1850
+ if (webkit) {
1851
+ var last = builder.content.lastChild
1852
+ if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab")))
1853
+ { builder.content.className = "cm-tab-wrap-hack" }
1854
+ }
1855
+
1856
+ signal(cm, "renderLine", cm, lineView.line, builder.pre)
1857
+ if (builder.pre.className)
1858
+ { builder.textClass = joinClasses(builder.pre.className, builder.textClass || "") }
1859
+
1860
+ return builder
1861
+ }
1862
+
1863
+ function defaultSpecialCharPlaceholder(ch) {
1864
+ var token = elt("span", "\u2022", "cm-invalidchar")
1865
+ token.title = "\\u" + ch.charCodeAt(0).toString(16)
1866
+ token.setAttribute("aria-label", token.title)
1867
+ return token
1868
+ }
1869
+
1870
+ // Build up the DOM representation for a single token, and add it to
1871
+ // the line map. Takes care to render special characters separately.
1872
+ function buildToken(builder, text, style, startStyle, endStyle, title, css) {
1873
+ if (!text) { return }
1874
+ var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text
1875
+ var special = builder.cm.state.specialChars, mustWrap = false
1876
+ var content
1877
+ if (!special.test(text)) {
1878
+ builder.col += text.length
1879
+ content = document.createTextNode(displayText)
1880
+ builder.map.push(builder.pos, builder.pos + text.length, content)
1881
+ if (ie && ie_version < 9) { mustWrap = true }
1882
+ builder.pos += text.length
1883
+ } else {
1884
+ content = document.createDocumentFragment()
1885
+ var pos = 0
1886
+ while (true) {
1887
+ special.lastIndex = pos
1888
+ var m = special.exec(text)
1889
+ var skipped = m ? m.index - pos : text.length - pos
1890
+ if (skipped) {
1891
+ var txt = document.createTextNode(displayText.slice(pos, pos + skipped))
1892
+ if (ie && ie_version < 9) { content.appendChild(elt("span", [txt])) }
1893
+ else { content.appendChild(txt) }
1894
+ builder.map.push(builder.pos, builder.pos + skipped, txt)
1895
+ builder.col += skipped
1896
+ builder.pos += skipped
1897
+ }
1898
+ if (!m) { break }
1899
+ pos += skipped + 1
1900
+ var txt$1 = (void 0)
1901
+ if (m[0] == "\t") {
1902
+ var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize
1903
+ txt$1 = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"))
1904
+ txt$1.setAttribute("role", "presentation")
1905
+ txt$1.setAttribute("cm-text", "\t")
1906
+ builder.col += tabWidth
1907
+ } else if (m[0] == "\r" || m[0] == "\n") {
1908
+ txt$1 = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar"))
1909
+ txt$1.setAttribute("cm-text", m[0])
1910
+ builder.col += 1
1911
+ } else {
1912
+ txt$1 = builder.cm.options.specialCharPlaceholder(m[0])
1913
+ txt$1.setAttribute("cm-text", m[0])
1914
+ if (ie && ie_version < 9) { content.appendChild(elt("span", [txt$1])) }
1915
+ else { content.appendChild(txt$1) }
1916
+ builder.col += 1
1917
+ }
1918
+ builder.map.push(builder.pos, builder.pos + 1, txt$1)
1919
+ builder.pos++
1920
+ }
1921
+ }
1922
+ builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32
1923
+ if (style || startStyle || endStyle || mustWrap || css) {
1924
+ var fullStyle = style || ""
1925
+ if (startStyle) { fullStyle += startStyle }
1926
+ if (endStyle) { fullStyle += endStyle }
1927
+ var token = elt("span", [content], fullStyle, css)
1928
+ if (title) { token.title = title }
1929
+ return builder.content.appendChild(token)
1930
+ }
1931
+ builder.content.appendChild(content)
1932
+ }
1933
+
1934
+ function splitSpaces(text, trailingBefore) {
1935
+ if (text.length > 1 && !/ /.test(text)) { return text }
1936
+ var spaceBefore = trailingBefore, result = ""
1937
+ for (var i = 0; i < text.length; i++) {
1938
+ var ch = text.charAt(i)
1939
+ if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32))
1940
+ { ch = "\u00a0" }
1941
+ result += ch
1942
+ spaceBefore = ch == " "
1943
+ }
1944
+ return result
1945
+ }
1946
+
1947
+ // Work around nonsense dimensions being reported for stretches of
1948
+ // right-to-left text.
1949
+ function buildTokenBadBidi(inner, order) {
1950
+ return function (builder, text, style, startStyle, endStyle, title, css) {
1951
+ style = style ? style + " cm-force-border" : "cm-force-border"
1952
+ var start = builder.pos, end = start + text.length
1953
+ for (;;) {
1954
+ // Find the part that overlaps with the start of this text
1955
+ var part = (void 0)
1956
+ for (var i = 0; i < order.length; i++) {
1957
+ part = order[i]
1958
+ if (part.to > start && part.from <= start) { break }
1959
+ }
1960
+ if (part.to >= end) { return inner(builder, text, style, startStyle, endStyle, title, css) }
1961
+ inner(builder, text.slice(0, part.to - start), style, startStyle, null, title, css)
1962
+ startStyle = null
1963
+ text = text.slice(part.to - start)
1964
+ start = part.to
1965
+ }
1966
+ }
1967
+ }
1968
+
1969
+ function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
1970
+ var widget = !ignoreWidget && marker.widgetNode
1971
+ if (widget) { builder.map.push(builder.pos, builder.pos + size, widget) }
1972
+ if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {
1973
+ if (!widget)
1974
+ { widget = builder.content.appendChild(document.createElement("span")) }
1975
+ widget.setAttribute("cm-marker", marker.id)
1976
+ }
1977
+ if (widget) {
1978
+ builder.cm.display.input.setUneditable(widget)
1979
+ builder.content.appendChild(widget)
1980
+ }
1981
+ builder.pos += size
1982
+ builder.trailingSpace = false
1983
+ }
1984
+
1985
+ // Outputs a number of spans to make up a line, taking highlighting
1986
+ // and marked text into account.
1987
+ function insertLineContent(line, builder, styles) {
1988
+ var spans = line.markedSpans, allText = line.text, at = 0
1989
+ if (!spans) {
1990
+ for (var i$1 = 1; i$1 < styles.length; i$1+=2)
1991
+ { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpretTokenStyle(styles[i$1+1], builder.cm.options)) }
1992
+ return
1993
+ }
1994
+
1995
+ var len = allText.length, pos = 0, i = 1, text = "", style, css
1996
+ var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed
1997
+ for (;;) {
1998
+ if (nextChange == pos) { // Update current marker set
1999
+ spanStyle = spanEndStyle = spanStartStyle = title = css = ""
2000
+ collapsed = null; nextChange = Infinity
2001
+ var foundBookmarks = [], endStyles = (void 0)
2002
+ for (var j = 0; j < spans.length; ++j) {
2003
+ var sp = spans[j], m = sp.marker
2004
+ if (m.type == "bookmark" && sp.from == pos && m.widgetNode) {
2005
+ foundBookmarks.push(m)
2006
+ } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) {
2007
+ if (sp.to != null && sp.to != pos && nextChange > sp.to) {
2008
+ nextChange = sp.to
2009
+ spanEndStyle = ""
2010
+ }
2011
+ if (m.className) { spanStyle += " " + m.className }
2012
+ if (m.css) { css = (css ? css + ";" : "") + m.css }
2013
+ if (m.startStyle && sp.from == pos) { spanStartStyle += " " + m.startStyle }
2014
+ if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [])).push(m.endStyle, sp.to) }
2015
+ if (m.title && !title) { title = m.title }
2016
+ if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))
2017
+ { collapsed = sp }
2018
+ } else if (sp.from > pos && nextChange > sp.from) {
2019
+ nextChange = sp.from
2020
+ }
2021
+ }
2022
+ if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2)
2023
+ { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += " " + endStyles[j$1] } } }
2024
+
2025
+ if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2)
2026
+ { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]) } }
2027
+ if (collapsed && (collapsed.from || 0) == pos) {
2028
+ buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos,
2029
+ collapsed.marker, collapsed.from == null)
2030
+ if (collapsed.to == null) { return }
2031
+ if (collapsed.to == pos) { collapsed = false }
2032
+ }
2033
+ }
2034
+ if (pos >= len) { break }
2035
+
2036
+ var upto = Math.min(len, nextChange)
2037
+ while (true) {
2038
+ if (text) {
2039
+ var end = pos + text.length
2040
+ if (!collapsed) {
2041
+ var tokenText = end > upto ? text.slice(0, upto - pos) : text
2042
+ builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
2043
+ spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", title, css)
2044
+ }
2045
+ if (end >= upto) {text = text.slice(upto - pos); pos = upto; break}
2046
+ pos = end
2047
+ spanStartStyle = ""
2048
+ }
2049
+ text = allText.slice(at, at = styles[i++])
2050
+ style = interpretTokenStyle(styles[i++], builder.cm.options)
2051
+ }
2052
+ }
2053
+ }
2054
+
2055
+
2056
+ // These objects are used to represent the visible (currently drawn)
2057
+ // part of the document. A LineView may correspond to multiple
2058
+ // logical lines, if those are connected by collapsed ranges.
2059
+ function LineView(doc, line, lineN) {
2060
+ // The starting line
2061
+ this.line = line
2062
+ // Continuing lines, if any
2063
+ this.rest = visualLineContinued(line)
2064
+ // Number of logical lines in this visual line
2065
+ this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1
2066
+ this.node = this.text = null
2067
+ this.hidden = lineIsHidden(doc, line)
2068
+ }
2069
+
2070
+ // Create a range of LineView objects for the given lines.
2071
+ function buildViewArray(cm, from, to) {
2072
+ var array = [], nextPos
2073
+ for (var pos = from; pos < to; pos = nextPos) {
2074
+ var view = new LineView(cm.doc, getLine(cm.doc, pos), pos)
2075
+ nextPos = pos + view.size
2076
+ array.push(view)
2077
+ }
2078
+ return array
2079
+ }
2080
+
2081
+ var operationGroup = null
2082
+
2083
+ function pushOperation(op) {
2084
+ if (operationGroup) {
2085
+ operationGroup.ops.push(op)
2086
+ } else {
2087
+ op.ownsGroup = operationGroup = {
2088
+ ops: [op],
2089
+ delayedCallbacks: []
2090
+ }
2091
+ }
2092
+ }
2093
+
2094
+ function fireCallbacksForOps(group) {
2095
+ // Calls delayed callbacks and cursorActivity handlers until no
2096
+ // new ones appear
2097
+ var callbacks = group.delayedCallbacks, i = 0
2098
+ do {
2099
+ for (; i < callbacks.length; i++)
2100
+ { callbacks[i].call(null) }
2101
+ for (var j = 0; j < group.ops.length; j++) {
2102
+ var op = group.ops[j]
2103
+ if (op.cursorActivityHandlers)
2104
+ { while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
2105
+ { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm) } }
2106
+ }
2107
+ } while (i < callbacks.length)
2108
+ }
2109
+
2110
+ function finishOperation(op, endCb) {
2111
+ var group = op.ownsGroup
2112
+ if (!group) { return }
2113
+
2114
+ try { fireCallbacksForOps(group) }
2115
+ finally {
2116
+ operationGroup = null
2117
+ endCb(group)
2118
+ }
2119
+ }
2120
+
2121
+ var orphanDelayedCallbacks = null
2122
+
2123
+ // Often, we want to signal events at a point where we are in the
2124
+ // middle of some work, but don't want the handler to start calling
2125
+ // other methods on the editor, which might be in an inconsistent
2126
+ // state or simply not expect any other events to happen.
2127
+ // signalLater looks whether there are any handlers, and schedules
2128
+ // them to be executed when the last operation ends, or, if no
2129
+ // operation is active, when a timeout fires.
2130
+ function signalLater(emitter, type /*, values...*/) {
2131
+ var arr = getHandlers(emitter, type)
2132
+ if (!arr.length) { return }
2133
+ var args = Array.prototype.slice.call(arguments, 2), list
2134
+ if (operationGroup) {
2135
+ list = operationGroup.delayedCallbacks
2136
+ } else if (orphanDelayedCallbacks) {
2137
+ list = orphanDelayedCallbacks
2138
+ } else {
2139
+ list = orphanDelayedCallbacks = []
2140
+ setTimeout(fireOrphanDelayed, 0)
2141
+ }
2142
+ var loop = function ( i ) {
2143
+ list.push(function () { return arr[i].apply(null, args); })
2144
+ };
2145
+
2146
+ for (var i = 0; i < arr.length; ++i)
2147
+ loop( i );
2148
+ }
2149
+
2150
+ function fireOrphanDelayed() {
2151
+ var delayed = orphanDelayedCallbacks
2152
+ orphanDelayedCallbacks = null
2153
+ for (var i = 0; i < delayed.length; ++i) { delayed[i]() }
2154
+ }
2155
+
2156
+ // When an aspect of a line changes, a string is added to
2157
+ // lineView.changes. This updates the relevant part of the line's
2158
+ // DOM structure.
2159
+ function updateLineForChanges(cm, lineView, lineN, dims) {
2160
+ for (var j = 0; j < lineView.changes.length; j++) {
2161
+ var type = lineView.changes[j]
2162
+ if (type == "text") { updateLineText(cm, lineView) }
2163
+ else if (type == "gutter") { updateLineGutter(cm, lineView, lineN, dims) }
2164
+ else if (type == "class") { updateLineClasses(cm, lineView) }
2165
+ else if (type == "widget") { updateLineWidgets(cm, lineView, dims) }
2166
+ }
2167
+ lineView.changes = null
2168
+ }
2169
+
2170
+ // Lines with gutter elements, widgets or a background class need to
2171
+ // be wrapped, and have the extra elements added to the wrapper div
2172
+ function ensureLineWrapped(lineView) {
2173
+ if (lineView.node == lineView.text) {
2174
+ lineView.node = elt("div", null, null, "position: relative")
2175
+ if (lineView.text.parentNode)
2176
+ { lineView.text.parentNode.replaceChild(lineView.node, lineView.text) }
2177
+ lineView.node.appendChild(lineView.text)
2178
+ if (ie && ie_version < 8) { lineView.node.style.zIndex = 2 }
2179
+ }
2180
+ return lineView.node
2181
+ }
2182
+
2183
+ function updateLineBackground(cm, lineView) {
2184
+ var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass
2185
+ if (cls) { cls += " CodeMirror-linebackground" }
2186
+ if (lineView.background) {
2187
+ if (cls) { lineView.background.className = cls }
2188
+ else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null }
2189
+ } else if (cls) {
2190
+ var wrap = ensureLineWrapped(lineView)
2191
+ lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild)
2192
+ cm.display.input.setUneditable(lineView.background)
2193
+ }
2194
+ }
2195
+
2196
+ // Wrapper around buildLineContent which will reuse the structure
2197
+ // in display.externalMeasured when possible.
2198
+ function getLineContent(cm, lineView) {
2199
+ var ext = cm.display.externalMeasured
2200
+ if (ext && ext.line == lineView.line) {
2201
+ cm.display.externalMeasured = null
2202
+ lineView.measure = ext.measure
2203
+ return ext.built
2204
+ }
2205
+ return buildLineContent(cm, lineView)
2206
+ }
2207
+
2208
+ // Redraw the line's text. Interacts with the background and text
2209
+ // classes because the mode may output tokens that influence these
2210
+ // classes.
2211
+ function updateLineText(cm, lineView) {
2212
+ var cls = lineView.text.className
2213
+ var built = getLineContent(cm, lineView)
2214
+ if (lineView.text == lineView.node) { lineView.node = built.pre }
2215
+ lineView.text.parentNode.replaceChild(built.pre, lineView.text)
2216
+ lineView.text = built.pre
2217
+ if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {
2218
+ lineView.bgClass = built.bgClass
2219
+ lineView.textClass = built.textClass
2220
+ updateLineClasses(cm, lineView)
2221
+ } else if (cls) {
2222
+ lineView.text.className = cls
2223
+ }
2224
+ }
2225
+
2226
+ function updateLineClasses(cm, lineView) {
2227
+ updateLineBackground(cm, lineView)
2228
+ if (lineView.line.wrapClass)
2229
+ { ensureLineWrapped(lineView).className = lineView.line.wrapClass }
2230
+ else if (lineView.node != lineView.text)
2231
+ { lineView.node.className = "" }
2232
+ var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass
2233
+ lineView.text.className = textClass || ""
2234
+ }
2235
+
2236
+ function updateLineGutter(cm, lineView, lineN, dims) {
2237
+ if (lineView.gutter) {
2238
+ lineView.node.removeChild(lineView.gutter)
2239
+ lineView.gutter = null
2240
+ }
2241
+ if (lineView.gutterBackground) {
2242
+ lineView.node.removeChild(lineView.gutterBackground)
2243
+ lineView.gutterBackground = null
2244
+ }
2245
+ if (lineView.line.gutterClass) {
2246
+ var wrap = ensureLineWrapped(lineView)
2247
+ lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass,
2248
+ ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px; width: " + (dims.gutterTotalWidth) + "px"))
2249
+ cm.display.input.setUneditable(lineView.gutterBackground)
2250
+ wrap.insertBefore(lineView.gutterBackground, lineView.text)
2251
+ }
2252
+ var markers = lineView.line.gutterMarkers
2253
+ if (cm.options.lineNumbers || markers) {
2254
+ var wrap$1 = ensureLineWrapped(lineView)
2255
+ var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"))
2256
+ cm.display.input.setUneditable(gutterWrap)
2257
+ wrap$1.insertBefore(gutterWrap, lineView.text)
2258
+ if (lineView.line.gutterClass)
2259
+ { gutterWrap.className += " " + lineView.line.gutterClass }
2260
+ if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
2261
+ { lineView.lineNumber = gutterWrap.appendChild(
2262
+ elt("div", lineNumberFor(cm.options, lineN),
2263
+ "CodeMirror-linenumber CodeMirror-gutter-elt",
2264
+ ("left: " + (dims.gutterLeft["CodeMirror-linenumbers"]) + "px; width: " + (cm.display.lineNumInnerWidth) + "px"))) }
2265
+ if (markers) { for (var k = 0; k < cm.options.gutters.length; ++k) {
2266
+ var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id]
2267
+ if (found)
2268
+ { gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt",
2269
+ ("left: " + (dims.gutterLeft[id]) + "px; width: " + (dims.gutterWidth[id]) + "px"))) }
2270
+ } }
2271
+ }
2272
+ }
2273
+
2274
+ function updateLineWidgets(cm, lineView, dims) {
2275
+ if (lineView.alignable) { lineView.alignable = null }
2276
+ for (var node = lineView.node.firstChild, next = (void 0); node; node = next) {
2277
+ next = node.nextSibling
2278
+ if (node.className == "CodeMirror-linewidget")
2279
+ { lineView.node.removeChild(node) }
2280
+ }
2281
+ insertLineWidgets(cm, lineView, dims)
2282
+ }
2283
+
2284
+ // Build a line's DOM representation from scratch
2285
+ function buildLineElement(cm, lineView, lineN, dims) {
2286
+ var built = getLineContent(cm, lineView)
2287
+ lineView.text = lineView.node = built.pre
2288
+ if (built.bgClass) { lineView.bgClass = built.bgClass }
2289
+ if (built.textClass) { lineView.textClass = built.textClass }
2290
+
2291
+ updateLineClasses(cm, lineView)
2292
+ updateLineGutter(cm, lineView, lineN, dims)
2293
+ insertLineWidgets(cm, lineView, dims)
2294
+ return lineView.node
2295
+ }
2296
+
2297
+ // A lineView may contain multiple logical lines (when merged by
2298
+ // collapsed spans). The widgets for all of them need to be drawn.
2299
+ function insertLineWidgets(cm, lineView, dims) {
2300
+ insertLineWidgetsFor(cm, lineView.line, lineView, dims, true)
2301
+ if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
2302
+ { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false) } }
2303
+ }
2304
+
2305
+ function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {
2306
+ if (!line.widgets) { return }
2307
+ var wrap = ensureLineWrapped(lineView)
2308
+ for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
2309
+ var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget")
2310
+ if (!widget.handleMouseEvents) { node.setAttribute("cm-ignore-events", "true") }
2311
+ positionLineWidget(widget, node, lineView, dims)
2312
+ cm.display.input.setUneditable(node)
2313
+ if (allowAbove && widget.above)
2314
+ { wrap.insertBefore(node, lineView.gutter || lineView.text) }
2315
+ else
2316
+ { wrap.appendChild(node) }
2317
+ signalLater(widget, "redraw")
2318
+ }
2319
+ }
2320
+
2321
+ function positionLineWidget(widget, node, lineView, dims) {
2322
+ if (widget.noHScroll) {
2323
+ ;(lineView.alignable || (lineView.alignable = [])).push(node)
2324
+ var width = dims.wrapperWidth
2325
+ node.style.left = dims.fixedPos + "px"
2326
+ if (!widget.coverGutter) {
2327
+ width -= dims.gutterTotalWidth
2328
+ node.style.paddingLeft = dims.gutterTotalWidth + "px"
2329
+ }
2330
+ node.style.width = width + "px"
2331
+ }
2332
+ if (widget.coverGutter) {
2333
+ node.style.zIndex = 5
2334
+ node.style.position = "relative"
2335
+ if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + "px" }
2336
+ }
2337
+ }
2338
+
2339
+ function widgetHeight(widget) {
2340
+ if (widget.height != null) { return widget.height }
2341
+ var cm = widget.doc.cm
2342
+ if (!cm) { return 0 }
2343
+ if (!contains(document.body, widget.node)) {
2344
+ var parentStyle = "position: relative;"
2345
+ if (widget.coverGutter)
2346
+ { parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;" }
2347
+ if (widget.noHScroll)
2348
+ { parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;" }
2349
+ removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle))
2350
+ }
2351
+ return widget.height = widget.node.parentNode.offsetHeight
2352
+ }
2353
+
2354
+ // Return true when the given mouse event happened in a widget
2355
+ function eventInWidget(display, e) {
2356
+ for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
2357
+ if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") ||
2358
+ (n.parentNode == display.sizer && n != display.mover))
2359
+ { return true }
2360
+ }
2361
+ }
2362
+
2363
+ // POSITION MEASUREMENT
2364
+
2365
+ function paddingTop(display) {return display.lineSpace.offsetTop}
2366
+ function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight}
2367
+ function paddingH(display) {
2368
+ if (display.cachedPaddingH) { return display.cachedPaddingH }
2369
+ var e = removeChildrenAndAdd(display.measure, elt("pre", "x"))
2370
+ var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle
2371
+ var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)}
2372
+ if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data }
2373
+ return data
2374
+ }
2375
+
2376
+ function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth }
2377
+ function displayWidth(cm) {
2378
+ return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth
2379
+ }
2380
+ function displayHeight(cm) {
2381
+ return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight
2382
+ }
2383
+
2384
+ // Ensure the lineView.wrapping.heights array is populated. This is
2385
+ // an array of bottom offsets for the lines that make up a drawn
2386
+ // line. When lineWrapping is on, there might be more than one
2387
+ // height.
2388
+ function ensureLineHeights(cm, lineView, rect) {
2389
+ var wrapping = cm.options.lineWrapping
2390
+ var curWidth = wrapping && displayWidth(cm)
2391
+ if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {
2392
+ var heights = lineView.measure.heights = []
2393
+ if (wrapping) {
2394
+ lineView.measure.width = curWidth
2395
+ var rects = lineView.text.firstChild.getClientRects()
2396
+ for (var i = 0; i < rects.length - 1; i++) {
2397
+ var cur = rects[i], next = rects[i + 1]
2398
+ if (Math.abs(cur.bottom - next.bottom) > 2)
2399
+ { heights.push((cur.bottom + next.top) / 2 - rect.top) }
2400
+ }
2401
+ }
2402
+ heights.push(rect.bottom - rect.top)
2403
+ }
2404
+ }
2405
+
2406
+ // Find a line map (mapping character offsets to text nodes) and a
2407
+ // measurement cache for the given line number. (A line view might
2408
+ // contain multiple lines when collapsed ranges are present.)
2409
+ function mapFromLineView(lineView, line, lineN) {
2410
+ if (lineView.line == line)
2411
+ { return {map: lineView.measure.map, cache: lineView.measure.cache} }
2412
+ for (var i = 0; i < lineView.rest.length; i++)
2413
+ { if (lineView.rest[i] == line)
2414
+ { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } }
2415
+ for (var i$1 = 0; i$1 < lineView.rest.length; i$1++)
2416
+ { if (lineNo(lineView.rest[i$1]) > lineN)
2417
+ { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } }
2418
+ }
2419
+
2420
+ // Render a line into the hidden node display.externalMeasured. Used
2421
+ // when measurement is needed for a line that's not in the viewport.
2422
+ function updateExternalMeasurement(cm, line) {
2423
+ line = visualLine(line)
2424
+ var lineN = lineNo(line)
2425
+ var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN)
2426
+ view.lineN = lineN
2427
+ var built = view.built = buildLineContent(cm, view)
2428
+ view.text = built.pre
2429
+ removeChildrenAndAdd(cm.display.lineMeasure, built.pre)
2430
+ return view
2431
+ }
2432
+
2433
+ // Get a {top, bottom, left, right} box (in line-local coordinates)
2434
+ // for a given character.
2435
+ function measureChar(cm, line, ch, bias) {
2436
+ return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias)
2437
+ }
2438
+
2439
+ // Find a line view that corresponds to the given line number.
2440
+ function findViewForLine(cm, lineN) {
2441
+ if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo)
2442
+ { return cm.display.view[findViewIndex(cm, lineN)] }
2443
+ var ext = cm.display.externalMeasured
2444
+ if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size)
2445
+ { return ext }
2446
+ }
2447
+
2448
+ // Measurement can be split in two steps, the set-up work that
2449
+ // applies to the whole line, and the measurement of the actual
2450
+ // character. Functions like coordsChar, that need to do a lot of
2451
+ // measurements in a row, can thus ensure that the set-up work is
2452
+ // only done once.
2453
+ function prepareMeasureForLine(cm, line) {
2454
+ var lineN = lineNo(line)
2455
+ var view = findViewForLine(cm, lineN)
2456
+ if (view && !view.text) {
2457
+ view = null
2458
+ } else if (view && view.changes) {
2459
+ updateLineForChanges(cm, view, lineN, getDimensions(cm))
2460
+ cm.curOp.forceUpdate = true
2461
+ }
2462
+ if (!view)
2463
+ { view = updateExternalMeasurement(cm, line) }
2464
+
2465
+ var info = mapFromLineView(view, line, lineN)
2466
+ return {
2467
+ line: line, view: view, rect: null,
2468
+ map: info.map, cache: info.cache, before: info.before,
2469
+ hasHeights: false
2470
+ }
2471
+ }
2472
+
2473
+ // Given a prepared measurement object, measures the position of an
2474
+ // actual character (or fetches it from the cache).
2475
+ function measureCharPrepared(cm, prepared, ch, bias, varHeight) {
2476
+ if (prepared.before) { ch = -1 }
2477
+ var key = ch + (bias || ""), found
2478
+ if (prepared.cache.hasOwnProperty(key)) {
2479
+ found = prepared.cache[key]
2480
+ } else {
2481
+ if (!prepared.rect)
2482
+ { prepared.rect = prepared.view.text.getBoundingClientRect() }
2483
+ if (!prepared.hasHeights) {
2484
+ ensureLineHeights(cm, prepared.view, prepared.rect)
2485
+ prepared.hasHeights = true
2486
+ }
2487
+ found = measureCharInner(cm, prepared, ch, bias)
2488
+ if (!found.bogus) { prepared.cache[key] = found }
2489
+ }
2490
+ return {left: found.left, right: found.right,
2491
+ top: varHeight ? found.rtop : found.top,
2492
+ bottom: varHeight ? found.rbottom : found.bottom}
2493
+ }
2494
+
2495
+ var nullRect = {left: 0, right: 0, top: 0, bottom: 0}
2496
+
2497
+ function nodeAndOffsetInLineMap(map, ch, bias) {
2498
+ var node, start, end, collapse, mStart, mEnd
2499
+ // First, search the line map for the text node corresponding to,
2500
+ // or closest to, the target character.
2501
+ for (var i = 0; i < map.length; i += 3) {
2502
+ mStart = map[i]
2503
+ mEnd = map[i + 1]
2504
+ if (ch < mStart) {
2505
+ start = 0; end = 1
2506
+ collapse = "left"
2507
+ } else if (ch < mEnd) {
2508
+ start = ch - mStart
2509
+ end = start + 1
2510
+ } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) {
2511
+ end = mEnd - mStart
2512
+ start = end - 1
2513
+ if (ch >= mEnd) { collapse = "right" }
2514
+ }
2515
+ if (start != null) {
2516
+ node = map[i + 2]
2517
+ if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right"))
2518
+ { collapse = bias }
2519
+ if (bias == "left" && start == 0)
2520
+ { while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) {
2521
+ node = map[(i -= 3) + 2]
2522
+ collapse = "left"
2523
+ } }
2524
+ if (bias == "right" && start == mEnd - mStart)
2525
+ { while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].insertLeft) {
2526
+ node = map[(i += 3) + 2]
2527
+ collapse = "right"
2528
+ } }
2529
+ break
2530
+ }
2531
+ }
2532
+ return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd}
2533
+ }
2534
+
2535
+ function getUsefulRect(rects, bias) {
2536
+ var rect = nullRect
2537
+ if (bias == "left") { for (var i = 0; i < rects.length; i++) {
2538
+ if ((rect = rects[i]).left != rect.right) { break }
2539
+ } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) {
2540
+ if ((rect = rects[i$1]).left != rect.right) { break }
2541
+ } }
2542
+ return rect
2543
+ }
2544
+
2545
+ function measureCharInner(cm, prepared, ch, bias) {
2546
+ var place = nodeAndOffsetInLineMap(prepared.map, ch, bias)
2547
+ var node = place.node, start = place.start, end = place.end, collapse = place.collapse
2548
+
2549
+ var rect
2550
+ if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.
2551
+ for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonsense rectangles are returned
2552
+ while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start }
2553
+ while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) { ++end }
2554
+ if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart)
2555
+ { rect = node.parentNode.getBoundingClientRect() }
2556
+ else
2557
+ { rect = getUsefulRect(range(node, start, end).getClientRects(), bias) }
2558
+ if (rect.left || rect.right || start == 0) { break }
2559
+ end = start
2560
+ start = start - 1
2561
+ collapse = "right"
2562
+ }
2563
+ if (ie && ie_version < 11) { rect = maybeUpdateRectForZooming(cm.display.measure, rect) }
2564
+ } else { // If it is a widget, simply get the box for the whole widget.
2565
+ if (start > 0) { collapse = bias = "right" }
2566
+ var rects
2567
+ if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)
2568
+ { rect = rects[bias == "right" ? rects.length - 1 : 0] }
2569
+ else
2570
+ { rect = node.getBoundingClientRect() }
2571
+ }
2572
+ if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {
2573
+ var rSpan = node.parentNode.getClientRects()[0]
2574
+ if (rSpan)
2575
+ { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom} }
2576
+ else
2577
+ { rect = nullRect }
2578
+ }
2579
+
2580
+ var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top
2581
+ var mid = (rtop + rbot) / 2
2582
+ var heights = prepared.view.measure.heights
2583
+ var i = 0
2584
+ for (; i < heights.length - 1; i++)
2585
+ { if (mid < heights[i]) { break } }
2586
+ var top = i ? heights[i - 1] : 0, bot = heights[i]
2587
+ var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left,
2588
+ right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left,
2589
+ top: top, bottom: bot}
2590
+ if (!rect.left && !rect.right) { result.bogus = true }
2591
+ if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot }
2592
+
2593
+ return result
2594
+ }
2595
+
2596
+ // Work around problem with bounding client rects on ranges being
2597
+ // returned incorrectly when zoomed on IE10 and below.
2598
+ function maybeUpdateRectForZooming(measure, rect) {
2599
+ if (!window.screen || screen.logicalXDPI == null ||
2600
+ screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))
2601
+ { return rect }
2602
+ var scaleX = screen.logicalXDPI / screen.deviceXDPI
2603
+ var scaleY = screen.logicalYDPI / screen.deviceYDPI
2604
+ return {left: rect.left * scaleX, right: rect.right * scaleX,
2605
+ top: rect.top * scaleY, bottom: rect.bottom * scaleY}
2606
+ }
2607
+
2608
+ function clearLineMeasurementCacheFor(lineView) {
2609
+ if (lineView.measure) {
2610
+ lineView.measure.cache = {}
2611
+ lineView.measure.heights = null
2612
+ if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
2613
+ { lineView.measure.caches[i] = {} } }
2614
+ }
2615
+ }
2616
+
2617
+ function clearLineMeasurementCache(cm) {
2618
+ cm.display.externalMeasure = null
2619
+ removeChildren(cm.display.lineMeasure)
2620
+ for (var i = 0; i < cm.display.view.length; i++)
2621
+ { clearLineMeasurementCacheFor(cm.display.view[i]) }
2622
+ }
2623
+
2624
+ function clearCaches(cm) {
2625
+ clearLineMeasurementCache(cm)
2626
+ cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null
2627
+ if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true }
2628
+ cm.display.lineNumChars = null
2629
+ }
2630
+
2631
+ function pageScrollX() {
2632
+ // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=489206
2633
+ // which causes page_Offset and bounding client rects to use
2634
+ // different reference viewports and invalidate our calculations.
2635
+ if (chrome && android) { return -(document.body.getBoundingClientRect().left - parseInt(getComputedStyle(document.body).marginLeft)) }
2636
+ return window.pageXOffset || (document.documentElement || document.body).scrollLeft
2637
+ }
2638
+ function pageScrollY() {
2639
+ if (chrome && android) { return -(document.body.getBoundingClientRect().top - parseInt(getComputedStyle(document.body).marginTop)) }
2640
+ return window.pageYOffset || (document.documentElement || document.body).scrollTop
2641
+ }
2642
+
2643
+ function widgetTopHeight(lineObj) {
2644
+ var height = 0
2645
+ if (lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above)
2646
+ { height += widgetHeight(lineObj.widgets[i]) } } }
2647
+ return height
2648
+ }
2649
+
2650
+ // Converts a {top, bottom, left, right} box from line-local
2651
+ // coordinates into another coordinate system. Context may be one of
2652
+ // "line", "div" (display.lineDiv), "local"./null (editor), "window",
2653
+ // or "page".
2654
+ function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) {
2655
+ if (!includeWidgets) {
2656
+ var height = widgetTopHeight(lineObj)
2657
+ rect.top += height; rect.bottom += height
2658
+ }
2659
+ if (context == "line") { return rect }
2660
+ if (!context) { context = "local" }
2661
+ var yOff = heightAtLine(lineObj)
2662
+ if (context == "local") { yOff += paddingTop(cm.display) }
2663
+ else { yOff -= cm.display.viewOffset }
2664
+ if (context == "page" || context == "window") {
2665
+ var lOff = cm.display.lineSpace.getBoundingClientRect()
2666
+ yOff += lOff.top + (context == "window" ? 0 : pageScrollY())
2667
+ var xOff = lOff.left + (context == "window" ? 0 : pageScrollX())
2668
+ rect.left += xOff; rect.right += xOff
2669
+ }
2670
+ rect.top += yOff; rect.bottom += yOff
2671
+ return rect
2672
+ }
2673
+
2674
+ // Coverts a box from "div" coords to another coordinate system.
2675
+ // Context may be "window", "page", "div", or "local"./null.
2676
+ function fromCoordSystem(cm, coords, context) {
2677
+ if (context == "div") { return coords }
2678
+ var left = coords.left, top = coords.top
2679
+ // First move into "page" coordinate system
2680
+ if (context == "page") {
2681
+ left -= pageScrollX()
2682
+ top -= pageScrollY()
2683
+ } else if (context == "local" || !context) {
2684
+ var localBox = cm.display.sizer.getBoundingClientRect()
2685
+ left += localBox.left
2686
+ top += localBox.top
2687
+ }
2688
+
2689
+ var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect()
2690
+ return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top}
2691
+ }
2692
+
2693
+ function charCoords(cm, pos, context, lineObj, bias) {
2694
+ if (!lineObj) { lineObj = getLine(cm.doc, pos.line) }
2695
+ return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context)
2696
+ }
2697
+
2698
+ // Returns a box for a given cursor position, which may have an
2699
+ // 'other' property containing the position of the secondary cursor
2700
+ // on a bidi boundary.
2701
+ // A cursor Pos(line, char, "before") is on the same visual line as `char - 1`
2702
+ // and after `char - 1` in writing order of `char - 1`
2703
+ // A cursor Pos(line, char, "after") is on the same visual line as `char`
2704
+ // and before `char` in writing order of `char`
2705
+ // Examples (upper-case letters are RTL, lower-case are LTR):
2706
+ // Pos(0, 1, ...)
2707
+ // before after
2708
+ // ab a|b a|b
2709
+ // aB a|B aB|
2710
+ // Ab |Ab A|b
2711
+ // AB B|A B|A
2712
+ // Every position after the last character on a line is considered to stick
2713
+ // to the last character on the line.
2714
+ function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
2715
+ lineObj = lineObj || getLine(cm.doc, pos.line)
2716
+ if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj) }
2717
+ function get(ch, right) {
2718
+ var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight)
2719
+ if (right) { m.left = m.right; } else { m.right = m.left }
2720
+ return intoCoordSystem(cm, lineObj, m, context)
2721
+ }
2722
+ var order = getOrder(lineObj, cm.doc.direction), ch = pos.ch, sticky = pos.sticky
2723
+ if (ch >= lineObj.text.length) {
2724
+ ch = lineObj.text.length
2725
+ sticky = "before"
2726
+ } else if (ch <= 0) {
2727
+ ch = 0
2728
+ sticky = "after"
2729
+ }
2730
+ if (!order) { return get(sticky == "before" ? ch - 1 : ch, sticky == "before") }
2731
+
2732
+ function getBidi(ch, partPos, invert) {
2733
+ var part = order[partPos], right = part.level == 1
2734
+ return get(invert ? ch - 1 : ch, right != invert)
2735
+ }
2736
+ var partPos = getBidiPartAt(order, ch, sticky)
2737
+ var other = bidiOther
2738
+ var val = getBidi(ch, partPos, sticky == "before")
2739
+ if (other != null) { val.other = getBidi(ch, other, sticky != "before") }
2740
+ return val
2741
+ }
2742
+
2743
+ // Used to cheaply estimate the coordinates for a position. Used for
2744
+ // intermediate scroll updates.
2745
+ function estimateCoords(cm, pos) {
2746
+ var left = 0
2747
+ pos = clipPos(cm.doc, pos)
2748
+ if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch }
2749
+ var lineObj = getLine(cm.doc, pos.line)
2750
+ var top = heightAtLine(lineObj) + paddingTop(cm.display)
2751
+ return {left: left, right: left, top: top, bottom: top + lineObj.height}
2752
+ }
2753
+
2754
+ // Positions returned by coordsChar contain some extra information.
2755
+ // xRel is the relative x position of the input coordinates compared
2756
+ // to the found position (so xRel > 0 means the coordinates are to
2757
+ // the right of the character position, for example). When outside
2758
+ // is true, that means the coordinates lie outside the line's
2759
+ // vertical range.
2760
+ function PosWithInfo(line, ch, sticky, outside, xRel) {
2761
+ var pos = Pos(line, ch, sticky)
2762
+ pos.xRel = xRel
2763
+ if (outside) { pos.outside = true }
2764
+ return pos
2765
+ }
2766
+
2767
+ // Compute the character position closest to the given coordinates.
2768
+ // Input must be lineSpace-local ("div" coordinate system).
2769
+ function coordsChar(cm, x, y) {
2770
+ var doc = cm.doc
2771
+ y += cm.display.viewOffset
2772
+ if (y < 0) { return PosWithInfo(doc.first, 0, null, true, -1) }
2773
+ var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1
2774
+ if (lineN > last)
2775
+ { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, true, 1) }
2776
+ if (x < 0) { x = 0 }
2777
+
2778
+ var lineObj = getLine(doc, lineN)
2779
+ for (;;) {
2780
+ var found = coordsCharInner(cm, lineObj, lineN, x, y)
2781
+ var merged = collapsedSpanAtEnd(lineObj)
2782
+ var mergedPos = merged && merged.find(0, true)
2783
+ if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0))
2784
+ { lineN = lineNo(lineObj = mergedPos.to.line) }
2785
+ else
2786
+ { return found }
2787
+ }
2788
+ }
2789
+
2790
+ function wrappedLineExtent(cm, lineObj, preparedMeasure, y) {
2791
+ y -= widgetTopHeight(lineObj)
2792
+ var end = lineObj.text.length
2793
+ var begin = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch - 1).bottom <= y; }, end, 0)
2794
+ end = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch).top > y; }, begin, end)
2795
+ return {begin: begin, end: end}
2796
+ }
2797
+
2798
+ function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) {
2799
+ if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj) }
2800
+ var targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), "line").top
2801
+ return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop)
2802
+ }
2803
+
2804
+ // Returns true if the given side of a box is after the given
2805
+ // coordinates, in top-to-bottom, left-to-right order.
2806
+ function boxIsAfter(box, x, y, left) {
2807
+ return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x
2808
+ }
2809
+
2810
+ function coordsCharInner(cm, lineObj, lineNo, x, y) {
2811
+ // Move y into line-local coordinate space
2812
+ y -= heightAtLine(lineObj)
2813
+ var preparedMeasure = prepareMeasureForLine(cm, lineObj)
2814
+ // When directly calling `measureCharPrepared`, we have to adjust
2815
+ // for the widgets at this line.
2816
+ var widgetHeight = widgetTopHeight(lineObj)
2817
+ var begin = 0, end = lineObj.text.length, ltr = true
2818
+
2819
+ var order = getOrder(lineObj, cm.doc.direction)
2820
+ // If the line isn't plain left-to-right text, first figure out
2821
+ // which bidi section the coordinates fall into.
2822
+ if (order) {
2823
+ var part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart)
2824
+ (cm, lineObj, lineNo, preparedMeasure, order, x, y)
2825
+ ltr = part.level != 1
2826
+ // The awkward -1 offsets are needed because findFirst (called
2827
+ // on these below) will treat its first bound as inclusive,
2828
+ // second as exclusive, but we want to actually address the
2829
+ // characters in the part's range
2830
+ begin = ltr ? part.from : part.to - 1
2831
+ end = ltr ? part.to : part.from - 1
2832
+ }
2833
+
2834
+ // A binary search to find the first character whose bounding box
2835
+ // starts after the coordinates. If we run across any whose box wrap
2836
+ // the coordinates, store that.
2837
+ var chAround = null, boxAround = null
2838
+ var ch = findFirst(function (ch) {
2839
+ var box = measureCharPrepared(cm, preparedMeasure, ch)
2840
+ box.top += widgetHeight; box.bottom += widgetHeight
2841
+ if (!boxIsAfter(box, x, y, false)) { return false }
2842
+ if (box.top <= y && box.left <= x) {
2843
+ chAround = ch
2844
+ boxAround = box
2845
+ }
2846
+ return true
2847
+ }, begin, end)
2848
+
2849
+ var baseX, sticky, outside = false
2850
+ // If a box around the coordinates was found, use that
2851
+ if (boxAround) {
2852
+ // Distinguish coordinates nearer to the left or right side of the box
2853
+ var atLeft = x - boxAround.left < boxAround.right - x, atStart = atLeft == ltr
2854
+ ch = chAround + (atStart ? 0 : 1)
2855
+ sticky = atStart ? "after" : "before"
2856
+ baseX = atLeft ? boxAround.left : boxAround.right
2857
+ } else {
2858
+ // (Adjust for extended bound, if necessary.)
2859
+ if (!ltr && (ch == end || ch == begin)) { ch++ }
2860
+ // To determine which side to associate with, get the box to the
2861
+ // left of the character and compare it's vertical position to the
2862
+ // coordinates
2863
+ sticky = ch == 0 ? "after" : ch == lineObj.text.length ? "before" :
2864
+ (measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight <= y) == ltr ?
2865
+ "after" : "before"
2866
+ // Now get accurate coordinates for this place, in order to get a
2867
+ // base X position
2868
+ var coords = cursorCoords(cm, Pos(lineNo, ch, sticky), "line", lineObj, preparedMeasure)
2869
+ baseX = coords.left
2870
+ outside = y < coords.top || y >= coords.bottom
2871
+ }
2872
+
2873
+ ch = skipExtendingChars(lineObj.text, ch, 1)
2874
+ return PosWithInfo(lineNo, ch, sticky, outside, x - baseX)
2875
+ }
2876
+
2877
+ function coordsBidiPart(cm, lineObj, lineNo, preparedMeasure, order, x, y) {
2878
+ // Bidi parts are sorted left-to-right, and in a non-line-wrapping
2879
+ // situation, we can take this ordering to correspond to the visual
2880
+ // ordering. This finds the first part whose end is after the given
2881
+ // coordinates.
2882
+ var index = findFirst(function (i) {
2883
+ var part = order[i], ltr = part.level != 1
2884
+ return boxIsAfter(cursorCoords(cm, Pos(lineNo, ltr ? part.to : part.from, ltr ? "before" : "after"),
2885
+ "line", lineObj, preparedMeasure), x, y, true)
2886
+ }, 0, order.length - 1)
2887
+ var part = order[index]
2888
+ // If this isn't the first part, the part's start is also after
2889
+ // the coordinates, and the coordinates aren't on the same line as
2890
+ // that start, move one part back.
2891
+ if (index > 0) {
2892
+ var ltr = part.level != 1
2893
+ var start = cursorCoords(cm, Pos(lineNo, ltr ? part.from : part.to, ltr ? "after" : "before"),
2894
+ "line", lineObj, preparedMeasure)
2895
+ if (boxIsAfter(start, x, y, true) && start.top > y)
2896
+ { part = order[index - 1] }
2897
+ }
2898
+ return part
2899
+ }
2900
+
2901
+ function coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x, y) {
2902
+ // In a wrapped line, rtl text on wrapping boundaries can do things
2903
+ // that don't correspond to the ordering in our `order` array at
2904
+ // all, so a binary search doesn't work, and we want to return a
2905
+ // part that only spans one line so that the binary search in
2906
+ // coordsCharInner is safe. As such, we first find the extent of the
2907
+ // wrapped line, and then do a flat search in which we discard any
2908
+ // spans that aren't on the line.
2909
+ var ref = wrappedLineExtent(cm, lineObj, preparedMeasure, y);
2910
+ var begin = ref.begin;
2911
+ var end = ref.end;
2912
+ if (/\s/.test(lineObj.text.charAt(end - 1))) { end-- }
2913
+ var part = null, closestDist = null
2914
+ for (var i = 0; i < order.length; i++) {
2915
+ var p = order[i]
2916
+ if (p.from >= end || p.to <= begin) { continue }
2917
+ var ltr = p.level != 1
2918
+ var endX = measureCharPrepared(cm, preparedMeasure, ltr ? Math.min(end, p.to) - 1 : Math.max(begin, p.from)).right
2919
+ // Weigh against spans ending before this, so that they are only
2920
+ // picked if nothing ends after
2921
+ var dist = endX < x ? x - endX + 1e9 : endX - x
2922
+ if (!part || closestDist > dist) {
2923
+ part = p
2924
+ closestDist = dist
2925
+ }
2926
+ }
2927
+ if (!part) { part = order[order.length - 1] }
2928
+ // Clip the part to the wrapped line.
2929
+ if (part.from < begin) { part = {from: begin, to: part.to, level: part.level} }
2930
+ if (part.to > end) { part = {from: part.from, to: end, level: part.level} }
2931
+ return part
2932
+ }
2933
+
2934
+ var measureText
2935
+ // Compute the default text height.
2936
+ function textHeight(display) {
2937
+ if (display.cachedTextHeight != null) { return display.cachedTextHeight }
2938
+ if (measureText == null) {
2939
+ measureText = elt("pre")
2940
+ // Measure a bunch of lines, for browsers that compute
2941
+ // fractional heights.
2942
+ for (var i = 0; i < 49; ++i) {
2943
+ measureText.appendChild(document.createTextNode("x"))
2944
+ measureText.appendChild(elt("br"))
2945
+ }
2946
+ measureText.appendChild(document.createTextNode("x"))
2947
+ }
2948
+ removeChildrenAndAdd(display.measure, measureText)
2949
+ var height = measureText.offsetHeight / 50
2950
+ if (height > 3) { display.cachedTextHeight = height }
2951
+ removeChildren(display.measure)
2952
+ return height || 1
2953
+ }
2954
+
2955
+ // Compute the default character width.
2956
+ function charWidth(display) {
2957
+ if (display.cachedCharWidth != null) { return display.cachedCharWidth }
2958
+ var anchor = elt("span", "xxxxxxxxxx")
2959
+ var pre = elt("pre", [anchor])
2960
+ removeChildrenAndAdd(display.measure, pre)
2961
+ var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10
2962
+ if (width > 2) { display.cachedCharWidth = width }
2963
+ return width || 10
2964
+ }
2965
+
2966
+ // Do a bulk-read of the DOM positions and sizes needed to draw the
2967
+ // view, so that we don't interleave reading and writing to the DOM.
2968
+ function getDimensions(cm) {
2969
+ var d = cm.display, left = {}, width = {}
2970
+ var gutterLeft = d.gutters.clientLeft
2971
+ for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
2972
+ left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft
2973
+ width[cm.options.gutters[i]] = n.clientWidth
2974
+ }
2975
+ return {fixedPos: compensateForHScroll(d),
2976
+ gutterTotalWidth: d.gutters.offsetWidth,
2977
+ gutterLeft: left,
2978
+ gutterWidth: width,
2979
+ wrapperWidth: d.wrapper.clientWidth}
2980
+ }
2981
+
2982
+ // Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
2983
+ // but using getBoundingClientRect to get a sub-pixel-accurate
2984
+ // result.
2985
+ function compensateForHScroll(display) {
2986
+ return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left
2987
+ }
2988
+
2989
+ // Returns a function that estimates the height of a line, to use as
2990
+ // first approximation until the line becomes visible (and is thus
2991
+ // properly measurable).
2992
+ function estimateHeight(cm) {
2993
+ var th = textHeight(cm.display), wrapping = cm.options.lineWrapping
2994
+ var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3)
2995
+ return function (line) {
2996
+ if (lineIsHidden(cm.doc, line)) { return 0 }
2997
+
2998
+ var widgetsHeight = 0
2999
+ if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) {
3000
+ if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height }
3001
+ } }
3002
+
3003
+ if (wrapping)
3004
+ { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th }
3005
+ else
3006
+ { return widgetsHeight + th }
3007
+ }
3008
+ }
3009
+
3010
+ function estimateLineHeights(cm) {
3011
+ var doc = cm.doc, est = estimateHeight(cm)
3012
+ doc.iter(function (line) {
3013
+ var estHeight = est(line)
3014
+ if (estHeight != line.height) { updateLineHeight(line, estHeight) }
3015
+ })
3016
+ }
3017
+
3018
+ // Given a mouse event, find the corresponding position. If liberal
3019
+ // is false, it checks whether a gutter or scrollbar was clicked,
3020
+ // and returns null if it was. forRect is used by rectangular
3021
+ // selections, and tries to estimate a character position even for
3022
+ // coordinates beyond the right of the text.
3023
+ function posFromMouse(cm, e, liberal, forRect) {
3024
+ var display = cm.display
3025
+ if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") { return null }
3026
+
3027
+ var x, y, space = display.lineSpace.getBoundingClientRect()
3028
+ // Fails unpredictably on IE[67] when mouse is dragged around quickly.
3029
+ try { x = e.clientX - space.left; y = e.clientY - space.top }
3030
+ catch (e) { return null }
3031
+ var coords = coordsChar(cm, x, y), line
3032
+ if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) {
3033
+ var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length
3034
+ coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff))
3035
+ }
3036
+ return coords
3037
+ }
3038
+
3039
+ // Find the view element corresponding to a given line. Return null
3040
+ // when the line isn't visible.
3041
+ function findViewIndex(cm, n) {
3042
+ if (n >= cm.display.viewTo) { return null }
3043
+ n -= cm.display.viewFrom
3044
+ if (n < 0) { return null }
3045
+ var view = cm.display.view
3046
+ for (var i = 0; i < view.length; i++) {
3047
+ n -= view[i].size
3048
+ if (n < 0) { return i }
3049
+ }
3050
+ }
3051
+
3052
+ function updateSelection(cm) {
3053
+ cm.display.input.showSelection(cm.display.input.prepareSelection())
3054
+ }
3055
+
3056
+ function prepareSelection(cm, primary) {
3057
+ if ( primary === void 0 ) primary = true;
3058
+
3059
+ var doc = cm.doc, result = {}
3060
+ var curFragment = result.cursors = document.createDocumentFragment()
3061
+ var selFragment = result.selection = document.createDocumentFragment()
3062
+
3063
+ for (var i = 0; i < doc.sel.ranges.length; i++) {
3064
+ if (!primary && i == doc.sel.primIndex) { continue }
3065
+ var range = doc.sel.ranges[i]
3066
+ if (range.from().line >= cm.display.viewTo || range.to().line < cm.display.viewFrom) { continue }
3067
+ var collapsed = range.empty()
3068
+ if (collapsed || cm.options.showCursorWhenSelecting)
3069
+ { drawSelectionCursor(cm, range.head, curFragment) }
3070
+ if (!collapsed)
3071
+ { drawSelectionRange(cm, range, selFragment) }
3072
+ }
3073
+ return result
3074
+ }
3075
+
3076
+ // Draws a cursor for the given range
3077
+ function drawSelectionCursor(cm, head, output) {
3078
+ var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine)
3079
+
3080
+ var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor"))
3081
+ cursor.style.left = pos.left + "px"
3082
+ cursor.style.top = pos.top + "px"
3083
+ cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px"
3084
+
3085
+ if (pos.other) {
3086
+ // Secondary cursor, shown when on a 'jump' in bi-directional text
3087
+ var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"))
3088
+ otherCursor.style.display = ""
3089
+ otherCursor.style.left = pos.other.left + "px"
3090
+ otherCursor.style.top = pos.other.top + "px"
3091
+ otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px"
3092
+ }
3093
+ }
3094
+
3095
+ function cmpCoords(a, b) { return a.top - b.top || a.left - b.left }
3096
+
3097
+ // Draws the given range as a highlighted selection
3098
+ function drawSelectionRange(cm, range, output) {
3099
+ var display = cm.display, doc = cm.doc
3100
+ var fragment = document.createDocumentFragment()
3101
+ var padding = paddingH(cm.display), leftSide = padding.left
3102
+ var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right
3103
+ var docLTR = doc.direction == "ltr"
3104
+
3105
+ function add(left, top, width, bottom) {
3106
+ if (top < 0) { top = 0 }
3107
+ top = Math.round(top)
3108
+ bottom = Math.round(bottom)
3109
+ fragment.appendChild(elt("div", null, "CodeMirror-selected", ("position: absolute; left: " + left + "px;\n top: " + top + "px; width: " + (width == null ? rightSide - left : width) + "px;\n height: " + (bottom - top) + "px")))
3110
+ }
3111
+
3112
+ function drawForLine(line, fromArg, toArg) {
3113
+ var lineObj = getLine(doc, line)
3114
+ var lineLen = lineObj.text.length
3115
+ var start, end
3116
+ function coords(ch, bias) {
3117
+ return charCoords(cm, Pos(line, ch), "div", lineObj, bias)
3118
+ }
3119
+
3120
+ function wrapX(pos, dir, side) {
3121
+ var extent = wrappedLineExtentChar(cm, lineObj, null, pos)
3122
+ var prop = (dir == "ltr") == (side == "after") ? "left" : "right"
3123
+ var ch = side == "after" ? extent.begin : extent.end - (/\s/.test(lineObj.text.charAt(extent.end - 1)) ? 2 : 1)
3124
+ return coords(ch, prop)[prop]
3125
+ }
3126
+
3127
+ var order = getOrder(lineObj, doc.direction)
3128
+ iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir, i) {
3129
+ var ltr = dir == "ltr"
3130
+ var fromPos = coords(from, ltr ? "left" : "right")
3131
+ var toPos = coords(to - 1, ltr ? "right" : "left")
3132
+
3133
+ var openStart = fromArg == null && from == 0, openEnd = toArg == null && to == lineLen
3134
+ var first = i == 0, last = !order || i == order.length - 1
3135
+ if (toPos.top - fromPos.top <= 3) { // Single line
3136
+ var openLeft = (docLTR ? openStart : openEnd) && first
3137
+ var openRight = (docLTR ? openEnd : openStart) && last
3138
+ var left = openLeft ? leftSide : (ltr ? fromPos : toPos).left
3139
+ var right = openRight ? rightSide : (ltr ? toPos : fromPos).right
3140
+ add(left, fromPos.top, right - left, fromPos.bottom)
3141
+ } else { // Multiple lines
3142
+ var topLeft, topRight, botLeft, botRight
3143
+ if (ltr) {
3144
+ topLeft = docLTR && openStart && first ? leftSide : fromPos.left
3145
+ topRight = docLTR ? rightSide : wrapX(from, dir, "before")
3146
+ botLeft = docLTR ? leftSide : wrapX(to, dir, "after")
3147
+ botRight = docLTR && openEnd && last ? rightSide : toPos.right
3148
+ } else {
3149
+ topLeft = !docLTR ? leftSide : wrapX(from, dir, "before")
3150
+ topRight = !docLTR && openStart && first ? rightSide : fromPos.right
3151
+ botLeft = !docLTR && openEnd && last ? leftSide : toPos.left
3152
+ botRight = !docLTR ? rightSide : wrapX(to, dir, "after")
3153
+ }
3154
+ add(topLeft, fromPos.top, topRight - topLeft, fromPos.bottom)
3155
+ if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top) }
3156
+ add(botLeft, toPos.top, botRight - botLeft, toPos.bottom)
3157
+ }
3158
+
3159
+ if (!start || cmpCoords(fromPos, start) < 0) { start = fromPos }
3160
+ if (cmpCoords(toPos, start) < 0) { start = toPos }
3161
+ if (!end || cmpCoords(fromPos, end) < 0) { end = fromPos }
3162
+ if (cmpCoords(toPos, end) < 0) { end = toPos }
3163
+ })
3164
+ return {start: start, end: end}
3165
+ }
3166
+
3167
+ var sFrom = range.from(), sTo = range.to()
3168
+ if (sFrom.line == sTo.line) {
3169
+ drawForLine(sFrom.line, sFrom.ch, sTo.ch)
3170
+ } else {
3171
+ var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line)
3172
+ var singleVLine = visualLine(fromLine) == visualLine(toLine)
3173
+ var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end
3174
+ var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start
3175
+ if (singleVLine) {
3176
+ if (leftEnd.top < rightStart.top - 2) {
3177
+ add(leftEnd.right, leftEnd.top, null, leftEnd.bottom)
3178
+ add(leftSide, rightStart.top, rightStart.left, rightStart.bottom)
3179
+ } else {
3180
+ add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom)
3181
+ }
3182
+ }
3183
+ if (leftEnd.bottom < rightStart.top)
3184
+ { add(leftSide, leftEnd.bottom, null, rightStart.top) }
3185
+ }
3186
+
3187
+ output.appendChild(fragment)
3188
+ }
3189
+
3190
+ // Cursor-blinking
3191
+ function restartBlink(cm) {
3192
+ if (!cm.state.focused) { return }
3193
+ var display = cm.display
3194
+ clearInterval(display.blinker)
3195
+ var on = true
3196
+ display.cursorDiv.style.visibility = ""
3197
+ if (cm.options.cursorBlinkRate > 0)
3198
+ { display.blinker = setInterval(function () { return display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; },
3199
+ cm.options.cursorBlinkRate) }
3200
+ else if (cm.options.cursorBlinkRate < 0)
3201
+ { display.cursorDiv.style.visibility = "hidden" }
3202
+ }
3203
+
3204
+ function ensureFocus(cm) {
3205
+ if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm) }
3206
+ }
3207
+
3208
+ function delayBlurEvent(cm) {
3209
+ cm.state.delayingBlurEvent = true
3210
+ setTimeout(function () { if (cm.state.delayingBlurEvent) {
3211
+ cm.state.delayingBlurEvent = false
3212
+ onBlur(cm)
3213
+ } }, 100)
3214
+ }
3215
+
3216
+ function onFocus(cm, e) {
3217
+ if (cm.state.delayingBlurEvent) { cm.state.delayingBlurEvent = false }
3218
+
3219
+ if (cm.options.readOnly == "nocursor") { return }
3220
+ if (!cm.state.focused) {
3221
+ signal(cm, "focus", cm, e)
3222
+ cm.state.focused = true
3223
+ addClass(cm.display.wrapper, "CodeMirror-focused")
3224
+ // This test prevents this from firing when a context
3225
+ // menu is closed (since the input reset would kill the
3226
+ // select-all detection hack)
3227
+ if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {
3228
+ cm.display.input.reset()
3229
+ if (webkit) { setTimeout(function () { return cm.display.input.reset(true); }, 20) } // Issue #1730
3230
+ }
3231
+ cm.display.input.receivedFocus()
3232
+ }
3233
+ restartBlink(cm)
3234
+ }
3235
+ function onBlur(cm, e) {
3236
+ if (cm.state.delayingBlurEvent) { return }
3237
+
3238
+ if (cm.state.focused) {
3239
+ signal(cm, "blur", cm, e)
3240
+ cm.state.focused = false
3241
+ rmClass(cm.display.wrapper, "CodeMirror-focused")
3242
+ }
3243
+ clearInterval(cm.display.blinker)
3244
+ setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false } }, 150)
3245
+ }
3246
+
3247
+ // Read the actual heights of the rendered lines, and update their
3248
+ // stored heights to match.
3249
+ function updateHeightsInViewport(cm) {
3250
+ var display = cm.display
3251
+ var prevBottom = display.lineDiv.offsetTop
3252
+ for (var i = 0; i < display.view.length; i++) {
3253
+ var cur = display.view[i], height = (void 0)
3254
+ if (cur.hidden) { continue }
3255
+ if (ie && ie_version < 8) {
3256
+ var bot = cur.node.offsetTop + cur.node.offsetHeight
3257
+ height = bot - prevBottom
3258
+ prevBottom = bot
3259
+ } else {
3260
+ var box = cur.node.getBoundingClientRect()
3261
+ height = box.bottom - box.top
3262
+ }
3263
+ var diff = cur.line.height - height
3264
+ if (height < 2) { height = textHeight(display) }
3265
+ if (diff > .005 || diff < -.005) {
3266
+ updateLineHeight(cur.line, height)
3267
+ updateWidgetHeight(cur.line)
3268
+ if (cur.rest) { for (var j = 0; j < cur.rest.length; j++)
3269
+ { updateWidgetHeight(cur.rest[j]) } }
3270
+ }
3271
+ }
3272
+ }
3273
+
3274
+ // Read and store the height of line widgets associated with the
3275
+ // given line.
3276
+ function updateWidgetHeight(line) {
3277
+ if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i)
3278
+ { line.widgets[i].height = line.widgets[i].node.parentNode.offsetHeight } }
3279
+ }
3280
+
3281
+ // Compute the lines that are visible in a given viewport (defaults
3282
+ // the the current scroll position). viewport may contain top,
3283
+ // height, and ensure (see op.scrollToPos) properties.
3284
+ function visibleLines(display, doc, viewport) {
3285
+ var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop
3286
+ top = Math.floor(top - paddingTop(display))
3287
+ var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight
3288
+
3289
+ var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom)
3290
+ // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
3291
+ // forces those lines into the viewport (if possible).
3292
+ if (viewport && viewport.ensure) {
3293
+ var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line
3294
+ if (ensureFrom < from) {
3295
+ from = ensureFrom
3296
+ to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight)
3297
+ } else if (Math.min(ensureTo, doc.lastLine()) >= to) {
3298
+ from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight)
3299
+ to = ensureTo
3300
+ }
3301
+ }
3302
+ return {from: from, to: Math.max(to, from + 1)}
3303
+ }
3304
+
3305
+ // Re-align line numbers and gutter marks to compensate for
3306
+ // horizontal scrolling.
3307
+ function alignHorizontally(cm) {
3308
+ var display = cm.display, view = display.view
3309
+ if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) { return }
3310
+ var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft
3311
+ var gutterW = display.gutters.offsetWidth, left = comp + "px"
3312
+ for (var i = 0; i < view.length; i++) { if (!view[i].hidden) {
3313
+ if (cm.options.fixedGutter) {
3314
+ if (view[i].gutter)
3315
+ { view[i].gutter.style.left = left }
3316
+ if (view[i].gutterBackground)
3317
+ { view[i].gutterBackground.style.left = left }
3318
+ }
3319
+ var align = view[i].alignable
3320
+ if (align) { for (var j = 0; j < align.length; j++)
3321
+ { align[j].style.left = left } }
3322
+ } }
3323
+ if (cm.options.fixedGutter)
3324
+ { display.gutters.style.left = (comp + gutterW) + "px" }
3325
+ }
3326
+
3327
+ // Used to ensure that the line number gutter is still the right
3328
+ // size for the current document size. Returns true when an update
3329
+ // is needed.
3330
+ function maybeUpdateLineNumberWidth(cm) {
3331
+ if (!cm.options.lineNumbers) { return false }
3332
+ var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display
3333
+ if (last.length != display.lineNumChars) {
3334
+ var test = display.measure.appendChild(elt("div", [elt("div", last)],
3335
+ "CodeMirror-linenumber CodeMirror-gutter-elt"))
3336
+ var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW
3337
+ display.lineGutter.style.width = ""
3338
+ display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1
3339
+ display.lineNumWidth = display.lineNumInnerWidth + padding
3340
+ display.lineNumChars = display.lineNumInnerWidth ? last.length : -1
3341
+ display.lineGutter.style.width = display.lineNumWidth + "px"
3342
+ updateGutterSpace(cm)
3343
+ return true
3344
+ }
3345
+ return false
3346
+ }
3347
+
3348
+ // SCROLLING THINGS INTO VIEW
3349
+
3350
+ // If an editor sits on the top or bottom of the window, partially
3351
+ // scrolled out of view, this ensures that the cursor is visible.
3352
+ function maybeScrollWindow(cm, rect) {
3353
+ if (signalDOMEvent(cm, "scrollCursorIntoView")) { return }
3354
+
3355
+ var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null
3356
+ if (rect.top + box.top < 0) { doScroll = true }
3357
+ else if (rect.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) { doScroll = false }
3358
+ if (doScroll != null && !phantom) {
3359
+ var scrollNode = elt("div", "\u200b", null, ("position: absolute;\n top: " + (rect.top - display.viewOffset - paddingTop(cm.display)) + "px;\n height: " + (rect.bottom - rect.top + scrollGap(cm) + display.barHeight) + "px;\n left: " + (rect.left) + "px; width: " + (Math.max(2, rect.right - rect.left)) + "px;"))
3360
+ cm.display.lineSpace.appendChild(scrollNode)
3361
+ scrollNode.scrollIntoView(doScroll)
3362
+ cm.display.lineSpace.removeChild(scrollNode)
3363
+ }
3364
+ }
3365
+
3366
+ // Scroll a given position into view (immediately), verifying that
3367
+ // it actually became visible (as line heights are accurately
3368
+ // measured, the position of something may 'drift' during drawing).
3369
+ function scrollPosIntoView(cm, pos, end, margin) {
3370
+ if (margin == null) { margin = 0 }
3371
+ var rect
3372
+ if (!cm.options.lineWrapping && pos == end) {
3373
+ // Set pos and end to the cursor positions around the character pos sticks to
3374
+ // If pos.sticky == "before", that is around pos.ch - 1, otherwise around pos.ch
3375
+ // If pos == Pos(_, 0, "before"), pos and end are unchanged
3376
+ pos = pos.ch ? Pos(pos.line, pos.sticky == "before" ? pos.ch - 1 : pos.ch, "after") : pos
3377
+ end = pos.sticky == "before" ? Pos(pos.line, pos.ch + 1, "before") : pos
3378
+ }
3379
+ for (var limit = 0; limit < 5; limit++) {
3380
+ var changed = false
3381
+ var coords = cursorCoords(cm, pos)
3382
+ var endCoords = !end || end == pos ? coords : cursorCoords(cm, end)
3383
+ rect = {left: Math.min(coords.left, endCoords.left),
3384
+ top: Math.min(coords.top, endCoords.top) - margin,
3385
+ right: Math.max(coords.left, endCoords.left),
3386
+ bottom: Math.max(coords.bottom, endCoords.bottom) + margin}
3387
+ var scrollPos = calculateScrollPos(cm, rect)
3388
+ var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft
3389
+ if (scrollPos.scrollTop != null) {
3390
+ updateScrollTop(cm, scrollPos.scrollTop)
3391
+ if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true }
3392
+ }
3393
+ if (scrollPos.scrollLeft != null) {
3394
+ setScrollLeft(cm, scrollPos.scrollLeft)
3395
+ if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true }
3396
+ }
3397
+ if (!changed) { break }
3398
+ }
3399
+ return rect
3400
+ }
3401
+
3402
+ // Scroll a given set of coordinates into view (immediately).
3403
+ function scrollIntoView(cm, rect) {
3404
+ var scrollPos = calculateScrollPos(cm, rect)
3405
+ if (scrollPos.scrollTop != null) { updateScrollTop(cm, scrollPos.scrollTop) }
3406
+ if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft) }
3407
+ }
3408
+
3409
+ // Calculate a new scroll position needed to scroll the given
3410
+ // rectangle into view. Returns an object with scrollTop and
3411
+ // scrollLeft properties. When these are undefined, the
3412
+ // vertical/horizontal position does not need to be adjusted.
3413
+ function calculateScrollPos(cm, rect) {
3414
+ var display = cm.display, snapMargin = textHeight(cm.display)
3415
+ if (rect.top < 0) { rect.top = 0 }
3416
+ var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop
3417
+ var screen = displayHeight(cm), result = {}
3418
+ if (rect.bottom - rect.top > screen) { rect.bottom = rect.top + screen }
3419
+ var docBottom = cm.doc.height + paddingVert(display)
3420
+ var atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMargin
3421
+ if (rect.top < screentop) {
3422
+ result.scrollTop = atTop ? 0 : rect.top
3423
+ } else if (rect.bottom > screentop + screen) {
3424
+ var newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen)
3425
+ if (newTop != screentop) { result.scrollTop = newTop }
3426
+ }
3427
+
3428
+ var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft
3429
+ var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0)
3430
+ var tooWide = rect.right - rect.left > screenw
3431
+ if (tooWide) { rect.right = rect.left + screenw }
3432
+ if (rect.left < 10)
3433
+ { result.scrollLeft = 0 }
3434
+ else if (rect.left < screenleft)
3435
+ { result.scrollLeft = Math.max(0, rect.left - (tooWide ? 0 : 10)) }
3436
+ else if (rect.right > screenw + screenleft - 3)
3437
+ { result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw }
3438
+ return result
3439
+ }
3440
+
3441
+ // Store a relative adjustment to the scroll position in the current
3442
+ // operation (to be applied when the operation finishes).
3443
+ function addToScrollTop(cm, top) {
3444
+ if (top == null) { return }
3445
+ resolveScrollToPos(cm)
3446
+ cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top
3447
+ }
3448
+
3449
+ // Make sure that at the end of the operation the current cursor is
3450
+ // shown.
3451
+ function ensureCursorVisible(cm) {
3452
+ resolveScrollToPos(cm)
3453
+ var cur = cm.getCursor()
3454
+ cm.curOp.scrollToPos = {from: cur, to: cur, margin: cm.options.cursorScrollMargin}
3455
+ }
3456
+
3457
+ function scrollToCoords(cm, x, y) {
3458
+ if (x != null || y != null) { resolveScrollToPos(cm) }
3459
+ if (x != null) { cm.curOp.scrollLeft = x }
3460
+ if (y != null) { cm.curOp.scrollTop = y }
3461
+ }
3462
+
3463
+ function scrollToRange(cm, range) {
3464
+ resolveScrollToPos(cm)
3465
+ cm.curOp.scrollToPos = range
3466
+ }
3467
+
3468
+ // When an operation has its scrollToPos property set, and another
3469
+ // scroll action is applied before the end of the operation, this
3470
+ // 'simulates' scrolling that position into view in a cheap way, so
3471
+ // that the effect of intermediate scroll commands is not ignored.
3472
+ function resolveScrollToPos(cm) {
3473
+ var range = cm.curOp.scrollToPos
3474
+ if (range) {
3475
+ cm.curOp.scrollToPos = null
3476
+ var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to)
3477
+ scrollToCoordsRange(cm, from, to, range.margin)
3478
+ }
3479
+ }
3480
+
3481
+ function scrollToCoordsRange(cm, from, to, margin) {
3482
+ var sPos = calculateScrollPos(cm, {
3483
+ left: Math.min(from.left, to.left),
3484
+ top: Math.min(from.top, to.top) - margin,
3485
+ right: Math.max(from.right, to.right),
3486
+ bottom: Math.max(from.bottom, to.bottom) + margin
3487
+ })
3488
+ scrollToCoords(cm, sPos.scrollLeft, sPos.scrollTop)
3489
+ }
3490
+
3491
+ // Sync the scrollable area and scrollbars, ensure the viewport
3492
+ // covers the visible area.
3493
+ function updateScrollTop(cm, val) {
3494
+ if (Math.abs(cm.doc.scrollTop - val) < 2) { return }
3495
+ if (!gecko) { updateDisplaySimple(cm, {top: val}) }
3496
+ setScrollTop(cm, val, true)
3497
+ if (gecko) { updateDisplaySimple(cm) }
3498
+ startWorker(cm, 100)
3499
+ }
3500
+
3501
+ function setScrollTop(cm, val, forceScroll) {
3502
+ val = Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val)
3503
+ if (cm.display.scroller.scrollTop == val && !forceScroll) { return }
3504
+ cm.doc.scrollTop = val
3505
+ cm.display.scrollbars.setScrollTop(val)
3506
+ if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = val }
3507
+ }
3508
+
3509
+ // Sync scroller and scrollbar, ensure the gutter elements are
3510
+ // aligned.
3511
+ function setScrollLeft(cm, val, isScroller, forceScroll) {
3512
+ val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth)
3513
+ if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) { return }
3514
+ cm.doc.scrollLeft = val
3515
+ alignHorizontally(cm)
3516
+ if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val }
3517
+ cm.display.scrollbars.setScrollLeft(val)
3518
+ }
3519
+
3520
+ // SCROLLBARS
3521
+
3522
+ // Prepare DOM reads needed to update the scrollbars. Done in one
3523
+ // shot to minimize update/measure roundtrips.
3524
+ function measureForScrollbars(cm) {
3525
+ var d = cm.display, gutterW = d.gutters.offsetWidth
3526
+ var docH = Math.round(cm.doc.height + paddingVert(cm.display))
3527
+ return {
3528
+ clientHeight: d.scroller.clientHeight,
3529
+ viewHeight: d.wrapper.clientHeight,
3530
+ scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,
3531
+ viewWidth: d.wrapper.clientWidth,
3532
+ barLeft: cm.options.fixedGutter ? gutterW : 0,
3533
+ docHeight: docH,
3534
+ scrollHeight: docH + scrollGap(cm) + d.barHeight,
3535
+ nativeBarWidth: d.nativeBarWidth,
3536
+ gutterWidth: gutterW
3537
+ }
3538
+ }
3539
+
3540
+ var NativeScrollbars = function(place, scroll, cm) {
3541
+ this.cm = cm
3542
+ var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar")
3543
+ var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar")
3544
+ place(vert); place(horiz)
3545
+
3546
+ on(vert, "scroll", function () {
3547
+ if (vert.clientHeight) { scroll(vert.scrollTop, "vertical") }
3548
+ })
3549
+ on(horiz, "scroll", function () {
3550
+ if (horiz.clientWidth) { scroll(horiz.scrollLeft, "horizontal") }
3551
+ })
3552
+
3553
+ this.checkedZeroWidth = false
3554
+ // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
3555
+ if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = "18px" }
3556
+ };
3557
+
3558
+ NativeScrollbars.prototype.update = function (measure) {
3559
+ var needsH = measure.scrollWidth > measure.clientWidth + 1
3560
+ var needsV = measure.scrollHeight > measure.clientHeight + 1
3561
+ var sWidth = measure.nativeBarWidth
3562
+
3563
+ if (needsV) {
3564
+ this.vert.style.display = "block"
3565
+ this.vert.style.bottom = needsH ? sWidth + "px" : "0"
3566
+ var totalHeight = measure.viewHeight - (needsH ? sWidth : 0)
3567
+ // A bug in IE8 can cause this value to be negative, so guard it.
3568
+ this.vert.firstChild.style.height =
3569
+ Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px"
3570
+ } else {
3571
+ this.vert.style.display = ""
3572
+ this.vert.firstChild.style.height = "0"
3573
+ }
3574
+
3575
+ if (needsH) {
3576
+ this.horiz.style.display = "block"
3577
+ this.horiz.style.right = needsV ? sWidth + "px" : "0"
3578
+ this.horiz.style.left = measure.barLeft + "px"
3579
+ var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0)
3580
+ this.horiz.firstChild.style.width =
3581
+ Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + "px"
3582
+ } else {
3583
+ this.horiz.style.display = ""
3584
+ this.horiz.firstChild.style.width = "0"
3585
+ }
3586
+
3587
+ if (!this.checkedZeroWidth && measure.clientHeight > 0) {
3588
+ if (sWidth == 0) { this.zeroWidthHack() }
3589
+ this.checkedZeroWidth = true
3590
+ }
3591
+
3592
+ return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0}
3593
+ };
3594
+
3595
+ NativeScrollbars.prototype.setScrollLeft = function (pos) {
3596
+ if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos }
3597
+ if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz, "horiz") }
3598
+ };
3599
+
3600
+ NativeScrollbars.prototype.setScrollTop = function (pos) {
3601
+ if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos }
3602
+ if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert, "vert") }
3603
+ };
3604
+
3605
+ NativeScrollbars.prototype.zeroWidthHack = function () {
3606
+ var w = mac && !mac_geMountainLion ? "12px" : "18px"
3607
+ this.horiz.style.height = this.vert.style.width = w
3608
+ this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none"
3609
+ this.disableHoriz = new Delayed
3610
+ this.disableVert = new Delayed
3611
+ };
3612
+
3613
+ NativeScrollbars.prototype.enableZeroWidthBar = function (bar, delay, type) {
3614
+ bar.style.pointerEvents = "auto"
3615
+ function maybeDisable() {
3616
+ // To find out whether the scrollbar is still visible, we
3617
+ // check whether the element under the pixel in the bottom
3618
+ // right corner of the scrollbar box is the scrollbar box
3619
+ // itself (when the bar is still visible) or its filler child
3620
+ // (when the bar is hidden). If it is still visible, we keep
3621
+ // it enabled, if it's hidden, we disable pointer events.
3622
+ var box = bar.getBoundingClientRect()
3623
+ var elt = type == "vert" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2)
3624
+ : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1)
3625
+ if (elt != bar) { bar.style.pointerEvents = "none" }
3626
+ else { delay.set(1000, maybeDisable) }
3627
+ }
3628
+ delay.set(1000, maybeDisable)
3629
+ };
3630
+
3631
+ NativeScrollbars.prototype.clear = function () {
3632
+ var parent = this.horiz.parentNode
3633
+ parent.removeChild(this.horiz)
3634
+ parent.removeChild(this.vert)
3635
+ };
3636
+
3637
+ var NullScrollbars = function () {};
3638
+
3639
+ NullScrollbars.prototype.update = function () { return {bottom: 0, right: 0} };
3640
+ NullScrollbars.prototype.setScrollLeft = function () {};
3641
+ NullScrollbars.prototype.setScrollTop = function () {};
3642
+ NullScrollbars.prototype.clear = function () {};
3643
+
3644
+ function updateScrollbars(cm, measure) {
3645
+ if (!measure) { measure = measureForScrollbars(cm) }
3646
+ var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight
3647
+ updateScrollbarsInner(cm, measure)
3648
+ for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) {
3649
+ if (startWidth != cm.display.barWidth && cm.options.lineWrapping)
3650
+ { updateHeightsInViewport(cm) }
3651
+ updateScrollbarsInner(cm, measureForScrollbars(cm))
3652
+ startWidth = cm.display.barWidth; startHeight = cm.display.barHeight
3653
+ }
3654
+ }
3655
+
3656
+ // Re-synchronize the fake scrollbars with the actual size of the
3657
+ // content.
3658
+ function updateScrollbarsInner(cm, measure) {
3659
+ var d = cm.display
3660
+ var sizes = d.scrollbars.update(measure)
3661
+
3662
+ d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px"
3663
+ d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px"
3664
+ d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent"
3665
+
3666
+ if (sizes.right && sizes.bottom) {
3667
+ d.scrollbarFiller.style.display = "block"
3668
+ d.scrollbarFiller.style.height = sizes.bottom + "px"
3669
+ d.scrollbarFiller.style.width = sizes.right + "px"
3670
+ } else { d.scrollbarFiller.style.display = "" }
3671
+ if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
3672
+ d.gutterFiller.style.display = "block"
3673
+ d.gutterFiller.style.height = sizes.bottom + "px"
3674
+ d.gutterFiller.style.width = measure.gutterWidth + "px"
3675
+ } else { d.gutterFiller.style.display = "" }
3676
+ }
3677
+
3678
+ var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars}
3679
+
3680
+ function initScrollbars(cm) {
3681
+ if (cm.display.scrollbars) {
3682
+ cm.display.scrollbars.clear()
3683
+ if (cm.display.scrollbars.addClass)
3684
+ { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass) }
3685
+ }
3686
+
3687
+ cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) {
3688
+ cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller)
3689
+ // Prevent clicks in the scrollbars from killing focus
3690
+ on(node, "mousedown", function () {
3691
+ if (cm.state.focused) { setTimeout(function () { return cm.display.input.focus(); }, 0) }
3692
+ })
3693
+ node.setAttribute("cm-not-content", "true")
3694
+ }, function (pos, axis) {
3695
+ if (axis == "horizontal") { setScrollLeft(cm, pos) }
3696
+ else { updateScrollTop(cm, pos) }
3697
+ }, cm)
3698
+ if (cm.display.scrollbars.addClass)
3699
+ { addClass(cm.display.wrapper, cm.display.scrollbars.addClass) }
3700
+ }
3701
+
3702
+ // Operations are used to wrap a series of changes to the editor
3703
+ // state in such a way that each change won't have to update the
3704
+ // cursor and display (which would be awkward, slow, and
3705
+ // error-prone). Instead, display updates are batched and then all
3706
+ // combined and executed at once.
3707
+
3708
+ var nextOpId = 0
3709
+ // Start a new operation.
3710
+ function startOperation(cm) {
3711
+ cm.curOp = {
3712
+ cm: cm,
3713
+ viewChanged: false, // Flag that indicates that lines might need to be redrawn
3714
+ startHeight: cm.doc.height, // Used to detect need to update scrollbar
3715
+ forceUpdate: false, // Used to force a redraw
3716
+ updateInput: null, // Whether to reset the input textarea
3717
+ typing: false, // Whether this reset should be careful to leave existing text (for compositing)
3718
+ changeObjs: null, // Accumulated changes, for firing change events
3719
+ cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on
3720
+ cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
3721
+ selectionChanged: false, // Whether the selection needs to be redrawn
3722
+ updateMaxLine: false, // Set when the widest line needs to be determined anew
3723
+ scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
3724
+ scrollToPos: null, // Used to scroll to a specific position
3725
+ focus: false,
3726
+ id: ++nextOpId // Unique ID
3727
+ }
3728
+ pushOperation(cm.curOp)
3729
+ }
3730
+
3731
+ // Finish an operation, updating the display and signalling delayed events
3732
+ function endOperation(cm) {
3733
+ var op = cm.curOp
3734
+ finishOperation(op, function (group) {
3735
+ for (var i = 0; i < group.ops.length; i++)
3736
+ { group.ops[i].cm.curOp = null }
3737
+ endOperations(group)
3738
+ })
3739
+ }
3740
+
3741
+ // The DOM updates done when an operation finishes are batched so
3742
+ // that the minimum number of relayouts are required.
3743
+ function endOperations(group) {
3744
+ var ops = group.ops
3745
+ for (var i = 0; i < ops.length; i++) // Read DOM
3746
+ { endOperation_R1(ops[i]) }
3747
+ for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe)
3748
+ { endOperation_W1(ops[i$1]) }
3749
+ for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM
3750
+ { endOperation_R2(ops[i$2]) }
3751
+ for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe)
3752
+ { endOperation_W2(ops[i$3]) }
3753
+ for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM
3754
+ { endOperation_finish(ops[i$4]) }
3755
+ }
3756
+
3757
+ function endOperation_R1(op) {
3758
+ var cm = op.cm, display = cm.display
3759
+ maybeClipScrollbars(cm)
3760
+ if (op.updateMaxLine) { findMaxLine(cm) }
3761
+
3762
+ op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||
3763
+ op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
3764
+ op.scrollToPos.to.line >= display.viewTo) ||
3765
+ display.maxLineChanged && cm.options.lineWrapping
3766
+ op.update = op.mustUpdate &&
3767
+ new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate)
3768
+ }
3769
+
3770
+ function endOperation_W1(op) {
3771
+ op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update)
3772
+ }
3773
+
3774
+ function endOperation_R2(op) {
3775
+ var cm = op.cm, display = cm.display
3776
+ if (op.updatedDisplay) { updateHeightsInViewport(cm) }
3777
+
3778
+ op.barMeasure = measureForScrollbars(cm)
3779
+
3780
+ // If the max line changed since it was last measured, measure it,
3781
+ // and ensure the document's width matches it.
3782
+ // updateDisplay_W2 will use these properties to do the actual resizing
3783
+ if (display.maxLineChanged && !cm.options.lineWrapping) {
3784
+ op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3
3785
+ cm.display.sizerWidth = op.adjustWidthTo
3786
+ op.barMeasure.scrollWidth =
3787
+ Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth)
3788
+ op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm))
3789
+ }
3790
+
3791
+ if (op.updatedDisplay || op.selectionChanged)
3792
+ { op.preparedSelection = display.input.prepareSelection() }
3793
+ }
3794
+
3795
+ function endOperation_W2(op) {
3796
+ var cm = op.cm
3797
+
3798
+ if (op.adjustWidthTo != null) {
3799
+ cm.display.sizer.style.minWidth = op.adjustWidthTo + "px"
3800
+ if (op.maxScrollLeft < cm.doc.scrollLeft)
3801
+ { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true) }
3802
+ cm.display.maxLineChanged = false
3803
+ }
3804
+
3805
+ var takeFocus = op.focus && op.focus == activeElt()
3806
+ if (op.preparedSelection)
3807
+ { cm.display.input.showSelection(op.preparedSelection, takeFocus) }
3808
+ if (op.updatedDisplay || op.startHeight != cm.doc.height)
3809
+ { updateScrollbars(cm, op.barMeasure) }
3810
+ if (op.updatedDisplay)
3811
+ { setDocumentHeight(cm, op.barMeasure) }
3812
+
3813
+ if (op.selectionChanged) { restartBlink(cm) }
3814
+
3815
+ if (cm.state.focused && op.updateInput)
3816
+ { cm.display.input.reset(op.typing) }
3817
+ if (takeFocus) { ensureFocus(op.cm) }
3818
+ }
3819
+
3820
+ function endOperation_finish(op) {
3821
+ var cm = op.cm, display = cm.display, doc = cm.doc
3822
+
3823
+ if (op.updatedDisplay) { postUpdateDisplay(cm, op.update) }
3824
+
3825
+ // Abort mouse wheel delta measurement, when scrolling explicitly
3826
+ if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))
3827
+ { display.wheelStartX = display.wheelStartY = null }
3828
+
3829
+ // Propagate the scroll position to the actual DOM scroller
3830
+ if (op.scrollTop != null) { setScrollTop(cm, op.scrollTop, op.forceScroll) }
3831
+
3832
+ if (op.scrollLeft != null) { setScrollLeft(cm, op.scrollLeft, true, true) }
3833
+ // If we need to scroll a specific position into view, do so.
3834
+ if (op.scrollToPos) {
3835
+ var rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
3836
+ clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin)
3837
+ maybeScrollWindow(cm, rect)
3838
+ }
3839
+
3840
+ // Fire events for markers that are hidden/unidden by editing or
3841
+ // undoing
3842
+ var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers
3843
+ if (hidden) { for (var i = 0; i < hidden.length; ++i)
3844
+ { if (!hidden[i].lines.length) { signal(hidden[i], "hide") } } }
3845
+ if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1)
3846
+ { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], "unhide") } } }
3847
+
3848
+ if (display.wrapper.offsetHeight)
3849
+ { doc.scrollTop = cm.display.scroller.scrollTop }
3850
+
3851
+ // Fire change events, and delayed event handlers
3852
+ if (op.changeObjs)
3853
+ { signal(cm, "changes", cm, op.changeObjs) }
3854
+ if (op.update)
3855
+ { op.update.finish() }
3856
+ }
3857
+
3858
+ // Run the given function in an operation
3859
+ function runInOp(cm, f) {
3860
+ if (cm.curOp) { return f() }
3861
+ startOperation(cm)
3862
+ try { return f() }
3863
+ finally { endOperation(cm) }
3864
+ }
3865
+ // Wraps a function in an operation. Returns the wrapped function.
3866
+ function operation(cm, f) {
3867
+ return function() {
3868
+ if (cm.curOp) { return f.apply(cm, arguments) }
3869
+ startOperation(cm)
3870
+ try { return f.apply(cm, arguments) }
3871
+ finally { endOperation(cm) }
3872
+ }
3873
+ }
3874
+ // Used to add methods to editor and doc instances, wrapping them in
3875
+ // operations.
3876
+ function methodOp(f) {
3877
+ return function() {
3878
+ if (this.curOp) { return f.apply(this, arguments) }
3879
+ startOperation(this)
3880
+ try { return f.apply(this, arguments) }
3881
+ finally { endOperation(this) }
3882
+ }
3883
+ }
3884
+ function docMethodOp(f) {
3885
+ return function() {
3886
+ var cm = this.cm
3887
+ if (!cm || cm.curOp) { return f.apply(this, arguments) }
3888
+ startOperation(cm)
3889
+ try { return f.apply(this, arguments) }
3890
+ finally { endOperation(cm) }
3891
+ }
3892
+ }
3893
+
3894
+ // Updates the display.view data structure for a given change to the
3895
+ // document. From and to are in pre-change coordinates. Lendiff is
3896
+ // the amount of lines added or subtracted by the change. This is
3897
+ // used for changes that span multiple lines, or change the way
3898
+ // lines are divided into visual lines. regLineChange (below)
3899
+ // registers single-line changes.
3900
+ function regChange(cm, from, to, lendiff) {
3901
+ if (from == null) { from = cm.doc.first }
3902
+ if (to == null) { to = cm.doc.first + cm.doc.size }
3903
+ if (!lendiff) { lendiff = 0 }
3904
+
3905
+ var display = cm.display
3906
+ if (lendiff && to < display.viewTo &&
3907
+ (display.updateLineNumbers == null || display.updateLineNumbers > from))
3908
+ { display.updateLineNumbers = from }
3909
+
3910
+ cm.curOp.viewChanged = true
3911
+
3912
+ if (from >= display.viewTo) { // Change after
3913
+ if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)
3914
+ { resetView(cm) }
3915
+ } else if (to <= display.viewFrom) { // Change before
3916
+ if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {
3917
+ resetView(cm)
3918
+ } else {
3919
+ display.viewFrom += lendiff
3920
+ display.viewTo += lendiff
3921
+ }
3922
+ } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap
3923
+ resetView(cm)
3924
+ } else if (from <= display.viewFrom) { // Top overlap
3925
+ var cut = viewCuttingPoint(cm, to, to + lendiff, 1)
3926
+ if (cut) {
3927
+ display.view = display.view.slice(cut.index)
3928
+ display.viewFrom = cut.lineN
3929
+ display.viewTo += lendiff
3930
+ } else {
3931
+ resetView(cm)
3932
+ }
3933
+ } else if (to >= display.viewTo) { // Bottom overlap
3934
+ var cut$1 = viewCuttingPoint(cm, from, from, -1)
3935
+ if (cut$1) {
3936
+ display.view = display.view.slice(0, cut$1.index)
3937
+ display.viewTo = cut$1.lineN
3938
+ } else {
3939
+ resetView(cm)
3940
+ }
3941
+ } else { // Gap in the middle
3942
+ var cutTop = viewCuttingPoint(cm, from, from, -1)
3943
+ var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1)
3944
+ if (cutTop && cutBot) {
3945
+ display.view = display.view.slice(0, cutTop.index)
3946
+ .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))
3947
+ .concat(display.view.slice(cutBot.index))
3948
+ display.viewTo += lendiff
3949
+ } else {
3950
+ resetView(cm)
3951
+ }
3952
+ }
3953
+
3954
+ var ext = display.externalMeasured
3955
+ if (ext) {
3956
+ if (to < ext.lineN)
3957
+ { ext.lineN += lendiff }
3958
+ else if (from < ext.lineN + ext.size)
3959
+ { display.externalMeasured = null }
3960
+ }
3961
+ }
3962
+
3963
+ // Register a change to a single line. Type must be one of "text",
3964
+ // "gutter", "class", "widget"
3965
+ function regLineChange(cm, line, type) {
3966
+ cm.curOp.viewChanged = true
3967
+ var display = cm.display, ext = cm.display.externalMeasured
3968
+ if (ext && line >= ext.lineN && line < ext.lineN + ext.size)
3969
+ { display.externalMeasured = null }
3970
+
3971
+ if (line < display.viewFrom || line >= display.viewTo) { return }
3972
+ var lineView = display.view[findViewIndex(cm, line)]
3973
+ if (lineView.node == null) { return }
3974
+ var arr = lineView.changes || (lineView.changes = [])
3975
+ if (indexOf(arr, type) == -1) { arr.push(type) }
3976
+ }
3977
+
3978
+ // Clear the view.
3979
+ function resetView(cm) {
3980
+ cm.display.viewFrom = cm.display.viewTo = cm.doc.first
3981
+ cm.display.view = []
3982
+ cm.display.viewOffset = 0
3983
+ }
3984
+
3985
+ function viewCuttingPoint(cm, oldN, newN, dir) {
3986
+ var index = findViewIndex(cm, oldN), diff, view = cm.display.view
3987
+ if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)
3988
+ { return {index: index, lineN: newN} }
3989
+ var n = cm.display.viewFrom
3990
+ for (var i = 0; i < index; i++)
3991
+ { n += view[i].size }
3992
+ if (n != oldN) {
3993
+ if (dir > 0) {
3994
+ if (index == view.length - 1) { return null }
3995
+ diff = (n + view[index].size) - oldN
3996
+ index++
3997
+ } else {
3998
+ diff = n - oldN
3999
+ }
4000
+ oldN += diff; newN += diff
4001
+ }
4002
+ while (visualLineNo(cm.doc, newN) != newN) {
4003
+ if (index == (dir < 0 ? 0 : view.length - 1)) { return null }
4004
+ newN += dir * view[index - (dir < 0 ? 1 : 0)].size
4005
+ index += dir
4006
+ }
4007
+ return {index: index, lineN: newN}
4008
+ }
4009
+
4010
+ // Force the view to cover a given range, adding empty view element
4011
+ // or clipping off existing ones as needed.
4012
+ function adjustView(cm, from, to) {
4013
+ var display = cm.display, view = display.view
4014
+ if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {
4015
+ display.view = buildViewArray(cm, from, to)
4016
+ display.viewFrom = from
4017
+ } else {
4018
+ if (display.viewFrom > from)
4019
+ { display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view) }
4020
+ else if (display.viewFrom < from)
4021
+ { display.view = display.view.slice(findViewIndex(cm, from)) }
4022
+ display.viewFrom = from
4023
+ if (display.viewTo < to)
4024
+ { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)) }
4025
+ else if (display.viewTo > to)
4026
+ { display.view = display.view.slice(0, findViewIndex(cm, to)) }
4027
+ }
4028
+ display.viewTo = to
4029
+ }
4030
+
4031
+ // Count the number of lines in the view whose DOM representation is
4032
+ // out of date (or nonexistent).
4033
+ function countDirtyView(cm) {
4034
+ var view = cm.display.view, dirty = 0
4035
+ for (var i = 0; i < view.length; i++) {
4036
+ var lineView = view[i]
4037
+ if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty }
4038
+ }
4039
+ return dirty
4040
+ }
4041
+
4042
+ // HIGHLIGHT WORKER
4043
+
4044
+ function startWorker(cm, time) {
4045
+ if (cm.doc.highlightFrontier < cm.display.viewTo)
4046
+ { cm.state.highlight.set(time, bind(highlightWorker, cm)) }
4047
+ }
4048
+
4049
+ function highlightWorker(cm) {
4050
+ var doc = cm.doc
4051
+ if (doc.highlightFrontier >= cm.display.viewTo) { return }
4052
+ var end = +new Date + cm.options.workTime
4053
+ var context = getContextBefore(cm, doc.highlightFrontier)
4054
+ var changedLines = []
4055
+
4056
+ doc.iter(context.line, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function (line) {
4057
+ if (context.line >= cm.display.viewFrom) { // Visible
4058
+ var oldStyles = line.styles
4059
+ var resetState = line.text.length > cm.options.maxHighlightLength ? copyState(doc.mode, context.state) : null
4060
+ var highlighted = highlightLine(cm, line, context, true)
4061
+ if (resetState) { context.state = resetState }
4062
+ line.styles = highlighted.styles
4063
+ var oldCls = line.styleClasses, newCls = highlighted.classes
4064
+ if (newCls) { line.styleClasses = newCls }
4065
+ else if (oldCls) { line.styleClasses = null }
4066
+ var ischange = !oldStyles || oldStyles.length != line.styles.length ||
4067
+ oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass)
4068
+ for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldStyles[i] != line.styles[i] }
4069
+ if (ischange) { changedLines.push(context.line) }
4070
+ line.stateAfter = context.save()
4071
+ context.nextLine()
4072
+ } else {
4073
+ if (line.text.length <= cm.options.maxHighlightLength)
4074
+ { processLine(cm, line.text, context) }
4075
+ line.stateAfter = context.line % 5 == 0 ? context.save() : null
4076
+ context.nextLine()
4077
+ }
4078
+ if (+new Date > end) {
4079
+ startWorker(cm, cm.options.workDelay)
4080
+ return true
4081
+ }
4082
+ })
4083
+ doc.highlightFrontier = context.line
4084
+ doc.modeFrontier = Math.max(doc.modeFrontier, context.line)
4085
+ if (changedLines.length) { runInOp(cm, function () {
4086
+ for (var i = 0; i < changedLines.length; i++)
4087
+ { regLineChange(cm, changedLines[i], "text") }
4088
+ }) }
4089
+ }
4090
+
4091
+ // DISPLAY DRAWING
4092
+
4093
+ var DisplayUpdate = function(cm, viewport, force) {
4094
+ var display = cm.display
4095
+
4096
+ this.viewport = viewport
4097
+ // Store some values that we'll need later (but don't want to force a relayout for)
4098
+ this.visible = visibleLines(display, cm.doc, viewport)
4099
+ this.editorIsHidden = !display.wrapper.offsetWidth
4100
+ this.wrapperHeight = display.wrapper.clientHeight
4101
+ this.wrapperWidth = display.wrapper.clientWidth
4102
+ this.oldDisplayWidth = displayWidth(cm)
4103
+ this.force = force
4104
+ this.dims = getDimensions(cm)
4105
+ this.events = []
4106
+ };
4107
+
4108
+ DisplayUpdate.prototype.signal = function (emitter, type) {
4109
+ if (hasHandler(emitter, type))
4110
+ { this.events.push(arguments) }
4111
+ };
4112
+ DisplayUpdate.prototype.finish = function () {
4113
+ var this$1 = this;
4114
+
4115
+ for (var i = 0; i < this.events.length; i++)
4116
+ { signal.apply(null, this$1.events[i]) }
4117
+ };
4118
+
4119
+ function maybeClipScrollbars(cm) {
4120
+ var display = cm.display
4121
+ if (!display.scrollbarsClipped && display.scroller.offsetWidth) {
4122
+ display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth
4123
+ display.heightForcer.style.height = scrollGap(cm) + "px"
4124
+ display.sizer.style.marginBottom = -display.nativeBarWidth + "px"
4125
+ display.sizer.style.borderRightWidth = scrollGap(cm) + "px"
4126
+ display.scrollbarsClipped = true
4127
+ }
4128
+ }
4129
+
4130
+ function selectionSnapshot(cm) {
4131
+ if (cm.hasFocus()) { return null }
4132
+ var active = activeElt()
4133
+ if (!active || !contains(cm.display.lineDiv, active)) { return null }
4134
+ var result = {activeElt: active}
4135
+ if (window.getSelection) {
4136
+ var sel = window.getSelection()
4137
+ if (sel.anchorNode && sel.extend && contains(cm.display.lineDiv, sel.anchorNode)) {
4138
+ result.anchorNode = sel.anchorNode
4139
+ result.anchorOffset = sel.anchorOffset
4140
+ result.focusNode = sel.focusNode
4141
+ result.focusOffset = sel.focusOffset
4142
+ }
4143
+ }
4144
+ return result
4145
+ }
4146
+
4147
+ function restoreSelection(snapshot) {
4148
+ if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt()) { return }
4149
+ snapshot.activeElt.focus()
4150
+ if (snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) {
4151
+ var sel = window.getSelection(), range = document.createRange()
4152
+ range.setEnd(snapshot.anchorNode, snapshot.anchorOffset)
4153
+ range.collapse(false)
4154
+ sel.removeAllRanges()
4155
+ sel.addRange(range)
4156
+ sel.extend(snapshot.focusNode, snapshot.focusOffset)
4157
+ }
4158
+ }
4159
+
4160
+ // Does the actual updating of the line display. Bails out
4161
+ // (returning false) when there is nothing to be done and forced is
4162
+ // false.
4163
+ function updateDisplayIfNeeded(cm, update) {
4164
+ var display = cm.display, doc = cm.doc
4165
+
4166
+ if (update.editorIsHidden) {
4167
+ resetView(cm)
4168
+ return false
4169
+ }
4170
+
4171
+ // Bail out if the visible area is already rendered and nothing changed.
4172
+ if (!update.force &&
4173
+ update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&
4174
+ (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&
4175
+ display.renderedView == display.view && countDirtyView(cm) == 0)
4176
+ { return false }
4177
+
4178
+ if (maybeUpdateLineNumberWidth(cm)) {
4179
+ resetView(cm)
4180
+ update.dims = getDimensions(cm)
4181
+ }
4182
+
4183
+ // Compute a suitable new viewport (from & to)
4184
+ var end = doc.first + doc.size
4185
+ var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first)
4186
+ var to = Math.min(end, update.visible.to + cm.options.viewportMargin)
4187
+ if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max(doc.first, display.viewFrom) }
4188
+ if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, display.viewTo) }
4189
+ if (sawCollapsedSpans) {
4190
+ from = visualLineNo(cm.doc, from)
4191
+ to = visualLineEndNo(cm.doc, to)
4192
+ }
4193
+
4194
+ var different = from != display.viewFrom || to != display.viewTo ||
4195
+ display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth
4196
+ adjustView(cm, from, to)
4197
+
4198
+ display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom))
4199
+ // Position the mover div to align with the current scroll position
4200
+ cm.display.mover.style.top = display.viewOffset + "px"
4201
+
4202
+ var toUpdate = countDirtyView(cm)
4203
+ if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view &&
4204
+ (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))
4205
+ { return false }
4206
+
4207
+ // For big changes, we hide the enclosing element during the
4208
+ // update, since that speeds up the operations on most browsers.
4209
+ var selSnapshot = selectionSnapshot(cm)
4210
+ if (toUpdate > 4) { display.lineDiv.style.display = "none" }
4211
+ patchDisplay(cm, display.updateLineNumbers, update.dims)
4212
+ if (toUpdate > 4) { display.lineDiv.style.display = "" }
4213
+ display.renderedView = display.view
4214
+ // There might have been a widget with a focused element that got
4215
+ // hidden or updated, if so re-focus it.
4216
+ restoreSelection(selSnapshot)
4217
+
4218
+ // Prevent selection and cursors from interfering with the scroll
4219
+ // width and height.
4220
+ removeChildren(display.cursorDiv)
4221
+ removeChildren(display.selectionDiv)
4222
+ display.gutters.style.height = display.sizer.style.minHeight = 0
4223
+
4224
+ if (different) {
4225
+ display.lastWrapHeight = update.wrapperHeight
4226
+ display.lastWrapWidth = update.wrapperWidth
4227
+ startWorker(cm, 400)
4228
+ }
4229
+
4230
+ display.updateLineNumbers = null
4231
+
4232
+ return true
4233
+ }
4234
+
4235
+ function postUpdateDisplay(cm, update) {
4236
+ var viewport = update.viewport
4237
+
4238
+ for (var first = true;; first = false) {
4239
+ if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) {
4240
+ // Clip forced viewport to actual scrollable area.
4241
+ if (viewport && viewport.top != null)
4242
+ { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)} }
4243
+ // Updated line heights might result in the drawn area not
4244
+ // actually covering the viewport. Keep looping until it does.
4245
+ update.visible = visibleLines(cm.display, cm.doc, viewport)
4246
+ if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)
4247
+ { break }
4248
+ }
4249
+ if (!updateDisplayIfNeeded(cm, update)) { break }
4250
+ updateHeightsInViewport(cm)
4251
+ var barMeasure = measureForScrollbars(cm)
4252
+ updateSelection(cm)
4253
+ updateScrollbars(cm, barMeasure)
4254
+ setDocumentHeight(cm, barMeasure)
4255
+ update.force = false
4256
+ }
4257
+
4258
+ update.signal(cm, "update", cm)
4259
+ if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {
4260
+ update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo)
4261
+ cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo
4262
+ }
4263
+ }
4264
+
4265
+ function updateDisplaySimple(cm, viewport) {
4266
+ var update = new DisplayUpdate(cm, viewport)
4267
+ if (updateDisplayIfNeeded(cm, update)) {
4268
+ updateHeightsInViewport(cm)
4269
+ postUpdateDisplay(cm, update)
4270
+ var barMeasure = measureForScrollbars(cm)
4271
+ updateSelection(cm)
4272
+ updateScrollbars(cm, barMeasure)
4273
+ setDocumentHeight(cm, barMeasure)
4274
+ update.finish()
4275
+ }
4276
+ }
4277
+
4278
+ // Sync the actual display DOM structure with display.view, removing
4279
+ // nodes for lines that are no longer in view, and creating the ones
4280
+ // that are not there yet, and updating the ones that are out of
4281
+ // date.
4282
+ function patchDisplay(cm, updateNumbersFrom, dims) {
4283
+ var display = cm.display, lineNumbers = cm.options.lineNumbers
4284
+ var container = display.lineDiv, cur = container.firstChild
4285
+
4286
+ function rm(node) {
4287
+ var next = node.nextSibling
4288
+ // Works around a throw-scroll bug in OS X Webkit
4289
+ if (webkit && mac && cm.display.currentWheelTarget == node)
4290
+ { node.style.display = "none" }
4291
+ else
4292
+ { node.parentNode.removeChild(node) }
4293
+ return next
4294
+ }
4295
+
4296
+ var view = display.view, lineN = display.viewFrom
4297
+ // Loop over the elements in the view, syncing cur (the DOM nodes
4298
+ // in display.lineDiv) with the view as we go.
4299
+ for (var i = 0; i < view.length; i++) {
4300
+ var lineView = view[i]
4301
+ if (lineView.hidden) {
4302
+ } else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet
4303
+ var node = buildLineElement(cm, lineView, lineN, dims)
4304
+ container.insertBefore(node, cur)
4305
+ } else { // Already drawn
4306
+ while (cur != lineView.node) { cur = rm(cur) }
4307
+ var updateNumber = lineNumbers && updateNumbersFrom != null &&
4308
+ updateNumbersFrom <= lineN && lineView.lineNumber
4309
+ if (lineView.changes) {
4310
+ if (indexOf(lineView.changes, "gutter") > -1) { updateNumber = false }
4311
+ updateLineForChanges(cm, lineView, lineN, dims)
4312
+ }
4313
+ if (updateNumber) {
4314
+ removeChildren(lineView.lineNumber)
4315
+ lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN)))
4316
+ }
4317
+ cur = lineView.node.nextSibling
4318
+ }
4319
+ lineN += lineView.size
4320
+ }
4321
+ while (cur) { cur = rm(cur) }
4322
+ }
4323
+
4324
+ function updateGutterSpace(cm) {
4325
+ var width = cm.display.gutters.offsetWidth
4326
+ cm.display.sizer.style.marginLeft = width + "px"
4327
+ }
4328
+
4329
+ function setDocumentHeight(cm, measure) {
4330
+ cm.display.sizer.style.minHeight = measure.docHeight + "px"
4331
+ cm.display.heightForcer.style.top = measure.docHeight + "px"
4332
+ cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px"
4333
+ }
4334
+
4335
+ // Rebuild the gutter elements, ensure the margin to the left of the
4336
+ // code matches their width.
4337
+ function updateGutters(cm) {
4338
+ var gutters = cm.display.gutters, specs = cm.options.gutters
4339
+ removeChildren(gutters)
4340
+ var i = 0
4341
+ for (; i < specs.length; ++i) {
4342
+ var gutterClass = specs[i]
4343
+ var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass))
4344
+ if (gutterClass == "CodeMirror-linenumbers") {
4345
+ cm.display.lineGutter = gElt
4346
+ gElt.style.width = (cm.display.lineNumWidth || 1) + "px"
4347
+ }
4348
+ }
4349
+ gutters.style.display = i ? "" : "none"
4350
+ updateGutterSpace(cm)
4351
+ }
4352
+
4353
+ // Make sure the gutters options contains the element
4354
+ // "CodeMirror-linenumbers" when the lineNumbers option is true.
4355
+ function setGuttersForLineNumbers(options) {
4356
+ var found = indexOf(options.gutters, "CodeMirror-linenumbers")
4357
+ if (found == -1 && options.lineNumbers) {
4358
+ options.gutters = options.gutters.concat(["CodeMirror-linenumbers"])
4359
+ } else if (found > -1 && !options.lineNumbers) {
4360
+ options.gutters = options.gutters.slice(0)
4361
+ options.gutters.splice(found, 1)
4362
+ }
4363
+ }
4364
+
4365
+ var wheelSamples = 0;
4366
+ var wheelPixelsPerUnit = null;
4367
+ // Fill in a browser-detected starting value on browsers where we
4368
+ // know one. These don't have to be accurate -- the result of them
4369
+ // being wrong would just be a slight flicker on the first wheel
4370
+ // scroll (if it is large enough).
4371
+ if (ie) { wheelPixelsPerUnit = -.53 }
4372
+ else if (gecko) { wheelPixelsPerUnit = 15 }
4373
+ else if (chrome) { wheelPixelsPerUnit = -.7 }
4374
+ else if (safari) { wheelPixelsPerUnit = -1/3 }
4375
+
4376
+ function wheelEventDelta(e) {
4377
+ var dx = e.wheelDeltaX, dy = e.wheelDeltaY
4378
+ if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail }
4379
+ if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail }
4380
+ else if (dy == null) { dy = e.wheelDelta }
4381
+ return {x: dx, y: dy}
4382
+ }
4383
+ function wheelEventPixels(e) {
4384
+ var delta = wheelEventDelta(e)
4385
+ delta.x *= wheelPixelsPerUnit
4386
+ delta.y *= wheelPixelsPerUnit
4387
+ return delta
4388
+ }
4389
+
4390
+ function onScrollWheel(cm, e) {
4391
+ var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y
4392
+
4393
+ var display = cm.display, scroll = display.scroller
4394
+ // Quit if there's nothing to scroll here
4395
+ var canScrollX = scroll.scrollWidth > scroll.clientWidth
4396
+ var canScrollY = scroll.scrollHeight > scroll.clientHeight
4397
+ if (!(dx && canScrollX || dy && canScrollY)) { return }
4398
+
4399
+ // Webkit browsers on OS X abort momentum scrolls when the target
4400
+ // of the scroll event is removed from the scrollable element.
4401
+ // This hack (see related code in patchDisplay) makes sure the
4402
+ // element is kept around.
4403
+ if (dy && mac && webkit) {
4404
+ outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {
4405
+ for (var i = 0; i < view.length; i++) {
4406
+ if (view[i].node == cur) {
4407
+ cm.display.currentWheelTarget = cur
4408
+ break outer
4409
+ }
4410
+ }
4411
+ }
4412
+ }
4413
+
4414
+ // On some browsers, horizontal scrolling will cause redraws to
4415
+ // happen before the gutter has been realigned, causing it to
4416
+ // wriggle around in a most unseemly way. When we have an
4417
+ // estimated pixels/delta value, we just handle horizontal
4418
+ // scrolling entirely here. It'll be slightly off from native, but
4419
+ // better than glitching out.
4420
+ if (dx && !gecko && !presto && wheelPixelsPerUnit != null) {
4421
+ if (dy && canScrollY)
4422
+ { updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * wheelPixelsPerUnit)) }
4423
+ setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * wheelPixelsPerUnit))
4424
+ // Only prevent default scrolling if vertical scrolling is
4425
+ // actually possible. Otherwise, it causes vertical scroll
4426
+ // jitter on OSX trackpads when deltaX is small and deltaY
4427
+ // is large (issue #3579)
4428
+ if (!dy || (dy && canScrollY))
4429
+ { e_preventDefault(e) }
4430
+ display.wheelStartX = null // Abort measurement, if in progress
4431
+ return
4432
+ }
4433
+
4434
+ // 'Project' the visible viewport to cover the area that is being
4435
+ // scrolled into view (if we know enough to estimate it).
4436
+ if (dy && wheelPixelsPerUnit != null) {
4437
+ var pixels = dy * wheelPixelsPerUnit
4438
+ var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight
4439
+ if (pixels < 0) { top = Math.max(0, top + pixels - 50) }
4440
+ else { bot = Math.min(cm.doc.height, bot + pixels + 50) }
4441
+ updateDisplaySimple(cm, {top: top, bottom: bot})
4442
+ }
4443
+
4444
+ if (wheelSamples < 20) {
4445
+ if (display.wheelStartX == null) {
4446
+ display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop
4447
+ display.wheelDX = dx; display.wheelDY = dy
4448
+ setTimeout(function () {
4449
+ if (display.wheelStartX == null) { return }
4450
+ var movedX = scroll.scrollLeft - display.wheelStartX
4451
+ var movedY = scroll.scrollTop - display.wheelStartY
4452
+ var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
4453
+ (movedX && display.wheelDX && movedX / display.wheelDX)
4454
+ display.wheelStartX = display.wheelStartY = null
4455
+ if (!sample) { return }
4456
+ wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1)
4457
+ ++wheelSamples
4458
+ }, 200)
4459
+ } else {
4460
+ display.wheelDX += dx; display.wheelDY += dy
4461
+ }
4462
+ }
4463
+ }
4464
+
4465
+ // Selection objects are immutable. A new one is created every time
4466
+ // the selection changes. A selection is one or more non-overlapping
4467
+ // (and non-touching) ranges, sorted, and an integer that indicates
4468
+ // which one is the primary selection (the one that's scrolled into
4469
+ // view, that getCursor returns, etc).
4470
+ var Selection = function(ranges, primIndex) {
4471
+ this.ranges = ranges
4472
+ this.primIndex = primIndex
4473
+ };
4474
+
4475
+ Selection.prototype.primary = function () { return this.ranges[this.primIndex] };
4476
+
4477
+ Selection.prototype.equals = function (other) {
4478
+ var this$1 = this;
4479
+
4480
+ if (other == this) { return true }
4481
+ if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false }
4482
+ for (var i = 0; i < this.ranges.length; i++) {
4483
+ var here = this$1.ranges[i], there = other.ranges[i]
4484
+ if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) { return false }
4485
+ }
4486
+ return true
4487
+ };
4488
+
4489
+ Selection.prototype.deepCopy = function () {
4490
+ var this$1 = this;
4491
+
4492
+ var out = []
4493
+ for (var i = 0; i < this.ranges.length; i++)
4494
+ { out[i] = new Range(copyPos(this$1.ranges[i].anchor), copyPos(this$1.ranges[i].head)) }
4495
+ return new Selection(out, this.primIndex)
4496
+ };
4497
+
4498
+ Selection.prototype.somethingSelected = function () {
4499
+ var this$1 = this;
4500
+
4501
+ for (var i = 0; i < this.ranges.length; i++)
4502
+ { if (!this$1.ranges[i].empty()) { return true } }
4503
+ return false
4504
+ };
4505
+
4506
+ Selection.prototype.contains = function (pos, end) {
4507
+ var this$1 = this;
4508
+
4509
+ if (!end) { end = pos }
4510
+ for (var i = 0; i < this.ranges.length; i++) {
4511
+ var range = this$1.ranges[i]
4512
+ if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)
4513
+ { return i }
4514
+ }
4515
+ return -1
4516
+ };
4517
+
4518
+ var Range = function(anchor, head) {
4519
+ this.anchor = anchor; this.head = head
4520
+ };
4521
+
4522
+ Range.prototype.from = function () { return minPos(this.anchor, this.head) };
4523
+ Range.prototype.to = function () { return maxPos(this.anchor, this.head) };
4524
+ Range.prototype.empty = function () { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch };
4525
+
4526
+ // Take an unsorted, potentially overlapping set of ranges, and
4527
+ // build a selection out of it. 'Consumes' ranges array (modifying
4528
+ // it).
4529
+ function normalizeSelection(ranges, primIndex) {
4530
+ var prim = ranges[primIndex]
4531
+ ranges.sort(function (a, b) { return cmp(a.from(), b.from()); })
4532
+ primIndex = indexOf(ranges, prim)
4533
+ for (var i = 1; i < ranges.length; i++) {
4534
+ var cur = ranges[i], prev = ranges[i - 1]
4535
+ if (cmp(prev.to(), cur.from()) >= 0) {
4536
+ var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to())
4537
+ var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head
4538
+ if (i <= primIndex) { --primIndex }
4539
+ ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to))
4540
+ }
4541
+ }
4542
+ return new Selection(ranges, primIndex)
4543
+ }
4544
+
4545
+ function simpleSelection(anchor, head) {
4546
+ return new Selection([new Range(anchor, head || anchor)], 0)
4547
+ }
4548
+
4549
+ // Compute the position of the end of a change (its 'to' property
4550
+ // refers to the pre-change end).
4551
+ function changeEnd(change) {
4552
+ if (!change.text) { return change.to }
4553
+ return Pos(change.from.line + change.text.length - 1,
4554
+ lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0))
4555
+ }
4556
+
4557
+ // Adjust a position to refer to the post-change position of the
4558
+ // same text, or the end of the change if the change covers it.
4559
+ function adjustForChange(pos, change) {
4560
+ if (cmp(pos, change.from) < 0) { return pos }
4561
+ if (cmp(pos, change.to) <= 0) { return changeEnd(change) }
4562
+
4563
+ var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch
4564
+ if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch }
4565
+ return Pos(line, ch)
4566
+ }
4567
+
4568
+ function computeSelAfterChange(doc, change) {
4569
+ var out = []
4570
+ for (var i = 0; i < doc.sel.ranges.length; i++) {
4571
+ var range = doc.sel.ranges[i]
4572
+ out.push(new Range(adjustForChange(range.anchor, change),
4573
+ adjustForChange(range.head, change)))
4574
+ }
4575
+ return normalizeSelection(out, doc.sel.primIndex)
4576
+ }
4577
+
4578
+ function offsetPos(pos, old, nw) {
4579
+ if (pos.line == old.line)
4580
+ { return Pos(nw.line, pos.ch - old.ch + nw.ch) }
4581
+ else
4582
+ { return Pos(nw.line + (pos.line - old.line), pos.ch) }
4583
+ }
4584
+
4585
+ // Used by replaceSelections to allow moving the selection to the
4586
+ // start or around the replaced test. Hint may be "start" or "around".
4587
+ function computeReplacedSel(doc, changes, hint) {
4588
+ var out = []
4589
+ var oldPrev = Pos(doc.first, 0), newPrev = oldPrev
4590
+ for (var i = 0; i < changes.length; i++) {
4591
+ var change = changes[i]
4592
+ var from = offsetPos(change.from, oldPrev, newPrev)
4593
+ var to = offsetPos(changeEnd(change), oldPrev, newPrev)
4594
+ oldPrev = change.to
4595
+ newPrev = to
4596
+ if (hint == "around") {
4597
+ var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0
4598
+ out[i] = new Range(inv ? to : from, inv ? from : to)
4599
+ } else {
4600
+ out[i] = new Range(from, from)
4601
+ }
4602
+ }
4603
+ return new Selection(out, doc.sel.primIndex)
4604
+ }
4605
+
4606
+ // Used to get the editor into a consistent state again when options change.
4607
+
4608
+ function loadMode(cm) {
4609
+ cm.doc.mode = getMode(cm.options, cm.doc.modeOption)
4610
+ resetModeState(cm)
4611
+ }
4612
+
4613
+ function resetModeState(cm) {
4614
+ cm.doc.iter(function (line) {
4615
+ if (line.stateAfter) { line.stateAfter = null }
4616
+ if (line.styles) { line.styles = null }
4617
+ })
4618
+ cm.doc.modeFrontier = cm.doc.highlightFrontier = cm.doc.first
4619
+ startWorker(cm, 100)
4620
+ cm.state.modeGen++
4621
+ if (cm.curOp) { regChange(cm) }
4622
+ }
4623
+
4624
+ // DOCUMENT DATA STRUCTURE
4625
+
4626
+ // By default, updates that start and end at the beginning of a line
4627
+ // are treated specially, in order to make the association of line
4628
+ // widgets and marker elements with the text behave more intuitive.
4629
+ function isWholeLineUpdate(doc, change) {
4630
+ return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" &&
4631
+ (!doc.cm || doc.cm.options.wholeLineUpdateBefore)
4632
+ }
4633
+
4634
+ // Perform a change on the document data structure.
4635
+ function updateDoc(doc, change, markedSpans, estimateHeight) {
4636
+ function spansFor(n) {return markedSpans ? markedSpans[n] : null}
4637
+ function update(line, text, spans) {
4638
+ updateLine(line, text, spans, estimateHeight)
4639
+ signalLater(line, "change", line, change)
4640
+ }
4641
+ function linesFor(start, end) {
4642
+ var result = []
4643
+ for (var i = start; i < end; ++i)
4644
+ { result.push(new Line(text[i], spansFor(i), estimateHeight)) }
4645
+ return result
4646
+ }
4647
+
4648
+ var from = change.from, to = change.to, text = change.text
4649
+ var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line)
4650
+ var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line
4651
+
4652
+ // Adjust the line structure
4653
+ if (change.full) {
4654
+ doc.insert(0, linesFor(0, text.length))
4655
+ doc.remove(text.length, doc.size - text.length)
4656
+ } else if (isWholeLineUpdate(doc, change)) {
4657
+ // This is a whole-line replace. Treated specially to make
4658
+ // sure line objects move the way they are supposed to.
4659
+ var added = linesFor(0, text.length - 1)
4660
+ update(lastLine, lastLine.text, lastSpans)
4661
+ if (nlines) { doc.remove(from.line, nlines) }
4662
+ if (added.length) { doc.insert(from.line, added) }
4663
+ } else if (firstLine == lastLine) {
4664
+ if (text.length == 1) {
4665
+ update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans)
4666
+ } else {
4667
+ var added$1 = linesFor(1, text.length - 1)
4668
+ added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight))
4669
+ update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0))
4670
+ doc.insert(from.line + 1, added$1)
4671
+ }
4672
+ } else if (text.length == 1) {
4673
+ update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0))
4674
+ doc.remove(from.line + 1, nlines)
4675
+ } else {
4676
+ update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0))
4677
+ update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans)
4678
+ var added$2 = linesFor(1, text.length - 1)
4679
+ if (nlines > 1) { doc.remove(from.line + 1, nlines - 1) }
4680
+ doc.insert(from.line + 1, added$2)
4681
+ }
4682
+
4683
+ signalLater(doc, "change", doc, change)
4684
+ }
4685
+
4686
+ // Call f for all linked documents.
4687
+ function linkedDocs(doc, f, sharedHistOnly) {
4688
+ function propagate(doc, skip, sharedHist) {
4689
+ if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) {
4690
+ var rel = doc.linked[i]
4691
+ if (rel.doc == skip) { continue }
4692
+ var shared = sharedHist && rel.sharedHist
4693
+ if (sharedHistOnly && !shared) { continue }
4694
+ f(rel.doc, shared)
4695
+ propagate(rel.doc, doc, shared)
4696
+ } }
4697
+ }
4698
+ propagate(doc, null, true)
4699
+ }
4700
+
4701
+ // Attach a document to an editor.
4702
+ function attachDoc(cm, doc) {
4703
+ if (doc.cm) { throw new Error("This document is already in use.") }
4704
+ cm.doc = doc
4705
+ doc.cm = cm
4706
+ estimateLineHeights(cm)
4707
+ loadMode(cm)
4708
+ setDirectionClass(cm)
4709
+ if (!cm.options.lineWrapping) { findMaxLine(cm) }
4710
+ cm.options.mode = doc.modeOption
4711
+ regChange(cm)
4712
+ }
4713
+
4714
+ function setDirectionClass(cm) {
4715
+ ;(cm.doc.direction == "rtl" ? addClass : rmClass)(cm.display.lineDiv, "CodeMirror-rtl")
4716
+ }
4717
+
4718
+ function directionChanged(cm) {
4719
+ runInOp(cm, function () {
4720
+ setDirectionClass(cm)
4721
+ regChange(cm)
4722
+ })
4723
+ }
4724
+
4725
+ function History(startGen) {
4726
+ // Arrays of change events and selections. Doing something adds an
4727
+ // event to done and clears undo. Undoing moves events from done
4728
+ // to undone, redoing moves them in the other direction.
4729
+ this.done = []; this.undone = []
4730
+ this.undoDepth = Infinity
4731
+ // Used to track when changes can be merged into a single undo
4732
+ // event
4733
+ this.lastModTime = this.lastSelTime = 0
4734
+ this.lastOp = this.lastSelOp = null
4735
+ this.lastOrigin = this.lastSelOrigin = null
4736
+ // Used by the isClean() method
4737
+ this.generation = this.maxGeneration = startGen || 1
4738
+ }
4739
+
4740
+ // Create a history change event from an updateDoc-style change
4741
+ // object.
4742
+ function historyChangeFromChange(doc, change) {
4743
+ var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)}
4744
+ attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1)
4745
+ linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); }, true)
4746
+ return histChange
4747
+ }
4748
+
4749
+ // Pop all selection events off the end of a history array. Stop at
4750
+ // a change event.
4751
+ function clearSelectionEvents(array) {
4752
+ while (array.length) {
4753
+ var last = lst(array)
4754
+ if (last.ranges) { array.pop() }
4755
+ else { break }
4756
+ }
4757
+ }
4758
+
4759
+ // Find the top change event in the history. Pop off selection
4760
+ // events that are in the way.
4761
+ function lastChangeEvent(hist, force) {
4762
+ if (force) {
4763
+ clearSelectionEvents(hist.done)
4764
+ return lst(hist.done)
4765
+ } else if (hist.done.length && !lst(hist.done).ranges) {
4766
+ return lst(hist.done)
4767
+ } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) {
4768
+ hist.done.pop()
4769
+ return lst(hist.done)
4770
+ }
4771
+ }
4772
+
4773
+ // Register a change in the history. Merges changes that are within
4774
+ // a single operation, or are close together with an origin that
4775
+ // allows merging (starting with "+") into a single event.
4776
+ function addChangeToHistory(doc, change, selAfter, opId) {
4777
+ var hist = doc.history
4778
+ hist.undone.length = 0
4779
+ var time = +new Date, cur
4780
+ var last
4781
+
4782
+ if ((hist.lastOp == opId ||
4783
+ hist.lastOrigin == change.origin && change.origin &&
4784
+ ((change.origin.charAt(0) == "+" && doc.cm && hist.lastModTime > time - doc.cm.options.historyEventDelay) ||
4785
+ change.origin.charAt(0) == "*")) &&
4786
+ (cur = lastChangeEvent(hist, hist.lastOp == opId))) {
4787
+ // Merge this change into the last event
4788
+ last = lst(cur.changes)
4789
+ if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) {
4790
+ // Optimized case for simple insertion -- don't want to add
4791
+ // new changesets for every character typed
4792
+ last.to = changeEnd(change)
4793
+ } else {
4794
+ // Add new sub-event
4795
+ cur.changes.push(historyChangeFromChange(doc, change))
4796
+ }
4797
+ } else {
4798
+ // Can not be merged, start a new event.
4799
+ var before = lst(hist.done)
4800
+ if (!before || !before.ranges)
4801
+ { pushSelectionToHistory(doc.sel, hist.done) }
4802
+ cur = {changes: [historyChangeFromChange(doc, change)],
4803
+ generation: hist.generation}
4804
+ hist.done.push(cur)
4805
+ while (hist.done.length > hist.undoDepth) {
4806
+ hist.done.shift()
4807
+ if (!hist.done[0].ranges) { hist.done.shift() }
4808
+ }
4809
+ }
4810
+ hist.done.push(selAfter)
4811
+ hist.generation = ++hist.maxGeneration
4812
+ hist.lastModTime = hist.lastSelTime = time
4813
+ hist.lastOp = hist.lastSelOp = opId
4814
+ hist.lastOrigin = hist.lastSelOrigin = change.origin
4815
+
4816
+ if (!last) { signal(doc, "historyAdded") }
4817
+ }
4818
+
4819
+ function selectionEventCanBeMerged(doc, origin, prev, sel) {
4820
+ var ch = origin.charAt(0)
4821
+ return ch == "*" ||
4822
+ ch == "+" &&
4823
+ prev.ranges.length == sel.ranges.length &&
4824
+ prev.somethingSelected() == sel.somethingSelected() &&
4825
+ new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500)
4826
+ }
4827
+
4828
+ // Called whenever the selection changes, sets the new selection as
4829
+ // the pending selection in the history, and pushes the old pending
4830
+ // selection into the 'done' array when it was significantly
4831
+ // different (in number of selected ranges, emptiness, or time).
4832
+ function addSelectionToHistory(doc, sel, opId, options) {
4833
+ var hist = doc.history, origin = options && options.origin
4834
+
4835
+ // A new event is started when the previous origin does not match
4836
+ // the current, or the origins don't allow matching. Origins
4837
+ // starting with * are always merged, those starting with + are
4838
+ // merged when similar and close together in time.
4839
+ if (opId == hist.lastSelOp ||
4840
+ (origin && hist.lastSelOrigin == origin &&
4841
+ (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||
4842
+ selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))
4843
+ { hist.done[hist.done.length - 1] = sel }
4844
+ else
4845
+ { pushSelectionToHistory(sel, hist.done) }
4846
+
4847
+ hist.lastSelTime = +new Date
4848
+ hist.lastSelOrigin = origin
4849
+ hist.lastSelOp = opId
4850
+ if (options && options.clearRedo !== false)
4851
+ { clearSelectionEvents(hist.undone) }
4852
+ }
4853
+
4854
+ function pushSelectionToHistory(sel, dest) {
4855
+ var top = lst(dest)
4856
+ if (!(top && top.ranges && top.equals(sel)))
4857
+ { dest.push(sel) }
4858
+ }
4859
+
4860
+ // Used to store marked span information in the history.
4861
+ function attachLocalSpans(doc, change, from, to) {
4862
+ var existing = change["spans_" + doc.id], n = 0
4863
+ doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function (line) {
4864
+ if (line.markedSpans)
4865
+ { (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans }
4866
+ ++n
4867
+ })
4868
+ }
4869
+
4870
+ // When un/re-doing restores text containing marked spans, those
4871
+ // that have been explicitly cleared should not be restored.
4872
+ function removeClearedSpans(spans) {
4873
+ if (!spans) { return null }
4874
+ var out
4875
+ for (var i = 0; i < spans.length; ++i) {
4876
+ if (spans[i].marker.explicitlyCleared) { if (!out) { out = spans.slice(0, i) } }
4877
+ else if (out) { out.push(spans[i]) }
4878
+ }
4879
+ return !out ? spans : out.length ? out : null
4880
+ }
4881
+
4882
+ // Retrieve and filter the old marked spans stored in a change event.
4883
+ function getOldSpans(doc, change) {
4884
+ var found = change["spans_" + doc.id]
4885
+ if (!found) { return null }
4886
+ var nw = []
4887
+ for (var i = 0; i < change.text.length; ++i)
4888
+ { nw.push(removeClearedSpans(found[i])) }
4889
+ return nw
4890
+ }
4891
+
4892
+ // Used for un/re-doing changes from the history. Combines the
4893
+ // result of computing the existing spans with the set of spans that
4894
+ // existed in the history (so that deleting around a span and then
4895
+ // undoing brings back the span).
4896
+ function mergeOldSpans(doc, change) {
4897
+ var old = getOldSpans(doc, change)
4898
+ var stretched = stretchSpansOverChange(doc, change)
4899
+ if (!old) { return stretched }
4900
+ if (!stretched) { return old }
4901
+
4902
+ for (var i = 0; i < old.length; ++i) {
4903
+ var oldCur = old[i], stretchCur = stretched[i]
4904
+ if (oldCur && stretchCur) {
4905
+ spans: for (var j = 0; j < stretchCur.length; ++j) {
4906
+ var span = stretchCur[j]
4907
+ for (var k = 0; k < oldCur.length; ++k)
4908
+ { if (oldCur[k].marker == span.marker) { continue spans } }
4909
+ oldCur.push(span)
4910
+ }
4911
+ } else if (stretchCur) {
4912
+ old[i] = stretchCur
4913
+ }
4914
+ }
4915
+ return old
4916
+ }
4917
+
4918
+ // Used both to provide a JSON-safe object in .getHistory, and, when
4919
+ // detaching a document, to split the history in two
4920
+ function copyHistoryArray(events, newGroup, instantiateSel) {
4921
+ var copy = []
4922
+ for (var i = 0; i < events.length; ++i) {
4923
+ var event = events[i]
4924
+ if (event.ranges) {
4925
+ copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event)
4926
+ continue
4927
+ }
4928
+ var changes = event.changes, newChanges = []
4929
+ copy.push({changes: newChanges})
4930
+ for (var j = 0; j < changes.length; ++j) {
4931
+ var change = changes[j], m = (void 0)
4932
+ newChanges.push({from: change.from, to: change.to, text: change.text})
4933
+ if (newGroup) { for (var prop in change) { if (m = prop.match(/^spans_(\d+)$/)) {
4934
+ if (indexOf(newGroup, Number(m[1])) > -1) {
4935
+ lst(newChanges)[prop] = change[prop]
4936
+ delete change[prop]
4937
+ }
4938
+ } } }
4939
+ }
4940
+ }
4941
+ return copy
4942
+ }
4943
+
4944
+ // The 'scroll' parameter given to many of these indicated whether
4945
+ // the new cursor position should be scrolled into view after
4946
+ // modifying the selection.
4947
+
4948
+ // If shift is held or the extend flag is set, extends a range to
4949
+ // include a given position (and optionally a second position).
4950
+ // Otherwise, simply returns the range between the given positions.
4951
+ // Used for cursor motion and such.
4952
+ function extendRange(range, head, other, extend) {
4953
+ if (extend) {
4954
+ var anchor = range.anchor
4955
+ if (other) {
4956
+ var posBefore = cmp(head, anchor) < 0
4957
+ if (posBefore != (cmp(other, anchor) < 0)) {
4958
+ anchor = head
4959
+ head = other
4960
+ } else if (posBefore != (cmp(head, other) < 0)) {
4961
+ head = other
4962
+ }
4963
+ }
4964
+ return new Range(anchor, head)
4965
+ } else {
4966
+ return new Range(other || head, head)
4967
+ }
4968
+ }
4969
+
4970
+ // Extend the primary selection range, discard the rest.
4971
+ function extendSelection(doc, head, other, options, extend) {
4972
+ if (extend == null) { extend = doc.cm && (doc.cm.display.shift || doc.extend) }
4973
+ setSelection(doc, new Selection([extendRange(doc.sel.primary(), head, other, extend)], 0), options)
4974
+ }
4975
+
4976
+ // Extend all selections (pos is an array of selections with length
4977
+ // equal the number of selections)
4978
+ function extendSelections(doc, heads, options) {
4979
+ var out = []
4980
+ var extend = doc.cm && (doc.cm.display.shift || doc.extend)
4981
+ for (var i = 0; i < doc.sel.ranges.length; i++)
4982
+ { out[i] = extendRange(doc.sel.ranges[i], heads[i], null, extend) }
4983
+ var newSel = normalizeSelection(out, doc.sel.primIndex)
4984
+ setSelection(doc, newSel, options)
4985
+ }
4986
+
4987
+ // Updates a single range in the selection.
4988
+ function replaceOneSelection(doc, i, range, options) {
4989
+ var ranges = doc.sel.ranges.slice(0)
4990
+ ranges[i] = range
4991
+ setSelection(doc, normalizeSelection(ranges, doc.sel.primIndex), options)
4992
+ }
4993
+
4994
+ // Reset the selection to a single range.
4995
+ function setSimpleSelection(doc, anchor, head, options) {
4996
+ setSelection(doc, simpleSelection(anchor, head), options)
4997
+ }
4998
+
4999
+ // Give beforeSelectionChange handlers a change to influence a
5000
+ // selection update.
5001
+ function filterSelectionChange(doc, sel, options) {
5002
+ var obj = {
5003
+ ranges: sel.ranges,
5004
+ update: function(ranges) {
5005
+ var this$1 = this;
5006
+
5007
+ this.ranges = []
5008
+ for (var i = 0; i < ranges.length; i++)
5009
+ { this$1.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),
5010
+ clipPos(doc, ranges[i].head)) }
5011
+ },
5012
+ origin: options && options.origin
5013
+ }
5014
+ signal(doc, "beforeSelectionChange", doc, obj)
5015
+ if (doc.cm) { signal(doc.cm, "beforeSelectionChange", doc.cm, obj) }
5016
+ if (obj.ranges != sel.ranges) { return normalizeSelection(obj.ranges, obj.ranges.length - 1) }
5017
+ else { return sel }
5018
+ }
5019
+
5020
+ function setSelectionReplaceHistory(doc, sel, options) {
5021
+ var done = doc.history.done, last = lst(done)
5022
+ if (last && last.ranges) {
5023
+ done[done.length - 1] = sel
5024
+ setSelectionNoUndo(doc, sel, options)
5025
+ } else {
5026
+ setSelection(doc, sel, options)
5027
+ }
5028
+ }
5029
+
5030
+ // Set a new selection.
5031
+ function setSelection(doc, sel, options) {
5032
+ setSelectionNoUndo(doc, sel, options)
5033
+ addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options)
5034
+ }
5035
+
5036
+ function setSelectionNoUndo(doc, sel, options) {
5037
+ if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange"))
5038
+ { sel = filterSelectionChange(doc, sel, options) }
5039
+
5040
+ var bias = options && options.bias ||
5041
+ (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1)
5042
+ setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true))
5043
+
5044
+ if (!(options && options.scroll === false) && doc.cm)
5045
+ { ensureCursorVisible(doc.cm) }
5046
+ }
5047
+
5048
+ function setSelectionInner(doc, sel) {
5049
+ if (sel.equals(doc.sel)) { return }
5050
+
5051
+ doc.sel = sel
5052
+
5053
+ if (doc.cm) {
5054
+ doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true
5055
+ signalCursorActivity(doc.cm)
5056
+ }
5057
+ signalLater(doc, "cursorActivity", doc)
5058
+ }
5059
+
5060
+ // Verify that the selection does not partially select any atomic
5061
+ // marked ranges.
5062
+ function reCheckSelection(doc) {
5063
+ setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false))
5064
+ }
5065
+
5066
+ // Return a selection that does not partially select any atomic
5067
+ // ranges.
5068
+ function skipAtomicInSelection(doc, sel, bias, mayClear) {
5069
+ var out
5070
+ for (var i = 0; i < sel.ranges.length; i++) {
5071
+ var range = sel.ranges[i]
5072
+ var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i]
5073
+ var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear)
5074
+ var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear)
5075
+ if (out || newAnchor != range.anchor || newHead != range.head) {
5076
+ if (!out) { out = sel.ranges.slice(0, i) }
5077
+ out[i] = new Range(newAnchor, newHead)
5078
+ }
5079
+ }
5080
+ return out ? normalizeSelection(out, sel.primIndex) : sel
5081
+ }
5082
+
5083
+ function skipAtomicInner(doc, pos, oldPos, dir, mayClear) {
5084
+ var line = getLine(doc, pos.line)
5085
+ if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
5086
+ var sp = line.markedSpans[i], m = sp.marker
5087
+ if ((sp.from == null || (m.inclusiveLeft ? sp.from <= pos.ch : sp.from < pos.ch)) &&
5088
+ (sp.to == null || (m.inclusiveRight ? sp.to >= pos.ch : sp.to > pos.ch))) {
5089
+ if (mayClear) {
5090
+ signal(m, "beforeCursorEnter")
5091
+ if (m.explicitlyCleared) {
5092
+ if (!line.markedSpans) { break }
5093
+ else {--i; continue}
5094
+ }
5095
+ }
5096
+ if (!m.atomic) { continue }
5097
+
5098
+ if (oldPos) {
5099
+ var near = m.find(dir < 0 ? 1 : -1), diff = (void 0)
5100
+ if (dir < 0 ? m.inclusiveRight : m.inclusiveLeft)
5101
+ { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null) }
5102
+ if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0))
5103
+ { return skipAtomicInner(doc, near, pos, dir, mayClear) }
5104
+ }
5105
+
5106
+ var far = m.find(dir < 0 ? -1 : 1)
5107
+ if (dir < 0 ? m.inclusiveLeft : m.inclusiveRight)
5108
+ { far = movePos(doc, far, dir, far.line == pos.line ? line : null) }
5109
+ return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null
5110
+ }
5111
+ } }
5112
+ return pos
5113
+ }
5114
+
5115
+ // Ensure a given position is not inside an atomic range.
5116
+ function skipAtomic(doc, pos, oldPos, bias, mayClear) {
5117
+ var dir = bias || 1
5118
+ var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) ||
5119
+ (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) ||
5120
+ skipAtomicInner(doc, pos, oldPos, -dir, mayClear) ||
5121
+ (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true))
5122
+ if (!found) {
5123
+ doc.cantEdit = true
5124
+ return Pos(doc.first, 0)
5125
+ }
5126
+ return found
5127
+ }
5128
+
5129
+ function movePos(doc, pos, dir, line) {
5130
+ if (dir < 0 && pos.ch == 0) {
5131
+ if (pos.line > doc.first) { return clipPos(doc, Pos(pos.line - 1)) }
5132
+ else { return null }
5133
+ } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) {
5134
+ if (pos.line < doc.first + doc.size - 1) { return Pos(pos.line + 1, 0) }
5135
+ else { return null }
5136
+ } else {
5137
+ return new Pos(pos.line, pos.ch + dir)
5138
+ }
5139
+ }
5140
+
5141
+ function selectAll(cm) {
5142
+ cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll)
5143
+ }
5144
+
5145
+ // UPDATING
5146
+
5147
+ // Allow "beforeChange" event handlers to influence a change
5148
+ function filterChange(doc, change, update) {
5149
+ var obj = {
5150
+ canceled: false,
5151
+ from: change.from,
5152
+ to: change.to,
5153
+ text: change.text,
5154
+ origin: change.origin,
5155
+ cancel: function () { return obj.canceled = true; }
5156
+ }
5157
+ if (update) { obj.update = function (from, to, text, origin) {
5158
+ if (from) { obj.from = clipPos(doc, from) }
5159
+ if (to) { obj.to = clipPos(doc, to) }
5160
+ if (text) { obj.text = text }
5161
+ if (origin !== undefined) { obj.origin = origin }
5162
+ } }
5163
+ signal(doc, "beforeChange", doc, obj)
5164
+ if (doc.cm) { signal(doc.cm, "beforeChange", doc.cm, obj) }
5165
+
5166
+ if (obj.canceled) { return null }
5167
+ return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin}
5168
+ }
5169
+
5170
+ // Apply a change to a document, and add it to the document's
5171
+ // history, and propagating it to all linked documents.
5172
+ function makeChange(doc, change, ignoreReadOnly) {
5173
+ if (doc.cm) {
5174
+ if (!doc.cm.curOp) { return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) }
5175
+ if (doc.cm.state.suppressEdits) { return }
5176
+ }
5177
+
5178
+ if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
5179
+ change = filterChange(doc, change, true)
5180
+ if (!change) { return }
5181
+ }
5182
+
5183
+ // Possibly split or suppress the update based on the presence
5184
+ // of read-only spans in its range.
5185
+ var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to)
5186
+ if (split) {
5187
+ for (var i = split.length - 1; i >= 0; --i)
5188
+ { makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text, origin: change.origin}) }
5189
+ } else {
5190
+ makeChangeInner(doc, change)
5191
+ }
5192
+ }
5193
+
5194
+ function makeChangeInner(doc, change) {
5195
+ if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) { return }
5196
+ var selAfter = computeSelAfterChange(doc, change)
5197
+ addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN)
5198
+
5199
+ makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change))
5200
+ var rebased = []
5201
+
5202
+ linkedDocs(doc, function (doc, sharedHist) {
5203
+ if (!sharedHist && indexOf(rebased, doc.history) == -1) {
5204
+ rebaseHist(doc.history, change)
5205
+ rebased.push(doc.history)
5206
+ }
5207
+ makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change))
5208
+ })
5209
+ }
5210
+
5211
+ // Revert a change stored in a document's history.
5212
+ function makeChangeFromHistory(doc, type, allowSelectionOnly) {
5213
+ if (doc.cm && doc.cm.state.suppressEdits && !allowSelectionOnly) { return }
5214
+
5215
+ var hist = doc.history, event, selAfter = doc.sel
5216
+ var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done
5217
+
5218
+ // Verify that there is a useable event (so that ctrl-z won't
5219
+ // needlessly clear selection events)
5220
+ var i = 0
5221
+ for (; i < source.length; i++) {
5222
+ event = source[i]
5223
+ if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges)
5224
+ { break }
5225
+ }
5226
+ if (i == source.length) { return }
5227
+ hist.lastOrigin = hist.lastSelOrigin = null
5228
+
5229
+ for (;;) {
5230
+ event = source.pop()
5231
+ if (event.ranges) {
5232
+ pushSelectionToHistory(event, dest)
5233
+ if (allowSelectionOnly && !event.equals(doc.sel)) {
5234
+ setSelection(doc, event, {clearRedo: false})
5235
+ return
5236
+ }
5237
+ selAfter = event
5238
+ }
5239
+ else { break }
5240
+ }
5241
+
5242
+ // Build up a reverse change object to add to the opposite history
5243
+ // stack (redo when undoing, and vice versa).
5244
+ var antiChanges = []
5245
+ pushSelectionToHistory(selAfter, dest)
5246
+ dest.push({changes: antiChanges, generation: hist.generation})
5247
+ hist.generation = event.generation || ++hist.maxGeneration
5248
+
5249
+ var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")
5250
+
5251
+ var loop = function ( i ) {
5252
+ var change = event.changes[i]
5253
+ change.origin = type
5254
+ if (filter && !filterChange(doc, change, false)) {
5255
+ source.length = 0
5256
+ return {}
5257
+ }
5258
+
5259
+ antiChanges.push(historyChangeFromChange(doc, change))
5260
+
5261
+ var after = i ? computeSelAfterChange(doc, change) : lst(source)
5262
+ makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change))
5263
+ if (!i && doc.cm) { doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}) }
5264
+ var rebased = []
5265
+
5266
+ // Propagate to the linked documents
5267
+ linkedDocs(doc, function (doc, sharedHist) {
5268
+ if (!sharedHist && indexOf(rebased, doc.history) == -1) {
5269
+ rebaseHist(doc.history, change)
5270
+ rebased.push(doc.history)
5271
+ }
5272
+ makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change))
5273
+ })
5274
+ };
5275
+
5276
+ for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) {
5277
+ var returned = loop( i$1 );
5278
+
5279
+ if ( returned ) return returned.v;
5280
+ }
5281
+ }
5282
+
5283
+ // Sub-views need their line numbers shifted when text is added
5284
+ // above or below them in the parent document.
5285
+ function shiftDoc(doc, distance) {
5286
+ if (distance == 0) { return }
5287
+ doc.first += distance
5288
+ doc.sel = new Selection(map(doc.sel.ranges, function (range) { return new Range(
5289
+ Pos(range.anchor.line + distance, range.anchor.ch),
5290
+ Pos(range.head.line + distance, range.head.ch)
5291
+ ); }), doc.sel.primIndex)
5292
+ if (doc.cm) {
5293
+ regChange(doc.cm, doc.first, doc.first - distance, distance)
5294
+ for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++)
5295
+ { regLineChange(doc.cm, l, "gutter") }
5296
+ }
5297
+ }
5298
+
5299
+ // More lower-level change function, handling only a single document
5300
+ // (not linked ones).
5301
+ function makeChangeSingleDoc(doc, change, selAfter, spans) {
5302
+ if (doc.cm && !doc.cm.curOp)
5303
+ { return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) }
5304
+
5305
+ if (change.to.line < doc.first) {
5306
+ shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line))
5307
+ return
5308
+ }
5309
+ if (change.from.line > doc.lastLine()) { return }
5310
+
5311
+ // Clip the change to the size of this doc
5312
+ if (change.from.line < doc.first) {
5313
+ var shift = change.text.length - 1 - (doc.first - change.from.line)
5314
+ shiftDoc(doc, shift)
5315
+ change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
5316
+ text: [lst(change.text)], origin: change.origin}
5317
+ }
5318
+ var last = doc.lastLine()
5319
+ if (change.to.line > last) {
5320
+ change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
5321
+ text: [change.text[0]], origin: change.origin}
5322
+ }
5323
+
5324
+ change.removed = getBetween(doc, change.from, change.to)
5325
+
5326
+ if (!selAfter) { selAfter = computeSelAfterChange(doc, change) }
5327
+ if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans) }
5328
+ else { updateDoc(doc, change, spans) }
5329
+ setSelectionNoUndo(doc, selAfter, sel_dontScroll)
5330
+ }
5331
+
5332
+ // Handle the interaction of a change to a document with the editor
5333
+ // that this document is part of.
5334
+ function makeChangeSingleDocInEditor(cm, change, spans) {
5335
+ var doc = cm.doc, display = cm.display, from = change.from, to = change.to
5336
+
5337
+ var recomputeMaxLength = false, checkWidthStart = from.line
5338
+ if (!cm.options.lineWrapping) {
5339
+ checkWidthStart = lineNo(visualLine(getLine(doc, from.line)))
5340
+ doc.iter(checkWidthStart, to.line + 1, function (line) {
5341
+ if (line == display.maxLine) {
5342
+ recomputeMaxLength = true
5343
+ return true
5344
+ }
5345
+ })
5346
+ }
5347
+
5348
+ if (doc.sel.contains(change.from, change.to) > -1)
5349
+ { signalCursorActivity(cm) }
5350
+
5351
+ updateDoc(doc, change, spans, estimateHeight(cm))
5352
+
5353
+ if (!cm.options.lineWrapping) {
5354
+ doc.iter(checkWidthStart, from.line + change.text.length, function (line) {
5355
+ var len = lineLength(line)
5356
+ if (len > display.maxLineLength) {
5357
+ display.maxLine = line
5358
+ display.maxLineLength = len
5359
+ display.maxLineChanged = true
5360
+ recomputeMaxLength = false
5361
+ }
5362
+ })
5363
+ if (recomputeMaxLength) { cm.curOp.updateMaxLine = true }
5364
+ }
5365
+
5366
+ retreatFrontier(doc, from.line)
5367
+ startWorker(cm, 400)
5368
+
5369
+ var lendiff = change.text.length - (to.line - from.line) - 1
5370
+ // Remember that these lines changed, for updating the display
5371
+ if (change.full)
5372
+ { regChange(cm) }
5373
+ else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change))
5374
+ { regLineChange(cm, from.line, "text") }
5375
+ else
5376
+ { regChange(cm, from.line, to.line + 1, lendiff) }
5377
+
5378
+ var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change")
5379
+ if (changeHandler || changesHandler) {
5380
+ var obj = {
5381
+ from: from, to: to,
5382
+ text: change.text,
5383
+ removed: change.removed,
5384
+ origin: change.origin
5385
+ }
5386
+ if (changeHandler) { signalLater(cm, "change", cm, obj) }
5387
+ if (changesHandler) { (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj) }
5388
+ }
5389
+ cm.display.selForContextMenu = null
5390
+ }
5391
+
5392
+ function replaceRange(doc, code, from, to, origin) {
5393
+ if (!to) { to = from }
5394
+ if (cmp(to, from) < 0) { var assign;
5395
+ (assign = [to, from], from = assign[0], to = assign[1], assign) }
5396
+ if (typeof code == "string") { code = doc.splitLines(code) }
5397
+ makeChange(doc, {from: from, to: to, text: code, origin: origin})
5398
+ }
5399
+
5400
+ // Rebasing/resetting history to deal with externally-sourced changes
5401
+
5402
+ function rebaseHistSelSingle(pos, from, to, diff) {
5403
+ if (to < pos.line) {
5404
+ pos.line += diff
5405
+ } else if (from < pos.line) {
5406
+ pos.line = from
5407
+ pos.ch = 0
5408
+ }
5409
+ }
5410
+
5411
+ // Tries to rebase an array of history events given a change in the
5412
+ // document. If the change touches the same lines as the event, the
5413
+ // event, and everything 'behind' it, is discarded. If the change is
5414
+ // before the event, the event's positions are updated. Uses a
5415
+ // copy-on-write scheme for the positions, to avoid having to
5416
+ // reallocate them all on every rebase, but also avoid problems with
5417
+ // shared position objects being unsafely updated.
5418
+ function rebaseHistArray(array, from, to, diff) {
5419
+ for (var i = 0; i < array.length; ++i) {
5420
+ var sub = array[i], ok = true
5421
+ if (sub.ranges) {
5422
+ if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true }
5423
+ for (var j = 0; j < sub.ranges.length; j++) {
5424
+ rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff)
5425
+ rebaseHistSelSingle(sub.ranges[j].head, from, to, diff)
5426
+ }
5427
+ continue
5428
+ }
5429
+ for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) {
5430
+ var cur = sub.changes[j$1]
5431
+ if (to < cur.from.line) {
5432
+ cur.from = Pos(cur.from.line + diff, cur.from.ch)
5433
+ cur.to = Pos(cur.to.line + diff, cur.to.ch)
5434
+ } else if (from <= cur.to.line) {
5435
+ ok = false
5436
+ break
5437
+ }
5438
+ }
5439
+ if (!ok) {
5440
+ array.splice(0, i + 1)
5441
+ i = 0
5442
+ }
5443
+ }
5444
+ }
5445
+
5446
+ function rebaseHist(hist, change) {
5447
+ var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1
5448
+ rebaseHistArray(hist.done, from, to, diff)
5449
+ rebaseHistArray(hist.undone, from, to, diff)
5450
+ }
5451
+
5452
+ // Utility for applying a change to a line by handle or number,
5453
+ // returning the number and optionally registering the line as
5454
+ // changed.
5455
+ function changeLine(doc, handle, changeType, op) {
5456
+ var no = handle, line = handle
5457
+ if (typeof handle == "number") { line = getLine(doc, clipLine(doc, handle)) }
5458
+ else { no = lineNo(handle) }
5459
+ if (no == null) { return null }
5460
+ if (op(line, no) && doc.cm) { regLineChange(doc.cm, no, changeType) }
5461
+ return line
5462
+ }
5463
+
5464
+ // The document is represented as a BTree consisting of leaves, with
5465
+ // chunk of lines in them, and branches, with up to ten leaves or
5466
+ // other branch nodes below them. The top node is always a branch
5467
+ // node, and is the document object itself (meaning it has
5468
+ // additional methods and properties).
5469
+ //
5470
+ // All nodes have parent links. The tree is used both to go from
5471
+ // line numbers to line objects, and to go from objects to numbers.
5472
+ // It also indexes by height, and is used to convert between height
5473
+ // and line object, and to find the total height of the document.
5474
+ //
5475
+ // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html
5476
+
5477
+ function LeafChunk(lines) {
5478
+ var this$1 = this;
5479
+
5480
+ this.lines = lines
5481
+ this.parent = null
5482
+ var height = 0
5483
+ for (var i = 0; i < lines.length; ++i) {
5484
+ lines[i].parent = this$1
5485
+ height += lines[i].height
5486
+ }
5487
+ this.height = height
5488
+ }
5489
+
5490
+ LeafChunk.prototype = {
5491
+ chunkSize: function chunkSize() { return this.lines.length },
5492
+
5493
+ // Remove the n lines at offset 'at'.
5494
+ removeInner: function removeInner(at, n) {
5495
+ var this$1 = this;
5496
+
5497
+ for (var i = at, e = at + n; i < e; ++i) {
5498
+ var line = this$1.lines[i]
5499
+ this$1.height -= line.height
5500
+ cleanUpLine(line)
5501
+ signalLater(line, "delete")
5502
+ }
5503
+ this.lines.splice(at, n)
5504
+ },
5505
+
5506
+ // Helper used to collapse a small branch into a single leaf.
5507
+ collapse: function collapse(lines) {
5508
+ lines.push.apply(lines, this.lines)
5509
+ },
5510
+
5511
+ // Insert the given array of lines at offset 'at', count them as
5512
+ // having the given height.
5513
+ insertInner: function insertInner(at, lines, height) {
5514
+ var this$1 = this;
5515
+
5516
+ this.height += height
5517
+ this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at))
5518
+ for (var i = 0; i < lines.length; ++i) { lines[i].parent = this$1 }
5519
+ },
5520
+
5521
+ // Used to iterate over a part of the tree.
5522
+ iterN: function iterN(at, n, op) {
5523
+ var this$1 = this;
5524
+
5525
+ for (var e = at + n; at < e; ++at)
5526
+ { if (op(this$1.lines[at])) { return true } }
5527
+ }
5528
+ }
5529
+
5530
+ function BranchChunk(children) {
5531
+ var this$1 = this;
5532
+
5533
+ this.children = children
5534
+ var size = 0, height = 0
5535
+ for (var i = 0; i < children.length; ++i) {
5536
+ var ch = children[i]
5537
+ size += ch.chunkSize(); height += ch.height
5538
+ ch.parent = this$1
5539
+ }
5540
+ this.size = size
5541
+ this.height = height
5542
+ this.parent = null
5543
+ }
5544
+
5545
+ BranchChunk.prototype = {
5546
+ chunkSize: function chunkSize() { return this.size },
5547
+
5548
+ removeInner: function removeInner(at, n) {
5549
+ var this$1 = this;
5550
+
5551
+ this.size -= n
5552
+ for (var i = 0; i < this.children.length; ++i) {
5553
+ var child = this$1.children[i], sz = child.chunkSize()
5554
+ if (at < sz) {
5555
+ var rm = Math.min(n, sz - at), oldHeight = child.height
5556
+ child.removeInner(at, rm)
5557
+ this$1.height -= oldHeight - child.height
5558
+ if (sz == rm) { this$1.children.splice(i--, 1); child.parent = null }
5559
+ if ((n -= rm) == 0) { break }
5560
+ at = 0
5561
+ } else { at -= sz }
5562
+ }
5563
+ // If the result is smaller than 25 lines, ensure that it is a
5564
+ // single leaf node.
5565
+ if (this.size - n < 25 &&
5566
+ (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) {
5567
+ var lines = []
5568
+ this.collapse(lines)
5569
+ this.children = [new LeafChunk(lines)]
5570
+ this.children[0].parent = this
5571
+ }
5572
+ },
5573
+
5574
+ collapse: function collapse(lines) {
5575
+ var this$1 = this;
5576
+
5577
+ for (var i = 0; i < this.children.length; ++i) { this$1.children[i].collapse(lines) }
5578
+ },
5579
+
5580
+ insertInner: function insertInner(at, lines, height) {
5581
+ var this$1 = this;
5582
+
5583
+ this.size += lines.length
5584
+ this.height += height
5585
+ for (var i = 0; i < this.children.length; ++i) {
5586
+ var child = this$1.children[i], sz = child.chunkSize()
5587
+ if (at <= sz) {
5588
+ child.insertInner(at, lines, height)
5589
+ if (child.lines && child.lines.length > 50) {
5590
+ // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced.
5591
+ // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest.
5592
+ var remaining = child.lines.length % 25 + 25
5593
+ for (var pos = remaining; pos < child.lines.length;) {
5594
+ var leaf = new LeafChunk(child.lines.slice(pos, pos += 25))
5595
+ child.height -= leaf.height
5596
+ this$1.children.splice(++i, 0, leaf)
5597
+ leaf.parent = this$1
5598
+ }
5599
+ child.lines = child.lines.slice(0, remaining)
5600
+ this$1.maybeSpill()
5601
+ }
5602
+ break
5603
+ }
5604
+ at -= sz
5605
+ }
5606
+ },
5607
+
5608
+ // When a node has grown, check whether it should be split.
5609
+ maybeSpill: function maybeSpill() {
5610
+ if (this.children.length <= 10) { return }
5611
+ var me = this
5612
+ do {
5613
+ var spilled = me.children.splice(me.children.length - 5, 5)
5614
+ var sibling = new BranchChunk(spilled)
5615
+ if (!me.parent) { // Become the parent node
5616
+ var copy = new BranchChunk(me.children)
5617
+ copy.parent = me
5618
+ me.children = [copy, sibling]
5619
+ me = copy
5620
+ } else {
5621
+ me.size -= sibling.size
5622
+ me.height -= sibling.height
5623
+ var myIndex = indexOf(me.parent.children, me)
5624
+ me.parent.children.splice(myIndex + 1, 0, sibling)
5625
+ }
5626
+ sibling.parent = me.parent
5627
+ } while (me.children.length > 10)
5628
+ me.parent.maybeSpill()
5629
+ },
5630
+
5631
+ iterN: function iterN(at, n, op) {
5632
+ var this$1 = this;
5633
+
5634
+ for (var i = 0; i < this.children.length; ++i) {
5635
+ var child = this$1.children[i], sz = child.chunkSize()
5636
+ if (at < sz) {
5637
+ var used = Math.min(n, sz - at)
5638
+ if (child.iterN(at, used, op)) { return true }
5639
+ if ((n -= used) == 0) { break }
5640
+ at = 0
5641
+ } else { at -= sz }
5642
+ }
5643
+ }
5644
+ }
5645
+
5646
+ // Line widgets are block elements displayed above or below a line.
5647
+
5648
+ var LineWidget = function(doc, node, options) {
5649
+ var this$1 = this;
5650
+
5651
+ if (options) { for (var opt in options) { if (options.hasOwnProperty(opt))
5652
+ { this$1[opt] = options[opt] } } }
5653
+ this.doc = doc
5654
+ this.node = node
5655
+ };
5656
+
5657
+ LineWidget.prototype.clear = function () {
5658
+ var this$1 = this;
5659
+
5660
+ var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line)
5661
+ if (no == null || !ws) { return }
5662
+ for (var i = 0; i < ws.length; ++i) { if (ws[i] == this$1) { ws.splice(i--, 1) } }
5663
+ if (!ws.length) { line.widgets = null }
5664
+ var height = widgetHeight(this)
5665
+ updateLineHeight(line, Math.max(0, line.height - height))
5666
+ if (cm) {
5667
+ runInOp(cm, function () {
5668
+ adjustScrollWhenAboveVisible(cm, line, -height)
5669
+ regLineChange(cm, no, "widget")
5670
+ })
5671
+ signalLater(cm, "lineWidgetCleared", cm, this, no)
5672
+ }
5673
+ };
5674
+
5675
+ LineWidget.prototype.changed = function () {
5676
+ var this$1 = this;
5677
+
5678
+ var oldH = this.height, cm = this.doc.cm, line = this.line
5679
+ this.height = null
5680
+ var diff = widgetHeight(this) - oldH
5681
+ if (!diff) { return }
5682
+ updateLineHeight(line, line.height + diff)
5683
+ if (cm) {
5684
+ runInOp(cm, function () {
5685
+ cm.curOp.forceUpdate = true
5686
+ adjustScrollWhenAboveVisible(cm, line, diff)
5687
+ signalLater(cm, "lineWidgetChanged", cm, this$1, lineNo(line))
5688
+ })
5689
+ }
5690
+ };
5691
+ eventMixin(LineWidget)
5692
+
5693
+ function adjustScrollWhenAboveVisible(cm, line, diff) {
5694
+ if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))
5695
+ { addToScrollTop(cm, diff) }
5696
+ }
5697
+
5698
+ function addLineWidget(doc, handle, node, options) {
5699
+ var widget = new LineWidget(doc, node, options)
5700
+ var cm = doc.cm
5701
+ if (cm && widget.noHScroll) { cm.display.alignWidgets = true }
5702
+ changeLine(doc, handle, "widget", function (line) {
5703
+ var widgets = line.widgets || (line.widgets = [])
5704
+ if (widget.insertAt == null) { widgets.push(widget) }
5705
+ else { widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget) }
5706
+ widget.line = line
5707
+ if (cm && !lineIsHidden(doc, line)) {
5708
+ var aboveVisible = heightAtLine(line) < doc.scrollTop
5709
+ updateLineHeight(line, line.height + widgetHeight(widget))
5710
+ if (aboveVisible) { addToScrollTop(cm, widget.height) }
5711
+ cm.curOp.forceUpdate = true
5712
+ }
5713
+ return true
5714
+ })
5715
+ signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle))
5716
+ return widget
5717
+ }
5718
+
5719
+ // TEXTMARKERS
5720
+
5721
+ // Created with markText and setBookmark methods. A TextMarker is a
5722
+ // handle that can be used to clear or find a marked position in the
5723
+ // document. Line objects hold arrays (markedSpans) containing
5724
+ // {from, to, marker} object pointing to such marker objects, and
5725
+ // indicating that such a marker is present on that line. Multiple
5726
+ // lines may point to the same marker when it spans across lines.
5727
+ // The spans will have null for their from/to properties when the
5728
+ // marker continues beyond the start/end of the line. Markers have
5729
+ // links back to the lines they currently touch.
5730
+
5731
+ // Collapsed markers have unique ids, in order to be able to order
5732
+ // them, which is needed for uniquely determining an outer marker
5733
+ // when they overlap (they may nest, but not partially overlap).
5734
+ var nextMarkerId = 0
5735
+
5736
+ var TextMarker = function(doc, type) {
5737
+ this.lines = []
5738
+ this.type = type
5739
+ this.doc = doc
5740
+ this.id = ++nextMarkerId
5741
+ };
5742
+
5743
+ // Clear the marker.
5744
+ TextMarker.prototype.clear = function () {
5745
+ var this$1 = this;
5746
+
5747
+ if (this.explicitlyCleared) { return }
5748
+ var cm = this.doc.cm, withOp = cm && !cm.curOp
5749
+ if (withOp) { startOperation(cm) }
5750
+ if (hasHandler(this, "clear")) {
5751
+ var found = this.find()
5752
+ if (found) { signalLater(this, "clear", found.from, found.to) }
5753
+ }
5754
+ var min = null, max = null
5755
+ for (var i = 0; i < this.lines.length; ++i) {
5756
+ var line = this$1.lines[i]
5757
+ var span = getMarkedSpanFor(line.markedSpans, this$1)
5758
+ if (cm && !this$1.collapsed) { regLineChange(cm, lineNo(line), "text") }
5759
+ else if (cm) {
5760
+ if (span.to != null) { max = lineNo(line) }
5761
+ if (span.from != null) { min = lineNo(line) }
5762
+ }
5763
+ line.markedSpans = removeMarkedSpan(line.markedSpans, span)
5764
+ if (span.from == null && this$1.collapsed && !lineIsHidden(this$1.doc, line) && cm)
5765
+ { updateLineHeight(line, textHeight(cm.display)) }
5766
+ }
5767
+ if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) {
5768
+ var visual = visualLine(this$1.lines[i$1]), len = lineLength(visual)
5769
+ if (len > cm.display.maxLineLength) {
5770
+ cm.display.maxLine = visual
5771
+ cm.display.maxLineLength = len
5772
+ cm.display.maxLineChanged = true
5773
+ }
5774
+ } }
5775
+
5776
+ if (min != null && cm && this.collapsed) { regChange(cm, min, max + 1) }
5777
+ this.lines.length = 0
5778
+ this.explicitlyCleared = true
5779
+ if (this.atomic && this.doc.cantEdit) {
5780
+ this.doc.cantEdit = false
5781
+ if (cm) { reCheckSelection(cm.doc) }
5782
+ }
5783
+ if (cm) { signalLater(cm, "markerCleared", cm, this, min, max) }
5784
+ if (withOp) { endOperation(cm) }
5785
+ if (this.parent) { this.parent.clear() }
5786
+ };
5787
+
5788
+ // Find the position of the marker in the document. Returns a {from,
5789
+ // to} object by default. Side can be passed to get a specific side
5790
+ // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the
5791
+ // Pos objects returned contain a line object, rather than a line
5792
+ // number (used to prevent looking up the same line twice).
5793
+ TextMarker.prototype.find = function (side, lineObj) {
5794
+ var this$1 = this;
5795
+
5796
+ if (side == null && this.type == "bookmark") { side = 1 }
5797
+ var from, to
5798
+ for (var i = 0; i < this.lines.length; ++i) {
5799
+ var line = this$1.lines[i]
5800
+ var span = getMarkedSpanFor(line.markedSpans, this$1)
5801
+ if (span.from != null) {
5802
+ from = Pos(lineObj ? line : lineNo(line), span.from)
5803
+ if (side == -1) { return from }
5804
+ }
5805
+ if (span.to != null) {
5806
+ to = Pos(lineObj ? line : lineNo(line), span.to)
5807
+ if (side == 1) { return to }
5808
+ }
5809
+ }
5810
+ return from && {from: from, to: to}
5811
+ };
5812
+
5813
+ // Signals that the marker's widget changed, and surrounding layout
5814
+ // should be recomputed.
5815
+ TextMarker.prototype.changed = function () {
5816
+ var this$1 = this;
5817
+
5818
+ var pos = this.find(-1, true), widget = this, cm = this.doc.cm
5819
+ if (!pos || !cm) { return }
5820
+ runInOp(cm, function () {
5821
+ var line = pos.line, lineN = lineNo(pos.line)
5822
+ var view = findViewForLine(cm, lineN)
5823
+ if (view) {
5824
+ clearLineMeasurementCacheFor(view)
5825
+ cm.curOp.selectionChanged = cm.curOp.forceUpdate = true
5826
+ }
5827
+ cm.curOp.updateMaxLine = true
5828
+ if (!lineIsHidden(widget.doc, line) && widget.height != null) {
5829
+ var oldHeight = widget.height
5830
+ widget.height = null
5831
+ var dHeight = widgetHeight(widget) - oldHeight
5832
+ if (dHeight)
5833
+ { updateLineHeight(line, line.height + dHeight) }
5834
+ }
5835
+ signalLater(cm, "markerChanged", cm, this$1)
5836
+ })
5837
+ };
5838
+
5839
+ TextMarker.prototype.attachLine = function (line) {
5840
+ if (!this.lines.length && this.doc.cm) {
5841
+ var op = this.doc.cm.curOp
5842
+ if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
5843
+ { (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this) }
5844
+ }
5845
+ this.lines.push(line)
5846
+ };
5847
+
5848
+ TextMarker.prototype.detachLine = function (line) {
5849
+ this.lines.splice(indexOf(this.lines, line), 1)
5850
+ if (!this.lines.length && this.doc.cm) {
5851
+ var op = this.doc.cm.curOp
5852
+ ;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this)
5853
+ }
5854
+ };
5855
+ eventMixin(TextMarker)
5856
+
5857
+ // Create a marker, wire it up to the right lines, and
5858
+ function markText(doc, from, to, options, type) {
5859
+ // Shared markers (across linked documents) are handled separately
5860
+ // (markTextShared will call out to this again, once per
5861
+ // document).
5862
+ if (options && options.shared) { return markTextShared(doc, from, to, options, type) }
5863
+ // Ensure we are in an operation.
5864
+ if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, markText)(doc, from, to, options, type) }
5865
+
5866
+ var marker = new TextMarker(doc, type), diff = cmp(from, to)
5867
+ if (options) { copyObj(options, marker, false) }
5868
+ // Don't connect empty markers unless clearWhenEmpty is false
5869
+ if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false)
5870
+ { return marker }
5871
+ if (marker.replacedWith) {
5872
+ // Showing up as a widget implies collapsed (widget replaces text)
5873
+ marker.collapsed = true
5874
+ marker.widgetNode = eltP("span", [marker.replacedWith], "CodeMirror-widget")
5875
+ if (!options.handleMouseEvents) { marker.widgetNode.setAttribute("cm-ignore-events", "true") }
5876
+ if (options.insertLeft) { marker.widgetNode.insertLeft = true }
5877
+ }
5878
+ if (marker.collapsed) {
5879
+ if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||
5880
+ from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker))
5881
+ { throw new Error("Inserting collapsed marker partially overlapping an existing one") }
5882
+ seeCollapsedSpans()
5883
+ }
5884
+
5885
+ if (marker.addToHistory)
5886
+ { addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN) }
5887
+
5888
+ var curLine = from.line, cm = doc.cm, updateMaxLine
5889
+ doc.iter(curLine, to.line + 1, function (line) {
5890
+ if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine)
5891
+ { updateMaxLine = true }
5892
+ if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0) }
5893
+ addMarkedSpan(line, new MarkedSpan(marker,
5894
+ curLine == from.line ? from.ch : null,
5895
+ curLine == to.line ? to.ch : null))
5896
+ ++curLine
5897
+ })
5898
+ // lineIsHidden depends on the presence of the spans, so needs a second pass
5899
+ if (marker.collapsed) { doc.iter(from.line, to.line + 1, function (line) {
5900
+ if (lineIsHidden(doc, line)) { updateLineHeight(line, 0) }
5901
+ }) }
5902
+
5903
+ if (marker.clearOnEnter) { on(marker, "beforeCursorEnter", function () { return marker.clear(); }) }
5904
+
5905
+ if (marker.readOnly) {
5906
+ seeReadOnlySpans()
5907
+ if (doc.history.done.length || doc.history.undone.length)
5908
+ { doc.clearHistory() }
5909
+ }
5910
+ if (marker.collapsed) {
5911
+ marker.id = ++nextMarkerId
5912
+ marker.atomic = true
5913
+ }
5914
+ if (cm) {
5915
+ // Sync editor state
5916
+ if (updateMaxLine) { cm.curOp.updateMaxLine = true }
5917
+ if (marker.collapsed)
5918
+ { regChange(cm, from.line, to.line + 1) }
5919
+ else if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.css)
5920
+ { for (var i = from.line; i <= to.line; i++) { regLineChange(cm, i, "text") } }
5921
+ if (marker.atomic) { reCheckSelection(cm.doc) }
5922
+ signalLater(cm, "markerAdded", cm, marker)
5923
+ }
5924
+ return marker
5925
+ }
5926
+
5927
+ // SHARED TEXTMARKERS
5928
+
5929
+ // A shared marker spans multiple linked documents. It is
5930
+ // implemented as a meta-marker-object controlling multiple normal
5931
+ // markers.
5932
+ var SharedTextMarker = function(markers, primary) {
5933
+ var this$1 = this;
5934
+
5935
+ this.markers = markers
5936
+ this.primary = primary
5937
+ for (var i = 0; i < markers.length; ++i)
5938
+ { markers[i].parent = this$1 }
5939
+ };
5940
+
5941
+ SharedTextMarker.prototype.clear = function () {
5942
+ var this$1 = this;
5943
+
5944
+ if (this.explicitlyCleared) { return }
5945
+ this.explicitlyCleared = true
5946
+ for (var i = 0; i < this.markers.length; ++i)
5947
+ { this$1.markers[i].clear() }
5948
+ signalLater(this, "clear")
5949
+ };
5950
+
5951
+ SharedTextMarker.prototype.find = function (side, lineObj) {
5952
+ return this.primary.find(side, lineObj)
5953
+ };
5954
+ eventMixin(SharedTextMarker)
5955
+
5956
+ function markTextShared(doc, from, to, options, type) {
5957
+ options = copyObj(options)
5958
+ options.shared = false
5959
+ var markers = [markText(doc, from, to, options, type)], primary = markers[0]
5960
+ var widget = options.widgetNode
5961
+ linkedDocs(doc, function (doc) {
5962
+ if (widget) { options.widgetNode = widget.cloneNode(true) }
5963
+ markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type))
5964
+ for (var i = 0; i < doc.linked.length; ++i)
5965
+ { if (doc.linked[i].isParent) { return } }
5966
+ primary = lst(markers)
5967
+ })
5968
+ return new SharedTextMarker(markers, primary)
5969
+ }
5970
+
5971
+ function findSharedMarkers(doc) {
5972
+ return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function (m) { return m.parent; })
5973
+ }
5974
+
5975
+ function copySharedMarkers(doc, markers) {
5976
+ for (var i = 0; i < markers.length; i++) {
5977
+ var marker = markers[i], pos = marker.find()
5978
+ var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to)
5979
+ if (cmp(mFrom, mTo)) {
5980
+ var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type)
5981
+ marker.markers.push(subMark)
5982
+ subMark.parent = marker
5983
+ }
5984
+ }
5985
+ }
5986
+
5987
+ function detachSharedMarkers(markers) {
5988
+ var loop = function ( i ) {
5989
+ var marker = markers[i], linked = [marker.primary.doc]
5990
+ linkedDocs(marker.primary.doc, function (d) { return linked.push(d); })
5991
+ for (var j = 0; j < marker.markers.length; j++) {
5992
+ var subMarker = marker.markers[j]
5993
+ if (indexOf(linked, subMarker.doc) == -1) {
5994
+ subMarker.parent = null
5995
+ marker.markers.splice(j--, 1)
5996
+ }
5997
+ }
5998
+ };
5999
+
6000
+ for (var i = 0; i < markers.length; i++) loop( i );
6001
+ }
6002
+
6003
+ var nextDocId = 0
6004
+ var Doc = function(text, mode, firstLine, lineSep, direction) {
6005
+ if (!(this instanceof Doc)) { return new Doc(text, mode, firstLine, lineSep, direction) }
6006
+ if (firstLine == null) { firstLine = 0 }
6007
+
6008
+ BranchChunk.call(this, [new LeafChunk([new Line("", null)])])
6009
+ this.first = firstLine
6010
+ this.scrollTop = this.scrollLeft = 0
6011
+ this.cantEdit = false
6012
+ this.cleanGeneration = 1
6013
+ this.modeFrontier = this.highlightFrontier = firstLine
6014
+ var start = Pos(firstLine, 0)
6015
+ this.sel = simpleSelection(start)
6016
+ this.history = new History(null)
6017
+ this.id = ++nextDocId
6018
+ this.modeOption = mode
6019
+ this.lineSep = lineSep
6020
+ this.direction = (direction == "rtl") ? "rtl" : "ltr"
6021
+ this.extend = false
6022
+
6023
+ if (typeof text == "string") { text = this.splitLines(text) }
6024
+ updateDoc(this, {from: start, to: start, text: text})
6025
+ setSelection(this, simpleSelection(start), sel_dontScroll)
6026
+ }
6027
+
6028
+ Doc.prototype = createObj(BranchChunk.prototype, {
6029
+ constructor: Doc,
6030
+ // Iterate over the document. Supports two forms -- with only one
6031
+ // argument, it calls that for each line in the document. With
6032
+ // three, it iterates over the range given by the first two (with
6033
+ // the second being non-inclusive).
6034
+ iter: function(from, to, op) {
6035
+ if (op) { this.iterN(from - this.first, to - from, op) }
6036
+ else { this.iterN(this.first, this.first + this.size, from) }
6037
+ },
6038
+
6039
+ // Non-public interface for adding and removing lines.
6040
+ insert: function(at, lines) {
6041
+ var height = 0
6042
+ for (var i = 0; i < lines.length; ++i) { height += lines[i].height }
6043
+ this.insertInner(at - this.first, lines, height)
6044
+ },
6045
+ remove: function(at, n) { this.removeInner(at - this.first, n) },
6046
+
6047
+ // From here, the methods are part of the public interface. Most
6048
+ // are also available from CodeMirror (editor) instances.
6049
+
6050
+ getValue: function(lineSep) {
6051
+ var lines = getLines(this, this.first, this.first + this.size)
6052
+ if (lineSep === false) { return lines }
6053
+ return lines.join(lineSep || this.lineSeparator())
6054
+ },
6055
+ setValue: docMethodOp(function(code) {
6056
+ var top = Pos(this.first, 0), last = this.first + this.size - 1
6057
+ makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
6058
+ text: this.splitLines(code), origin: "setValue", full: true}, true)
6059
+ if (this.cm) { scrollToCoords(this.cm, 0, 0) }
6060
+ setSelection(this, simpleSelection(top), sel_dontScroll)
6061
+ }),
6062
+ replaceRange: function(code, from, to, origin) {
6063
+ from = clipPos(this, from)
6064
+ to = to ? clipPos(this, to) : from
6065
+ replaceRange(this, code, from, to, origin)
6066
+ },
6067
+ getRange: function(from, to, lineSep) {
6068
+ var lines = getBetween(this, clipPos(this, from), clipPos(this, to))
6069
+ if (lineSep === false) { return lines }
6070
+ return lines.join(lineSep || this.lineSeparator())
6071
+ },
6072
+
6073
+ getLine: function(line) {var l = this.getLineHandle(line); return l && l.text},
6074
+
6075
+ getLineHandle: function(line) {if (isLine(this, line)) { return getLine(this, line) }},
6076
+ getLineNumber: function(line) {return lineNo(line)},
6077
+
6078
+ getLineHandleVisualStart: function(line) {
6079
+ if (typeof line == "number") { line = getLine(this, line) }
6080
+ return visualLine(line)
6081
+ },
6082
+
6083
+ lineCount: function() {return this.size},
6084
+ firstLine: function() {return this.first},
6085
+ lastLine: function() {return this.first + this.size - 1},
6086
+
6087
+ clipPos: function(pos) {return clipPos(this, pos)},
6088
+
6089
+ getCursor: function(start) {
6090
+ var range = this.sel.primary(), pos
6091
+ if (start == null || start == "head") { pos = range.head }
6092
+ else if (start == "anchor") { pos = range.anchor }
6093
+ else if (start == "end" || start == "to" || start === false) { pos = range.to() }
6094
+ else { pos = range.from() }
6095
+ return pos
6096
+ },
6097
+ listSelections: function() { return this.sel.ranges },
6098
+ somethingSelected: function() {return this.sel.somethingSelected()},
6099
+
6100
+ setCursor: docMethodOp(function(line, ch, options) {
6101
+ setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options)
6102
+ }),
6103
+ setSelection: docMethodOp(function(anchor, head, options) {
6104
+ setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options)
6105
+ }),
6106
+ extendSelection: docMethodOp(function(head, other, options) {
6107
+ extendSelection(this, clipPos(this, head), other && clipPos(this, other), options)
6108
+ }),
6109
+ extendSelections: docMethodOp(function(heads, options) {
6110
+ extendSelections(this, clipPosArray(this, heads), options)
6111
+ }),
6112
+ extendSelectionsBy: docMethodOp(function(f, options) {
6113
+ var heads = map(this.sel.ranges, f)
6114
+ extendSelections(this, clipPosArray(this, heads), options)
6115
+ }),
6116
+ setSelections: docMethodOp(function(ranges, primary, options) {
6117
+ var this$1 = this;
6118
+
6119
+ if (!ranges.length) { return }
6120
+ var out = []
6121
+ for (var i = 0; i < ranges.length; i++)
6122
+ { out[i] = new Range(clipPos(this$1, ranges[i].anchor),
6123
+ clipPos(this$1, ranges[i].head)) }
6124
+ if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex) }
6125
+ setSelection(this, normalizeSelection(out, primary), options)
6126
+ }),
6127
+ addSelection: docMethodOp(function(anchor, head, options) {
6128
+ var ranges = this.sel.ranges.slice(0)
6129
+ ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)))
6130
+ setSelection(this, normalizeSelection(ranges, ranges.length - 1), options)
6131
+ }),
6132
+
6133
+ getSelection: function(lineSep) {
6134
+ var this$1 = this;
6135
+
6136
+ var ranges = this.sel.ranges, lines
6137
+ for (var i = 0; i < ranges.length; i++) {
6138
+ var sel = getBetween(this$1, ranges[i].from(), ranges[i].to())
6139
+ lines = lines ? lines.concat(sel) : sel
6140
+ }
6141
+ if (lineSep === false) { return lines }
6142
+ else { return lines.join(lineSep || this.lineSeparator()) }
6143
+ },
6144
+ getSelections: function(lineSep) {
6145
+ var this$1 = this;
6146
+
6147
+ var parts = [], ranges = this.sel.ranges
6148
+ for (var i = 0; i < ranges.length; i++) {
6149
+ var sel = getBetween(this$1, ranges[i].from(), ranges[i].to())
6150
+ if (lineSep !== false) { sel = sel.join(lineSep || this$1.lineSeparator()) }
6151
+ parts[i] = sel
6152
+ }
6153
+ return parts
6154
+ },
6155
+ replaceSelection: function(code, collapse, origin) {
6156
+ var dup = []
6157
+ for (var i = 0; i < this.sel.ranges.length; i++)
6158
+ { dup[i] = code }
6159
+ this.replaceSelections(dup, collapse, origin || "+input")
6160
+ },
6161
+ replaceSelections: docMethodOp(function(code, collapse, origin) {
6162
+ var this$1 = this;
6163
+
6164
+ var changes = [], sel = this.sel
6165
+ for (var i = 0; i < sel.ranges.length; i++) {
6166
+ var range = sel.ranges[i]
6167
+ changes[i] = {from: range.from(), to: range.to(), text: this$1.splitLines(code[i]), origin: origin}
6168
+ }
6169
+ var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse)
6170
+ for (var i$1 = changes.length - 1; i$1 >= 0; i$1--)
6171
+ { makeChange(this$1, changes[i$1]) }
6172
+ if (newSel) { setSelectionReplaceHistory(this, newSel) }
6173
+ else if (this.cm) { ensureCursorVisible(this.cm) }
6174
+ }),
6175
+ undo: docMethodOp(function() {makeChangeFromHistory(this, "undo")}),
6176
+ redo: docMethodOp(function() {makeChangeFromHistory(this, "redo")}),
6177
+ undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true)}),
6178
+ redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true)}),
6179
+
6180
+ setExtending: function(val) {this.extend = val},
6181
+ getExtending: function() {return this.extend},
6182
+
6183
+ historySize: function() {
6184
+ var hist = this.history, done = 0, undone = 0
6185
+ for (var i = 0; i < hist.done.length; i++) { if (!hist.done[i].ranges) { ++done } }
6186
+ for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].ranges) { ++undone } }
6187
+ return {undo: done, redo: undone}
6188
+ },
6189
+ clearHistory: function() {this.history = new History(this.history.maxGeneration)},
6190
+
6191
+ markClean: function() {
6192
+ this.cleanGeneration = this.changeGeneration(true)
6193
+ },
6194
+ changeGeneration: function(forceSplit) {
6195
+ if (forceSplit)
6196
+ { this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null }
6197
+ return this.history.generation
6198
+ },
6199
+ isClean: function (gen) {
6200
+ return this.history.generation == (gen || this.cleanGeneration)
6201
+ },
6202
+
6203
+ getHistory: function() {
6204
+ return {done: copyHistoryArray(this.history.done),
6205
+ undone: copyHistoryArray(this.history.undone)}
6206
+ },
6207
+ setHistory: function(histData) {
6208
+ var hist = this.history = new History(this.history.maxGeneration)
6209
+ hist.done = copyHistoryArray(histData.done.slice(0), null, true)
6210
+ hist.undone = copyHistoryArray(histData.undone.slice(0), null, true)
6211
+ },
6212
+
6213
+ setGutterMarker: docMethodOp(function(line, gutterID, value) {
6214
+ return changeLine(this, line, "gutter", function (line) {
6215
+ var markers = line.gutterMarkers || (line.gutterMarkers = {})
6216
+ markers[gutterID] = value
6217
+ if (!value && isEmpty(markers)) { line.gutterMarkers = null }
6218
+ return true
6219
+ })
6220
+ }),
6221
+
6222
+ clearGutter: docMethodOp(function(gutterID) {
6223
+ var this$1 = this;
6224
+
6225
+ this.iter(function (line) {
6226
+ if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
6227
+ changeLine(this$1, line, "gutter", function () {
6228
+ line.gutterMarkers[gutterID] = null
6229
+ if (isEmpty(line.gutterMarkers)) { line.gutterMarkers = null }
6230
+ return true
6231
+ })
6232
+ }
6233
+ })
6234
+ }),
6235
+
6236
+ lineInfo: function(line) {
6237
+ var n
6238
+ if (typeof line == "number") {
6239
+ if (!isLine(this, line)) { return null }
6240
+ n = line
6241
+ line = getLine(this, line)
6242
+ if (!line) { return null }
6243
+ } else {
6244
+ n = lineNo(line)
6245
+ if (n == null) { return null }
6246
+ }
6247
+ return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
6248
+ textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
6249
+ widgets: line.widgets}
6250
+ },
6251
+
6252
+ addLineClass: docMethodOp(function(handle, where, cls) {
6253
+ return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
6254
+ var prop = where == "text" ? "textClass"
6255
+ : where == "background" ? "bgClass"
6256
+ : where == "gutter" ? "gutterClass" : "wrapClass"
6257
+ if (!line[prop]) { line[prop] = cls }
6258
+ else if (classTest(cls).test(line[prop])) { return false }
6259
+ else { line[prop] += " " + cls }
6260
+ return true
6261
+ })
6262
+ }),
6263
+ removeLineClass: docMethodOp(function(handle, where, cls) {
6264
+ return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
6265
+ var prop = where == "text" ? "textClass"
6266
+ : where == "background" ? "bgClass"
6267
+ : where == "gutter" ? "gutterClass" : "wrapClass"
6268
+ var cur = line[prop]
6269
+ if (!cur) { return false }
6270
+ else if (cls == null) { line[prop] = null }
6271
+ else {
6272
+ var found = cur.match(classTest(cls))
6273
+ if (!found) { return false }
6274
+ var end = found.index + found[0].length
6275
+ line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null
6276
+ }
6277
+ return true
6278
+ })
6279
+ }),
6280
+
6281
+ addLineWidget: docMethodOp(function(handle, node, options) {
6282
+ return addLineWidget(this, handle, node, options)
6283
+ }),
6284
+ removeLineWidget: function(widget) { widget.clear() },
6285
+
6286
+ markText: function(from, to, options) {
6287
+ return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range")
6288
+ },
6289
+ setBookmark: function(pos, options) {
6290
+ var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
6291
+ insertLeft: options && options.insertLeft,
6292
+ clearWhenEmpty: false, shared: options && options.shared,
6293
+ handleMouseEvents: options && options.handleMouseEvents}
6294
+ pos = clipPos(this, pos)
6295
+ return markText(this, pos, pos, realOpts, "bookmark")
6296
+ },
6297
+ findMarksAt: function(pos) {
6298
+ pos = clipPos(this, pos)
6299
+ var markers = [], spans = getLine(this, pos.line).markedSpans
6300
+ if (spans) { for (var i = 0; i < spans.length; ++i) {
6301
+ var span = spans[i]
6302
+ if ((span.from == null || span.from <= pos.ch) &&
6303
+ (span.to == null || span.to >= pos.ch))
6304
+ { markers.push(span.marker.parent || span.marker) }
6305
+ } }
6306
+ return markers
6307
+ },
6308
+ findMarks: function(from, to, filter) {
6309
+ from = clipPos(this, from); to = clipPos(this, to)
6310
+ var found = [], lineNo = from.line
6311
+ this.iter(from.line, to.line + 1, function (line) {
6312
+ var spans = line.markedSpans
6313
+ if (spans) { for (var i = 0; i < spans.length; i++) {
6314
+ var span = spans[i]
6315
+ if (!(span.to != null && lineNo == from.line && from.ch >= span.to ||
6316
+ span.from == null && lineNo != from.line ||
6317
+ span.from != null && lineNo == to.line && span.from >= to.ch) &&
6318
+ (!filter || filter(span.marker)))
6319
+ { found.push(span.marker.parent || span.marker) }
6320
+ } }
6321
+ ++lineNo
6322
+ })
6323
+ return found
6324
+ },
6325
+ getAllMarks: function() {
6326
+ var markers = []
6327
+ this.iter(function (line) {
6328
+ var sps = line.markedSpans
6329
+ if (sps) { for (var i = 0; i < sps.length; ++i)
6330
+ { if (sps[i].from != null) { markers.push(sps[i].marker) } } }
6331
+ })
6332
+ return markers
6333
+ },
6334
+
6335
+ posFromIndex: function(off) {
6336
+ var ch, lineNo = this.first, sepSize = this.lineSeparator().length
6337
+ this.iter(function (line) {
6338
+ var sz = line.text.length + sepSize
6339
+ if (sz > off) { ch = off; return true }
6340
+ off -= sz
6341
+ ++lineNo
6342
+ })
6343
+ return clipPos(this, Pos(lineNo, ch))
6344
+ },
6345
+ indexFromPos: function (coords) {
6346
+ coords = clipPos(this, coords)
6347
+ var index = coords.ch
6348
+ if (coords.line < this.first || coords.ch < 0) { return 0 }
6349
+ var sepSize = this.lineSeparator().length
6350
+ this.iter(this.first, coords.line, function (line) { // iter aborts when callback returns a truthy value
6351
+ index += line.text.length + sepSize
6352
+ })
6353
+ return index
6354
+ },
6355
+
6356
+ copy: function(copyHistory) {
6357
+ var doc = new Doc(getLines(this, this.first, this.first + this.size),
6358
+ this.modeOption, this.first, this.lineSep, this.direction)
6359
+ doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft
6360
+ doc.sel = this.sel
6361
+ doc.extend = false
6362
+ if (copyHistory) {
6363
+ doc.history.undoDepth = this.history.undoDepth
6364
+ doc.setHistory(this.getHistory())
6365
+ }
6366
+ return doc
6367
+ },
6368
+
6369
+ linkedDoc: function(options) {
6370
+ if (!options) { options = {} }
6371
+ var from = this.first, to = this.first + this.size
6372
+ if (options.from != null && options.from > from) { from = options.from }
6373
+ if (options.to != null && options.to < to) { to = options.to }
6374
+ var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep, this.direction)
6375
+ if (options.sharedHist) { copy.history = this.history
6376
+ ; }(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist})
6377
+ copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}]
6378
+ copySharedMarkers(copy, findSharedMarkers(this))
6379
+ return copy
6380
+ },
6381
+ unlinkDoc: function(other) {
6382
+ var this$1 = this;
6383
+
6384
+ if (other instanceof CodeMirror) { other = other.doc }
6385
+ if (this.linked) { for (var i = 0; i < this.linked.length; ++i) {
6386
+ var link = this$1.linked[i]
6387
+ if (link.doc != other) { continue }
6388
+ this$1.linked.splice(i, 1)
6389
+ other.unlinkDoc(this$1)
6390
+ detachSharedMarkers(findSharedMarkers(this$1))
6391
+ break
6392
+ } }
6393
+ // If the histories were shared, split them again
6394
+ if (other.history == this.history) {
6395
+ var splitIds = [other.id]
6396
+ linkedDocs(other, function (doc) { return splitIds.push(doc.id); }, true)
6397
+ other.history = new History(null)
6398
+ other.history.done = copyHistoryArray(this.history.done, splitIds)
6399
+ other.history.undone = copyHistoryArray(this.history.undone, splitIds)
6400
+ }
6401
+ },
6402
+ iterLinkedDocs: function(f) {linkedDocs(this, f)},
6403
+
6404
+ getMode: function() {return this.mode},
6405
+ getEditor: function() {return this.cm},
6406
+
6407
+ splitLines: function(str) {
6408
+ if (this.lineSep) { return str.split(this.lineSep) }
6409
+ return splitLinesAuto(str)
6410
+ },
6411
+ lineSeparator: function() { return this.lineSep || "\n" },
6412
+
6413
+ setDirection: docMethodOp(function (dir) {
6414
+ if (dir != "rtl") { dir = "ltr" }
6415
+ if (dir == this.direction) { return }
6416
+ this.direction = dir
6417
+ this.iter(function (line) { return line.order = null; })
6418
+ if (this.cm) { directionChanged(this.cm) }
6419
+ })
6420
+ })
6421
+
6422
+ // Public alias.
6423
+ Doc.prototype.eachLine = Doc.prototype.iter
6424
+
6425
+ // Kludge to work around strange IE behavior where it'll sometimes
6426
+ // re-fire a series of drag-related events right after the drop (#1551)
6427
+ var lastDrop = 0
6428
+
6429
+ function onDrop(e) {
6430
+ var cm = this
6431
+ clearDragCursor(cm)
6432
+ if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))
6433
+ { return }
6434
+ e_preventDefault(e)
6435
+ if (ie) { lastDrop = +new Date }
6436
+ var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files
6437
+ if (!pos || cm.isReadOnly()) { return }
6438
+ // Might be a file drop, in which case we simply extract the text
6439
+ // and insert it.
6440
+ if (files && files.length && window.FileReader && window.File) {
6441
+ var n = files.length, text = Array(n), read = 0
6442
+ var loadFile = function (file, i) {
6443
+ if (cm.options.allowDropFileTypes &&
6444
+ indexOf(cm.options.allowDropFileTypes, file.type) == -1)
6445
+ { return }
6446
+
6447
+ var reader = new FileReader
6448
+ reader.onload = operation(cm, function () {
6449
+ var content = reader.result
6450
+ if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) { content = "" }
6451
+ text[i] = content
6452
+ if (++read == n) {
6453
+ pos = clipPos(cm.doc, pos)
6454
+ var change = {from: pos, to: pos,
6455
+ text: cm.doc.splitLines(text.join(cm.doc.lineSeparator())),
6456
+ origin: "paste"}
6457
+ makeChange(cm.doc, change)
6458
+ setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change)))
6459
+ }
6460
+ })
6461
+ reader.readAsText(file)
6462
+ }
6463
+ for (var i = 0; i < n; ++i) { loadFile(files[i], i) }
6464
+ } else { // Normal drop
6465
+ // Don't do a replace if the drop happened inside of the selected text.
6466
+ if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {
6467
+ cm.state.draggingText(e)
6468
+ // Ensure the editor is re-focused
6469
+ setTimeout(function () { return cm.display.input.focus(); }, 20)
6470
+ return
6471
+ }
6472
+ try {
6473
+ var text$1 = e.dataTransfer.getData("Text")
6474
+ if (text$1) {
6475
+ var selected
6476
+ if (cm.state.draggingText && !cm.state.draggingText.copy)
6477
+ { selected = cm.listSelections() }
6478
+ setSelectionNoUndo(cm.doc, simpleSelection(pos, pos))
6479
+ if (selected) { for (var i$1 = 0; i$1 < selected.length; ++i$1)
6480
+ { replaceRange(cm.doc, "", selected[i$1].anchor, selected[i$1].head, "drag") } }
6481
+ cm.replaceSelection(text$1, "around", "paste")
6482
+ cm.display.input.focus()
6483
+ }
6484
+ }
6485
+ catch(e){}
6486
+ }
6487
+ }
6488
+
6489
+ function onDragStart(cm, e) {
6490
+ if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return }
6491
+ if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return }
6492
+
6493
+ e.dataTransfer.setData("Text", cm.getSelection())
6494
+ e.dataTransfer.effectAllowed = "copyMove"
6495
+
6496
+ // Use dummy image instead of default browsers image.
6497
+ // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
6498
+ if (e.dataTransfer.setDragImage && !safari) {
6499
+ var img = elt("img", null, null, "position: fixed; left: 0; top: 0;")
6500
+ img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
6501
+ if (presto) {
6502
+ img.width = img.height = 1
6503
+ cm.display.wrapper.appendChild(img)
6504
+ // Force a relayout, or Opera won't use our image for some obscure reason
6505
+ img._top = img.offsetTop
6506
+ }
6507
+ e.dataTransfer.setDragImage(img, 0, 0)
6508
+ if (presto) { img.parentNode.removeChild(img) }
6509
+ }
6510
+ }
6511
+
6512
+ function onDragOver(cm, e) {
6513
+ var pos = posFromMouse(cm, e)
6514
+ if (!pos) { return }
6515
+ var frag = document.createDocumentFragment()
6516
+ drawSelectionCursor(cm, pos, frag)
6517
+ if (!cm.display.dragCursor) {
6518
+ cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors")
6519
+ cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv)
6520
+ }
6521
+ removeChildrenAndAdd(cm.display.dragCursor, frag)
6522
+ }
6523
+
6524
+ function clearDragCursor(cm) {
6525
+ if (cm.display.dragCursor) {
6526
+ cm.display.lineSpace.removeChild(cm.display.dragCursor)
6527
+ cm.display.dragCursor = null
6528
+ }
6529
+ }
6530
+
6531
+ // These must be handled carefully, because naively registering a
6532
+ // handler for each editor will cause the editors to never be
6533
+ // garbage collected.
6534
+
6535
+ function forEachCodeMirror(f) {
6536
+ if (!document.getElementsByClassName) { return }
6537
+ var byClass = document.getElementsByClassName("CodeMirror")
6538
+ for (var i = 0; i < byClass.length; i++) {
6539
+ var cm = byClass[i].CodeMirror
6540
+ if (cm) { f(cm) }
6541
+ }
6542
+ }
6543
+
6544
+ var globalsRegistered = false
6545
+ function ensureGlobalHandlers() {
6546
+ if (globalsRegistered) { return }
6547
+ registerGlobalHandlers()
6548
+ globalsRegistered = true
6549
+ }
6550
+ function registerGlobalHandlers() {
6551
+ // When the window resizes, we need to refresh active editors.
6552
+ var resizeTimer
6553
+ on(window, "resize", function () {
6554
+ if (resizeTimer == null) { resizeTimer = setTimeout(function () {
6555
+ resizeTimer = null
6556
+ forEachCodeMirror(onResize)
6557
+ }, 100) }
6558
+ })
6559
+ // When the window loses focus, we want to show the editor as blurred
6560
+ on(window, "blur", function () { return forEachCodeMirror(onBlur); })
6561
+ }
6562
+ // Called when the window resizes
6563
+ function onResize(cm) {
6564
+ var d = cm.display
6565
+ if (d.lastWrapHeight == d.wrapper.clientHeight && d.lastWrapWidth == d.wrapper.clientWidth)
6566
+ { return }
6567
+ // Might be a text scaling operation, clear size caches.
6568
+ d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null
6569
+ d.scrollbarsClipped = false
6570
+ cm.setSize()
6571
+ }
6572
+
6573
+ var keyNames = {
6574
+ 3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
6575
+ 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
6576
+ 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
6577
+ 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod",
6578
+ 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 127: "Delete",
6579
+ 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
6580
+ 221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete",
6581
+ 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert"
6582
+ }
6583
+
6584
+ // Number keys
6585
+ for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i) }
6586
+ // Alphabetic keys
6587
+ for (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1) }
6588
+ // Function keys
6589
+ for (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235] = "F" + i$2 }
6590
+
6591
+ var keyMap = {}
6592
+
6593
+ keyMap.basic = {
6594
+ "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
6595
+ "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
6596
+ "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore",
6597
+ "Tab": "defaultTab", "Shift-Tab": "indentAuto",
6598
+ "Enter": "newlineAndIndent", "Insert": "toggleOverwrite",
6599
+ "Esc": "singleSelection"
6600
+ }
6601
+ // Note that the save and find-related commands aren't defined by
6602
+ // default. User code or addons can define them. Unknown commands
6603
+ // are simply ignored.
6604
+ keyMap.pcDefault = {
6605
+ "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
6606
+ "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown",
6607
+ "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
6608
+ "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
6609
+ "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
6610
+ "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
6611
+ "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection",
6612
+ fallthrough: "basic"
6613
+ }
6614
+ // Very basic readline/emacs-style bindings, which are standard on Mac.
6615
+ keyMap.emacsy = {
6616
+ "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
6617
+ "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
6618
+ "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
6619
+ "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars",
6620
+ "Ctrl-O": "openLine"
6621
+ }
6622
+ keyMap.macDefault = {
6623
+ "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
6624
+ "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
6625
+ "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore",
6626
+ "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
6627
+ "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
6628
+ "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight",
6629
+ "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd",
6630
+ fallthrough: ["basic", "emacsy"]
6631
+ }
6632
+ keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault
6633
+
6634
+ // KEYMAP DISPATCH
6635
+
6636
+ function normalizeKeyName(name) {
6637
+ var parts = name.split(/-(?!$)/)
6638
+ name = parts[parts.length - 1]
6639
+ var alt, ctrl, shift, cmd
6640
+ for (var i = 0; i < parts.length - 1; i++) {
6641
+ var mod = parts[i]
6642
+ if (/^(cmd|meta|m)$/i.test(mod)) { cmd = true }
6643
+ else if (/^a(lt)?$/i.test(mod)) { alt = true }
6644
+ else if (/^(c|ctrl|control)$/i.test(mod)) { ctrl = true }
6645
+ else if (/^s(hift)?$/i.test(mod)) { shift = true }
6646
+ else { throw new Error("Unrecognized modifier name: " + mod) }
6647
+ }
6648
+ if (alt) { name = "Alt-" + name }
6649
+ if (ctrl) { name = "Ctrl-" + name }
6650
+ if (cmd) { name = "Cmd-" + name }
6651
+ if (shift) { name = "Shift-" + name }
6652
+ return name
6653
+ }
6654
+
6655
+ // This is a kludge to keep keymaps mostly working as raw objects
6656
+ // (backwards compatibility) while at the same time support features
6657
+ // like normalization and multi-stroke key bindings. It compiles a
6658
+ // new normalized keymap, and then updates the old object to reflect
6659
+ // this.
6660
+ function normalizeKeyMap(keymap) {
6661
+ var copy = {}
6662
+ for (var keyname in keymap) { if (keymap.hasOwnProperty(keyname)) {
6663
+ var value = keymap[keyname]
6664
+ if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) { continue }
6665
+ if (value == "...") { delete keymap[keyname]; continue }
6666
+
6667
+ var keys = map(keyname.split(" "), normalizeKeyName)
6668
+ for (var i = 0; i < keys.length; i++) {
6669
+ var val = (void 0), name = (void 0)
6670
+ if (i == keys.length - 1) {
6671
+ name = keys.join(" ")
6672
+ val = value
6673
+ } else {
6674
+ name = keys.slice(0, i + 1).join(" ")
6675
+ val = "..."
6676
+ }
6677
+ var prev = copy[name]
6678
+ if (!prev) { copy[name] = val }
6679
+ else if (prev != val) { throw new Error("Inconsistent bindings for " + name) }
6680
+ }
6681
+ delete keymap[keyname]
6682
+ } }
6683
+ for (var prop in copy) { keymap[prop] = copy[prop] }
6684
+ return keymap
6685
+ }
6686
+
6687
+ function lookupKey(key, map, handle, context) {
6688
+ map = getKeyMap(map)
6689
+ var found = map.call ? map.call(key, context) : map[key]
6690
+ if (found === false) { return "nothing" }
6691
+ if (found === "...") { return "multi" }
6692
+ if (found != null && handle(found)) { return "handled" }
6693
+
6694
+ if (map.fallthrough) {
6695
+ if (Object.prototype.toString.call(map.fallthrough) != "[object Array]")
6696
+ { return lookupKey(key, map.fallthrough, handle, context) }
6697
+ for (var i = 0; i < map.fallthrough.length; i++) {
6698
+ var result = lookupKey(key, map.fallthrough[i], handle, context)
6699
+ if (result) { return result }
6700
+ }
6701
+ }
6702
+ }
6703
+
6704
+ // Modifier key presses don't count as 'real' key presses for the
6705
+ // purpose of keymap fallthrough.
6706
+ function isModifierKey(value) {
6707
+ var name = typeof value == "string" ? value : keyNames[value.keyCode]
6708
+ return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod"
6709
+ }
6710
+
6711
+ function addModifierNames(name, event, noShift) {
6712
+ var base = name
6713
+ if (event.altKey && base != "Alt") { name = "Alt-" + name }
6714
+ if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") { name = "Ctrl-" + name }
6715
+ if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") { name = "Cmd-" + name }
6716
+ if (!noShift && event.shiftKey && base != "Shift") { name = "Shift-" + name }
6717
+ return name
6718
+ }
6719
+
6720
+ // Look up the name of a key as indicated by an event object.
6721
+ function keyName(event, noShift) {
6722
+ if (presto && event.keyCode == 34 && event["char"]) { return false }
6723
+ var name = keyNames[event.keyCode]
6724
+ if (name == null || event.altGraphKey) { return false }
6725
+ return addModifierNames(name, event, noShift)
6726
+ }
6727
+
6728
+ function getKeyMap(val) {
6729
+ return typeof val == "string" ? keyMap[val] : val
6730
+ }
6731
+
6732
+ // Helper for deleting text near the selection(s), used to implement
6733
+ // backspace, delete, and similar functionality.
6734
+ function deleteNearSelection(cm, compute) {
6735
+ var ranges = cm.doc.sel.ranges, kill = []
6736
+ // Build up a set of ranges to kill first, merging overlapping
6737
+ // ranges.
6738
+ for (var i = 0; i < ranges.length; i++) {
6739
+ var toKill = compute(ranges[i])
6740
+ while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {
6741
+ var replaced = kill.pop()
6742
+ if (cmp(replaced.from, toKill.from) < 0) {
6743
+ toKill.from = replaced.from
6744
+ break
6745
+ }
6746
+ }
6747
+ kill.push(toKill)
6748
+ }
6749
+ // Next, remove those actual ranges.
6750
+ runInOp(cm, function () {
6751
+ for (var i = kill.length - 1; i >= 0; i--)
6752
+ { replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete") }
6753
+ ensureCursorVisible(cm)
6754
+ })
6755
+ }
6756
+
6757
+ function moveCharLogically(line, ch, dir) {
6758
+ var target = skipExtendingChars(line.text, ch + dir, dir)
6759
+ return target < 0 || target > line.text.length ? null : target
6760
+ }
6761
+
6762
+ function moveLogically(line, start, dir) {
6763
+ var ch = moveCharLogically(line, start.ch, dir)
6764
+ return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before")
6765
+ }
6766
+
6767
+ function endOfLine(visually, cm, lineObj, lineNo, dir) {
6768
+ if (visually) {
6769
+ var order = getOrder(lineObj, cm.doc.direction)
6770
+ if (order) {
6771
+ var part = dir < 0 ? lst(order) : order[0]
6772
+ var moveInStorageOrder = (dir < 0) == (part.level == 1)
6773
+ var sticky = moveInStorageOrder ? "after" : "before"
6774
+ var ch
6775
+ // With a wrapped rtl chunk (possibly spanning multiple bidi parts),
6776
+ // it could be that the last bidi part is not on the last visual line,
6777
+ // since visual lines contain content order-consecutive chunks.
6778
+ // Thus, in rtl, we are looking for the first (content-order) character
6779
+ // in the rtl chunk that is on the last line (that is, the same line
6780
+ // as the last (content-order) character).
6781
+ if (part.level > 0 || cm.doc.direction == "rtl") {
6782
+ var prep = prepareMeasureForLine(cm, lineObj)
6783
+ ch = dir < 0 ? lineObj.text.length - 1 : 0
6784
+ var targetTop = measureCharPrepared(cm, prep, ch).top
6785
+ ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch)
6786
+ if (sticky == "before") { ch = moveCharLogically(lineObj, ch, 1) }
6787
+ } else { ch = dir < 0 ? part.to : part.from }
6788
+ return new Pos(lineNo, ch, sticky)
6789
+ }
6790
+ }
6791
+ return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after")
6792
+ }
6793
+
6794
+ function moveVisually(cm, line, start, dir) {
6795
+ var bidi = getOrder(line, cm.doc.direction)
6796
+ if (!bidi) { return moveLogically(line, start, dir) }
6797
+ if (start.ch >= line.text.length) {
6798
+ start.ch = line.text.length
6799
+ start.sticky = "before"
6800
+ } else if (start.ch <= 0) {
6801
+ start.ch = 0
6802
+ start.sticky = "after"
6803
+ }
6804
+ var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos]
6805
+ if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) {
6806
+ // Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines,
6807
+ // nothing interesting happens.
6808
+ return moveLogically(line, start, dir)
6809
+ }
6810
+
6811
+ var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); }
6812
+ var prep
6813
+ var getWrappedLineExtent = function (ch) {
6814
+ if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} }
6815
+ prep = prep || prepareMeasureForLine(cm, line)
6816
+ return wrappedLineExtentChar(cm, line, prep, ch)
6817
+ }
6818
+ var wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch)
6819
+
6820
+ if (cm.doc.direction == "rtl" || part.level == 1) {
6821
+ var moveInStorageOrder = (part.level == 1) == (dir < 0)
6822
+ var ch = mv(start, moveInStorageOrder ? 1 : -1)
6823
+ if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) {
6824
+ // Case 2: We move within an rtl part or in an rtl editor on the same visual line
6825
+ var sticky = moveInStorageOrder ? "before" : "after"
6826
+ return new Pos(start.line, ch, sticky)
6827
+ }
6828
+ }
6829
+
6830
+ // Case 3: Could not move within this bidi part in this visual line, so leave
6831
+ // the current bidi part
6832
+
6833
+ var searchInVisualLine = function (partPos, dir, wrappedLineExtent) {
6834
+ var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder
6835
+ ? new Pos(start.line, mv(ch, 1), "before")
6836
+ : new Pos(start.line, ch, "after"); }
6837
+
6838
+ for (; partPos >= 0 && partPos < bidi.length; partPos += dir) {
6839
+ var part = bidi[partPos]
6840
+ var moveInStorageOrder = (dir > 0) == (part.level != 1)
6841
+ var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1)
6842
+ if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) }
6843
+ ch = moveInStorageOrder ? part.from : mv(part.to, -1)
6844
+ if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) }
6845
+ }
6846
+ }
6847
+
6848
+ // Case 3a: Look for other bidi parts on the same visual line
6849
+ var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent)
6850
+ if (res) { return res }
6851
+
6852
+ // Case 3b: Look for other bidi parts on the next visual line
6853
+ var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1)
6854
+ if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) {
6855
+ res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh))
6856
+ if (res) { return res }
6857
+ }
6858
+
6859
+ // Case 4: Nowhere to move
6860
+ return null
6861
+ }
6862
+
6863
+ // Commands are parameter-less actions that can be performed on an
6864
+ // editor, mostly used for keybindings.
6865
+ var commands = {
6866
+ selectAll: selectAll,
6867
+ singleSelection: function (cm) { return cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll); },
6868
+ killLine: function (cm) { return deleteNearSelection(cm, function (range) {
6869
+ if (range.empty()) {
6870
+ var len = getLine(cm.doc, range.head.line).text.length
6871
+ if (range.head.ch == len && range.head.line < cm.lastLine())
6872
+ { return {from: range.head, to: Pos(range.head.line + 1, 0)} }
6873
+ else
6874
+ { return {from: range.head, to: Pos(range.head.line, len)} }
6875
+ } else {
6876
+ return {from: range.from(), to: range.to()}
6877
+ }
6878
+ }); },
6879
+ deleteLine: function (cm) { return deleteNearSelection(cm, function (range) { return ({
6880
+ from: Pos(range.from().line, 0),
6881
+ to: clipPos(cm.doc, Pos(range.to().line + 1, 0))
6882
+ }); }); },
6883
+ delLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { return ({
6884
+ from: Pos(range.from().line, 0), to: range.from()
6885
+ }); }); },
6886
+ delWrappedLineLeft: function (cm) { return deleteNearSelection(cm, function (range) {
6887
+ var top = cm.charCoords(range.head, "div").top + 5
6888
+ var leftPos = cm.coordsChar({left: 0, top: top}, "div")
6889
+ return {from: leftPos, to: range.from()}
6890
+ }); },
6891
+ delWrappedLineRight: function (cm) { return deleteNearSelection(cm, function (range) {
6892
+ var top = cm.charCoords(range.head, "div").top + 5
6893
+ var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div")
6894
+ return {from: range.from(), to: rightPos }
6895
+ }); },
6896
+ undo: function (cm) { return cm.undo(); },
6897
+ redo: function (cm) { return cm.redo(); },
6898
+ undoSelection: function (cm) { return cm.undoSelection(); },
6899
+ redoSelection: function (cm) { return cm.redoSelection(); },
6900
+ goDocStart: function (cm) { return cm.extendSelection(Pos(cm.firstLine(), 0)); },
6901
+ goDocEnd: function (cm) { return cm.extendSelection(Pos(cm.lastLine())); },
6902
+ goLineStart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStart(cm, range.head.line); },
6903
+ {origin: "+move", bias: 1}
6904
+ ); },
6905
+ goLineStartSmart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStartSmart(cm, range.head); },
6906
+ {origin: "+move", bias: 1}
6907
+ ); },
6908
+ goLineEnd: function (cm) { return cm.extendSelectionsBy(function (range) { return lineEnd(cm, range.head.line); },
6909
+ {origin: "+move", bias: -1}
6910
+ ); },
6911
+ goLineRight: function (cm) { return cm.extendSelectionsBy(function (range) {
6912
+ var top = cm.cursorCoords(range.head, "div").top + 5
6913
+ return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div")
6914
+ }, sel_move); },
6915
+ goLineLeft: function (cm) { return cm.extendSelectionsBy(function (range) {
6916
+ var top = cm.cursorCoords(range.head, "div").top + 5
6917
+ return cm.coordsChar({left: 0, top: top}, "div")
6918
+ }, sel_move); },
6919
+ goLineLeftSmart: function (cm) { return cm.extendSelectionsBy(function (range) {
6920
+ var top = cm.cursorCoords(range.head, "div").top + 5
6921
+ var pos = cm.coordsChar({left: 0, top: top}, "div")
6922
+ if (pos.ch < cm.getLine(pos.line).search(/\S/)) { return lineStartSmart(cm, range.head) }
6923
+ return pos
6924
+ }, sel_move); },
6925
+ goLineUp: function (cm) { return cm.moveV(-1, "line"); },
6926
+ goLineDown: function (cm) { return cm.moveV(1, "line"); },
6927
+ goPageUp: function (cm) { return cm.moveV(-1, "page"); },
6928
+ goPageDown: function (cm) { return cm.moveV(1, "page"); },
6929
+ goCharLeft: function (cm) { return cm.moveH(-1, "char"); },
6930
+ goCharRight: function (cm) { return cm.moveH(1, "char"); },
6931
+ goColumnLeft: function (cm) { return cm.moveH(-1, "column"); },
6932
+ goColumnRight: function (cm) { return cm.moveH(1, "column"); },
6933
+ goWordLeft: function (cm) { return cm.moveH(-1, "word"); },
6934
+ goGroupRight: function (cm) { return cm.moveH(1, "group"); },
6935
+ goGroupLeft: function (cm) { return cm.moveH(-1, "group"); },
6936
+ goWordRight: function (cm) { return cm.moveH(1, "word"); },
6937
+ delCharBefore: function (cm) { return cm.deleteH(-1, "char"); },
6938
+ delCharAfter: function (cm) { return cm.deleteH(1, "char"); },
6939
+ delWordBefore: function (cm) { return cm.deleteH(-1, "word"); },
6940
+ delWordAfter: function (cm) { return cm.deleteH(1, "word"); },
6941
+ delGroupBefore: function (cm) { return cm.deleteH(-1, "group"); },
6942
+ delGroupAfter: function (cm) { return cm.deleteH(1, "group"); },
6943
+ indentAuto: function (cm) { return cm.indentSelection("smart"); },
6944
+ indentMore: function (cm) { return cm.indentSelection("add"); },
6945
+ indentLess: function (cm) { return cm.indentSelection("subtract"); },
6946
+ insertTab: function (cm) { return cm.replaceSelection("\t"); },
6947
+ insertSoftTab: function (cm) {
6948
+ var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize
6949
+ for (var i = 0; i < ranges.length; i++) {
6950
+ var pos = ranges[i].from()
6951
+ var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize)
6952
+ spaces.push(spaceStr(tabSize - col % tabSize))
6953
+ }
6954
+ cm.replaceSelections(spaces)
6955
+ },
6956
+ defaultTab: function (cm) {
6957
+ if (cm.somethingSelected()) { cm.indentSelection("add") }
6958
+ else { cm.execCommand("insertTab") }
6959
+ },
6960
+ // Swap the two chars left and right of each selection's head.
6961
+ // Move cursor behind the two swapped characters afterwards.
6962
+ //
6963
+ // Doesn't consider line feeds a character.
6964
+ // Doesn't scan more than one line above to find a character.
6965
+ // Doesn't do anything on an empty line.
6966
+ // Doesn't do anything with non-empty selections.
6967
+ transposeChars: function (cm) { return runInOp(cm, function () {
6968
+ var ranges = cm.listSelections(), newSel = []
6969
+ for (var i = 0; i < ranges.length; i++) {
6970
+ if (!ranges[i].empty()) { continue }
6971
+ var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text
6972
+ if (line) {
6973
+ if (cur.ch == line.length) { cur = new Pos(cur.line, cur.ch - 1) }
6974
+ if (cur.ch > 0) {
6975
+ cur = new Pos(cur.line, cur.ch + 1)
6976
+ cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),
6977
+ Pos(cur.line, cur.ch - 2), cur, "+transpose")
6978
+ } else if (cur.line > cm.doc.first) {
6979
+ var prev = getLine(cm.doc, cur.line - 1).text
6980
+ if (prev) {
6981
+ cur = new Pos(cur.line, 1)
6982
+ cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() +
6983
+ prev.charAt(prev.length - 1),
6984
+ Pos(cur.line - 1, prev.length - 1), cur, "+transpose")
6985
+ }
6986
+ }
6987
+ }
6988
+ newSel.push(new Range(cur, cur))
6989
+ }
6990
+ cm.setSelections(newSel)
6991
+ }); },
6992
+ newlineAndIndent: function (cm) { return runInOp(cm, function () {
6993
+ var sels = cm.listSelections()
6994
+ for (var i = sels.length - 1; i >= 0; i--)
6995
+ { cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input") }
6996
+ sels = cm.listSelections()
6997
+ for (var i$1 = 0; i$1 < sels.length; i$1++)
6998
+ { cm.indentLine(sels[i$1].from().line, null, true) }
6999
+ ensureCursorVisible(cm)
7000
+ }); },
7001
+ openLine: function (cm) { return cm.replaceSelection("\n", "start"); },
7002
+ toggleOverwrite: function (cm) { return cm.toggleOverwrite(); }
7003
+ }
7004
+
7005
+
7006
+ function lineStart(cm, lineN) {
7007
+ var line = getLine(cm.doc, lineN)
7008
+ var visual = visualLine(line)
7009
+ if (visual != line) { lineN = lineNo(visual) }
7010
+ return endOfLine(true, cm, visual, lineN, 1)
7011
+ }
7012
+ function lineEnd(cm, lineN) {
7013
+ var line = getLine(cm.doc, lineN)
7014
+ var visual = visualLineEnd(line)
7015
+ if (visual != line) { lineN = lineNo(visual) }
7016
+ return endOfLine(true, cm, line, lineN, -1)
7017
+ }
7018
+ function lineStartSmart(cm, pos) {
7019
+ var start = lineStart(cm, pos.line)
7020
+ var line = getLine(cm.doc, start.line)
7021
+ var order = getOrder(line, cm.doc.direction)
7022
+ if (!order || order[0].level == 0) {
7023
+ var firstNonWS = Math.max(0, line.text.search(/\S/))
7024
+ var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch
7025
+ return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky)
7026
+ }
7027
+ return start
7028
+ }
7029
+
7030
+ // Run a handler that was bound to a key.
7031
+ function doHandleBinding(cm, bound, dropShift) {
7032
+ if (typeof bound == "string") {
7033
+ bound = commands[bound]
7034
+ if (!bound) { return false }
7035
+ }
7036
+ // Ensure previous input has been read, so that the handler sees a
7037
+ // consistent view of the document
7038
+ cm.display.input.ensurePolled()
7039
+ var prevShift = cm.display.shift, done = false
7040
+ try {
7041
+ if (cm.isReadOnly()) { cm.state.suppressEdits = true }
7042
+ if (dropShift) { cm.display.shift = false }
7043
+ done = bound(cm) != Pass
7044
+ } finally {
7045
+ cm.display.shift = prevShift
7046
+ cm.state.suppressEdits = false
7047
+ }
7048
+ return done
7049
+ }
7050
+
7051
+ function lookupKeyForEditor(cm, name, handle) {
7052
+ for (var i = 0; i < cm.state.keyMaps.length; i++) {
7053
+ var result = lookupKey(name, cm.state.keyMaps[i], handle, cm)
7054
+ if (result) { return result }
7055
+ }
7056
+ return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm))
7057
+ || lookupKey(name, cm.options.keyMap, handle, cm)
7058
+ }
7059
+
7060
+ // Note that, despite the name, this function is also used to check
7061
+ // for bound mouse clicks.
7062
+
7063
+ var stopSeq = new Delayed
7064
+ function dispatchKey(cm, name, e, handle) {
7065
+ var seq = cm.state.keySeq
7066
+ if (seq) {
7067
+ if (isModifierKey(name)) { return "handled" }
7068
+ stopSeq.set(50, function () {
7069
+ if (cm.state.keySeq == seq) {
7070
+ cm.state.keySeq = null
7071
+ cm.display.input.reset()
7072
+ }
7073
+ })
7074
+ name = seq + " " + name
7075
+ }
7076
+ var result = lookupKeyForEditor(cm, name, handle)
7077
+
7078
+ if (result == "multi")
7079
+ { cm.state.keySeq = name }
7080
+ if (result == "handled")
7081
+ { signalLater(cm, "keyHandled", cm, name, e) }
7082
+
7083
+ if (result == "handled" || result == "multi") {
7084
+ e_preventDefault(e)
7085
+ restartBlink(cm)
7086
+ }
7087
+
7088
+ if (seq && !result && /\'$/.test(name)) {
7089
+ e_preventDefault(e)
7090
+ return true
7091
+ }
7092
+ return !!result
7093
+ }
7094
+
7095
+ // Handle a key from the keydown event.
7096
+ function handleKeyBinding(cm, e) {
7097
+ var name = keyName(e, true)
7098
+ if (!name) { return false }
7099
+
7100
+ if (e.shiftKey && !cm.state.keySeq) {
7101
+ // First try to resolve full name (including 'Shift-'). Failing
7102
+ // that, see if there is a cursor-motion command (starting with
7103
+ // 'go') bound to the keyname without 'Shift-'.
7104
+ return dispatchKey(cm, "Shift-" + name, e, function (b) { return doHandleBinding(cm, b, true); })
7105
+ || dispatchKey(cm, name, e, function (b) {
7106
+ if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
7107
+ { return doHandleBinding(cm, b) }
7108
+ })
7109
+ } else {
7110
+ return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b); })
7111
+ }
7112
+ }
7113
+
7114
+ // Handle a key from the keypress event
7115
+ function handleCharBinding(cm, e, ch) {
7116
+ return dispatchKey(cm, "'" + ch + "'", e, function (b) { return doHandleBinding(cm, b, true); })
7117
+ }
7118
+
7119
+ var lastStoppedKey = null
7120
+ function onKeyDown(e) {
7121
+ var cm = this
7122
+ cm.curOp.focus = activeElt()
7123
+ if (signalDOMEvent(cm, e)) { return }
7124
+ // IE does strange things with escape.
7125
+ if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false }
7126
+ var code = e.keyCode
7127
+ cm.display.shift = code == 16 || e.shiftKey
7128
+ var handled = handleKeyBinding(cm, e)
7129
+ if (presto) {
7130
+ lastStoppedKey = handled ? code : null
7131
+ // Opera has no cut event... we try to at least catch the key combo
7132
+ if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
7133
+ { cm.replaceSelection("", null, "cut") }
7134
+ }
7135
+
7136
+ // Turn mouse into crosshair when Alt is held on Mac.
7137
+ if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className))
7138
+ { showCrossHair(cm) }
7139
+ }
7140
+
7141
+ function showCrossHair(cm) {
7142
+ var lineDiv = cm.display.lineDiv
7143
+ addClass(lineDiv, "CodeMirror-crosshair")
7144
+
7145
+ function up(e) {
7146
+ if (e.keyCode == 18 || !e.altKey) {
7147
+ rmClass(lineDiv, "CodeMirror-crosshair")
7148
+ off(document, "keyup", up)
7149
+ off(document, "mouseover", up)
7150
+ }
7151
+ }
7152
+ on(document, "keyup", up)
7153
+ on(document, "mouseover", up)
7154
+ }
7155
+
7156
+ function onKeyUp(e) {
7157
+ if (e.keyCode == 16) { this.doc.sel.shift = false }
7158
+ signalDOMEvent(this, e)
7159
+ }
7160
+
7161
+ function onKeyPress(e) {
7162
+ var cm = this
7163
+ if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return }
7164
+ var keyCode = e.keyCode, charCode = e.charCode
7165
+ if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return}
7166
+ if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) { return }
7167
+ var ch = String.fromCharCode(charCode == null ? keyCode : charCode)
7168
+ // Some browsers fire keypress events for backspace
7169
+ if (ch == "\x08") { return }
7170
+ if (handleCharBinding(cm, e, ch)) { return }
7171
+ cm.display.input.onKeyPress(e)
7172
+ }
7173
+
7174
+ var DOUBLECLICK_DELAY = 400
7175
+
7176
+ var PastClick = function(time, pos, button) {
7177
+ this.time = time
7178
+ this.pos = pos
7179
+ this.button = button
7180
+ };
7181
+
7182
+ PastClick.prototype.compare = function (time, pos, button) {
7183
+ return this.time + DOUBLECLICK_DELAY > time &&
7184
+ cmp(pos, this.pos) == 0 && button == this.button
7185
+ };
7186
+
7187
+ var lastClick;
7188
+ var lastDoubleClick;
7189
+ function clickRepeat(pos, button) {
7190
+ var now = +new Date
7191
+ if (lastDoubleClick && lastDoubleClick.compare(now, pos, button)) {
7192
+ lastClick = lastDoubleClick = null
7193
+ return "triple"
7194
+ } else if (lastClick && lastClick.compare(now, pos, button)) {
7195
+ lastDoubleClick = new PastClick(now, pos, button)
7196
+ lastClick = null
7197
+ return "double"
7198
+ } else {
7199
+ lastClick = new PastClick(now, pos, button)
7200
+ lastDoubleClick = null
7201
+ return "single"
7202
+ }
7203
+ }
7204
+
7205
+ // A mouse down can be a single click, double click, triple click,
7206
+ // start of selection drag, start of text drag, new cursor
7207
+ // (ctrl-click), rectangle drag (alt-drag), or xwin
7208
+ // middle-click-paste. Or it might be a click on something we should
7209
+ // not interfere with, such as a scrollbar or widget.
7210
+ function onMouseDown(e) {
7211
+ var cm = this, display = cm.display
7212
+ if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) { return }
7213
+ display.input.ensurePolled()
7214
+ display.shift = e.shiftKey
7215
+
7216
+ if (eventInWidget(display, e)) {
7217
+ if (!webkit) {
7218
+ // Briefly turn off draggability, to allow widgets to do
7219
+ // normal dragging things.
7220
+ display.scroller.draggable = false
7221
+ setTimeout(function () { return display.scroller.draggable = true; }, 100)
7222
+ }
7223
+ return
7224
+ }
7225
+ if (clickInGutter(cm, e)) { return }
7226
+ var pos = posFromMouse(cm, e), button = e_button(e), repeat = pos ? clickRepeat(pos, button) : "single"
7227
+ window.focus()
7228
+
7229
+ // #3261: make sure, that we're not starting a second selection
7230
+ if (button == 1 && cm.state.selectingText)
7231
+ { cm.state.selectingText(e) }
7232
+
7233
+ if (pos && handleMappedButton(cm, button, pos, repeat, e)) { return }
7234
+
7235
+ if (button == 1) {
7236
+ if (pos) { leftButtonDown(cm, pos, repeat, e) }
7237
+ else if (e_target(e) == display.scroller) { e_preventDefault(e) }
7238
+ } else if (button == 2) {
7239
+ if (pos) { extendSelection(cm.doc, pos) }
7240
+ setTimeout(function () { return display.input.focus(); }, 20)
7241
+ } else if (button == 3) {
7242
+ if (captureRightClick) { onContextMenu(cm, e) }
7243
+ else { delayBlurEvent(cm) }
7244
+ }
7245
+ }
7246
+
7247
+ function handleMappedButton(cm, button, pos, repeat, event) {
7248
+ var name = "Click"
7249
+ if (repeat == "double") { name = "Double" + name }
7250
+ else if (repeat == "triple") { name = "Triple" + name }
7251
+ name = (button == 1 ? "Left" : button == 2 ? "Middle" : "Right") + name
7252
+
7253
+ return dispatchKey(cm, addModifierNames(name, event), event, function (bound) {
7254
+ if (typeof bound == "string") { bound = commands[bound] }
7255
+ if (!bound) { return false }
7256
+ var done = false
7257
+ try {
7258
+ if (cm.isReadOnly()) { cm.state.suppressEdits = true }
7259
+ done = bound(cm, pos) != Pass
7260
+ } finally {
7261
+ cm.state.suppressEdits = false
7262
+ }
7263
+ return done
7264
+ })
7265
+ }
7266
+
7267
+ function configureMouse(cm, repeat, event) {
7268
+ var option = cm.getOption("configureMouse")
7269
+ var value = option ? option(cm, repeat, event) : {}
7270
+ if (value.unit == null) {
7271
+ var rect = chromeOS ? event.shiftKey && event.metaKey : event.altKey
7272
+ value.unit = rect ? "rectangle" : repeat == "single" ? "char" : repeat == "double" ? "word" : "line"
7273
+ }
7274
+ if (value.extend == null || cm.doc.extend) { value.extend = cm.doc.extend || event.shiftKey }
7275
+ if (value.addNew == null) { value.addNew = mac ? event.metaKey : event.ctrlKey }
7276
+ if (value.moveOnDrag == null) { value.moveOnDrag = !(mac ? event.altKey : event.ctrlKey) }
7277
+ return value
7278
+ }
7279
+
7280
+ function leftButtonDown(cm, pos, repeat, event) {
7281
+ if (ie) { setTimeout(bind(ensureFocus, cm), 0) }
7282
+ else { cm.curOp.focus = activeElt() }
7283
+
7284
+ var behavior = configureMouse(cm, repeat, event)
7285
+
7286
+ var sel = cm.doc.sel, contained
7287
+ if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() &&
7288
+ repeat == "single" && (contained = sel.contains(pos)) > -1 &&
7289
+ (cmp((contained = sel.ranges[contained]).from(), pos) < 0 || pos.xRel > 0) &&
7290
+ (cmp(contained.to(), pos) > 0 || pos.xRel < 0))
7291
+ { leftButtonStartDrag(cm, event, pos, behavior) }
7292
+ else
7293
+ { leftButtonSelect(cm, event, pos, behavior) }
7294
+ }
7295
+
7296
+ // Start a text drag. When it ends, see if any dragging actually
7297
+ // happen, and treat as a click if it didn't.
7298
+ function leftButtonStartDrag(cm, event, pos, behavior) {
7299
+ var display = cm.display, moved = false
7300
+ var dragEnd = operation(cm, function (e) {
7301
+ if (webkit) { display.scroller.draggable = false }
7302
+ cm.state.draggingText = false
7303
+ off(document, "mouseup", dragEnd)
7304
+ off(document, "mousemove", mouseMove)
7305
+ off(display.scroller, "dragstart", dragStart)
7306
+ off(display.scroller, "drop", dragEnd)
7307
+ if (!moved) {
7308
+ e_preventDefault(e)
7309
+ if (!behavior.addNew)
7310
+ { extendSelection(cm.doc, pos, null, null, behavior.extend) }
7311
+ // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)
7312
+ if (webkit || ie && ie_version == 9)
7313
+ { setTimeout(function () {document.body.focus(); display.input.focus()}, 20) }
7314
+ else
7315
+ { display.input.focus() }
7316
+ }
7317
+ })
7318
+ var mouseMove = function(e2) {
7319
+ moved = moved || Math.abs(event.clientX - e2.clientX) + Math.abs(event.clientY - e2.clientY) >= 10
7320
+ }
7321
+ var dragStart = function () { return moved = true; }
7322
+ // Let the drag handler handle this.
7323
+ if (webkit) { display.scroller.draggable = true }
7324
+ cm.state.draggingText = dragEnd
7325
+ dragEnd.copy = !behavior.moveOnDrag
7326
+ // IE's approach to draggable
7327
+ if (display.scroller.dragDrop) { display.scroller.dragDrop() }
7328
+ on(document, "mouseup", dragEnd)
7329
+ on(document, "mousemove", mouseMove)
7330
+ on(display.scroller, "dragstart", dragStart)
7331
+ on(display.scroller, "drop", dragEnd)
7332
+
7333
+ delayBlurEvent(cm)
7334
+ setTimeout(function () { return display.input.focus(); }, 20)
7335
+ }
7336
+
7337
+ function rangeForUnit(cm, pos, unit) {
7338
+ if (unit == "char") { return new Range(pos, pos) }
7339
+ if (unit == "word") { return cm.findWordAt(pos) }
7340
+ if (unit == "line") { return new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) }
7341
+ var result = unit(cm, pos)
7342
+ return new Range(result.from, result.to)
7343
+ }
7344
+
7345
+ // Normal selection, as opposed to text dragging.
7346
+ function leftButtonSelect(cm, event, start, behavior) {
7347
+ var display = cm.display, doc = cm.doc
7348
+ e_preventDefault(event)
7349
+
7350
+ var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges
7351
+ if (behavior.addNew && !behavior.extend) {
7352
+ ourIndex = doc.sel.contains(start)
7353
+ if (ourIndex > -1)
7354
+ { ourRange = ranges[ourIndex] }
7355
+ else
7356
+ { ourRange = new Range(start, start) }
7357
+ } else {
7358
+ ourRange = doc.sel.primary()
7359
+ ourIndex = doc.sel.primIndex
7360
+ }
7361
+
7362
+ if (behavior.unit == "rectangle") {
7363
+ if (!behavior.addNew) { ourRange = new Range(start, start) }
7364
+ start = posFromMouse(cm, event, true, true)
7365
+ ourIndex = -1
7366
+ } else {
7367
+ var range = rangeForUnit(cm, start, behavior.unit)
7368
+ if (behavior.extend)
7369
+ { ourRange = extendRange(ourRange, range.anchor, range.head, behavior.extend) }
7370
+ else
7371
+ { ourRange = range }
7372
+ }
7373
+
7374
+ if (!behavior.addNew) {
7375
+ ourIndex = 0
7376
+ setSelection(doc, new Selection([ourRange], 0), sel_mouse)
7377
+ startSel = doc.sel
7378
+ } else if (ourIndex == -1) {
7379
+ ourIndex = ranges.length
7380
+ setSelection(doc, normalizeSelection(ranges.concat([ourRange]), ourIndex),
7381
+ {scroll: false, origin: "*mouse"})
7382
+ } else if (ranges.length > 1 && ranges[ourIndex].empty() && behavior.unit == "char" && !behavior.extend) {
7383
+ setSelection(doc, normalizeSelection(ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0),
7384
+ {scroll: false, origin: "*mouse"})
7385
+ startSel = doc.sel
7386
+ } else {
7387
+ replaceOneSelection(doc, ourIndex, ourRange, sel_mouse)
7388
+ }
7389
+
7390
+ var lastPos = start
7391
+ function extendTo(pos) {
7392
+ if (cmp(lastPos, pos) == 0) { return }
7393
+ lastPos = pos
7394
+
7395
+ if (behavior.unit == "rectangle") {
7396
+ var ranges = [], tabSize = cm.options.tabSize
7397
+ var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize)
7398
+ var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize)
7399
+ var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol)
7400
+ for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line));
7401
+ line <= end; line++) {
7402
+ var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize)
7403
+ if (left == right)
7404
+ { ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))) }
7405
+ else if (text.length > leftPos)
7406
+ { ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))) }
7407
+ }
7408
+ if (!ranges.length) { ranges.push(new Range(start, start)) }
7409
+ setSelection(doc, normalizeSelection(startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex),
7410
+ {origin: "*mouse", scroll: false})
7411
+ cm.scrollIntoView(pos)
7412
+ } else {
7413
+ var oldRange = ourRange
7414
+ var range = rangeForUnit(cm, pos, behavior.unit)
7415
+ var anchor = oldRange.anchor, head
7416
+ if (cmp(range.anchor, anchor) > 0) {
7417
+ head = range.head
7418
+ anchor = minPos(oldRange.from(), range.anchor)
7419
+ } else {
7420
+ head = range.anchor
7421
+ anchor = maxPos(oldRange.to(), range.head)
7422
+ }
7423
+ var ranges$1 = startSel.ranges.slice(0)
7424
+ ranges$1[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc, anchor), head))
7425
+ setSelection(doc, normalizeSelection(ranges$1, ourIndex), sel_mouse)
7426
+ }
7427
+ }
7428
+
7429
+ var editorSize = display.wrapper.getBoundingClientRect()
7430
+ // Used to ensure timeout re-tries don't fire when another extend
7431
+ // happened in the meantime (clearTimeout isn't reliable -- at
7432
+ // least on Chrome, the timeouts still happen even when cleared,
7433
+ // if the clear happens after their scheduled firing time).
7434
+ var counter = 0
7435
+
7436
+ function extend(e) {
7437
+ var curCount = ++counter
7438
+ var cur = posFromMouse(cm, e, true, behavior.unit == "rectangle")
7439
+ if (!cur) { return }
7440
+ if (cmp(cur, lastPos) != 0) {
7441
+ cm.curOp.focus = activeElt()
7442
+ extendTo(cur)
7443
+ var visible = visibleLines(display, doc)
7444
+ if (cur.line >= visible.to || cur.line < visible.from)
7445
+ { setTimeout(operation(cm, function () {if (counter == curCount) { extend(e) }}), 150) }
7446
+ } else {
7447
+ var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0
7448
+ if (outside) { setTimeout(operation(cm, function () {
7449
+ if (counter != curCount) { return }
7450
+ display.scroller.scrollTop += outside
7451
+ extend(e)
7452
+ }), 50) }
7453
+ }
7454
+ }
7455
+
7456
+ function done(e) {
7457
+ cm.state.selectingText = false
7458
+ counter = Infinity
7459
+ e_preventDefault(e)
7460
+ display.input.focus()
7461
+ off(document, "mousemove", move)
7462
+ off(document, "mouseup", up)
7463
+ doc.history.lastSelOrigin = null
7464
+ }
7465
+
7466
+ var move = operation(cm, function (e) {
7467
+ if (!e_button(e)) { done(e) }
7468
+ else { extend(e) }
7469
+ })
7470
+ var up = operation(cm, done)
7471
+ cm.state.selectingText = up
7472
+ on(document, "mousemove", move)
7473
+ on(document, "mouseup", up)
7474
+ }
7475
+
7476
+ // Used when mouse-selecting to adjust the anchor to the proper side
7477
+ // of a bidi jump depending on the visual position of the head.
7478
+ function bidiSimplify(cm, range) {
7479
+ var anchor = range.anchor;
7480
+ var head = range.head;
7481
+ var anchorLine = getLine(cm.doc, anchor.line)
7482
+ if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) { return range }
7483
+ var order = getOrder(anchorLine)
7484
+ if (!order) { return range }
7485
+ var index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index]
7486
+ if (part.from != anchor.ch && part.to != anchor.ch) { return range }
7487
+ var boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1)
7488
+ if (boundary == 0 || boundary == order.length) { return range }
7489
+
7490
+ // Compute the relative visual position of the head compared to the
7491
+ // anchor (<0 is to the left, >0 to the right)
7492
+ var leftSide
7493
+ if (head.line != anchor.line) {
7494
+ leftSide = (head.line - anchor.line) * (cm.doc.direction == "ltr" ? 1 : -1) > 0
7495
+ } else {
7496
+ var headIndex = getBidiPartAt(order, head.ch, head.sticky)
7497
+ var dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1)
7498
+ if (headIndex == boundary - 1 || headIndex == boundary)
7499
+ { leftSide = dir < 0 }
7500
+ else
7501
+ { leftSide = dir > 0 }
7502
+ }
7503
+
7504
+ var usePart = order[boundary + (leftSide ? -1 : 0)]
7505
+ var from = leftSide == (usePart.level == 1)
7506
+ var ch = from ? usePart.from : usePart.to, sticky = from ? "after" : "before"
7507
+ return anchor.ch == ch && anchor.sticky == sticky ? range : new Range(new Pos(anchor.line, ch, sticky), head)
7508
+ }
7509
+
7510
+
7511
+ // Determines whether an event happened in the gutter, and fires the
7512
+ // handlers for the corresponding event.
7513
+ function gutterEvent(cm, e, type, prevent) {
7514
+ var mX, mY
7515
+ if (e.touches) {
7516
+ mX = e.touches[0].clientX
7517
+ mY = e.touches[0].clientY
7518
+ } else {
7519
+ try { mX = e.clientX; mY = e.clientY }
7520
+ catch(e) { return false }
7521
+ }
7522
+ if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false }
7523
+ if (prevent) { e_preventDefault(e) }
7524
+
7525
+ var display = cm.display
7526
+ var lineBox = display.lineDiv.getBoundingClientRect()
7527
+
7528
+ if (mY > lineBox.bottom || !hasHandler(cm, type)) { return e_defaultPrevented(e) }
7529
+ mY -= lineBox.top - display.viewOffset
7530
+
7531
+ for (var i = 0; i < cm.options.gutters.length; ++i) {
7532
+ var g = display.gutters.childNodes[i]
7533
+ if (g && g.getBoundingClientRect().right >= mX) {
7534
+ var line = lineAtHeight(cm.doc, mY)
7535
+ var gutter = cm.options.gutters[i]
7536
+ signal(cm, type, cm, line, gutter, e)
7537
+ return e_defaultPrevented(e)
7538
+ }
7539
+ }
7540
+ }
7541
+
7542
+ function clickInGutter(cm, e) {
7543
+ return gutterEvent(cm, e, "gutterClick", true)
7544
+ }
7545
+
7546
+ // CONTEXT MENU HANDLING
7547
+
7548
+ // To make the context menu work, we need to briefly unhide the
7549
+ // textarea (making it as unobtrusive as possible) to let the
7550
+ // right-click take effect on it.
7551
+ function onContextMenu(cm, e) {
7552
+ if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) { return }
7553
+ if (signalDOMEvent(cm, e, "contextmenu")) { return }
7554
+ cm.display.input.onContextMenu(e)
7555
+ }
7556
+
7557
+ function contextMenuInGutter(cm, e) {
7558
+ if (!hasHandler(cm, "gutterContextMenu")) { return false }
7559
+ return gutterEvent(cm, e, "gutterContextMenu", false)
7560
+ }
7561
+
7562
+ function themeChanged(cm) {
7563
+ cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
7564
+ cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-")
7565
+ clearCaches(cm)
7566
+ }
7567
+
7568
+ var Init = {toString: function(){return "CodeMirror.Init"}}
7569
+
7570
+ var defaults = {}
7571
+ var optionHandlers = {}
7572
+
7573
+ function defineOptions(CodeMirror) {
7574
+ var optionHandlers = CodeMirror.optionHandlers
7575
+
7576
+ function option(name, deflt, handle, notOnInit) {
7577
+ CodeMirror.defaults[name] = deflt
7578
+ if (handle) { optionHandlers[name] =
7579
+ notOnInit ? function (cm, val, old) {if (old != Init) { handle(cm, val, old) }} : handle }
7580
+ }
7581
+
7582
+ CodeMirror.defineOption = option
7583
+
7584
+ // Passed to option handlers when there is no old value.
7585
+ CodeMirror.Init = Init
7586
+
7587
+ // These two are, on init, called from the constructor because they
7588
+ // have to be initialized before the editor can start at all.
7589
+ option("value", "", function (cm, val) { return cm.setValue(val); }, true)
7590
+ option("mode", null, function (cm, val) {
7591
+ cm.doc.modeOption = val
7592
+ loadMode(cm)
7593
+ }, true)
7594
+
7595
+ option("indentUnit", 2, loadMode, true)
7596
+ option("indentWithTabs", false)
7597
+ option("smartIndent", true)
7598
+ option("tabSize", 4, function (cm) {
7599
+ resetModeState(cm)
7600
+ clearCaches(cm)
7601
+ regChange(cm)
7602
+ }, true)
7603
+ option("lineSeparator", null, function (cm, val) {
7604
+ cm.doc.lineSep = val
7605
+ if (!val) { return }
7606
+ var newBreaks = [], lineNo = cm.doc.first
7607
+ cm.doc.iter(function (line) {
7608
+ for (var pos = 0;;) {
7609
+ var found = line.text.indexOf(val, pos)
7610
+ if (found == -1) { break }
7611
+ pos = found + val.length
7612
+ newBreaks.push(Pos(lineNo, found))
7613
+ }
7614
+ lineNo++
7615
+ })
7616
+ for (var i = newBreaks.length - 1; i >= 0; i--)
7617
+ { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)) }
7618
+ })
7619
+ option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff]/g, function (cm, val, old) {
7620
+ cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g")
7621
+ if (old != Init) { cm.refresh() }
7622
+ })
7623
+ option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function (cm) { return cm.refresh(); }, true)
7624
+ option("electricChars", true)
7625
+ option("inputStyle", mobile ? "contenteditable" : "textarea", function () {
7626
+ throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME
7627
+ }, true)
7628
+ option("spellcheck", false, function (cm, val) { return cm.getInputField().spellcheck = val; }, true)
7629
+ option("rtlMoveVisually", !windows)
7630
+ option("wholeLineUpdateBefore", true)
7631
+
7632
+ option("theme", "default", function (cm) {
7633
+ themeChanged(cm)
7634
+ guttersChanged(cm)
7635
+ }, true)
7636
+ option("keyMap", "default", function (cm, val, old) {
7637
+ var next = getKeyMap(val)
7638
+ var prev = old != Init && getKeyMap(old)
7639
+ if (prev && prev.detach) { prev.detach(cm, next) }
7640
+ if (next.attach) { next.attach(cm, prev || null) }
7641
+ })
7642
+ option("extraKeys", null)
7643
+ option("configureMouse", null)
7644
+
7645
+ option("lineWrapping", false, wrappingChanged, true)
7646
+ option("gutters", [], function (cm) {
7647
+ setGuttersForLineNumbers(cm.options)
7648
+ guttersChanged(cm)
7649
+ }, true)
7650
+ option("fixedGutter", true, function (cm, val) {
7651
+ cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0"
7652
+ cm.refresh()
7653
+ }, true)
7654
+ option("coverGutterNextToScrollbar", false, function (cm) { return updateScrollbars(cm); }, true)
7655
+ option("scrollbarStyle", "native", function (cm) {
7656
+ initScrollbars(cm)
7657
+ updateScrollbars(cm)
7658
+ cm.display.scrollbars.setScrollTop(cm.doc.scrollTop)
7659
+ cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft)
7660
+ }, true)
7661
+ option("lineNumbers", false, function (cm) {
7662
+ setGuttersForLineNumbers(cm.options)
7663
+ guttersChanged(cm)
7664
+ }, true)
7665
+ option("firstLineNumber", 1, guttersChanged, true)
7666
+ option("lineNumberFormatter", function (integer) { return integer; }, guttersChanged, true)
7667
+ option("showCursorWhenSelecting", false, updateSelection, true)
7668
+
7669
+ option("resetSelectionOnContextMenu", true)
7670
+ option("lineWiseCopyCut", true)
7671
+ option("pasteLinesPerSelection", true)
7672
+
7673
+ option("readOnly", false, function (cm, val) {
7674
+ if (val == "nocursor") {
7675
+ onBlur(cm)
7676
+ cm.display.input.blur()
7677
+ }
7678
+ cm.display.input.readOnlyChanged(val)
7679
+ })
7680
+ option("disableInput", false, function (cm, val) {if (!val) { cm.display.input.reset() }}, true)
7681
+ option("dragDrop", true, dragDropChanged)
7682
+ option("allowDropFileTypes", null)
7683
+
7684
+ option("cursorBlinkRate", 530)
7685
+ option("cursorScrollMargin", 0)
7686
+ option("cursorHeight", 1, updateSelection, true)
7687
+ option("singleCursorHeightPerLine", true, updateSelection, true)
7688
+ option("workTime", 100)
7689
+ option("workDelay", 100)
7690
+ option("flattenSpans", true, resetModeState, true)
7691
+ option("addModeClass", false, resetModeState, true)
7692
+ option("pollInterval", 100)
7693
+ option("undoDepth", 200, function (cm, val) { return cm.doc.history.undoDepth = val; })
7694
+ option("historyEventDelay", 1250)
7695
+ option("viewportMargin", 10, function (cm) { return cm.refresh(); }, true)
7696
+ option("maxHighlightLength", 10000, resetModeState, true)
7697
+ option("moveInputWithCursor", true, function (cm, val) {
7698
+ if (!val) { cm.display.input.resetPosition() }
7699
+ })
7700
+
7701
+ option("tabindex", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || ""; })
7702
+ option("autofocus", null)
7703
+ option("direction", "ltr", function (cm, val) { return cm.doc.setDirection(val); }, true)
7704
+ }
7705
+
7706
+ function guttersChanged(cm) {
7707
+ updateGutters(cm)
7708
+ regChange(cm)
7709
+ alignHorizontally(cm)
7710
+ }
7711
+
7712
+ function dragDropChanged(cm, value, old) {
7713
+ var wasOn = old && old != Init
7714
+ if (!value != !wasOn) {
7715
+ var funcs = cm.display.dragFunctions
7716
+ var toggle = value ? on : off
7717
+ toggle(cm.display.scroller, "dragstart", funcs.start)
7718
+ toggle(cm.display.scroller, "dragenter", funcs.enter)
7719
+ toggle(cm.display.scroller, "dragover", funcs.over)
7720
+ toggle(cm.display.scroller, "dragleave", funcs.leave)
7721
+ toggle(cm.display.scroller, "drop", funcs.drop)
7722
+ }
7723
+ }
7724
+
7725
+ function wrappingChanged(cm) {
7726
+ if (cm.options.lineWrapping) {
7727
+ addClass(cm.display.wrapper, "CodeMirror-wrap")
7728
+ cm.display.sizer.style.minWidth = ""
7729
+ cm.display.sizerWidth = null
7730
+ } else {
7731
+ rmClass(cm.display.wrapper, "CodeMirror-wrap")
7732
+ findMaxLine(cm)
7733
+ }
7734
+ estimateLineHeights(cm)
7735
+ regChange(cm)
7736
+ clearCaches(cm)
7737
+ setTimeout(function () { return updateScrollbars(cm); }, 100)
7738
+ }
7739
+
7740
+ // A CodeMirror instance represents an editor. This is the object
7741
+ // that user code is usually dealing with.
7742
+
7743
+ function CodeMirror(place, options) {
7744
+ var this$1 = this;
7745
+
7746
+ if (!(this instanceof CodeMirror)) { return new CodeMirror(place, options) }
7747
+
7748
+ this.options = options = options ? copyObj(options) : {}
7749
+ // Determine effective options based on given values and defaults.
7750
+ copyObj(defaults, options, false)
7751
+ setGuttersForLineNumbers(options)
7752
+
7753
+ var doc = options.value
7754
+ if (typeof doc == "string") { doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction) }
7755
+ this.doc = doc
7756
+
7757
+ var input = new CodeMirror.inputStyles[options.inputStyle](this)
7758
+ var display = this.display = new Display(place, doc, input)
7759
+ display.wrapper.CodeMirror = this
7760
+ updateGutters(this)
7761
+ themeChanged(this)
7762
+ if (options.lineWrapping)
7763
+ { this.display.wrapper.className += " CodeMirror-wrap" }
7764
+ initScrollbars(this)
7765
+
7766
+ this.state = {
7767
+ keyMaps: [], // stores maps added by addKeyMap
7768
+ overlays: [], // highlighting overlays, as added by addOverlay
7769
+ modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info
7770
+ overwrite: false,
7771
+ delayingBlurEvent: false,
7772
+ focused: false,
7773
+ suppressEdits: false, // used to disable editing during key handlers when in readOnly mode
7774
+ pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edits in input.poll
7775
+ selectingText: false,
7776
+ draggingText: false,
7777
+ highlight: new Delayed(), // stores highlight worker timeout
7778
+ keySeq: null, // Unfinished key sequence
7779
+ specialChars: null
7780
+ }
7781
+
7782
+ if (options.autofocus && !mobile) { display.input.focus() }
7783
+
7784
+ // Override magic textarea content restore that IE sometimes does
7785
+ // on our hidden textarea on reload
7786
+ if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20) }
7787
+
7788
+ registerEventHandlers(this)
7789
+ ensureGlobalHandlers()
7790
+
7791
+ startOperation(this)
7792
+ this.curOp.forceUpdate = true
7793
+ attachDoc(this, doc)
7794
+
7795
+ if ((options.autofocus && !mobile) || this.hasFocus())
7796
+ { setTimeout(bind(onFocus, this), 20) }
7797
+ else
7798
+ { onBlur(this) }
7799
+
7800
+ for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt))
7801
+ { optionHandlers[opt](this$1, options[opt], Init) } }
7802
+ maybeUpdateLineNumberWidth(this)
7803
+ if (options.finishInit) { options.finishInit(this) }
7804
+ for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this$1) }
7805
+ endOperation(this)
7806
+ // Suppress optimizelegibility in Webkit, since it breaks text
7807
+ // measuring on line wrapping boundaries.
7808
+ if (webkit && options.lineWrapping &&
7809
+ getComputedStyle(display.lineDiv).textRendering == "optimizelegibility")
7810
+ { display.lineDiv.style.textRendering = "auto" }
7811
+ }
7812
+
7813
+ // The default configuration options.
7814
+ CodeMirror.defaults = defaults
7815
+ // Functions to run when options are changed.
7816
+ CodeMirror.optionHandlers = optionHandlers
7817
+
7818
+ // Attach the necessary event handlers when initializing the editor
7819
+ function registerEventHandlers(cm) {
7820
+ var d = cm.display
7821
+ on(d.scroller, "mousedown", operation(cm, onMouseDown))
7822
+ // Older IE's will not fire a second mousedown for a double click
7823
+ if (ie && ie_version < 11)
7824
+ { on(d.scroller, "dblclick", operation(cm, function (e) {
7825
+ if (signalDOMEvent(cm, e)) { return }
7826
+ var pos = posFromMouse(cm, e)
7827
+ if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) { return }
7828
+ e_preventDefault(e)
7829
+ var word = cm.findWordAt(pos)
7830
+ extendSelection(cm.doc, word.anchor, word.head)
7831
+ })) }
7832
+ else
7833
+ { on(d.scroller, "dblclick", function (e) { return signalDOMEvent(cm, e) || e_preventDefault(e); }) }
7834
+ // Some browsers fire contextmenu *after* opening the menu, at
7835
+ // which point we can't mess with it anymore. Context menu is
7836
+ // handled in onMouseDown for these browsers.
7837
+ if (!captureRightClick) { on(d.scroller, "contextmenu", function (e) { return onContextMenu(cm, e); }) }
7838
+
7839
+ // Used to suppress mouse event handling when a touch happens
7840
+ var touchFinished, prevTouch = {end: 0}
7841
+ function finishTouch() {
7842
+ if (d.activeTouch) {
7843
+ touchFinished = setTimeout(function () { return d.activeTouch = null; }, 1000)
7844
+ prevTouch = d.activeTouch
7845
+ prevTouch.end = +new Date
7846
+ }
7847
+ }
7848
+ function isMouseLikeTouchEvent(e) {
7849
+ if (e.touches.length != 1) { return false }
7850
+ var touch = e.touches[0]
7851
+ return touch.radiusX <= 1 && touch.radiusY <= 1
7852
+ }
7853
+ function farAway(touch, other) {
7854
+ if (other.left == null) { return true }
7855
+ var dx = other.left - touch.left, dy = other.top - touch.top
7856
+ return dx * dx + dy * dy > 20 * 20
7857
+ }
7858
+ on(d.scroller, "touchstart", function (e) {
7859
+ if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e) && !clickInGutter(cm, e)) {
7860
+ d.input.ensurePolled()
7861
+ clearTimeout(touchFinished)
7862
+ var now = +new Date
7863
+ d.activeTouch = {start: now, moved: false,
7864
+ prev: now - prevTouch.end <= 300 ? prevTouch : null}
7865
+ if (e.touches.length == 1) {
7866
+ d.activeTouch.left = e.touches[0].pageX
7867
+ d.activeTouch.top = e.touches[0].pageY
7868
+ }
7869
+ }
7870
+ })
7871
+ on(d.scroller, "touchmove", function () {
7872
+ if (d.activeTouch) { d.activeTouch.moved = true }
7873
+ })
7874
+ on(d.scroller, "touchend", function (e) {
7875
+ var touch = d.activeTouch
7876
+ if (touch && !eventInWidget(d, e) && touch.left != null &&
7877
+ !touch.moved && new Date - touch.start < 300) {
7878
+ var pos = cm.coordsChar(d.activeTouch, "page"), range
7879
+ if (!touch.prev || farAway(touch, touch.prev)) // Single tap
7880
+ { range = new Range(pos, pos) }
7881
+ else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap
7882
+ { range = cm.findWordAt(pos) }
7883
+ else // Triple tap
7884
+ { range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) }
7885
+ cm.setSelection(range.anchor, range.head)
7886
+ cm.focus()
7887
+ e_preventDefault(e)
7888
+ }
7889
+ finishTouch()
7890
+ })
7891
+ on(d.scroller, "touchcancel", finishTouch)
7892
+
7893
+ // Sync scrolling between fake scrollbars and real scrollable
7894
+ // area, ensure viewport is updated when scrolling.
7895
+ on(d.scroller, "scroll", function () {
7896
+ if (d.scroller.clientHeight) {
7897
+ updateScrollTop(cm, d.scroller.scrollTop)
7898
+ setScrollLeft(cm, d.scroller.scrollLeft, true)
7899
+ signal(cm, "scroll", cm)
7900
+ }
7901
+ })
7902
+
7903
+ // Listen to wheel events in order to try and update the viewport on time.
7904
+ on(d.scroller, "mousewheel", function (e) { return onScrollWheel(cm, e); })
7905
+ on(d.scroller, "DOMMouseScroll", function (e) { return onScrollWheel(cm, e); })
7906
+
7907
+ // Prevent wrapper from ever scrolling
7908
+ on(d.wrapper, "scroll", function () { return d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; })
7909
+
7910
+ d.dragFunctions = {
7911
+ enter: function (e) {if (!signalDOMEvent(cm, e)) { e_stop(e) }},
7912
+ over: function (e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e) }},
7913
+ start: function (e) { return onDragStart(cm, e); },
7914
+ drop: operation(cm, onDrop),
7915
+ leave: function (e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm) }}
7916
+ }
7917
+
7918
+ var inp = d.input.getField()
7919
+ on(inp, "keyup", function (e) { return onKeyUp.call(cm, e); })
7920
+ on(inp, "keydown", operation(cm, onKeyDown))
7921
+ on(inp, "keypress", operation(cm, onKeyPress))
7922
+ on(inp, "focus", function (e) { return onFocus(cm, e); })
7923
+ on(inp, "blur", function (e) { return onBlur(cm, e); })
7924
+ }
7925
+
7926
+ var initHooks = []
7927
+ CodeMirror.defineInitHook = function (f) { return initHooks.push(f); }
7928
+
7929
+ // Indent the given line. The how parameter can be "smart",
7930
+ // "add"/null, "subtract", or "prev". When aggressive is false
7931
+ // (typically set to true for forced single-line indents), empty
7932
+ // lines are not indented, and places where the mode returns Pass
7933
+ // are left alone.
7934
+ function indentLine(cm, n, how, aggressive) {
7935
+ var doc = cm.doc, state
7936
+ if (how == null) { how = "add" }
7937
+ if (how == "smart") {
7938
+ // Fall back to "prev" when the mode doesn't have an indentation
7939
+ // method.
7940
+ if (!doc.mode.indent) { how = "prev" }
7941
+ else { state = getContextBefore(cm, n).state }
7942
+ }
7943
+
7944
+ var tabSize = cm.options.tabSize
7945
+ var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize)
7946
+ if (line.stateAfter) { line.stateAfter = null }
7947
+ var curSpaceString = line.text.match(/^\s*/)[0], indentation
7948
+ if (!aggressive && !/\S/.test(line.text)) {
7949
+ indentation = 0
7950
+ how = "not"
7951
+ } else if (how == "smart") {
7952
+ indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text)
7953
+ if (indentation == Pass || indentation > 150) {
7954
+ if (!aggressive) { return }
7955
+ how = "prev"
7956
+ }
7957
+ }
7958
+ if (how == "prev") {
7959
+ if (n > doc.first) { indentation = countColumn(getLine(doc, n-1).text, null, tabSize) }
7960
+ else { indentation = 0 }
7961
+ } else if (how == "add") {
7962
+ indentation = curSpace + cm.options.indentUnit
7963
+ } else if (how == "subtract") {
7964
+ indentation = curSpace - cm.options.indentUnit
7965
+ } else if (typeof how == "number") {
7966
+ indentation = curSpace + how
7967
+ }
7968
+ indentation = Math.max(0, indentation)
7969
+
7970
+ var indentString = "", pos = 0
7971
+ if (cm.options.indentWithTabs)
7972
+ { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t"} }
7973
+ if (pos < indentation) { indentString += spaceStr(indentation - pos) }
7974
+
7975
+ if (indentString != curSpaceString) {
7976
+ replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input")
7977
+ line.stateAfter = null
7978
+ return true
7979
+ } else {
7980
+ // Ensure that, if the cursor was in the whitespace at the start
7981
+ // of the line, it is moved to the end of that space.
7982
+ for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) {
7983
+ var range = doc.sel.ranges[i$1]
7984
+ if (range.head.line == n && range.head.ch < curSpaceString.length) {
7985
+ var pos$1 = Pos(n, curSpaceString.length)
7986
+ replaceOneSelection(doc, i$1, new Range(pos$1, pos$1))
7987
+ break
7988
+ }
7989
+ }
7990
+ }
7991
+ }
7992
+
7993
+ // This will be set to a {lineWise: bool, text: [string]} object, so
7994
+ // that, when pasting, we know what kind of selections the copied
7995
+ // text was made out of.
7996
+ var lastCopied = null
7997
+
7998
+ function setLastCopied(newLastCopied) {
7999
+ lastCopied = newLastCopied
8000
+ }
8001
+
8002
+ function applyTextInput(cm, inserted, deleted, sel, origin) {
8003
+ var doc = cm.doc
8004
+ cm.display.shift = false
8005
+ if (!sel) { sel = doc.sel }
8006
+
8007
+ var paste = cm.state.pasteIncoming || origin == "paste"
8008
+ var textLines = splitLinesAuto(inserted), multiPaste = null
8009
+ // When pasing N lines into N selections, insert one line per selection
8010
+ if (paste && sel.ranges.length > 1) {
8011
+ if (lastCopied && lastCopied.text.join("\n") == inserted) {
8012
+ if (sel.ranges.length % lastCopied.text.length == 0) {
8013
+ multiPaste = []
8014
+ for (var i = 0; i < lastCopied.text.length; i++)
8015
+ { multiPaste.push(doc.splitLines(lastCopied.text[i])) }
8016
+ }
8017
+ } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) {
8018
+ multiPaste = map(textLines, function (l) { return [l]; })
8019
+ }
8020
+ }
8021
+
8022
+ var updateInput
8023
+ // Normal behavior is to insert the new text into every selection
8024
+ for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) {
8025
+ var range = sel.ranges[i$1]
8026
+ var from = range.from(), to = range.to()
8027
+ if (range.empty()) {
8028
+ if (deleted && deleted > 0) // Handle deletion
8029
+ { from = Pos(from.line, from.ch - deleted) }
8030
+ else if (cm.state.overwrite && !paste) // Handle overwrite
8031
+ { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)) }
8032
+ else if (lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == inserted)
8033
+ { from = to = Pos(from.line, 0) }
8034
+ }
8035
+ updateInput = cm.curOp.updateInput
8036
+ var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines,
8037
+ origin: origin || (paste ? "paste" : cm.state.cutIncoming ? "cut" : "+input")}
8038
+ makeChange(cm.doc, changeEvent)
8039
+ signalLater(cm, "inputRead", cm, changeEvent)
8040
+ }
8041
+ if (inserted && !paste)
8042
+ { triggerElectric(cm, inserted) }
8043
+
8044
+ ensureCursorVisible(cm)
8045
+ cm.curOp.updateInput = updateInput
8046
+ cm.curOp.typing = true
8047
+ cm.state.pasteIncoming = cm.state.cutIncoming = false
8048
+ }
8049
+
8050
+ function handlePaste(e, cm) {
8051
+ var pasted = e.clipboardData && e.clipboardData.getData("Text")
8052
+ if (pasted) {
8053
+ e.preventDefault()
8054
+ if (!cm.isReadOnly() && !cm.options.disableInput)
8055
+ { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, "paste"); }) }
8056
+ return true
8057
+ }
8058
+ }
8059
+
8060
+ function triggerElectric(cm, inserted) {
8061
+ // When an 'electric' character is inserted, immediately trigger a reindent
8062
+ if (!cm.options.electricChars || !cm.options.smartIndent) { return }
8063
+ var sel = cm.doc.sel
8064
+
8065
+ for (var i = sel.ranges.length - 1; i >= 0; i--) {
8066
+ var range = sel.ranges[i]
8067
+ if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head.line)) { continue }
8068
+ var mode = cm.getModeAt(range.head)
8069
+ var indented = false
8070
+ if (mode.electricChars) {
8071
+ for (var j = 0; j < mode.electricChars.length; j++)
8072
+ { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
8073
+ indented = indentLine(cm, range.head.line, "smart")
8074
+ break
8075
+ } }
8076
+ } else if (mode.electricInput) {
8077
+ if (mode.electricInput.test(getLine(cm.doc, range.head.line).text.slice(0, range.head.ch)))
8078
+ { indented = indentLine(cm, range.head.line, "smart") }
8079
+ }
8080
+ if (indented) { signalLater(cm, "electricInput", cm, range.head.line) }
8081
+ }
8082
+ }
8083
+
8084
+ function copyableRanges(cm) {
8085
+ var text = [], ranges = []
8086
+ for (var i = 0; i < cm.doc.sel.ranges.length; i++) {
8087
+ var line = cm.doc.sel.ranges[i].head.line
8088
+ var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)}
8089
+ ranges.push(lineRange)
8090
+ text.push(cm.getRange(lineRange.anchor, lineRange.head))
8091
+ }
8092
+ return {text: text, ranges: ranges}
8093
+ }
8094
+
8095
+ function disableBrowserMagic(field, spellcheck) {
8096
+ field.setAttribute("autocorrect", "off")
8097
+ field.setAttribute("autocapitalize", "off")
8098
+ field.setAttribute("spellcheck", !!spellcheck)
8099
+ }
8100
+
8101
+ function hiddenTextarea() {
8102
+ var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none")
8103
+ var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;")
8104
+ // The textarea is kept positioned near the cursor to prevent the
8105
+ // fact that it'll be scrolled into view on input from scrolling
8106
+ // our fake cursor out of view. On webkit, when wrap=off, paste is
8107
+ // very slow. So make the area wide instead.
8108
+ if (webkit) { te.style.width = "1000px" }
8109
+ else { te.setAttribute("wrap", "off") }
8110
+ // If border: 0; -- iOS fails to open keyboard (issue #1287)
8111
+ if (ios) { te.style.border = "1px solid black" }
8112
+ disableBrowserMagic(te)
8113
+ return div
8114
+ }
8115
+
8116
+ // The publicly visible API. Note that methodOp(f) means
8117
+ // 'wrap f in an operation, performed on its `this` parameter'.
8118
+
8119
+ // This is not the complete set of editor methods. Most of the
8120
+ // methods defined on the Doc type are also injected into
8121
+ // CodeMirror.prototype, for backwards compatibility and
8122
+ // convenience.
8123
+
8124
+ function addEditorMethods(CodeMirror) {
8125
+ var optionHandlers = CodeMirror.optionHandlers
8126
+
8127
+ var helpers = CodeMirror.helpers = {}
8128
+
8129
+ CodeMirror.prototype = {
8130
+ constructor: CodeMirror,
8131
+ focus: function(){window.focus(); this.display.input.focus()},
8132
+
8133
+ setOption: function(option, value) {
8134
+ var options = this.options, old = options[option]
8135
+ if (options[option] == value && option != "mode") { return }
8136
+ options[option] = value
8137
+ if (optionHandlers.hasOwnProperty(option))
8138
+ { operation(this, optionHandlers[option])(this, value, old) }
8139
+ signal(this, "optionChange", this, option)
8140
+ },
8141
+
8142
+ getOption: function(option) {return this.options[option]},
8143
+ getDoc: function() {return this.doc},
8144
+
8145
+ addKeyMap: function(map, bottom) {
8146
+ this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map))
8147
+ },
8148
+ removeKeyMap: function(map) {
8149
+ var maps = this.state.keyMaps
8150
+ for (var i = 0; i < maps.length; ++i)
8151
+ { if (maps[i] == map || maps[i].name == map) {
8152
+ maps.splice(i, 1)
8153
+ return true
8154
+ } }
8155
+ },
8156
+
8157
+ addOverlay: methodOp(function(spec, options) {
8158
+ var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec)
8159
+ if (mode.startState) { throw new Error("Overlays may not be stateful.") }
8160
+ insertSorted(this.state.overlays,
8161
+ {mode: mode, modeSpec: spec, opaque: options && options.opaque,
8162
+ priority: (options && options.priority) || 0},
8163
+ function (overlay) { return overlay.priority; })
8164
+ this.state.modeGen++
8165
+ regChange(this)
8166
+ }),
8167
+ removeOverlay: methodOp(function(spec) {
8168
+ var this$1 = this;
8169
+
8170
+ var overlays = this.state.overlays
8171
+ for (var i = 0; i < overlays.length; ++i) {
8172
+ var cur = overlays[i].modeSpec
8173
+ if (cur == spec || typeof spec == "string" && cur.name == spec) {
8174
+ overlays.splice(i, 1)
8175
+ this$1.state.modeGen++
8176
+ regChange(this$1)
8177
+ return
8178
+ }
8179
+ }
8180
+ }),
8181
+
8182
+ indentLine: methodOp(function(n, dir, aggressive) {
8183
+ if (typeof dir != "string" && typeof dir != "number") {
8184
+ if (dir == null) { dir = this.options.smartIndent ? "smart" : "prev" }
8185
+ else { dir = dir ? "add" : "subtract" }
8186
+ }
8187
+ if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive) }
8188
+ }),
8189
+ indentSelection: methodOp(function(how) {
8190
+ var this$1 = this;
8191
+
8192
+ var ranges = this.doc.sel.ranges, end = -1
8193
+ for (var i = 0; i < ranges.length; i++) {
8194
+ var range = ranges[i]
8195
+ if (!range.empty()) {
8196
+ var from = range.from(), to = range.to()
8197
+ var start = Math.max(end, from.line)
8198
+ end = Math.min(this$1.lastLine(), to.line - (to.ch ? 0 : 1)) + 1
8199
+ for (var j = start; j < end; ++j)
8200
+ { indentLine(this$1, j, how) }
8201
+ var newRanges = this$1.doc.sel.ranges
8202
+ if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0)
8203
+ { replaceOneSelection(this$1.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll) }
8204
+ } else if (range.head.line > end) {
8205
+ indentLine(this$1, range.head.line, how, true)
8206
+ end = range.head.line
8207
+ if (i == this$1.doc.sel.primIndex) { ensureCursorVisible(this$1) }
8208
+ }
8209
+ }
8210
+ }),
8211
+
8212
+ // Fetch the parser token for a given character. Useful for hacks
8213
+ // that want to inspect the mode state (say, for completion).
8214
+ getTokenAt: function(pos, precise) {
8215
+ return takeToken(this, pos, precise)
8216
+ },
8217
+
8218
+ getLineTokens: function(line, precise) {
8219
+ return takeToken(this, Pos(line), precise, true)
8220
+ },
8221
+
8222
+ getTokenTypeAt: function(pos) {
8223
+ pos = clipPos(this.doc, pos)
8224
+ var styles = getLineStyles(this, getLine(this.doc, pos.line))
8225
+ var before = 0, after = (styles.length - 1) / 2, ch = pos.ch
8226
+ var type
8227
+ if (ch == 0) { type = styles[2] }
8228
+ else { for (;;) {
8229
+ var mid = (before + after) >> 1
8230
+ if ((mid ? styles[mid * 2 - 1] : 0) >= ch) { after = mid }
8231
+ else if (styles[mid * 2 + 1] < ch) { before = mid + 1 }
8232
+ else { type = styles[mid * 2 + 2]; break }
8233
+ } }
8234
+ var cut = type ? type.indexOf("overlay ") : -1
8235
+ return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1)
8236
+ },
8237
+
8238
+ getModeAt: function(pos) {
8239
+ var mode = this.doc.mode
8240
+ if (!mode.innerMode) { return mode }
8241
+ return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode
8242
+ },
8243
+
8244
+ getHelper: function(pos, type) {
8245
+ return this.getHelpers(pos, type)[0]
8246
+ },
8247
+
8248
+ getHelpers: function(pos, type) {
8249
+ var this$1 = this;
8250
+
8251
+ var found = []
8252
+ if (!helpers.hasOwnProperty(type)) { return found }
8253
+ var help = helpers[type], mode = this.getModeAt(pos)
8254
+ if (typeof mode[type] == "string") {
8255
+ if (help[mode[type]]) { found.push(help[mode[type]]) }
8256
+ } else if (mode[type]) {
8257
+ for (var i = 0; i < mode[type].length; i++) {
8258
+ var val = help[mode[type][i]]
8259
+ if (val) { found.push(val) }
8260
+ }
8261
+ } else if (mode.helperType && help[mode.helperType]) {
8262
+ found.push(help[mode.helperType])
8263
+ } else if (help[mode.name]) {
8264
+ found.push(help[mode.name])
8265
+ }
8266
+ for (var i$1 = 0; i$1 < help._global.length; i$1++) {
8267
+ var cur = help._global[i$1]
8268
+ if (cur.pred(mode, this$1) && indexOf(found, cur.val) == -1)
8269
+ { found.push(cur.val) }
8270
+ }
8271
+ return found
8272
+ },
8273
+
8274
+ getStateAfter: function(line, precise) {
8275
+ var doc = this.doc
8276
+ line = clipLine(doc, line == null ? doc.first + doc.size - 1: line)
8277
+ return getContextBefore(this, line + 1, precise).state
8278
+ },
8279
+
8280
+ cursorCoords: function(start, mode) {
8281
+ var pos, range = this.doc.sel.primary()
8282
+ if (start == null) { pos = range.head }
8283
+ else if (typeof start == "object") { pos = clipPos(this.doc, start) }
8284
+ else { pos = start ? range.from() : range.to() }
8285
+ return cursorCoords(this, pos, mode || "page")
8286
+ },
8287
+
8288
+ charCoords: function(pos, mode) {
8289
+ return charCoords(this, clipPos(this.doc, pos), mode || "page")
8290
+ },
8291
+
8292
+ coordsChar: function(coords, mode) {
8293
+ coords = fromCoordSystem(this, coords, mode || "page")
8294
+ return coordsChar(this, coords.left, coords.top)
8295
+ },
8296
+
8297
+ lineAtHeight: function(height, mode) {
8298
+ height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top
8299
+ return lineAtHeight(this.doc, height + this.display.viewOffset)
8300
+ },
8301
+ heightAtLine: function(line, mode, includeWidgets) {
8302
+ var end = false, lineObj
8303
+ if (typeof line == "number") {
8304
+ var last = this.doc.first + this.doc.size - 1
8305
+ if (line < this.doc.first) { line = this.doc.first }
8306
+ else if (line > last) { line = last; end = true }
8307
+ lineObj = getLine(this.doc, line)
8308
+ } else {
8309
+ lineObj = line
8310
+ }
8311
+ return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page", includeWidgets || end).top +
8312
+ (end ? this.doc.height - heightAtLine(lineObj) : 0)
8313
+ },
8314
+
8315
+ defaultTextHeight: function() { return textHeight(this.display) },
8316
+ defaultCharWidth: function() { return charWidth(this.display) },
8317
+
8318
+ getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}},
8319
+
8320
+ addWidget: function(pos, node, scroll, vert, horiz) {
8321
+ var display = this.display
8322
+ pos = cursorCoords(this, clipPos(this.doc, pos))
8323
+ var top = pos.bottom, left = pos.left
8324
+ node.style.position = "absolute"
8325
+ node.setAttribute("cm-ignore-events", "true")
8326
+ this.display.input.setUneditable(node)
8327
+ display.sizer.appendChild(node)
8328
+ if (vert == "over") {
8329
+ top = pos.top
8330
+ } else if (vert == "above" || vert == "near") {
8331
+ var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
8332
+ hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth)
8333
+ // Default to positioning above (if specified and possible); otherwise default to positioning below
8334
+ if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
8335
+ { top = pos.top - node.offsetHeight }
8336
+ else if (pos.bottom + node.offsetHeight <= vspace)
8337
+ { top = pos.bottom }
8338
+ if (left + node.offsetWidth > hspace)
8339
+ { left = hspace - node.offsetWidth }
8340
+ }
8341
+ node.style.top = top + "px"
8342
+ node.style.left = node.style.right = ""
8343
+ if (horiz == "right") {
8344
+ left = display.sizer.clientWidth - node.offsetWidth
8345
+ node.style.right = "0px"
8346
+ } else {
8347
+ if (horiz == "left") { left = 0 }
8348
+ else if (horiz == "middle") { left = (display.sizer.clientWidth - node.offsetWidth) / 2 }
8349
+ node.style.left = left + "px"
8350
+ }
8351
+ if (scroll)
8352
+ { scrollIntoView(this, {left: left, top: top, right: left + node.offsetWidth, bottom: top + node.offsetHeight}) }
8353
+ },
8354
+
8355
+ triggerOnKeyDown: methodOp(onKeyDown),
8356
+ triggerOnKeyPress: methodOp(onKeyPress),
8357
+ triggerOnKeyUp: onKeyUp,
8358
+ triggerOnMouseDown: methodOp(onMouseDown),
8359
+
8360
+ execCommand: function(cmd) {
8361
+ if (commands.hasOwnProperty(cmd))
8362
+ { return commands[cmd].call(null, this) }
8363
+ },
8364
+
8365
+ triggerElectric: methodOp(function(text) { triggerElectric(this, text) }),
8366
+
8367
+ findPosH: function(from, amount, unit, visually) {
8368
+ var this$1 = this;
8369
+
8370
+ var dir = 1
8371
+ if (amount < 0) { dir = -1; amount = -amount }
8372
+ var cur = clipPos(this.doc, from)
8373
+ for (var i = 0; i < amount; ++i) {
8374
+ cur = findPosH(this$1.doc, cur, dir, unit, visually)
8375
+ if (cur.hitSide) { break }
8376
+ }
8377
+ return cur
8378
+ },
8379
+
8380
+ moveH: methodOp(function(dir, unit) {
8381
+ var this$1 = this;
8382
+
8383
+ this.extendSelectionsBy(function (range) {
8384
+ if (this$1.display.shift || this$1.doc.extend || range.empty())
8385
+ { return findPosH(this$1.doc, range.head, dir, unit, this$1.options.rtlMoveVisually) }
8386
+ else
8387
+ { return dir < 0 ? range.from() : range.to() }
8388
+ }, sel_move)
8389
+ }),
8390
+
8391
+ deleteH: methodOp(function(dir, unit) {
8392
+ var sel = this.doc.sel, doc = this.doc
8393
+ if (sel.somethingSelected())
8394
+ { doc.replaceSelection("", null, "+delete") }
8395
+ else
8396
+ { deleteNearSelection(this, function (range) {
8397
+ var other = findPosH(doc, range.head, dir, unit, false)
8398
+ return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other}
8399
+ }) }
8400
+ }),
8401
+
8402
+ findPosV: function(from, amount, unit, goalColumn) {
8403
+ var this$1 = this;
8404
+
8405
+ var dir = 1, x = goalColumn
8406
+ if (amount < 0) { dir = -1; amount = -amount }
8407
+ var cur = clipPos(this.doc, from)
8408
+ for (var i = 0; i < amount; ++i) {
8409
+ var coords = cursorCoords(this$1, cur, "div")
8410
+ if (x == null) { x = coords.left }
8411
+ else { coords.left = x }
8412
+ cur = findPosV(this$1, coords, dir, unit)
8413
+ if (cur.hitSide) { break }
8414
+ }
8415
+ return cur
8416
+ },
8417
+
8418
+ moveV: methodOp(function(dir, unit) {
8419
+ var this$1 = this;
8420
+
8421
+ var doc = this.doc, goals = []
8422
+ var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected()
8423
+ doc.extendSelectionsBy(function (range) {
8424
+ if (collapse)
8425
+ { return dir < 0 ? range.from() : range.to() }
8426
+ var headPos = cursorCoords(this$1, range.head, "div")
8427
+ if (range.goalColumn != null) { headPos.left = range.goalColumn }
8428
+ goals.push(headPos.left)
8429
+ var pos = findPosV(this$1, headPos, dir, unit)
8430
+ if (unit == "page" && range == doc.sel.primary())
8431
+ { addToScrollTop(this$1, charCoords(this$1, pos, "div").top - headPos.top) }
8432
+ return pos
8433
+ }, sel_move)
8434
+ if (goals.length) { for (var i = 0; i < doc.sel.ranges.length; i++)
8435
+ { doc.sel.ranges[i].goalColumn = goals[i] } }
8436
+ }),
8437
+
8438
+ // Find the word at the given position (as returned by coordsChar).
8439
+ findWordAt: function(pos) {
8440
+ var doc = this.doc, line = getLine(doc, pos.line).text
8441
+ var start = pos.ch, end = pos.ch
8442
+ if (line) {
8443
+ var helper = this.getHelper(pos, "wordChars")
8444
+ if ((pos.sticky == "before" || end == line.length) && start) { --start; } else { ++end }
8445
+ var startChar = line.charAt(start)
8446
+ var check = isWordChar(startChar, helper)
8447
+ ? function (ch) { return isWordChar(ch, helper); }
8448
+ : /\s/.test(startChar) ? function (ch) { return /\s/.test(ch); }
8449
+ : function (ch) { return (!/\s/.test(ch) && !isWordChar(ch)); }
8450
+ while (start > 0 && check(line.charAt(start - 1))) { --start }
8451
+ while (end < line.length && check(line.charAt(end))) { ++end }
8452
+ }
8453
+ return new Range(Pos(pos.line, start), Pos(pos.line, end))
8454
+ },
8455
+
8456
+ toggleOverwrite: function(value) {
8457
+ if (value != null && value == this.state.overwrite) { return }
8458
+ if (this.state.overwrite = !this.state.overwrite)
8459
+ { addClass(this.display.cursorDiv, "CodeMirror-overwrite") }
8460
+ else
8461
+ { rmClass(this.display.cursorDiv, "CodeMirror-overwrite") }
8462
+
8463
+ signal(this, "overwriteToggle", this, this.state.overwrite)
8464
+ },
8465
+ hasFocus: function() { return this.display.input.getField() == activeElt() },
8466
+ isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) },
8467
+
8468
+ scrollTo: methodOp(function (x, y) { scrollToCoords(this, x, y) }),
8469
+ getScrollInfo: function() {
8470
+ var scroller = this.display.scroller
8471
+ return {left: scroller.scrollLeft, top: scroller.scrollTop,
8472
+ height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight,
8473
+ width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth,
8474
+ clientHeight: displayHeight(this), clientWidth: displayWidth(this)}
8475
+ },
8476
+
8477
+ scrollIntoView: methodOp(function(range, margin) {
8478
+ if (range == null) {
8479
+ range = {from: this.doc.sel.primary().head, to: null}
8480
+ if (margin == null) { margin = this.options.cursorScrollMargin }
8481
+ } else if (typeof range == "number") {
8482
+ range = {from: Pos(range, 0), to: null}
8483
+ } else if (range.from == null) {
8484
+ range = {from: range, to: null}
8485
+ }
8486
+ if (!range.to) { range.to = range.from }
8487
+ range.margin = margin || 0
8488
+
8489
+ if (range.from.line != null) {
8490
+ scrollToRange(this, range)
8491
+ } else {
8492
+ scrollToCoordsRange(this, range.from, range.to, range.margin)
8493
+ }
8494
+ }),
8495
+
8496
+ setSize: methodOp(function(width, height) {
8497
+ var this$1 = this;
8498
+
8499
+ var interpret = function (val) { return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; }
8500
+ if (width != null) { this.display.wrapper.style.width = interpret(width) }
8501
+ if (height != null) { this.display.wrapper.style.height = interpret(height) }
8502
+ if (this.options.lineWrapping) { clearLineMeasurementCache(this) }
8503
+ var lineNo = this.display.viewFrom
8504
+ this.doc.iter(lineNo, this.display.viewTo, function (line) {
8505
+ if (line.widgets) { for (var i = 0; i < line.widgets.length; i++)
8506
+ { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo, "widget"); break } } }
8507
+ ++lineNo
8508
+ })
8509
+ this.curOp.forceUpdate = true
8510
+ signal(this, "refresh", this)
8511
+ }),
8512
+
8513
+ operation: function(f){return runInOp(this, f)},
8514
+ startOperation: function(){return startOperation(this)},
8515
+ endOperation: function(){return endOperation(this)},
8516
+
8517
+ refresh: methodOp(function() {
8518
+ var oldHeight = this.display.cachedTextHeight
8519
+ regChange(this)
8520
+ this.curOp.forceUpdate = true
8521
+ clearCaches(this)
8522
+ scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop)
8523
+ updateGutterSpace(this)
8524
+ if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5)
8525
+ { estimateLineHeights(this) }
8526
+ signal(this, "refresh", this)
8527
+ }),
8528
+
8529
+ swapDoc: methodOp(function(doc) {
8530
+ var old = this.doc
8531
+ old.cm = null
8532
+ attachDoc(this, doc)
8533
+ clearCaches(this)
8534
+ this.display.input.reset()
8535
+ scrollToCoords(this, doc.scrollLeft, doc.scrollTop)
8536
+ this.curOp.forceScroll = true
8537
+ signalLater(this, "swapDoc", this, old)
8538
+ return old
8539
+ }),
8540
+
8541
+ getInputField: function(){return this.display.input.getField()},
8542
+ getWrapperElement: function(){return this.display.wrapper},
8543
+ getScrollerElement: function(){return this.display.scroller},
8544
+ getGutterElement: function(){return this.display.gutters}
8545
+ }
8546
+ eventMixin(CodeMirror)
8547
+
8548
+ CodeMirror.registerHelper = function(type, name, value) {
8549
+ if (!helpers.hasOwnProperty(type)) { helpers[type] = CodeMirror[type] = {_global: []} }
8550
+ helpers[type][name] = value
8551
+ }
8552
+ CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {
8553
+ CodeMirror.registerHelper(type, name, value)
8554
+ helpers[type]._global.push({pred: predicate, val: value})
8555
+ }
8556
+ }
8557
+
8558
+ // Used for horizontal relative motion. Dir is -1 or 1 (left or
8559
+ // right), unit can be "char", "column" (like char, but doesn't
8560
+ // cross line boundaries), "word" (across next word), or "group" (to
8561
+ // the start of next group of word or non-word-non-whitespace
8562
+ // chars). The visually param controls whether, in right-to-left
8563
+ // text, direction 1 means to move towards the next index in the
8564
+ // string, or towards the character to the right of the current
8565
+ // position. The resulting position will have a hitSide=true
8566
+ // property if it reached the end of the document.
8567
+ function findPosH(doc, pos, dir, unit, visually) {
8568
+ var oldPos = pos
8569
+ var origDir = dir
8570
+ var lineObj = getLine(doc, pos.line)
8571
+ function findNextLine() {
8572
+ var l = pos.line + dir
8573
+ if (l < doc.first || l >= doc.first + doc.size) { return false }
8574
+ pos = new Pos(l, pos.ch, pos.sticky)
8575
+ return lineObj = getLine(doc, l)
8576
+ }
8577
+ function moveOnce(boundToLine) {
8578
+ var next
8579
+ if (visually) {
8580
+ next = moveVisually(doc.cm, lineObj, pos, dir)
8581
+ } else {
8582
+ next = moveLogically(lineObj, pos, dir)
8583
+ }
8584
+ if (next == null) {
8585
+ if (!boundToLine && findNextLine())
8586
+ { pos = endOfLine(visually, doc.cm, lineObj, pos.line, dir) }
8587
+ else
8588
+ { return false }
8589
+ } else {
8590
+ pos = next
8591
+ }
8592
+ return true
8593
+ }
8594
+
8595
+ if (unit == "char") {
8596
+ moveOnce()
8597
+ } else if (unit == "column") {
8598
+ moveOnce(true)
8599
+ } else if (unit == "word" || unit == "group") {
8600
+ var sawType = null, group = unit == "group"
8601
+ var helper = doc.cm && doc.cm.getHelper(pos, "wordChars")
8602
+ for (var first = true;; first = false) {
8603
+ if (dir < 0 && !moveOnce(!first)) { break }
8604
+ var cur = lineObj.text.charAt(pos.ch) || "\n"
8605
+ var type = isWordChar(cur, helper) ? "w"
8606
+ : group && cur == "\n" ? "n"
8607
+ : !group || /\s/.test(cur) ? null
8608
+ : "p"
8609
+ if (group && !first && !type) { type = "s" }
8610
+ if (sawType && sawType != type) {
8611
+ if (dir < 0) {dir = 1; moveOnce(); pos.sticky = "after"}
8612
+ break
8613
+ }
8614
+
8615
+ if (type) { sawType = type }
8616
+ if (dir > 0 && !moveOnce(!first)) { break }
8617
+ }
8618
+ }
8619
+ var result = skipAtomic(doc, pos, oldPos, origDir, true)
8620
+ if (equalCursorPos(oldPos, result)) { result.hitSide = true }
8621
+ return result
8622
+ }
8623
+
8624
+ // For relative vertical movement. Dir may be -1 or 1. Unit can be
8625
+ // "page" or "line". The resulting position will have a hitSide=true
8626
+ // property if it reached the end of the document.
8627
+ function findPosV(cm, pos, dir, unit) {
8628
+ var doc = cm.doc, x = pos.left, y
8629
+ if (unit == "page") {
8630
+ var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight)
8631
+ var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3)
8632
+ y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount
8633
+
8634
+ } else if (unit == "line") {
8635
+ y = dir > 0 ? pos.bottom + 3 : pos.top - 3
8636
+ }
8637
+ var target
8638
+ for (;;) {
8639
+ target = coordsChar(cm, x, y)
8640
+ if (!target.outside) { break }
8641
+ if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break }
8642
+ y += dir * 5
8643
+ }
8644
+ return target
8645
+ }
8646
+
8647
+ // CONTENTEDITABLE INPUT STYLE
8648
+
8649
+ var ContentEditableInput = function(cm) {
8650
+ this.cm = cm
8651
+ this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null
8652
+ this.polling = new Delayed()
8653
+ this.composing = null
8654
+ this.gracePeriod = false
8655
+ this.readDOMTimeout = null
8656
+ };
8657
+
8658
+ ContentEditableInput.prototype.init = function (display) {
8659
+ var this$1 = this;
8660
+
8661
+ var input = this, cm = input.cm
8662
+ var div = input.div = display.lineDiv
8663
+ disableBrowserMagic(div, cm.options.spellcheck)
8664
+
8665
+ on(div, "paste", function (e) {
8666
+ if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
8667
+ // IE doesn't fire input events, so we schedule a read for the pasted content in this way
8668
+ if (ie_version <= 11) { setTimeout(operation(cm, function () { return this$1.updateFromDOM(); }), 20) }
8669
+ })
8670
+
8671
+ on(div, "compositionstart", function (e) {
8672
+ this$1.composing = {data: e.data, done: false}
8673
+ })
8674
+ on(div, "compositionupdate", function (e) {
8675
+ if (!this$1.composing) { this$1.composing = {data: e.data, done: false} }
8676
+ })
8677
+ on(div, "compositionend", function (e) {
8678
+ if (this$1.composing) {
8679
+ if (e.data != this$1.composing.data) { this$1.readFromDOMSoon() }
8680
+ this$1.composing.done = true
8681
+ }
8682
+ })
8683
+
8684
+ on(div, "touchstart", function () { return input.forceCompositionEnd(); })
8685
+
8686
+ on(div, "input", function () {
8687
+ if (!this$1.composing) { this$1.readFromDOMSoon() }
8688
+ })
8689
+
8690
+ function onCopyCut(e) {
8691
+ if (signalDOMEvent(cm, e)) { return }
8692
+ if (cm.somethingSelected()) {
8693
+ setLastCopied({lineWise: false, text: cm.getSelections()})
8694
+ if (e.type == "cut") { cm.replaceSelection("", null, "cut") }
8695
+ } else if (!cm.options.lineWiseCopyCut) {
8696
+ return
8697
+ } else {
8698
+ var ranges = copyableRanges(cm)
8699
+ setLastCopied({lineWise: true, text: ranges.text})
8700
+ if (e.type == "cut") {
8701
+ cm.operation(function () {
8702
+ cm.setSelections(ranges.ranges, 0, sel_dontScroll)
8703
+ cm.replaceSelection("", null, "cut")
8704
+ })
8705
+ }
8706
+ }
8707
+ if (e.clipboardData) {
8708
+ e.clipboardData.clearData()
8709
+ var content = lastCopied.text.join("\n")
8710
+ // iOS exposes the clipboard API, but seems to discard content inserted into it
8711
+ e.clipboardData.setData("Text", content)
8712
+ if (e.clipboardData.getData("Text") == content) {
8713
+ e.preventDefault()
8714
+ return
8715
+ }
8716
+ }
8717
+ // Old-fashioned briefly-focus-a-textarea hack
8718
+ var kludge = hiddenTextarea(), te = kludge.firstChild
8719
+ cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild)
8720
+ te.value = lastCopied.text.join("\n")
8721
+ var hadFocus = document.activeElement
8722
+ selectInput(te)
8723
+ setTimeout(function () {
8724
+ cm.display.lineSpace.removeChild(kludge)
8725
+ hadFocus.focus()
8726
+ if (hadFocus == div) { input.showPrimarySelection() }
8727
+ }, 50)
8728
+ }
8729
+ on(div, "copy", onCopyCut)
8730
+ on(div, "cut", onCopyCut)
8731
+ };
8732
+
8733
+ ContentEditableInput.prototype.prepareSelection = function () {
8734
+ var result = prepareSelection(this.cm, false)
8735
+ result.focus = this.cm.state.focused
8736
+ return result
8737
+ };
8738
+
8739
+ ContentEditableInput.prototype.showSelection = function (info, takeFocus) {
8740
+ if (!info || !this.cm.display.view.length) { return }
8741
+ if (info.focus || takeFocus) { this.showPrimarySelection() }
8742
+ this.showMultipleSelections(info)
8743
+ };
8744
+
8745
+ ContentEditableInput.prototype.showPrimarySelection = function () {
8746
+ var sel = window.getSelection(), cm = this.cm, prim = cm.doc.sel.primary()
8747
+ var from = prim.from(), to = prim.to()
8748
+
8749
+ if (cm.display.viewTo == cm.display.viewFrom || from.line >= cm.display.viewTo || to.line < cm.display.viewFrom) {
8750
+ sel.removeAllRanges()
8751
+ return
8752
+ }
8753
+
8754
+ var curAnchor = domToPos(cm, sel.anchorNode, sel.anchorOffset)
8755
+ var curFocus = domToPos(cm, sel.focusNode, sel.focusOffset)
8756
+ if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad &&
8757
+ cmp(minPos(curAnchor, curFocus), from) == 0 &&
8758
+ cmp(maxPos(curAnchor, curFocus), to) == 0)
8759
+ { return }
8760
+
8761
+ var view = cm.display.view
8762
+ var start = (from.line >= cm.display.viewFrom && posToDOM(cm, from)) ||
8763
+ {node: view[0].measure.map[2], offset: 0}
8764
+ var end = to.line < cm.display.viewTo && posToDOM(cm, to)
8765
+ if (!end) {
8766
+ var measure = view[view.length - 1].measure
8767
+ var map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map
8768
+ end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map.length - 3]}
8769
+ }
8770
+
8771
+ if (!start || !end) {
8772
+ sel.removeAllRanges()
8773
+ return
8774
+ }
8775
+
8776
+ var old = sel.rangeCount && sel.getRangeAt(0), rng
8777
+ try { rng = range(start.node, start.offset, end.offset, end.node) }
8778
+ catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible
8779
+ if (rng) {
8780
+ if (!gecko && cm.state.focused) {
8781
+ sel.collapse(start.node, start.offset)
8782
+ if (!rng.collapsed) {
8783
+ sel.removeAllRanges()
8784
+ sel.addRange(rng)
8785
+ }
8786
+ } else {
8787
+ sel.removeAllRanges()
8788
+ sel.addRange(rng)
8789
+ }
8790
+ if (old && sel.anchorNode == null) { sel.addRange(old) }
8791
+ else if (gecko) { this.startGracePeriod() }
8792
+ }
8793
+ this.rememberSelection()
8794
+ };
8795
+
8796
+ ContentEditableInput.prototype.startGracePeriod = function () {
8797
+ var this$1 = this;
8798
+
8799
+ clearTimeout(this.gracePeriod)
8800
+ this.gracePeriod = setTimeout(function () {
8801
+ this$1.gracePeriod = false
8802
+ if (this$1.selectionChanged())
8803
+ { this$1.cm.operation(function () { return this$1.cm.curOp.selectionChanged = true; }) }
8804
+ }, 20)
8805
+ };
8806
+
8807
+ ContentEditableInput.prototype.showMultipleSelections = function (info) {
8808
+ removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors)
8809
+ removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection)
8810
+ };
8811
+
8812
+ ContentEditableInput.prototype.rememberSelection = function () {
8813
+ var sel = window.getSelection()
8814
+ this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset
8815
+ this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset
8816
+ };
8817
+
8818
+ ContentEditableInput.prototype.selectionInEditor = function () {
8819
+ var sel = window.getSelection()
8820
+ if (!sel.rangeCount) { return false }
8821
+ var node = sel.getRangeAt(0).commonAncestorContainer
8822
+ return contains(this.div, node)
8823
+ };
8824
+
8825
+ ContentEditableInput.prototype.focus = function () {
8826
+ if (this.cm.options.readOnly != "nocursor") {
8827
+ if (!this.selectionInEditor())
8828
+ { this.showSelection(this.prepareSelection(), true) }
8829
+ this.div.focus()
8830
+ }
8831
+ };
8832
+ ContentEditableInput.prototype.blur = function () { this.div.blur() };
8833
+ ContentEditableInput.prototype.getField = function () { return this.div };
8834
+
8835
+ ContentEditableInput.prototype.supportsTouch = function () { return true };
8836
+
8837
+ ContentEditableInput.prototype.receivedFocus = function () {
8838
+ var input = this
8839
+ if (this.selectionInEditor())
8840
+ { this.pollSelection() }
8841
+ else
8842
+ { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }) }
8843
+
8844
+ function poll() {
8845
+ if (input.cm.state.focused) {
8846
+ input.pollSelection()
8847
+ input.polling.set(input.cm.options.pollInterval, poll)
8848
+ }
8849
+ }
8850
+ this.polling.set(this.cm.options.pollInterval, poll)
8851
+ };
8852
+
8853
+ ContentEditableInput.prototype.selectionChanged = function () {
8854
+ var sel = window.getSelection()
8855
+ return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||
8856
+ sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset
8857
+ };
8858
+
8859
+ ContentEditableInput.prototype.pollSelection = function () {
8860
+ if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) { return }
8861
+ var sel = window.getSelection(), cm = this.cm
8862
+ // On Android Chrome (version 56, at least), backspacing into an
8863
+ // uneditable block element will put the cursor in that element,
8864
+ // and then, because it's not editable, hide the virtual keyboard.
8865
+ // Because Android doesn't allow us to actually detect backspace
8866
+ // presses in a sane way, this code checks for when that happens
8867
+ // and simulates a backspace press in this case.
8868
+ if (android && chrome && this.cm.options.gutters.length && isInGutter(sel.anchorNode)) {
8869
+ this.cm.triggerOnKeyDown({type: "keydown", keyCode: 8, preventDefault: Math.abs})
8870
+ this.blur()
8871
+ this.focus()
8872
+ return
8873
+ }
8874
+ if (this.composing) { return }
8875
+ this.rememberSelection()
8876
+ var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset)
8877
+ var head = domToPos(cm, sel.focusNode, sel.focusOffset)
8878
+ if (anchor && head) { runInOp(cm, function () {
8879
+ setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll)
8880
+ if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true }
8881
+ }) }
8882
+ };
8883
+
8884
+ ContentEditableInput.prototype.pollContent = function () {
8885
+ if (this.readDOMTimeout != null) {
8886
+ clearTimeout(this.readDOMTimeout)
8887
+ this.readDOMTimeout = null
8888
+ }
8889
+
8890
+ var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary()
8891
+ var from = sel.from(), to = sel.to()
8892
+ if (from.ch == 0 && from.line > cm.firstLine())
8893
+ { from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length) }
8894
+ if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine())
8895
+ { to = Pos(to.line + 1, 0) }
8896
+ if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return false }
8897
+
8898
+ var fromIndex, fromLine, fromNode
8899
+ if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) {
8900
+ fromLine = lineNo(display.view[0].line)
8901
+ fromNode = display.view[0].node
8902
+ } else {
8903
+ fromLine = lineNo(display.view[fromIndex].line)
8904
+ fromNode = display.view[fromIndex - 1].node.nextSibling
8905
+ }
8906
+ var toIndex = findViewIndex(cm, to.line)
8907
+ var toLine, toNode
8908
+ if (toIndex == display.view.length - 1) {
8909
+ toLine = display.viewTo - 1
8910
+ toNode = display.lineDiv.lastChild
8911
+ } else {
8912
+ toLine = lineNo(display.view[toIndex + 1].line) - 1
8913
+ toNode = display.view[toIndex + 1].node.previousSibling
8914
+ }
8915
+
8916
+ if (!fromNode) { return false }
8917
+ var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine))
8918
+ var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length))
8919
+ while (newText.length > 1 && oldText.length > 1) {
8920
+ if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine-- }
8921
+ else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++ }
8922
+ else { break }
8923
+ }
8924
+
8925
+ var cutFront = 0, cutEnd = 0
8926
+ var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length)
8927
+ while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront))
8928
+ { ++cutFront }
8929
+ var newBot = lst(newText), oldBot = lst(oldText)
8930
+ var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0),
8931
+ oldBot.length - (oldText.length == 1 ? cutFront : 0))
8932
+ while (cutEnd < maxCutEnd &&
8933
+ newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1))
8934
+ { ++cutEnd }
8935
+ // Try to move start of change to start of selection if ambiguous
8936
+ if (newText.length == 1 && oldText.length == 1 && fromLine == from.line) {
8937
+ while (cutFront && cutFront > from.ch &&
8938
+ newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) {
8939
+ cutFront--
8940
+ cutEnd++
8941
+ }
8942
+ }
8943
+
8944
+ newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, "")
8945
+ newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, "")
8946
+
8947
+ var chFrom = Pos(fromLine, cutFront)
8948
+ var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0)
8949
+ if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {
8950
+ replaceRange(cm.doc, newText, chFrom, chTo, "+input")
8951
+ return true
8952
+ }
8953
+ };
8954
+
8955
+ ContentEditableInput.prototype.ensurePolled = function () {
8956
+ this.forceCompositionEnd()
8957
+ };
8958
+ ContentEditableInput.prototype.reset = function () {
8959
+ this.forceCompositionEnd()
8960
+ };
8961
+ ContentEditableInput.prototype.forceCompositionEnd = function () {
8962
+ if (!this.composing) { return }
8963
+ clearTimeout(this.readDOMTimeout)
8964
+ this.composing = null
8965
+ this.updateFromDOM()
8966
+ this.div.blur()
8967
+ this.div.focus()
8968
+ };
8969
+ ContentEditableInput.prototype.readFromDOMSoon = function () {
8970
+ var this$1 = this;
8971
+
8972
+ if (this.readDOMTimeout != null) { return }
8973
+ this.readDOMTimeout = setTimeout(function () {
8974
+ this$1.readDOMTimeout = null
8975
+ if (this$1.composing) {
8976
+ if (this$1.composing.done) { this$1.composing = null }
8977
+ else { return }
8978
+ }
8979
+ this$1.updateFromDOM()
8980
+ }, 80)
8981
+ };
8982
+
8983
+ ContentEditableInput.prototype.updateFromDOM = function () {
8984
+ var this$1 = this;
8985
+
8986
+ if (this.cm.isReadOnly() || !this.pollContent())
8987
+ { runInOp(this.cm, function () { return regChange(this$1.cm); }) }
8988
+ };
8989
+
8990
+ ContentEditableInput.prototype.setUneditable = function (node) {
8991
+ node.contentEditable = "false"
8992
+ };
8993
+
8994
+ ContentEditableInput.prototype.onKeyPress = function (e) {
8995
+ if (e.charCode == 0) { return }
8996
+ e.preventDefault()
8997
+ if (!this.cm.isReadOnly())
8998
+ { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0) }
8999
+ };
9000
+
9001
+ ContentEditableInput.prototype.readOnlyChanged = function (val) {
9002
+ this.div.contentEditable = String(val != "nocursor")
9003
+ };
9004
+
9005
+ ContentEditableInput.prototype.onContextMenu = function () {};
9006
+ ContentEditableInput.prototype.resetPosition = function () {};
9007
+
9008
+ ContentEditableInput.prototype.needsContentAttribute = true
9009
+
9010
+ function posToDOM(cm, pos) {
9011
+ var view = findViewForLine(cm, pos.line)
9012
+ if (!view || view.hidden) { return null }
9013
+ var line = getLine(cm.doc, pos.line)
9014
+ var info = mapFromLineView(view, line, pos.line)
9015
+
9016
+ var order = getOrder(line, cm.doc.direction), side = "left"
9017
+ if (order) {
9018
+ var partPos = getBidiPartAt(order, pos.ch)
9019
+ side = partPos % 2 ? "right" : "left"
9020
+ }
9021
+ var result = nodeAndOffsetInLineMap(info.map, pos.ch, side)
9022
+ result.offset = result.collapse == "right" ? result.end : result.start
9023
+ return result
9024
+ }
9025
+
9026
+ function isInGutter(node) {
9027
+ for (var scan = node; scan; scan = scan.parentNode)
9028
+ { if (/CodeMirror-gutter-wrapper/.test(scan.className)) { return true } }
9029
+ return false
9030
+ }
9031
+
9032
+ function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos }
9033
+
9034
+ function domTextBetween(cm, from, to, fromLine, toLine) {
9035
+ var text = "", closing = false, lineSep = cm.doc.lineSeparator()
9036
+ function recognizeMarker(id) { return function (marker) { return marker.id == id; } }
9037
+ function close() {
9038
+ if (closing) {
9039
+ text += lineSep
9040
+ closing = false
9041
+ }
9042
+ }
9043
+ function addText(str) {
9044
+ if (str) {
9045
+ close()
9046
+ text += str
9047
+ }
9048
+ }
9049
+ function walk(node) {
9050
+ if (node.nodeType == 1) {
9051
+ var cmText = node.getAttribute("cm-text")
9052
+ if (cmText != null) {
9053
+ addText(cmText || node.textContent.replace(/\u200b/g, ""))
9054
+ return
9055
+ }
9056
+ var markerID = node.getAttribute("cm-marker"), range
9057
+ if (markerID) {
9058
+ var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID))
9059
+ if (found.length && (range = found[0].find(0)))
9060
+ { addText(getBetween(cm.doc, range.from, range.to).join(lineSep)) }
9061
+ return
9062
+ }
9063
+ if (node.getAttribute("contenteditable") == "false") { return }
9064
+ var isBlock = /^(pre|div|p)$/i.test(node.nodeName)
9065
+ if (isBlock) { close() }
9066
+ for (var i = 0; i < node.childNodes.length; i++)
9067
+ { walk(node.childNodes[i]) }
9068
+ if (isBlock) { closing = true }
9069
+ } else if (node.nodeType == 3) {
9070
+ addText(node.nodeValue)
9071
+ }
9072
+ }
9073
+ for (;;) {
9074
+ walk(from)
9075
+ if (from == to) { break }
9076
+ from = from.nextSibling
9077
+ }
9078
+ return text
9079
+ }
9080
+
9081
+ function domToPos(cm, node, offset) {
9082
+ var lineNode
9083
+ if (node == cm.display.lineDiv) {
9084
+ lineNode = cm.display.lineDiv.childNodes[offset]
9085
+ if (!lineNode) { return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) }
9086
+ node = null; offset = 0
9087
+ } else {
9088
+ for (lineNode = node;; lineNode = lineNode.parentNode) {
9089
+ if (!lineNode || lineNode == cm.display.lineDiv) { return null }
9090
+ if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) { break }
9091
+ }
9092
+ }
9093
+ for (var i = 0; i < cm.display.view.length; i++) {
9094
+ var lineView = cm.display.view[i]
9095
+ if (lineView.node == lineNode)
9096
+ { return locateNodeInLineView(lineView, node, offset) }
9097
+ }
9098
+ }
9099
+
9100
+ function locateNodeInLineView(lineView, node, offset) {
9101
+ var wrapper = lineView.text.firstChild, bad = false
9102
+ if (!node || !contains(wrapper, node)) { return badPos(Pos(lineNo(lineView.line), 0), true) }
9103
+ if (node == wrapper) {
9104
+ bad = true
9105
+ node = wrapper.childNodes[offset]
9106
+ offset = 0
9107
+ if (!node) {
9108
+ var line = lineView.rest ? lst(lineView.rest) : lineView.line
9109
+ return badPos(Pos(lineNo(line), line.text.length), bad)
9110
+ }
9111
+ }
9112
+
9113
+ var textNode = node.nodeType == 3 ? node : null, topNode = node
9114
+ if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {
9115
+ textNode = node.firstChild
9116
+ if (offset) { offset = textNode.nodeValue.length }
9117
+ }
9118
+ while (topNode.parentNode != wrapper) { topNode = topNode.parentNode }
9119
+ var measure = lineView.measure, maps = measure.maps
9120
+
9121
+ function find(textNode, topNode, offset) {
9122
+ for (var i = -1; i < (maps ? maps.length : 0); i++) {
9123
+ var map = i < 0 ? measure.map : maps[i]
9124
+ for (var j = 0; j < map.length; j += 3) {
9125
+ var curNode = map[j + 2]
9126
+ if (curNode == textNode || curNode == topNode) {
9127
+ var line = lineNo(i < 0 ? lineView.line : lineView.rest[i])
9128
+ var ch = map[j] + offset
9129
+ if (offset < 0 || curNode != textNode) { ch = map[j + (offset ? 1 : 0)] }
9130
+ return Pos(line, ch)
9131
+ }
9132
+ }
9133
+ }
9134
+ }
9135
+ var found = find(textNode, topNode, offset)
9136
+ if (found) { return badPos(found, bad) }
9137
+
9138
+ // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems
9139
+ for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) {
9140
+ found = find(after, after.firstChild, 0)
9141
+ if (found)
9142
+ { return badPos(Pos(found.line, found.ch - dist), bad) }
9143
+ else
9144
+ { dist += after.textContent.length }
9145
+ }
9146
+ for (var before = topNode.previousSibling, dist$1 = offset; before; before = before.previousSibling) {
9147
+ found = find(before, before.firstChild, -1)
9148
+ if (found)
9149
+ { return badPos(Pos(found.line, found.ch + dist$1), bad) }
9150
+ else
9151
+ { dist$1 += before.textContent.length }
9152
+ }
9153
+ }
9154
+
9155
+ // TEXTAREA INPUT STYLE
9156
+
9157
+ var TextareaInput = function(cm) {
9158
+ this.cm = cm
9159
+ // See input.poll and input.reset
9160
+ this.prevInput = ""
9161
+
9162
+ // Flag that indicates whether we expect input to appear real soon
9163
+ // now (after some event like 'keypress' or 'input') and are
9164
+ // polling intensively.
9165
+ this.pollingFast = false
9166
+ // Self-resetting timeout for the poller
9167
+ this.polling = new Delayed()
9168
+ // Used to work around IE issue with selection being forgotten when focus moves away from textarea
9169
+ this.hasSelection = false
9170
+ this.composing = null
9171
+ };
9172
+
9173
+ TextareaInput.prototype.init = function (display) {
9174
+ var this$1 = this;
9175
+
9176
+ var input = this, cm = this.cm
9177
+
9178
+ // Wraps and hides input textarea
9179
+ var div = this.wrapper = hiddenTextarea()
9180
+ // The semihidden textarea that is focused when the editor is
9181
+ // focused, and receives input.
9182
+ var te = this.textarea = div.firstChild
9183
+ display.wrapper.insertBefore(div, display.wrapper.firstChild)
9184
+
9185
+ // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)
9186
+ if (ios) { te.style.width = "0px" }
9187
+
9188
+ on(te, "input", function () {
9189
+ if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = null }
9190
+ input.poll()
9191
+ })
9192
+
9193
+ on(te, "paste", function (e) {
9194
+ if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
9195
+
9196
+ cm.state.pasteIncoming = true
9197
+ input.fastPoll()
9198
+ })
9199
+
9200
+ function prepareCopyCut(e) {
9201
+ if (signalDOMEvent(cm, e)) { return }
9202
+ if (cm.somethingSelected()) {
9203
+ setLastCopied({lineWise: false, text: cm.getSelections()})
9204
+ } else if (!cm.options.lineWiseCopyCut) {
9205
+ return
9206
+ } else {
9207
+ var ranges = copyableRanges(cm)
9208
+ setLastCopied({lineWise: true, text: ranges.text})
9209
+ if (e.type == "cut") {
9210
+ cm.setSelections(ranges.ranges, null, sel_dontScroll)
9211
+ } else {
9212
+ input.prevInput = ""
9213
+ te.value = ranges.text.join("\n")
9214
+ selectInput(te)
9215
+ }
9216
+ }
9217
+ if (e.type == "cut") { cm.state.cutIncoming = true }
9218
+ }
9219
+ on(te, "cut", prepareCopyCut)
9220
+ on(te, "copy", prepareCopyCut)
9221
+
9222
+ on(display.scroller, "paste", function (e) {
9223
+ if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return }
9224
+ cm.state.pasteIncoming = true
9225
+ input.focus()
9226
+ })
9227
+
9228
+ // Prevent normal selection in the editor (we handle our own)
9229
+ on(display.lineSpace, "selectstart", function (e) {
9230
+ if (!eventInWidget(display, e)) { e_preventDefault(e) }
9231
+ })
9232
+
9233
+ on(te, "compositionstart", function () {
9234
+ var start = cm.getCursor("from")
9235
+ if (input.composing) { input.composing.range.clear() }
9236
+ input.composing = {
9237
+ start: start,
9238
+ range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"})
9239
+ }
9240
+ })
9241
+ on(te, "compositionend", function () {
9242
+ if (input.composing) {
9243
+ input.poll()
9244
+ input.composing.range.clear()
9245
+ input.composing = null
9246
+ }
9247
+ })
9248
+ };
9249
+
9250
+ TextareaInput.prototype.prepareSelection = function () {
9251
+ // Redraw the selection and/or cursor
9252
+ var cm = this.cm, display = cm.display, doc = cm.doc
9253
+ var result = prepareSelection(cm)
9254
+
9255
+ // Move the hidden textarea near the cursor to prevent scrolling artifacts
9256
+ if (cm.options.moveInputWithCursor) {
9257
+ var headPos = cursorCoords(cm, doc.sel.primary().head, "div")
9258
+ var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect()
9259
+ result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
9260
+ headPos.top + lineOff.top - wrapOff.top))
9261
+ result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
9262
+ headPos.left + lineOff.left - wrapOff.left))
9263
+ }
9264
+
9265
+ return result
9266
+ };
9267
+
9268
+ TextareaInput.prototype.showSelection = function (drawn) {
9269
+ var cm = this.cm, display = cm.display
9270
+ removeChildrenAndAdd(display.cursorDiv, drawn.cursors)
9271
+ removeChildrenAndAdd(display.selectionDiv, drawn.selection)
9272
+ if (drawn.teTop != null) {
9273
+ this.wrapper.style.top = drawn.teTop + "px"
9274
+ this.wrapper.style.left = drawn.teLeft + "px"
9275
+ }
9276
+ };
9277
+
9278
+ // Reset the input to correspond to the selection (or to be empty,
9279
+ // when not typing and nothing is selected)
9280
+ TextareaInput.prototype.reset = function (typing) {
9281
+ if (this.contextMenuPending || this.composing) { return }
9282
+ var cm = this.cm
9283
+ if (cm.somethingSelected()) {
9284
+ this.prevInput = ""
9285
+ var content = cm.getSelection()
9286
+ this.textarea.value = content
9287
+ if (cm.state.focused) { selectInput(this.textarea) }
9288
+ if (ie && ie_version >= 9) { this.hasSelection = content }
9289
+ } else if (!typing) {
9290
+ this.prevInput = this.textarea.value = ""
9291
+ if (ie && ie_version >= 9) { this.hasSelection = null }
9292
+ }
9293
+ };
9294
+
9295
+ TextareaInput.prototype.getField = function () { return this.textarea };
9296
+
9297
+ TextareaInput.prototype.supportsTouch = function () { return false };
9298
+
9299
+ TextareaInput.prototype.focus = function () {
9300
+ if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) {
9301
+ try { this.textarea.focus() }
9302
+ catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM
9303
+ }
9304
+ };
9305
+
9306
+ TextareaInput.prototype.blur = function () { this.textarea.blur() };
9307
+
9308
+ TextareaInput.prototype.resetPosition = function () {
9309
+ this.wrapper.style.top = this.wrapper.style.left = 0
9310
+ };
9311
+
9312
+ TextareaInput.prototype.receivedFocus = function () { this.slowPoll() };
9313
+
9314
+ // Poll for input changes, using the normal rate of polling. This
9315
+ // runs as long as the editor is focused.
9316
+ TextareaInput.prototype.slowPoll = function () {
9317
+ var this$1 = this;
9318
+
9319
+ if (this.pollingFast) { return }
9320
+ this.polling.set(this.cm.options.pollInterval, function () {
9321
+ this$1.poll()
9322
+ if (this$1.cm.state.focused) { this$1.slowPoll() }
9323
+ })
9324
+ };
9325
+
9326
+ // When an event has just come in that is likely to add or change
9327
+ // something in the input textarea, we poll faster, to ensure that
9328
+ // the change appears on the screen quickly.
9329
+ TextareaInput.prototype.fastPoll = function () {
9330
+ var missed = false, input = this
9331
+ input.pollingFast = true
9332
+ function p() {
9333
+ var changed = input.poll()
9334
+ if (!changed && !missed) {missed = true; input.polling.set(60, p)}
9335
+ else {input.pollingFast = false; input.slowPoll()}
9336
+ }
9337
+ input.polling.set(20, p)
9338
+ };
9339
+
9340
+ // Read input from the textarea, and update the document to match.
9341
+ // When something is selected, it is present in the textarea, and
9342
+ // selected (unless it is huge, in which case a placeholder is
9343
+ // used). When nothing is selected, the cursor sits after previously
9344
+ // seen text (can be empty), which is stored in prevInput (we must
9345
+ // not reset the textarea when typing, because that breaks IME).
9346
+ TextareaInput.prototype.poll = function () {
9347
+ var this$1 = this;
9348
+
9349
+ var cm = this.cm, input = this.textarea, prevInput = this.prevInput
9350
+ // Since this is called a *lot*, try to bail out as cheaply as
9351
+ // possible when it is clear that nothing happened. hasSelection
9352
+ // will be the case when there is a lot of text in the textarea,
9353
+ // in which case reading its value would be expensive.
9354
+ if (this.contextMenuPending || !cm.state.focused ||
9355
+ (hasSelection(input) && !prevInput && !this.composing) ||
9356
+ cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq)
9357
+ { return false }
9358
+
9359
+ var text = input.value
9360
+ // If nothing changed, bail.
9361
+ if (text == prevInput && !cm.somethingSelected()) { return false }
9362
+ // Work around nonsensical selection resetting in IE9/10, and
9363
+ // inexplicable appearance of private area unicode characters on
9364
+ // some key combos in Mac (#2689).
9365
+ if (ie && ie_version >= 9 && this.hasSelection === text ||
9366
+ mac && /[\uf700-\uf7ff]/.test(text)) {
9367
+ cm.display.input.reset()
9368
+ return false
9369
+ }
9370
+
9371
+ if (cm.doc.sel == cm.display.selForContextMenu) {
9372
+ var first = text.charCodeAt(0)
9373
+ if (first == 0x200b && !prevInput) { prevInput = "\u200b" }
9374
+ if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") }
9375
+ }
9376
+ // Find the part of the input that is actually new
9377
+ var same = 0, l = Math.min(prevInput.length, text.length)
9378
+ while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same }
9379
+
9380
+ runInOp(cm, function () {
9381
+ applyTextInput(cm, text.slice(same), prevInput.length - same,
9382
+ null, this$1.composing ? "*compose" : null)
9383
+
9384
+ // Don't leave long text in the textarea, since it makes further polling slow
9385
+ if (text.length > 1000 || text.indexOf("\n") > -1) { input.value = this$1.prevInput = "" }
9386
+ else { this$1.prevInput = text }
9387
+
9388
+ if (this$1.composing) {
9389
+ this$1.composing.range.clear()
9390
+ this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor("to"),
9391
+ {className: "CodeMirror-composing"})
9392
+ }
9393
+ })
9394
+ return true
9395
+ };
9396
+
9397
+ TextareaInput.prototype.ensurePolled = function () {
9398
+ if (this.pollingFast && this.poll()) { this.pollingFast = false }
9399
+ };
9400
+
9401
+ TextareaInput.prototype.onKeyPress = function () {
9402
+ if (ie && ie_version >= 9) { this.hasSelection = null }
9403
+ this.fastPoll()
9404
+ };
9405
+
9406
+ TextareaInput.prototype.onContextMenu = function (e) {
9407
+ var input = this, cm = input.cm, display = cm.display, te = input.textarea
9408
+ var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop
9409
+ if (!pos || presto) { return } // Opera is difficult.
9410
+
9411
+ // Reset the current text selection only if the click is done outside of the selection
9412
+ // and 'resetSelectionOnContextMenu' option is true.
9413
+ var reset = cm.options.resetSelectionOnContextMenu
9414
+ if (reset && cm.doc.sel.contains(pos) == -1)
9415
+ { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll) }
9416
+
9417
+ var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText
9418
+ input.wrapper.style.cssText = "position: absolute"
9419
+ var wrapperBox = input.wrapper.getBoundingClientRect()
9420
+ te.style.cssText = "position: absolute; width: 30px; height: 30px;\n top: " + (e.clientY - wrapperBox.top - 5) + "px; left: " + (e.clientX - wrapperBox.left - 5) + "px;\n z-index: 1000; background: " + (ie ? "rgba(255, 255, 255, .05)" : "transparent") + ";\n outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);"
9421
+ var oldScrollY
9422
+ if (webkit) { oldScrollY = window.scrollY } // Work around Chrome issue (#2712)
9423
+ display.input.focus()
9424
+ if (webkit) { window.scrollTo(null, oldScrollY) }
9425
+ display.input.reset()
9426
+ // Adds "Select all" to context menu in FF
9427
+ if (!cm.somethingSelected()) { te.value = input.prevInput = " " }
9428
+ input.contextMenuPending = true
9429
+ display.selForContextMenu = cm.doc.sel
9430
+ clearTimeout(display.detectingSelectAll)
9431
+
9432
+ // Select-all will be greyed out if there's nothing to select, so
9433
+ // this adds a zero-width space so that we can later check whether
9434
+ // it got selected.
9435
+ function prepareSelectAllHack() {
9436
+ if (te.selectionStart != null) {
9437
+ var selected = cm.somethingSelected()
9438
+ var extval = "\u200b" + (selected ? te.value : "")
9439
+ te.value = "\u21da" // Used to catch context-menu undo
9440
+ te.value = extval
9441
+ input.prevInput = selected ? "" : "\u200b"
9442
+ te.selectionStart = 1; te.selectionEnd = extval.length
9443
+ // Re-set this, in case some other handler touched the
9444
+ // selection in the meantime.
9445
+ display.selForContextMenu = cm.doc.sel
9446
+ }
9447
+ }
9448
+ function rehide() {
9449
+ input.contextMenuPending = false
9450
+ input.wrapper.style.cssText = oldWrapperCSS
9451
+ te.style.cssText = oldCSS
9452
+ if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos) }
9453
+
9454
+ // Try to detect the user choosing select-all
9455
+ if (te.selectionStart != null) {
9456
+ if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack() }
9457
+ var i = 0, poll = function () {
9458
+ if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 &&
9459
+ te.selectionEnd > 0 && input.prevInput == "\u200b") {
9460
+ operation(cm, selectAll)(cm)
9461
+ } else if (i++ < 10) {
9462
+ display.detectingSelectAll = setTimeout(poll, 500)
9463
+ } else {
9464
+ display.selForContextMenu = null
9465
+ display.input.reset()
9466
+ }
9467
+ }
9468
+ display.detectingSelectAll = setTimeout(poll, 200)
9469
+ }
9470
+ }
9471
+
9472
+ if (ie && ie_version >= 9) { prepareSelectAllHack() }
9473
+ if (captureRightClick) {
9474
+ e_stop(e)
9475
+ var mouseup = function () {
9476
+ off(window, "mouseup", mouseup)
9477
+ setTimeout(rehide, 20)
9478
+ }
9479
+ on(window, "mouseup", mouseup)
9480
+ } else {
9481
+ setTimeout(rehide, 50)
9482
+ }
9483
+ };
9484
+
9485
+ TextareaInput.prototype.readOnlyChanged = function (val) {
9486
+ if (!val) { this.reset() }
9487
+ this.textarea.disabled = val == "nocursor"
9488
+ };
9489
+
9490
+ TextareaInput.prototype.setUneditable = function () {};
9491
+
9492
+ TextareaInput.prototype.needsContentAttribute = false
9493
+
9494
+ function fromTextArea(textarea, options) {
9495
+ options = options ? copyObj(options) : {}
9496
+ options.value = textarea.value
9497
+ if (!options.tabindex && textarea.tabIndex)
9498
+ { options.tabindex = textarea.tabIndex }
9499
+ if (!options.placeholder && textarea.placeholder)
9500
+ { options.placeholder = textarea.placeholder }
9501
+ // Set autofocus to true if this textarea is focused, or if it has
9502
+ // autofocus and no other element is focused.
9503
+ if (options.autofocus == null) {
9504
+ var hasFocus = activeElt()
9505
+ options.autofocus = hasFocus == textarea ||
9506
+ textarea.getAttribute("autofocus") != null && hasFocus == document.body
9507
+ }
9508
+
9509
+ function save() {textarea.value = cm.getValue()}
9510
+
9511
+ var realSubmit
9512
+ if (textarea.form) {
9513
+ on(textarea.form, "submit", save)
9514
+ // Deplorable hack to make the submit method do the right thing.
9515
+ if (!options.leaveSubmitMethodAlone) {
9516
+ var form = textarea.form
9517
+ realSubmit = form.submit
9518
+ try {
9519
+ var wrappedSubmit = form.submit = function () {
9520
+ save()
9521
+ form.submit = realSubmit
9522
+ form.submit()
9523
+ form.submit = wrappedSubmit
9524
+ }
9525
+ } catch(e) {}
9526
+ }
9527
+ }
9528
+
9529
+ options.finishInit = function (cm) {
9530
+ cm.save = save
9531
+ cm.getTextArea = function () { return textarea; }
9532
+ cm.toTextArea = function () {
9533
+ cm.toTextArea = isNaN // Prevent this from being ran twice
9534
+ save()
9535
+ textarea.parentNode.removeChild(cm.getWrapperElement())
9536
+ textarea.style.display = ""
9537
+ if (textarea.form) {
9538
+ off(textarea.form, "submit", save)
9539
+ if (typeof textarea.form.submit == "function")
9540
+ { textarea.form.submit = realSubmit }
9541
+ }
9542
+ }
9543
+ }
9544
+
9545
+ textarea.style.display = "none"
9546
+ var cm = CodeMirror(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); },
9547
+ options)
9548
+ return cm
9549
+ }
9550
+
9551
+ function addLegacyProps(CodeMirror) {
9552
+ CodeMirror.off = off
9553
+ CodeMirror.on = on
9554
+ CodeMirror.wheelEventPixels = wheelEventPixels
9555
+ CodeMirror.Doc = Doc
9556
+ CodeMirror.splitLines = splitLinesAuto
9557
+ CodeMirror.countColumn = countColumn
9558
+ CodeMirror.findColumn = findColumn
9559
+ CodeMirror.isWordChar = isWordCharBasic
9560
+ CodeMirror.Pass = Pass
9561
+ CodeMirror.signal = signal
9562
+ CodeMirror.Line = Line
9563
+ CodeMirror.changeEnd = changeEnd
9564
+ CodeMirror.scrollbarModel = scrollbarModel
9565
+ CodeMirror.Pos = Pos
9566
+ CodeMirror.cmpPos = cmp
9567
+ CodeMirror.modes = modes
9568
+ CodeMirror.mimeModes = mimeModes
9569
+ CodeMirror.resolveMode = resolveMode
9570
+ CodeMirror.getMode = getMode
9571
+ CodeMirror.modeExtensions = modeExtensions
9572
+ CodeMirror.extendMode = extendMode
9573
+ CodeMirror.copyState = copyState
9574
+ CodeMirror.startState = startState
9575
+ CodeMirror.innerMode = innerMode
9576
+ CodeMirror.commands = commands
9577
+ CodeMirror.keyMap = keyMap
9578
+ CodeMirror.keyName = keyName
9579
+ CodeMirror.isModifierKey = isModifierKey
9580
+ CodeMirror.lookupKey = lookupKey
9581
+ CodeMirror.normalizeKeyMap = normalizeKeyMap
9582
+ CodeMirror.StringStream = StringStream
9583
+ CodeMirror.SharedTextMarker = SharedTextMarker
9584
+ CodeMirror.TextMarker = TextMarker
9585
+ CodeMirror.LineWidget = LineWidget
9586
+ CodeMirror.e_preventDefault = e_preventDefault
9587
+ CodeMirror.e_stopPropagation = e_stopPropagation
9588
+ CodeMirror.e_stop = e_stop
9589
+ CodeMirror.addClass = addClass
9590
+ CodeMirror.contains = contains
9591
+ CodeMirror.rmClass = rmClass
9592
+ CodeMirror.keyNames = keyNames
9593
+ }
9594
+
9595
+ // EDITOR CONSTRUCTOR
9596
+
9597
+ defineOptions(CodeMirror)
9598
+
9599
+ addEditorMethods(CodeMirror)
9600
+
9601
+ // Set up methods on CodeMirror's prototype to redirect to the editor's document.
9602
+ var dontDelegate = "iter insert remove copy getEditor constructor".split(" ")
9603
+ for (var prop in Doc.prototype) { if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
9604
+ { CodeMirror.prototype[prop] = (function(method) {
9605
+ return function() {return method.apply(this.doc, arguments)}
9606
+ })(Doc.prototype[prop]) } }
9607
+
9608
+ eventMixin(Doc)
9609
+
9610
+ // INPUT HANDLING
9611
+
9612
+ CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput}
9613
+
9614
+ // MODE DEFINITION AND QUERYING
9615
+
9616
+ // Extra arguments are stored as the mode's dependencies, which is
9617
+ // used by (legacy) mechanisms like loadmode.js to automatically
9618
+ // load a mode. (Preferred mechanism is the require/define calls.)
9619
+ CodeMirror.defineMode = function(name/*, mode, …*/) {
9620
+ if (!CodeMirror.defaults.mode && name != "null") { CodeMirror.defaults.mode = name }
9621
+ defineMode.apply(this, arguments)
9622
+ }
9623
+
9624
+ CodeMirror.defineMIME = defineMIME
9625
+
9626
+ // Minimal default mode.
9627
+ CodeMirror.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); })
9628
+ CodeMirror.defineMIME("text/plain", "null")
9629
+
9630
+ // EXTENSIONS
9631
+
9632
+ CodeMirror.defineExtension = function (name, func) {
9633
+ CodeMirror.prototype[name] = func
9634
+ }
9635
+ CodeMirror.defineDocExtension = function (name, func) {
9636
+ Doc.prototype[name] = func
9637
+ }
9638
+
9639
+ CodeMirror.fromTextArea = fromTextArea
9640
+
9641
+ addLegacyProps(CodeMirror)
9642
+
9643
+ CodeMirror.version = "5.31.0"
9644
+
9645
+ return CodeMirror;
9646
+
9647
+ })));
lib/codemirror/mode/css/css.js ADDED
@@ -0,0 +1,832 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // CodeMirror, copyright (c) by Marijn Haverbeke and others
2
+ // Distributed under an MIT license: http://codemirror.net/LICENSE
3
+
4
+ (function(mod) {
5
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
6
+ mod(require("../../lib/codemirror"));
7
+ else if (typeof define == "function" && define.amd) // AMD
8
+ define(["../../lib/codemirror"], mod);
9
+ else // Plain browser env
10
+ mod(CodeMirror);
11
+ })(function(CodeMirror) {
12
+ "use strict";
13
+
14
+ CodeMirror.defineMode("css", function(config, parserConfig) {
15
+ var inline = parserConfig.inline
16
+ if (!parserConfig.propertyKeywords) parserConfig = CodeMirror.resolveMode("text/css");
17
+
18
+ var indentUnit = config.indentUnit,
19
+ tokenHooks = parserConfig.tokenHooks,
20
+ documentTypes = parserConfig.documentTypes || {},
21
+ mediaTypes = parserConfig.mediaTypes || {},
22
+ mediaFeatures = parserConfig.mediaFeatures || {},
23
+ mediaValueKeywords = parserConfig.mediaValueKeywords || {},
24
+ propertyKeywords = parserConfig.propertyKeywords || {},
25
+ nonStandardPropertyKeywords = parserConfig.nonStandardPropertyKeywords || {},
26
+ fontProperties = parserConfig.fontProperties || {},
27
+ counterDescriptors = parserConfig.counterDescriptors || {},
28
+ colorKeywords = parserConfig.colorKeywords || {},
29
+ valueKeywords = parserConfig.valueKeywords || {},
30
+ allowNested = parserConfig.allowNested,
31
+ lineComment = parserConfig.lineComment,
32
+ supportsAtComponent = parserConfig.supportsAtComponent === true;
33
+
34
+ var type, override;
35
+ function ret(style, tp) { type = tp; return style; }
36
+
37
+ // Tokenizers
38
+
39
+ function tokenBase(stream, state) {
40
+ var ch = stream.next();
41
+ if (tokenHooks[ch]) {
42
+ var result = tokenHooks[ch](stream, state);
43
+ if (result !== false) return result;
44
+ }
45
+ if (ch == "@") {
46
+ stream.eatWhile(/[\w\\\-]/);
47
+ return ret("def", stream.current());
48
+ } else if (ch == "=" || (ch == "~" || ch == "|") && stream.eat("=")) {
49
+ return ret(null, "compare");
50
+ } else if (ch == "\"" || ch == "'") {
51
+ state.tokenize = tokenString(ch);
52
+ return state.tokenize(stream, state);
53
+ } else if (ch == "#") {
54
+ stream.eatWhile(/[\w\\\-]/);
55
+ return ret("atom", "hash");
56
+ } else if (ch == "!") {
57
+ stream.match(/^\s*\w*/);
58
+ return ret("keyword", "important");
59
+ } else if (/\d/.test(ch) || ch == "." && stream.eat(/\d/)) {
60
+ stream.eatWhile(/[\w.%]/);
61
+ return ret("number", "unit");
62
+ } else if (ch === "-") {
63
+ if (/[\d.]/.test(stream.peek())) {
64
+ stream.eatWhile(/[\w.%]/);
65
+ return ret("number", "unit");
66
+ } else if (stream.match(/^-[\w\\\-]+/)) {
67
+ stream.eatWhile(/[\w\\\-]/);
68
+ if (stream.match(/^\s*:/, false))
69
+ return ret("variable-2", "variable-definition");
70
+ return ret("variable-2", "variable");
71
+ } else if (stream.match(/^\w+-/)) {
72
+ return ret("meta", "meta");
73
+ }
74
+ } else if (/[,+>*\/]/.test(ch)) {
75
+ return ret(null, "select-op");
76
+ } else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {
77
+ return ret("qualifier", "qualifier");
78
+ } else if (/[:;{}\[\]\(\)]/.test(ch)) {
79
+ return ret(null, ch);
80
+ } else if ((ch == "u" && stream.match(/rl(-prefix)?\(/)) ||
81
+ (ch == "d" && stream.match("omain(")) ||
82
+ (ch == "r" && stream.match("egexp("))) {
83
+ stream.backUp(1);
84
+ state.tokenize = tokenParenthesized;
85
+ return ret("property", "word");
86
+ } else if (/[\w\\\-]/.test(ch)) {
87
+ stream.eatWhile(/[\w\\\-]/);
88
+ return ret("property", "word");
89
+ } else {
90
+ return ret(null, null);
91
+ }
92
+ }
93
+
94
+ function tokenString(quote) {
95
+ return function(stream, state) {
96
+ var escaped = false, ch;
97
+ while ((ch = stream.next()) != null) {
98
+ if (ch == quote && !escaped) {
99
+ if (quote == ")") stream.backUp(1);
100
+ break;
101
+ }
102
+ escaped = !escaped && ch == "\\";
103
+ }
104
+ if (ch == quote || !escaped && quote != ")") state.tokenize = null;
105
+ return ret("string", "string");
106
+ };
107
+ }
108
+
109
+ function tokenParenthesized(stream, state) {
110
+ stream.next(); // Must be '('
111
+ if (!stream.match(/\s*[\"\')]/, false))
112
+ state.tokenize = tokenString(")");
113
+ else
114
+ state.tokenize = null;
115
+ return ret(null, "(");
116
+ }
117
+
118
+ // Context management
119
+
120
+ function Context(type, indent, prev) {
121
+ this.type = type;
122
+ this.indent = indent;
123
+ this.prev = prev;
124
+ }
125
+
126
+ function pushContext(state, stream, type, indent) {
127
+ state.context = new Context(type, stream.indentation() + (indent === false ? 0 : indentUnit), state.context);
128
+ return type;
129
+ }
130
+
131
+ function popContext(state) {
132
+ if (state.context.prev)
133
+ state.context = state.context.prev;
134
+ return state.context.type;
135
+ }
136
+
137
+ function pass(type, stream, state) {
138
+ return states[state.context.type](type, stream, state);
139
+ }
140
+ function popAndPass(type, stream, state, n) {
141
+ for (var i = n || 1; i > 0; i--)
142
+ state.context = state.context.prev;
143
+ return pass(type, stream, state);
144
+ }
145
+
146
+ // Parser
147
+
148
+ function wordAsValue(stream) {
149
+ var word = stream.current().toLowerCase();
150
+ if (valueKeywords.hasOwnProperty(word))
151
+ override = "atom";
152
+ else if (colorKeywords.hasOwnProperty(word))
153
+ override = "keyword";
154
+ else
155
+ override = "variable";
156
+ }
157
+
158
+ var states = {};
159
+
160
+ states.top = function(type, stream, state) {
161
+ if (type == "{") {
162
+ return pushContext(state, stream, "block");
163
+ } else if (type == "}" && state.context.prev) {
164
+ return popContext(state);
165
+ } else if (supportsAtComponent && /@component/.test(type)) {
166
+ return pushContext(state, stream, "atComponentBlock");
167
+ } else if (/^@(-moz-)?document$/.test(type)) {
168
+ return pushContext(state, stream, "documentTypes");
169
+ } else if (/^@(media|supports|(-moz-)?document|import)$/.test(type)) {
170
+ return pushContext(state, stream, "atBlock");
171
+ } else if (/^@(font-face|counter-style)/.test(type)) {
172
+ state.stateArg = type;
173
+ return "restricted_atBlock_before";
174
+ } else if (/^@(-(moz|ms|o|webkit)-)?keyframes$/.test(type)) {
175
+ return "keyframes";
176
+ } else if (type && type.charAt(0) == "@") {
177
+ return pushContext(state, stream, "at");
178
+ } else if (type == "hash") {
179
+ override = "builtin";
180
+ } else if (type == "word") {
181
+ override = "tag";
182
+ } else if (type == "variable-definition") {
183
+ return "maybeprop";
184
+ } else if (type == "interpolation") {
185
+ return pushContext(state, stream, "interpolation");
186
+ } else if (type == ":") {
187
+ return "pseudo";
188
+ } else if (allowNested && type == "(") {
189
+ return pushContext(state, stream, "parens");
190
+ }
191
+ return state.context.type;
192
+ };
193
+
194
+ states.block = function(type, stream, state) {
195
+ if (type == "word") {
196
+ var word = stream.current().toLowerCase();
197
+ if (propertyKeywords.hasOwnProperty(word)) {
198
+ override = "property";
199
+ return "maybeprop";
200
+ } else if (nonStandardPropertyKeywords.hasOwnProperty(word)) {
201
+ override = "string-2";
202
+ return "maybeprop";
203
+ } else if (allowNested) {
204
+ override = stream.match(/^\s*:(?:\s|$)/, false) ? "property" : "tag";
205
+ return "block";
206
+ } else {
207
+ override += " error";
208
+ return "maybeprop";
209
+ }
210
+ } else if (type == "meta") {
211
+ return "block";
212
+ } else if (!allowNested && (type == "hash" || type == "qualifier")) {
213
+ override = "error";
214
+ return "block";
215
+ } else {
216
+ return states.top(type, stream, state);
217
+ }
218
+ };
219
+
220
+ states.maybeprop = function(type, stream, state) {
221
+ if (type == ":") return pushContext(state, stream, "prop");
222
+ return pass(type, stream, state);
223
+ };
224
+
225
+ states.prop = function(type, stream, state) {
226
+ if (type == ";") return popContext(state);
227
+ if (type == "{" && allowNested) return pushContext(state, stream, "propBlock");
228
+ if (type == "}" || type == "{") return popAndPass(type, stream, state);
229
+ if (type == "(") return pushContext(state, stream, "parens");
230
+
231
+ if (type == "hash" && !/^#([0-9a-fA-f]{3,4}|[0-9a-fA-f]{6}|[0-9a-fA-f]{8})$/.test(stream.current())) {
232
+ override += " error";
233
+ } else if (type == "word") {
234
+ wordAsValue(stream);
235
+ } else if (type == "interpolation") {
236
+ return pushContext(state, stream, "interpolation");
237
+ }
238
+ return "prop";
239
+ };
240
+
241
+ states.propBlock = function(type, _stream, state) {
242
+ if (type == "}") return popContext(state);
243
+ if (type == "word") { override = "property"; return "maybeprop"; }
244
+ return state.context.type;
245
+ };
246
+
247
+ states.parens = function(type, stream, state) {
248
+ if (type == "{" || type == "}") return popAndPass(type, stream, state);
249
+ if (type == ")") return popContext(state);
250
+ if (type == "(") return pushContext(state, stream, "parens");
251
+ if (type == "interpolation") return pushContext(state, stream, "interpolation");
252
+ if (type == "word") wordAsValue(stream);
253
+ return "parens";
254
+ };
255
+
256
+ states.pseudo = function(type, stream, state) {
257
+ if (type == "meta") return "pseudo";
258
+
259
+ if (type == "word") {
260
+ override = "variable-3";
261
+ return state.context.type;
262
+ }
263
+ return pass(type, stream, state);
264
+ };
265
+
266
+ states.documentTypes = function(type, stream, state) {
267
+ if (type == "word" && documentTypes.hasOwnProperty(stream.current())) {
268
+ override = "tag";
269
+ return state.context.type;
270
+ } else {
271
+ return states.atBlock(type, stream, state);
272
+ }
273
+ };
274
+
275
+ states.atBlock = function(type, stream, state) {
276
+ if (type == "(") return pushContext(state, stream, "atBlock_parens");
277
+ if (type == "}" || type == ";") return popAndPass(type, stream, state);
278
+ if (type == "{") return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top");
279
+
280
+ if (type == "interpolation") return pushContext(state, stream, "interpolation");
281
+
282
+ if (type == "word") {
283
+ var word = stream.current().toLowerCase();
284
+ if (word == "only" || word == "not" || word == "and" || word == "or")
285
+ override = "keyword";
286
+ else if (mediaTypes.hasOwnProperty(word))
287
+ override = "attribute";
288
+ else if (mediaFeatures.hasOwnProperty(word))
289
+ override = "property";
290
+ else if (mediaValueKeywords.hasOwnProperty(word))
291
+ override = "keyword";
292
+ else if (propertyKeywords.hasOwnProperty(word))
293
+ override = "property";
294
+ else if (nonStandardPropertyKeywords.hasOwnProperty(word))
295
+ override = "string-2";
296
+ else if (valueKeywords.hasOwnProperty(word))
297
+ override = "atom";
298
+ else if (colorKeywords.hasOwnProperty(word))
299
+ override = "keyword";
300
+ else
301
+ override = "error";
302
+ }
303
+ return state.context.type;
304
+ };
305
+
306
+ states.atComponentBlock = function(type, stream, state) {
307
+ if (type == "}")
308
+ return popAndPass(type, stream, state);
309
+ if (type == "{")
310
+ return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top", false);
311
+ if (type == "word")
312
+ override = "error";
313
+ return state.context.type;
314
+ };
315
+
316
+ states.atBlock_parens = function(type, stream, state) {
317
+ if (type == ")") return popContext(state);
318
+ if (type == "{" || type == "}") return popAndPass(type, stream, state, 2);
319
+ return states.atBlock(type, stream, state);
320
+ };
321
+
322
+ states.restricted_atBlock_before = function(type, stream, state) {
323
+ if (type == "{")
324
+ return pushContext(state, stream, "restricted_atBlock");
325
+ if (type == "word" && state.stateArg == "@counter-style") {
326
+ override = "variable";
327
+ return "restricted_atBlock_before";
328
+ }
329
+ return pass(type, stream, state);
330
+ };
331
+
332
+ states.restricted_atBlock = function(type, stream, state) {
333
+ if (type == "}") {
334
+ state.stateArg = null;
335
+ return popContext(state);
336
+ }
337
+ if (type == "word") {
338
+ if ((state.stateArg == "@font-face" && !fontProperties.hasOwnProperty(stream.current().toLowerCase())) ||
339
+ (state.stateArg == "@counter-style" && !counterDescriptors.hasOwnProperty(stream.current().toLowerCase())))
340
+ override = "error";
341
+ else
342
+ override = "property";
343
+ return "maybeprop";
344
+ }
345
+ return "restricted_atBlock";
346
+ };
347
+
348
+ states.keyframes = function(type, stream, state) {
349
+ if (type == "word") { override = "variable"; return "keyframes"; }
350
+ if (type == "{") return pushContext(state, stream, "top");
351
+ return pass(type, stream, state);
352
+ };
353
+
354
+ states.at = function(type, stream, state) {
355
+ if (type == ";") return popContext(state);
356
+ if (type == "{" || type == "}") return popAndPass(type, stream, state);
357
+ if (type == "word") override = "tag";
358
+ else if (type == "hash") override = "builtin";
359
+ return "at";
360
+ };
361
+
362
+ states.interpolation = function(type, stream, state) {
363
+ if (type == "}") return popContext(state);
364
+ if (type == "{" || type == ";") return popAndPass(type, stream, state);
365
+ if (type == "word") override = "variable";
366
+ else if (type != "variable" && type != "(" && type != ")") override = "error";
367
+ return "interpolation";
368
+ };
369
+
370
+ return {
371
+ startState: function(base) {
372
+ return {tokenize: null,
373
+ state: inline ? "block" : "top",
374
+ stateArg: null,
375
+ context: new Context(inline ? "block" : "top", base || 0, null)};
376
+ },
377
+
378
+ token: function(stream, state) {
379
+ if (!state.tokenize && stream.eatSpace()) return null;
380
+ var style = (state.tokenize || tokenBase)(stream, state);
381
+ if (style && typeof style == "object") {
382
+ type = style[1];
383
+ style = style[0];
384
+ }
385
+ override = style;
386
+ if (type != "comment")
387
+ state.state = states[state.state](type, stream, state);
388
+ return override;
389
+ },
390
+
391
+ indent: function(state, textAfter) {
392
+ var cx = state.context, ch = textAfter && textAfter.charAt(0);
393
+ var indent = cx.indent;
394
+ if (cx.type == "prop" && (ch == "}" || ch == ")")) cx = cx.prev;
395
+ if (cx.prev) {
396
+ if (ch == "}" && (cx.type == "block" || cx.type == "top" ||
397
+ cx.type == "interpolation" || cx.type == "restricted_atBlock")) {
398
+ // Resume indentation from parent context.
399
+ cx = cx.prev;
400
+ indent = cx.indent;
401
+ } else if (ch == ")" && (cx.type == "parens" || cx.type == "atBlock_parens") ||
402
+ ch == "{" && (cx.type == "at" || cx.type == "atBlock")) {
403
+ // Dedent relative to current context.
404
+ indent = Math.max(0, cx.indent - indentUnit);
405
+ }
406
+ }
407
+ return indent;
408
+ },
409
+
410
+ electricChars: "}",
411
+ blockCommentStart: "/*",
412
+ blockCommentEnd: "*/",
413
+ blockCommentContinue: " * ",
414
+ lineComment: lineComment,
415
+ fold: "brace"
416
+ };
417
+ });
418
+
419
+ function keySet(array) {
420
+ var keys = {};
421
+ for (var i = 0; i < array.length; ++i) {
422
+ keys[array[i].toLowerCase()] = true;
423
+ }
424
+ return keys;
425
+ }
426
+
427
+ var documentTypes_ = [
428
+ "domain", "regexp", "url", "url-prefix"
429
+ ], documentTypes = keySet(documentTypes_);
430
+
431
+ var mediaTypes_ = [
432
+ "all", "aural", "braille", "handheld", "print", "projection", "screen",
433
+ "tty", "tv", "embossed"
434
+ ], mediaTypes = keySet(mediaTypes_);
435
+
436
+ var mediaFeatures_ = [
437
+ "width", "min-width", "max-width", "height", "min-height", "max-height",
438
+ "device-width", "min-device-width", "max-device-width", "device-height",
439
+ "min-device-height", "max-device-height", "aspect-ratio",
440
+ "min-aspect-ratio", "max-aspect-ratio", "device-aspect-ratio",
441
+ "min-device-aspect-ratio", "max-device-aspect-ratio", "color", "min-color",
442
+ "max-color", "color-index", "min-color-index", "max-color-index",
443
+ "monochrome", "min-monochrome", "max-monochrome", "resolution",
444
+ "min-resolution", "max-resolution", "scan", "grid", "orientation",
445
+ "device-pixel-ratio", "min-device-pixel-ratio", "max-device-pixel-ratio",
446
+ "pointer", "any-pointer", "hover", "any-hover"
447
+ ], mediaFeatures = keySet(mediaFeatures_);
448
+
449
+ var mediaValueKeywords_ = [
450
+ "landscape", "portrait", "none", "coarse", "fine", "on-demand", "hover",
451
+ "interlace", "progressive"
452
+ ], mediaValueKeywords = keySet(mediaValueKeywords_);
453
+
454
+ var propertyKeywords_ = [
455
+ "align-content", "align-items", "align-self", "alignment-adjust",
456
+ "alignment-baseline", "anchor-point", "animation", "animation-delay",
457
+ "animation-direction", "animation-duration", "animation-fill-mode",
458
+ "animation-iteration-count", "animation-name", "animation-play-state",
459
+ "animation-timing-function", "appearance", "azimuth", "backface-visibility",
460
+ "background", "background-attachment", "background-blend-mode", "background-clip",
461
+ "background-color", "background-image", "background-origin", "background-position",
462
+ "background-repeat", "background-size", "baseline-shift", "binding",
463
+ "bleed", "bookmark-label", "bookmark-level", "bookmark-state",
464
+ "bookmark-target", "border", "border-bottom", "border-bottom-color",
465
+ "border-bottom-left-radius", "border-bottom-right-radius",
466
+ "border-bottom-style", "border-bottom-width", "border-collapse",
467
+ "border-color", "border-image", "border-image-outset",
468
+ "border-image-repeat", "border-image-slice", "border-image-source",
469
+ "border-image-width", "border-left", "border-left-color",
470
+ "border-left-style", "border-left-width", "border-radius", "border-right",
471
+ "border-right-color", "border-right-style", "border-right-width",
472
+ "border-spacing", "border-style", "border-top", "border-top-color",
473
+ "border-top-left-radius", "border-top-right-radius", "border-top-style",
474
+ "border-top-width", "border-width", "bottom", "box-decoration-break",
475
+ "box-shadow", "box-sizing", "break-after", "break-before", "break-inside",
476
+ "caption-side", "caret-color", "clear", "clip", "color", "color-profile", "column-count",
477
+ "column-fill", "column-gap", "column-rule", "column-rule-color",
478
+ "column-rule-style", "column-rule-width", "column-span", "column-width",
479
+ "columns", "content", "counter-increment", "counter-reset", "crop", "cue",
480
+ "cue-after", "cue-before", "cursor", "direction", "display",
481
+ "dominant-baseline", "drop-initial-after-adjust",
482
+ "drop-initial-after-align", "drop-initial-before-adjust",
483
+ "drop-initial-before-align", "drop-initial-size", "drop-initial-value",
484
+ "elevation", "empty-cells", "fit", "fit-position", "flex", "flex-basis",
485
+ "flex-direction", "flex-flow", "flex-grow", "flex-shrink", "flex-wrap",
486
+ "float", "float-offset", "flow-from", "flow-into", "font", "font-feature-settings",
487
+ "font-family", "font-kerning", "font-language-override", "font-size", "font-size-adjust",
488
+ "font-stretch", "font-style", "font-synthesis", "font-variant",
489
+ "font-variant-alternates", "font-variant-caps", "font-variant-east-asian",
490
+ "font-variant-ligatures", "font-variant-numeric", "font-variant-position",
491
+ "font-weight", "grid", "grid-area", "grid-auto-columns", "grid-auto-flow",
492
+ "grid-auto-rows", "grid-column", "grid-column-end", "grid-column-gap",
493
+ "grid-column-start", "grid-gap", "grid-row", "grid-row-end", "grid-row-gap",
494
+ "grid-row-start", "grid-template", "grid-template-areas", "grid-template-columns",
495
+ "grid-template-rows", "hanging-punctuation", "height", "hyphens",
496
+ "icon", "image-orientation", "image-rendering", "image-resolution",
497
+ "inline-box-align", "justify-content", "justify-items", "justify-self", "left", "letter-spacing",
498
+ "line-break", "line-height", "line-stacking", "line-stacking-ruby",
499
+ "line-stacking-shift", "line-stacking-strategy", "list-style",
500
+ "list-style-image", "list-style-position", "list-style-type", "margin",
501
+ "margin-bottom", "margin-left", "margin-right", "margin-top",
502
+ "marks", "marquee-direction", "marquee-loop",
503
+ "marquee-play-count", "marquee-speed", "marquee-style", "max-height",
504
+ "max-width", "min-height", "min-width", "move-to", "nav-down", "nav-index",
505
+ "nav-left", "nav-right", "nav-up", "object-fit", "object-position",
506
+ "opacity", "order", "orphans", "outline",
507
+ "outline-color", "outline-offset", "outline-style", "outline-width",
508
+ "overflow", "overflow-style", "overflow-wrap", "overflow-x", "overflow-y",
509
+ "padding", "padding-bottom", "padding-left", "padding-right", "padding-top",
510
+ "page", "page-break-after", "page-break-before", "page-break-inside",
511
+ "page-policy", "pause", "pause-after", "pause-before", "perspective",
512
+ "perspective-origin", "pitch", "pitch-range", "place-content", "place-items", "place-self", "play-during", "position",
513
+ "presentation-level", "punctuation-trim", "quotes", "region-break-after",
514
+ "region-break-before", "region-break-inside", "region-fragment",
515
+ "rendering-intent", "resize", "rest", "rest-after", "rest-before", "richness",
516
+ "right", "rotation", "rotation-point", "ruby-align", "ruby-overhang",
517
+ "ruby-position", "ruby-span", "shape-image-threshold", "shape-inside", "shape-margin",
518
+ "shape-outside", "size", "speak", "speak-as", "speak-header",
519
+ "speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set",
520
+ "tab-size", "table-layout", "target", "target-name", "target-new",
521
+ "target-position", "text-align", "text-align-last", "text-decoration",
522
+ "text-decoration-color", "text-decoration-line", "text-decoration-skip",
523
+ "text-decoration-style", "text-emphasis", "text-emphasis-color",
524
+ "text-emphasis-position", "text-emphasis-style", "text-height",
525
+ "text-indent", "text-justify", "text-outline", "text-overflow", "text-shadow",
526
+ "text-size-adjust", "text-space-collapse", "text-transform", "text-underline-position",
527
+ "text-wrap", "top", "transform", "transform-origin", "transform-style",
528
+ "transition", "transition-delay", "transition-duration",
529
+ "transition-property", "transition-timing-function", "unicode-bidi",
530
+ "user-select", "vertical-align", "visibility", "voice-balance", "voice-duration",
531
+ "voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress",
532
+ "voice-volume", "volume", "white-space", "widows", "width", "will-change", "word-break",
533
+ "word-spacing", "word-wrap", "z-index",
534
+ // SVG-specific
535
+ "clip-path", "clip-rule", "mask", "enable-background", "filter", "flood-color",
536
+ "flood-opacity", "lighting-color", "stop-color", "stop-opacity", "pointer-events",
537
+ "color-interpolation", "color-interpolation-filters",
538
+ "color-rendering", "fill", "fill-opacity", "fill-rule", "image-rendering",
539
+ "marker", "marker-end", "marker-mid", "marker-start", "shape-rendering", "stroke",
540
+ "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin",
541
+ "stroke-miterlimit", "stroke-opacity", "stroke-width", "text-rendering",
542
+ "baseline-shift", "dominant-baseline", "glyph-orientation-horizontal",
543
+ "glyph-orientation-vertical", "text-anchor", "writing-mode"
544
+ ], propertyKeywords = keySet(propertyKeywords_);
545
+
546
+ var nonStandardPropertyKeywords_ = [
547
+ "scrollbar-arrow-color", "scrollbar-base-color", "scrollbar-dark-shadow-color",
548
+ "scrollbar-face-color", "scrollbar-highlight-color", "scrollbar-shadow-color",
549
+ "scrollbar-3d-light-color", "scrollbar-track-color", "shape-inside",
550
+ "searchfield-cancel-button", "searchfield-decoration", "searchfield-results-button",
551
+ "searchfield-results-decoration", "zoom"
552
+ ], nonStandardPropertyKeywords = keySet(nonStandardPropertyKeywords_);
553
+
554
+ var fontProperties_ = [
555
+ "font-family", "src", "unicode-range", "font-variant", "font-feature-settings",
556
+ "font-stretch", "font-weight", "font-style"
557
+ ], fontProperties = keySet(fontProperties_);
558
+
559
+ var counterDescriptors_ = [
560
+ "additive-symbols", "fallback", "negative", "pad", "prefix", "range",
561
+ "speak-as", "suffix", "symbols", "system"
562
+ ], counterDescriptors = keySet(counterDescriptors_);
563
+
564
+ var colorKeywords_ = [
565
+ "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige",
566
+ "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown",
567
+ "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue",
568
+ "cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod",
569
+ "darkgray", "darkgreen", "darkkhaki", "darkmagenta", "darkolivegreen",
570
+ "darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen",
571
+ "darkslateblue", "darkslategray", "darkturquoise", "darkviolet",
572
+ "deeppink", "deepskyblue", "dimgray", "dodgerblue", "firebrick",
573
+ "floralwhite", "forestgreen", "fuchsia", "gainsboro", "ghostwhite",
574
+ "gold", "goldenrod", "gray", "grey", "green", "greenyellow", "honeydew",
575
+ "hotpink", "indianred", "indigo", "ivory", "khaki", "lavender",
576
+ "lavenderblush", "lawngreen", "lemonchiffon", "lightblue", "lightcoral",
577
+ "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightpink",
578
+ "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray",
579
+ "lightsteelblue", "lightyellow", "lime", "limegreen", "linen", "magenta",
580
+ "maroon", "mediumaquamarine", "mediumblue", "mediumorchid", "mediumpurple",
581
+ "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise",
582
+ "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin",
583
+ "navajowhite", "navy", "oldlace", "olive", "olivedrab", "orange", "orangered",
584
+ "orchid", "palegoldenrod", "palegreen", "paleturquoise", "palevioletred",
585
+ "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue",
586
+ "purple", "rebeccapurple", "red", "rosybrown", "royalblue", "saddlebrown",
587
+ "salmon", "sandybrown", "seagreen", "seashell", "sienna", "silver", "skyblue",
588
+ "slateblue", "slategray", "snow", "springgreen", "steelblue", "tan",
589
+ "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white",
590
+ "whitesmoke", "yellow", "yellowgreen"
591
+ ], colorKeywords = keySet(colorKeywords_);
592
+
593
+ var valueKeywords_ = [
594
+ "above", "absolute", "activeborder", "additive", "activecaption", "afar",
595
+ "after-white-space", "ahead", "alias", "all", "all-scroll", "alphabetic", "alternate",
596
+ "always", "amharic", "amharic-abegede", "antialiased", "appworkspace",
597
+ "arabic-indic", "armenian", "asterisks", "attr", "auto", "auto-flow", "avoid", "avoid-column", "avoid-page",
598
+ "avoid-region", "background", "backwards", "baseline", "below", "bidi-override", "binary",
599
+ "bengali", "blink", "block", "block-axis", "bold", "bolder", "border", "border-box",
600
+ "both", "bottom", "break", "break-all", "break-word", "bullets", "button", "button-bevel",
601
+ "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "calc", "cambodian",
602
+ "capitalize", "caps-lock-indicator", "caption", "captiontext", "caret",
603
+ "cell", "center", "checkbox", "circle", "cjk-decimal", "cjk-earthly-branch",
604
+ "cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote",
605
+ "col-resize", "collapse", "color", "color-burn", "color-dodge", "column", "column-reverse",
606
+ "compact", "condensed", "contain", "content", "contents",
607
+ "content-box", "context-menu", "continuous", "copy", "counter", "counters", "cover", "crop",
608
+ "cross", "crosshair", "currentcolor", "cursive", "cyclic", "darken", "dashed", "decimal",
609
+ "decimal-leading-zero", "default", "default-button", "dense", "destination-atop",
610
+ "destination-in", "destination-out", "destination-over", "devanagari", "difference",
611
+ "disc", "discard", "disclosure-closed", "disclosure-open", "document",
612
+ "dot-dash", "dot-dot-dash",
613
+ "dotted", "double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out",
614
+ "element", "ellipse", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede",
615
+ "ethiopic-abegede-am-et", "ethiopic-abegede-gez", "ethiopic-abegede-ti-er",
616
+ "ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er",
617
+ "ethiopic-halehame-aa-et", "ethiopic-halehame-am-et",
618
+ "ethiopic-halehame-gez", "ethiopic-halehame-om-et",
619
+ "ethiopic-halehame-sid-et", "ethiopic-halehame-so-et",
620
+ "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et", "ethiopic-halehame-tig",
621
+ "ethiopic-numeric", "ew-resize", "exclusion", "expanded", "extends", "extra-condensed",
622
+ "extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "flex", "flex-end", "flex-start", "footnotes",
623
+ "forwards", "from", "geometricPrecision", "georgian", "graytext", "grid", "groove",
624
+ "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hard-light", "hebrew",
625
+ "help", "hidden", "hide", "higher", "highlight", "highlighttext",
626
+ "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "hue", "icon", "ignore",
627
+ "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite",
628
+ "infobackground", "infotext", "inherit", "initial", "inline", "inline-axis",
629
+ "inline-block", "inline-flex", "inline-grid", "inline-table", "inset", "inside", "intrinsic", "invert",
630
+ "italic", "japanese-formal", "japanese-informal", "justify", "kannada",
631
+ "katakana", "katakana-iroha", "keep-all", "khmer",
632
+ "korean-hangul-formal", "korean-hanja-formal", "korean-hanja-informal",
633
+ "landscape", "lao", "large", "larger", "left", "level", "lighter", "lighten",
634
+ "line-through", "linear", "linear-gradient", "lines", "list-item", "listbox", "listitem",
635
+ "local", "logical", "loud", "lower", "lower-alpha", "lower-armenian",
636
+ "lower-greek", "lower-hexadecimal", "lower-latin", "lower-norwegian",
637
+ "lower-roman", "lowercase", "ltr", "luminosity", "malayalam", "match", "matrix", "matrix3d",
638
+ "media-controls-background", "media-current-time-display",
639
+ "media-fullscreen-button", "media-mute-button", "media-play-button",
640
+ "media-return-to-realtime-button", "media-rewind-button",
641
+ "media-seek-back-button", "media-seek-forward-button", "media-slider",
642
+ "media-sliderthumb", "media-time-remaining-display", "media-volume-slider",
643
+ "media-volume-slider-container", "media-volume-sliderthumb", "medium",
644
+ "menu", "menulist", "menulist-button", "menulist-text",
645
+ "menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic",
646
+ "mix", "mongolian", "monospace", "move", "multiple", "multiply", "myanmar", "n-resize",
647
+ "narrower", "ne-resize", "nesw-resize", "no-close-quote", "no-drop",
648
+ "no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap",
649
+ "ns-resize", "numbers", "numeric", "nw-resize", "nwse-resize", "oblique", "octal", "opacity", "open-quote",
650
+ "optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset",
651
+ "outside", "outside-shape", "overlay", "overline", "padding", "padding-box",
652
+ "painted", "page", "paused", "persian", "perspective", "plus-darker", "plus-lighter",
653
+ "pointer", "polygon", "portrait", "pre", "pre-line", "pre-wrap", "preserve-3d",
654
+ "progress", "push-button", "radial-gradient", "radio", "read-only",
655
+ "read-write", "read-write-plaintext-only", "rectangle", "region",
656
+ "relative", "repeat", "repeating-linear-gradient",
657
+ "repeating-radial-gradient", "repeat-x", "repeat-y", "reset", "reverse",
658
+ "rgb", "rgba", "ridge", "right", "rotate", "rotate3d", "rotateX", "rotateY",
659
+ "rotateZ", "round", "row", "row-resize", "row-reverse", "rtl", "run-in", "running",
660
+ "s-resize", "sans-serif", "saturation", "scale", "scale3d", "scaleX", "scaleY", "scaleZ", "screen",
661
+ "scroll", "scrollbar", "scroll-position", "se-resize", "searchfield",
662
+ "searchfield-cancel-button", "searchfield-decoration",
663
+ "searchfield-results-button", "searchfield-results-decoration", "self-start", "self-end",
664
+ "semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama",
665
+ "simp-chinese-formal", "simp-chinese-informal", "single",
666
+ "skew", "skewX", "skewY", "skip-white-space", "slide", "slider-horizontal",
667
+ "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow",
668
+ "small", "small-caps", "small-caption", "smaller", "soft-light", "solid", "somali",
669
+ "source-atop", "source-in", "source-out", "source-over", "space", "space-around", "space-between", "space-evenly", "spell-out", "square",
670
+ "square-button", "start", "static", "status-bar", "stretch", "stroke", "sub",
671
+ "subpixel-antialiased", "super", "sw-resize", "symbolic", "symbols", "system-ui", "table",
672
+ "table-caption", "table-cell", "table-column", "table-column-group",
673
+ "table-footer-group", "table-header-group", "table-row", "table-row-group",
674
+ "tamil",
675
+ "telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai",
676
+ "thick", "thin", "threeddarkshadow", "threedface", "threedhighlight",
677
+ "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er",
678
+ "tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top",
679
+ "trad-chinese-formal", "trad-chinese-informal", "transform",
680
+ "translate", "translate3d", "translateX", "translateY", "translateZ",
681
+ "transparent", "ultra-condensed", "ultra-expanded", "underline", "unset", "up",
682
+ "upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal",
683
+ "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url",
684
+ "var", "vertical", "vertical-text", "visible", "visibleFill", "visiblePainted",
685
+ "visibleStroke", "visual", "w-resize", "wait", "wave", "wider",
686
+ "window", "windowframe", "windowtext", "words", "wrap", "wrap-reverse", "x-large", "x-small", "xor",
687
+ "xx-large", "xx-small"
688
+ ], valueKeywords = keySet(valueKeywords_);
689
+
690
+ var allWords = documentTypes_.concat(mediaTypes_).concat(mediaFeatures_).concat(mediaValueKeywords_)
691
+ .concat(propertyKeywords_).concat(nonStandardPropertyKeywords_).concat(colorKeywords_)
692
+ .concat(valueKeywords_);
693
+ CodeMirror.registerHelper("hintWords", "css", allWords);
694
+
695
+ function tokenCComment(stream, state) {
696
+ var maybeEnd = false, ch;
697
+ while ((ch = stream.next()) != null) {
698
+ if (maybeEnd && ch == "/") {
699
+ state.tokenize = null;
700
+ break;
701
+ }
702
+ maybeEnd = (ch == "*");
703
+ }
704
+ return ["comment", "comment"];
705
+ }
706
+
707
+ CodeMirror.defineMIME("text/css", {
708
+ documentTypes: documentTypes,
709
+ mediaTypes: mediaTypes,
710
+ mediaFeatures: mediaFeatures,
711
+ mediaValueKeywords: mediaValueKeywords,
712
+ propertyKeywords: propertyKeywords,
713
+ nonStandardPropertyKeywords: nonStandardPropertyKeywords,
714
+ fontProperties: fontProperties,
715
+ counterDescriptors: counterDescriptors,
716
+ colorKeywords: colorKeywords,
717
+ valueKeywords: valueKeywords,
718
+ tokenHooks: {
719
+ "/": function(stream, state) {
720
+ if (!stream.eat("*")) return false;
721
+ state.tokenize = tokenCComment;
722
+ return tokenCComment(stream, state);
723
+ }
724
+ },
725
+ name: "css"
726
+ });
727
+
728
+ CodeMirror.defineMIME("text/x-scss", {
729
+ mediaTypes: mediaTypes,
730
+ mediaFeatures: mediaFeatures,
731
+ mediaValueKeywords: mediaValueKeywords,
732
+ propertyKeywords: propertyKeywords,
733
+ nonStandardPropertyKeywords: nonStandardPropertyKeywords,
734
+ colorKeywords: colorKeywords,
735
+ valueKeywords: valueKeywords,
736
+ fontProperties: fontProperties,
737
+ allowNested: true,
738
+ lineComment: "//",
739
+ tokenHooks: {
740
+ "/": function(stream, state) {
741
+ if (stream.eat("/")) {
742
+ stream.skipToEnd();
743
+ return ["comment", "comment"];
744
+ } else if (stream.eat("*")) {
745
+ state.tokenize = tokenCComment;
746
+ return tokenCComment(stream, state);
747
+ } else {
748
+ return ["operator", "operator"];
749
+ }
750
+ },
751
+ ":": function(stream) {
752
+ if (stream.match(/\s*\{/, false))
753
+ return [null, null]
754
+ return false;
755
+ },
756
+ "$": function(stream) {
757
+ stream.match(/^[\w-]+/);
758
+ if (stream.match(/^\s*:/, false))
759
+ return ["variable-2", "variable-definition"];
760
+ return ["variable-2", "variable"];
761
+ },
762
+ "#": function(stream) {
763
+ if (!stream.eat("{")) return false;
764
+ return [null, "interpolation"];
765
+ }
766
+ },
767
+ name: "css",
768
+ helperType: "scss"
769
+ });
770
+
771
+ CodeMirror.defineMIME("text/x-less", {
772
+ mediaTypes: mediaTypes,
773
+ mediaFeatures: mediaFeatures,
774
+ mediaValueKeywords: mediaValueKeywords,
775
+ propertyKeywords: propertyKeywords,
776
+ nonStandardPropertyKeywords: nonStandardPropertyKeywords,
777
+ colorKeywords: colorKeywords,
778
+ valueKeywords: valueKeywords,
779
+ fontProperties: fontProperties,
780
+ allowNested: true,
781
+ lineComment: "//",
782
+ tokenHooks: {
783
+ "/": function(stream, state) {
784
+ if (stream.eat("/")) {
785
+ stream.skipToEnd();
786
+ return ["comment", "comment"];
787
+ } else if (stream.eat("*")) {
788
+ state.tokenize = tokenCComment;
789
+ return tokenCComment(stream, state);
790
+ } else {
791
+ return ["operator", "operator"];
792
+ }
793
+ },
794
+ "@": function(stream) {
795
+ if (stream.eat("{")) return [null, "interpolation"];
796
+ if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/, false)) return false;
797
+ stream.eatWhile(/[\w\\\-]/);
798
+ if (stream.match(/^\s*:/, false))
799
+ return ["variable-2", "variable-definition"];
800
+ return ["variable-2", "variable"];
801
+ },
802
+ "&": function() {
803
+ return ["atom", "atom"];
804
+ }
805
+ },
806
+ name: "css",
807
+ helperType: "less"
808
+ });
809
+
810
+ CodeMirror.defineMIME("text/x-gss", {
811
+ documentTypes: documentTypes,
812
+ mediaTypes: mediaTypes,
813
+ mediaFeatures: mediaFeatures,
814
+ propertyKeywords: propertyKeywords,
815
+ nonStandardPropertyKeywords: nonStandardPropertyKeywords,
816
+ fontProperties: fontProperties,
817
+ counterDescriptors: counterDescriptors,
818
+ colorKeywords: colorKeywords,
819
+ valueKeywords: valueKeywords,
820
+ supportsAtComponent: true,
821
+ tokenHooks: {
822
+ "/": function(stream, state) {
823
+ if (!stream.eat("*")) return false;
824
+ state.tokenize = tokenCComment;
825
+ return tokenCComment(stream, state);
826
+ }
827
+ },
828
+ name: "css",
829
+ helperType: "gss"
830
+ });
831
+
832
+ });
lib/codemirror/theme/tomorrow-night-bright.css ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+
3
+ Name: Tomorrow Night - Bright
4
+ Author: Chris Kempson
5
+
6
+ Port done by Gerard Braad <me@gbraad.nl>
7
+
8
+ */
9
+
10
+ .cm-s-tomorrow-night-bright.CodeMirror { background: #000000; color: #eaeaea; }
11
+ .cm-s-tomorrow-night-bright div.CodeMirror-selected { background: #424242; }
12
+ .cm-s-tomorrow-night-bright .CodeMirror-gutters { background: #000000; border-right: 0px; }
13
+ .cm-s-tomorrow-night-bright .CodeMirror-guttermarker { color: #e78c45; }
14
+ .cm-s-tomorrow-night-bright .CodeMirror-guttermarker-subtle { color: #777; }
15
+ .cm-s-tomorrow-night-bright .CodeMirror-linenumber { color: #424242; }
16
+ .cm-s-tomorrow-night-bright .CodeMirror-cursor { border-left: 1px solid #6A6A6A; }
17
+
18
+ .cm-s-tomorrow-night-bright span.cm-comment { color: #d27b53; }
19
+ .cm-s-tomorrow-night-bright span.cm-atom { color: #a16a94; }
20
+ .cm-s-tomorrow-night-bright span.cm-number { color: #a16a94; }
21
+
22
+ .cm-s-tomorrow-night-bright span.cm-property, .cm-s-tomorrow-night-bright span.cm-attribute { color: #99cc99; }
23
+ .cm-s-tomorrow-night-bright span.cm-keyword { color: #d54e53; }
24
+ .cm-s-tomorrow-night-bright span.cm-string { color: #e7c547; }
25
+
26
+ .cm-s-tomorrow-night-bright span.cm-variable { color: #b9ca4a; }
27
+ .cm-s-tomorrow-night-bright span.cm-variable-2 { color: #7aa6da; }
28
+ .cm-s-tomorrow-night-bright span.cm-def { color: #e78c45; }
29
+ .cm-s-tomorrow-night-bright span.cm-bracket { color: #eaeaea; }
30
+ .cm-s-tomorrow-night-bright span.cm-tag { color: #d54e53; }
31
+ .cm-s-tomorrow-night-bright span.cm-link { color: #a16a94; }
32
+ .cm-s-tomorrow-night-bright span.cm-error { background: #d54e53; color: #6A6A6A; }
33
+
34
+ .cm-s-tomorrow-night-bright .CodeMirror-activeline-background { background: #2a2a2a; }
35
+ .cm-s-tomorrow-night-bright .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; }
readme.txt CHANGED
@@ -2,8 +2,8 @@
2
  Contributors: danieledesantis
3
  Tags: css, custom css, style, styles, stylesheet, custom stylesheet, single post css, site css, single page css, custom post type css, product css
4
  Requires at least: 4.0
5
- Tested up to: 4.8
6
- Stable tag: 1.0.1
7
  License: GPLv2
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
 
@@ -63,6 +63,9 @@ Click on the "Screen option" link from your post/page editing area and be sure t
63
 
64
  == Changelog ==
65
 
 
 
 
66
  = 1.0.1 =
67
  * Fixed issues with ssl on some server configurations
68
 
@@ -96,6 +99,9 @@ Click on the "Screen option" link from your post/page editing area and be sure t
96
 
97
  == Upgrade Notice ==
98
 
 
 
 
99
  = 1.0.1 =
100
  * Fixed issues with ssl on some server configurations
101
 
@@ -122,4 +128,4 @@ Click on the "Screen option" link from your post/page editing area and be sure t
122
  * Minor layout change
123
 
124
  = 0.9.1 =
125
- Fixed double quotes rendering issue
2
  Contributors: danieledesantis
3
  Tags: css, custom css, style, styles, stylesheet, custom stylesheet, single post css, site css, single page css, custom post type css, product css
4
  Requires at least: 4.0
5
+ Tested up to: 4.9.4
6
+ Stable tag: 1.1.0
7
  License: GPLv2
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
 
63
 
64
  == Changelog ==
65
 
66
+ = 1.1.0 =
67
+ * Added CodeMirror text editor
68
+
69
  = 1.0.1 =
70
  * Fixed issues with ssl on some server configurations
71
 
99
 
100
  == Upgrade Notice ==
101
 
102
+ = 1.1.0 =
103
+ * Added CodeMirror text editor
104
+
105
  = 1.0.1 =
106
  * Fixed issues with ssl on some server configurations
107
 
128
  * Minor layout change
129
 
130
  = 0.9.1 =
131
+ Fixed double quotes rendering issue
wordpress-add-custom-css.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: WP Add Custom CSS
4
  Plugin URI: http://www.danieledesantis.net
5
  Description: Add custom css to the whole website and to specific posts, pages and custom post types.
6
- Version: 1.0.1
7
  Author: Daniele De Santis
8
  Author URI: http://www.danieledesantis.net
9
  Text Domain: wp-add-custom-css
@@ -12,10 +12,10 @@ License: GPL2
12
  */
13
 
14
  /*
15
- Copyright 2014-2017 Daniele De Santis (email : hello@danieledesantis.net)
16
 
17
  This program is free software; you can redistribute it and/or modify
18
- it under the terms of the GNU General Public License, version 2, as
19
  published by the Free Software Foundation.
20
 
21
  This program is distributed in the hope that it will be useful,
@@ -35,49 +35,73 @@ if(!class_exists('Wpacc'))
35
  class Wpacc
36
  {
37
  private $options;
38
-
39
  public function __construct() {
40
  add_action('admin_menu', array($this, 'add_menu'));
41
  add_action( 'admin_init', array( $this, 'init_settings' ) );
 
42
  add_action( 'add_meta_boxes', array($this, 'add_meta_box' ) );
43
  add_action( 'save_post', array( $this, 'single_save' ) );
44
  add_action('init', array($this, 'init'));
45
  add_filter('query_vars', array($this, 'add_wp_var'));
46
  add_action( 'wp_enqueue_scripts', array($this, 'add_custom_css'), 999 );
47
  add_action('wp_head', array($this, 'single_custom_css'));
48
- }
49
-
50
  public function init() {
51
  load_plugin_textdomain( 'wp-add-custom-css', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' );
52
  }
53
-
54
  public static function uninstall() {
55
- self::delete_options();
56
  self::delete_custom_meta();
57
  }
58
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
  public function add_meta_box( $post_type ) {
60
- $this->options = get_option( 'wpacc_settings' );
61
- $post_types = array('post', 'page');
62
- if ( isset($this->options['selected_post_types']) ) {
63
- $post_types = array_merge( $post_types, $this->options['selected_post_types'] );
64
- }
65
- if ( in_array( $post_type, $post_types )) {
66
  add_meta_box('wp_add_custom_css', __( 'Custom CSS', 'wp-add-custom-css' ), array( $this, 'render_meta_box_content' ), $post_type, 'advanced', 'high');
67
  }
68
  }
69
-
70
  public function single_save( $post_id ) {
71
  if ( ! isset( $_POST['wp_add_custom_css_box_nonce'] ) || ! wp_verify_nonce( $_POST['wp_add_custom_css_box_nonce'], 'single_add_custom_css_box' ) ) {
72
  return;
73
  }
74
- if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
75
  return;
76
  }
77
  if ( 'page' == $_POST['post_type'] ) {
78
  if ( ! current_user_can( 'edit_page', $post_id ) )
79
- return;
80
- } else {
81
  if ( ! current_user_can( 'edit_post', $post_id ) )
82
  return;
83
  }
@@ -85,19 +109,20 @@ if(!class_exists('Wpacc'))
85
  $single_custom_css = wp_kses( $_POST['single_custom_css'], array( '\'', '\"' ) );
86
  update_post_meta( $post_id, '_single_add_custom_css', $single_custom_css );
87
  }
88
-
89
  public function render_meta_box_content( $post ) {
90
  wp_nonce_field( 'single_add_custom_css_box', 'wp_add_custom_css_box_nonce' );
91
  $single_custom_css = get_post_meta( $post->ID, '_single_add_custom_css', true );
 
92
  echo '<p>'. sprintf( __( 'Add custom CSS rules for this %s', 'wp-add-custom-css' ), $post->post_type ). '</p> ';
93
- echo '<textarea id="single_custom_css" name="single_custom_css" style="width:100%; min-height:200px;">' . esc_attr( $single_custom_css ) . '</textarea>';
94
  }
95
-
96
  public function add_menu() {
97
  global $wpacc_settings_page;
98
  $wpacc_settings_page = add_menu_page( __('Wordpress Add Custom CSS', 'wp-add-custom-css'), __('Add Custom CSS', 'wp-add-custom-css'), 'manage_options', 'wp-add-custom-css_settings', array($this, 'create_settings_page'), plugin_dir_url( __FILE__ ) . '/images/icon.png');
99
  }
100
-
101
  public function create_settings_page() {
102
  $this->options = get_option( 'wpacc_settings' );
103
  ?>
@@ -110,25 +135,27 @@ if(!class_exists('Wpacc'))
110
  </form>
111
  <h3><?php echo __('Credits', 'wp-add-custom-css'); ?></h3>
112
  <ul>
 
113
  <li><?php echo __('"WP Add Custom CSS" is a plugin by', 'wp-add-custom-css'); ?> <a href="http://www.danieledesantis.net/" target="_blank" title="Daniele De Santis">Daniele De Santis</a></li>
114
  </ul>
115
  </div>
116
  <?php
117
  }
118
-
119
  public function print_section_info() {
120
  echo __('Write here the CSS rules you want to apply to the whole website.', 'wp-add-custom-css');
121
  }
122
-
123
  public function main_css_input() {
124
  $custom_rules = isset( $this->options['main_custom_style'] ) ? esc_attr( $this->options['main_custom_style'] ) : '';
125
- echo '<textarea name="wpacc_settings[main_custom_style]" style="width:100%; min-height:300px;">' . $custom_rules . '</textarea>';
 
126
  }
127
-
128
  public function print_section_2_info() {
129
  echo __('Enable page specific CSS for the post types below.', 'wp-add-custom-css');
130
  }
131
-
132
  public function post_types_checkboxes() {
133
  $available_post_types = get_post_types( array('public' => true, '_builtin' => false), 'objects' );
134
  foreach ( $available_post_types as $post_type ) {
@@ -140,12 +167,39 @@ if(!class_exists('Wpacc'))
140
  echo '<div style="margin-bottom:10px"><input type="checkbox" name="wpacc_settings[selected_post_types][]" value="' . $post_type->name . '"' . $checked . '>' . $post_type->label . '</div>'; // output checkbox
141
  }
142
  }
143
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
144
  public function init_settings() {
145
  register_setting(
146
  'wpacc_group',
147
  'wpacc_settings'
148
- );
149
  add_settings_section(
150
  'wpacc_main_style',
151
  __('Main CSS', 'wp-add-custom-css'),
@@ -157,7 +211,7 @@ if(!class_exists('Wpacc'))
157
  __('CSS rules', 'wp-add-custom-css'),
158
  array( $this, 'main_css_input' ),
159
  'wp-add-custom-css_settings',
160
- 'wpacc_main_style'
161
  );
162
  add_settings_section(
163
  'wpacc_post_types',
@@ -170,27 +224,47 @@ if(!class_exists('Wpacc'))
170
  __('Available post types', 'wp-add-custom-css'),
171
  array( $this, 'post_types_checkboxes' ),
172
  'wp-add-custom-css_settings',
173
- 'wpacc_post_types'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
174
  );
175
  }
176
-
177
  public function delete_options() {
178
  unregister_setting(
179
  'wpacc_group',
180
  'wpacc_settings'
181
  );
182
- delete_option('wpacc_settings');
183
  }
184
-
185
  public function delete_custom_meta() {
186
- delete_post_meta_by_key('_single_add_custom_css');
187
  }
188
-
189
  public static function add_wp_var($public_query_vars) {
190
  $public_query_vars[] = 'display_custom_css';
191
  return $public_query_vars;
192
  }
193
-
194
  public static function display_custom_css(){
195
  $display_css = get_query_var('display_custom_css');
196
  if ($display_css == 'css'){
@@ -198,7 +272,7 @@ if(!class_exists('Wpacc'))
198
  exit;
199
  }
200
  }
201
-
202
  public function add_custom_css() {
203
  $this->options = get_option( 'wpacc_settings' );
204
  if ( isset($this->options['main_custom_style']) && $this->options['main_custom_style'] != '') {
@@ -214,21 +288,16 @@ if(!class_exists('Wpacc'))
214
  }
215
  }
216
  wp_register_style( 'wp-add-custom-css', $css_base_url . '?display_custom_css=css' );
217
- wp_enqueue_style( 'wp-add-custom-css' );
218
  }
219
  }
220
-
221
  public function single_custom_css() {
222
  if ( is_single() || is_page() ) {
223
- global $post;
224
- $enabled_post_types = array('post', 'page');
225
- $this->options = get_option( 'wpacc_settings' );
226
- if ( isset($this->options['selected_post_types']) ) {
227
- $enabled_post_types = array_merge( $enabled_post_types, $this->options['selected_post_types'] );
228
- }
229
- if ( ! in_array( $post->post_type, $enabled_post_types )) {
230
  return;
231
- }
 
232
  $single_custom_css = get_post_meta( $post->ID, '_single_add_custom_css', true );
233
  if ( $single_custom_css !== '' ) {
234
  $single_custom_css = str_replace ( '&gt;' , '>' , $single_custom_css );
@@ -237,8 +306,8 @@ if(!class_exists('Wpacc'))
237
  }
238
  }
239
  }
240
-
241
-
242
  }
243
  }
244
 
@@ -248,12 +317,12 @@ if(class_exists('Wpacc')) {
248
  $wpacc = new Wpacc();
249
  }
250
 
251
- if(isset($wpacc)) {
252
  function wpacc_settings_link($links) {
253
  $settings_link = '<a href="admin.php?page=wp-add-custom-css_settings">' . __('Settings', 'wp-add-custom-css') . '</a>';
254
  array_unshift($links, $settings_link);
255
- return $links;
256
  }
257
  add_filter('plugin_action_links_' . plugin_basename(__FILE__), 'wpacc_settings_link');
258
  }
259
- ?>
3
  Plugin Name: WP Add Custom CSS
4
  Plugin URI: http://www.danieledesantis.net
5
  Description: Add custom css to the whole website and to specific posts, pages and custom post types.
6
+ Version: 1.1.0
7
  Author: Daniele De Santis
8
  Author URI: http://www.danieledesantis.net
9
  Text Domain: wp-add-custom-css
12
  */
13
 
14
  /*
15
+ Copyright 2014-2018 Daniele De Santis (email : hello@danieledesantis.net)
16
 
17
  This program is free software; you can redistribute it and/or modify
18
+ it under the terms of the GNU General Public License, version 2, as
19
  published by the Free Software Foundation.
20
 
21
  This program is distributed in the hope that it will be useful,
35
  class Wpacc
36
  {
37
  private $options;
38
+
39
  public function __construct() {
40
  add_action('admin_menu', array($this, 'add_menu'));
41
  add_action( 'admin_init', array( $this, 'init_settings' ) );
42
+ add_action( 'admin_enqueue_scripts', array( $this, 'load_admin_scripts' ) );
43
  add_action( 'add_meta_boxes', array($this, 'add_meta_box' ) );
44
  add_action( 'save_post', array( $this, 'single_save' ) );
45
  add_action('init', array($this, 'init'));
46
  add_filter('query_vars', array($this, 'add_wp_var'));
47
  add_action( 'wp_enqueue_scripts', array($this, 'add_custom_css'), 999 );
48
  add_action('wp_head', array($this, 'single_custom_css'));
49
+ }
50
+
51
  public function init() {
52
  load_plugin_textdomain( 'wp-add-custom-css', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' );
53
  }
54
+
55
  public static function uninstall() {
56
+ self::delete_options();
57
  self::delete_custom_meta();
58
  }
59
+
60
+ private function is_enabled_post_type( $post_type = false ) {
61
+ if ( ! $post_type ) {
62
+ global $post;
63
+ $post_type = $post->post_type;
64
+ }
65
+ $enabled_post_types = array('post', 'page');
66
+ $this->options = get_option( 'wpacc_settings' );
67
+ if ( isset($this->options['selected_post_types']) ) {
68
+ $enabled_post_types = array_merge( $enabled_post_types, $this->options['selected_post_types'] );
69
+ }
70
+ return in_array( $post_type, $enabled_post_types );
71
+ }
72
+
73
+ public function load_admin_scripts( $hook ) {
74
+ if ( ( in_array( $hook, array('post.php', 'post-new.php') ) && $this->is_enabled_post_type() ) || $hook === 'toplevel_page_wp-add-custom-css_settings' ) {
75
+ $this->options = get_option( 'wpacc_settings' );
76
+ if ( isset($this->options['enable_advanced_editor']) ) {
77
+ wp_enqueue_style( 'wpacc_codemirror', plugin_dir_url( __FILE__ ) . '/lib/codemirror/codemirror.css' );
78
+ if ( isset($this->options['advanced_editor_theme']) && $this->options['advanced_editor_theme'] === 'dark' ) {
79
+ wp_enqueue_style( 'wpacc_codemirror_dark', plugin_dir_url( __FILE__ ) . '/lib/codemirror/theme/tomorrow-night-bright.css', array('wpacc_codemirror') );
80
+ }
81
+ wp_enqueue_script( 'wpacc_codemirror', plugin_dir_url( __FILE__ ) . '/lib/codemirror/codemirror.js' );
82
+ wp_enqueue_script( 'wpacc_codemirror_css', plugin_dir_url( __FILE__ ) . '/lib/codemirror/mode/css/css.js', array('wpacc_codemirror') );
83
+ wp_enqueue_script( 'wpacc_scripts', plugin_dir_url( __FILE__ ) . '/js/scripts.js', array('jquery', 'wpacc_codemirror_css') );
84
+ }
85
+ }
86
+ }
87
+
88
  public function add_meta_box( $post_type ) {
89
+ if ( $this->is_enabled_post_type($post_type) ) {
 
 
 
 
 
90
  add_meta_box('wp_add_custom_css', __( 'Custom CSS', 'wp-add-custom-css' ), array( $this, 'render_meta_box_content' ), $post_type, 'advanced', 'high');
91
  }
92
  }
93
+
94
  public function single_save( $post_id ) {
95
  if ( ! isset( $_POST['wp_add_custom_css_box_nonce'] ) || ! wp_verify_nonce( $_POST['wp_add_custom_css_box_nonce'], 'single_add_custom_css_box' ) ) {
96
  return;
97
  }
98
+ if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
99
  return;
100
  }
101
  if ( 'page' == $_POST['post_type'] ) {
102
  if ( ! current_user_can( 'edit_page', $post_id ) )
103
+ return;
104
+ } else {
105
  if ( ! current_user_can( 'edit_post', $post_id ) )
106
  return;
107
  }
109
  $single_custom_css = wp_kses( $_POST['single_custom_css'], array( '\'', '\"' ) );
110
  update_post_meta( $post_id, '_single_add_custom_css', $single_custom_css );
111
  }
112
+
113
  public function render_meta_box_content( $post ) {
114
  wp_nonce_field( 'single_add_custom_css_box', 'wp_add_custom_css_box_nonce' );
115
  $single_custom_css = get_post_meta( $post->ID, '_single_add_custom_css', true );
116
+ $class = ( isset($this->options['advanced_editor_theme']) && $this->options['advanced_editor_theme'] === 'dark' ) ? ' class="wpacc_editor_dark"' : '';
117
  echo '<p>'. sprintf( __( 'Add custom CSS rules for this %s', 'wp-add-custom-css' ), $post->post_type ). '</p> ';
118
+ echo '<textarea id="single_custom_css" name="single_custom_css" style="width:100%; min-height:200px;"' . $class . '>' . esc_attr( $single_custom_css ) . '</textarea>';
119
  }
120
+
121
  public function add_menu() {
122
  global $wpacc_settings_page;
123
  $wpacc_settings_page = add_menu_page( __('Wordpress Add Custom CSS', 'wp-add-custom-css'), __('Add Custom CSS', 'wp-add-custom-css'), 'manage_options', 'wp-add-custom-css_settings', array($this, 'create_settings_page'), plugin_dir_url( __FILE__ ) . '/images/icon.png');
124
  }
125
+
126
  public function create_settings_page() {
127
  $this->options = get_option( 'wpacc_settings' );
128
  ?>
135
  </form>
136
  <h3><?php echo __('Credits', 'wp-add-custom-css'); ?></h3>
137
  <ul>
138
+ <li><?php echo __('If you like "WP Add Custom CSS" leave a', 'wp-add-custom-css'); ?> <a href="https://wordpress.org/support/plugin/wp-add-custom-css/reviews/?filter=5" target="_blank" title="Daniele De Santis"><?php echo __('5 star review,', 'wp-add-custom-css'); ?></a> <?php echo __('thank you!', 'wp-add-custom-css'); ?></li>
139
  <li><?php echo __('"WP Add Custom CSS" is a plugin by', 'wp-add-custom-css'); ?> <a href="http://www.danieledesantis.net/" target="_blank" title="Daniele De Santis">Daniele De Santis</a></li>
140
  </ul>
141
  </div>
142
  <?php
143
  }
144
+
145
  public function print_section_info() {
146
  echo __('Write here the CSS rules you want to apply to the whole website.', 'wp-add-custom-css');
147
  }
148
+
149
  public function main_css_input() {
150
  $custom_rules = isset( $this->options['main_custom_style'] ) ? esc_attr( $this->options['main_custom_style'] ) : '';
151
+ $class = ( isset($this->options['advanced_editor_theme']) && $this->options['advanced_editor_theme'] === 'dark' ) ? ' class="wpacc_editor_dark"' : '';
152
+ echo '<textarea id="main_custom_css" name="wpacc_settings[main_custom_style]" style="width:100%; min-height:300px;"' . $class . '>' . $custom_rules . '</textarea>';
153
  }
154
+
155
  public function print_section_2_info() {
156
  echo __('Enable page specific CSS for the post types below.', 'wp-add-custom-css');
157
  }
158
+
159
  public function post_types_checkboxes() {
160
  $available_post_types = get_post_types( array('public' => true, '_builtin' => false), 'objects' );
161
  foreach ( $available_post_types as $post_type ) {
167
  echo '<div style="margin-bottom:10px"><input type="checkbox" name="wpacc_settings[selected_post_types][]" value="' . $post_type->name . '"' . $checked . '>' . $post_type->label . '</div>'; // output checkbox
168
  }
169
  }
170
+
171
+ public function print_section_3_info() {
172
+ echo __('Enable advanced css editor, including line numbers and code coloring.', 'wp-add-custom-css');
173
+ }
174
+
175
+ public function advanced_editor_checkbox() {
176
+ if ( isset( $this->options['enable_advanced_editor'] ) ) {
177
+ $checked = ' checked';
178
+ } else {
179
+ $checked = '';
180
+ }
181
+ echo '<div style="margin-bottom:10px"><input type="checkbox" name="wpacc_settings[enable_advanced_editor]" value="true"' . $checked . '></div>'; // output checkbox
182
+ }
183
+
184
+ public function advanced_editor_select() {
185
+ echo '<div style="margin-bottom:10px"><select name="wpacc_settings[advanced_editor_theme]">';
186
+ $available_themes = array( 'dark' => __('Dark', 'wp-add-custom-css'), 'light' => __('Light', 'wp-add-custom-css') );
187
+ foreach ( $available_themes as $theme_value => $theme_name ) {
188
+ if ( isset( $this->options['advanced_editor_theme'] ) ) {
189
+ $selected = ( $theme_value === $this->options['advanced_editor_theme'] ) ? ' selected' : '';
190
+ } else {
191
+ $selected = '';
192
+ }
193
+ echo '<option value="' . $theme_value . '"' . $selected . '>' . $theme_name . '</option>';
194
+ }
195
+ echo '</select></div>';
196
+ }
197
+
198
  public function init_settings() {
199
  register_setting(
200
  'wpacc_group',
201
  'wpacc_settings'
202
+ );
203
  add_settings_section(
204
  'wpacc_main_style',
205
  __('Main CSS', 'wp-add-custom-css'),
211
  __('CSS rules', 'wp-add-custom-css'),
212
  array( $this, 'main_css_input' ),
213
  'wp-add-custom-css_settings',
214
+ 'wpacc_main_style'
215
  );
216
  add_settings_section(
217
  'wpacc_post_types',
224
  __('Available post types', 'wp-add-custom-css'),
225
  array( $this, 'post_types_checkboxes' ),
226
  'wp-add-custom-css_settings',
227
+ 'wpacc_post_types'
228
+ );
229
+ add_settings_section(
230
+ 'wpacc_advanced_editor',
231
+ __('Advanced editor', 'wp-add-custom-css'),
232
+ array( $this, 'print_section_3_info' ),
233
+ 'wp-add-custom-css_settings'
234
+ );
235
+ add_settings_field(
236
+ 'enable_advanced_editor',
237
+ __('Enable', 'wp-add-custom-css'),
238
+ array( $this, 'advanced_editor_checkbox' ),
239
+ 'wp-add-custom-css_settings',
240
+ 'wpacc_advanced_editor'
241
+ );
242
+ add_settings_field(
243
+ 'advanced_editor_theme',
244
+ __('Advanced editor layout', 'wp-add-custom-css'),
245
+ array( $this, 'advanced_editor_select' ),
246
+ 'wp-add-custom-css_settings',
247
+ 'wpacc_advanced_editor'
248
  );
249
  }
250
+
251
  public function delete_options() {
252
  unregister_setting(
253
  'wpacc_group',
254
  'wpacc_settings'
255
  );
256
+ delete_option('wpacc_settings');
257
  }
258
+
259
  public function delete_custom_meta() {
260
+ delete_post_meta_by_key('_single_add_custom_css');
261
  }
262
+
263
  public static function add_wp_var($public_query_vars) {
264
  $public_query_vars[] = 'display_custom_css';
265
  return $public_query_vars;
266
  }
267
+
268
  public static function display_custom_css(){
269
  $display_css = get_query_var('display_custom_css');
270
  if ($display_css == 'css'){
272
  exit;
273
  }
274
  }
275
+
276
  public function add_custom_css() {
277
  $this->options = get_option( 'wpacc_settings' );
278
  if ( isset($this->options['main_custom_style']) && $this->options['main_custom_style'] != '') {
288
  }
289
  }
290
  wp_register_style( 'wp-add-custom-css', $css_base_url . '?display_custom_css=css' );
291
+ wp_enqueue_style( 'wp-add-custom-css' );
292
  }
293
  }
294
+
295
  public function single_custom_css() {
296
  if ( is_single() || is_page() ) {
297
+ if ( ! $this->is_enabled_post_type() ) {
 
 
 
 
 
 
298
  return;
299
+ }
300
+ global $post;
301
  $single_custom_css = get_post_meta( $post->ID, '_single_add_custom_css', true );
302
  if ( $single_custom_css !== '' ) {
303
  $single_custom_css = str_replace ( '&gt;' , '>' , $single_custom_css );
306
  }
307
  }
308
  }
309
+
310
+
311
  }
312
  }
313
 
317
  $wpacc = new Wpacc();
318
  }
319
 
320
+ if(isset($wpacc)) {
321
  function wpacc_settings_link($links) {
322
  $settings_link = '<a href="admin.php?page=wp-add-custom-css_settings">' . __('Settings', 'wp-add-custom-css') . '</a>';
323
  array_unshift($links, $settings_link);
324
+ return $links;
325
  }
326
  add_filter('plugin_action_links_' . plugin_basename(__FILE__), 'wpacc_settings_link');
327
  }
328
+ ?>