PluginProbe
HTML Forms – Simple WordPress Forms Plugin / 1.3.13
HTML Forms – Simple WordPress Forms Plugin v1.3.13
1.7.0 trunk 1.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.1 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.2.0 1.3.0 1.3.1 1.3.10 1.3.11 1.3.12 1.3.13 1.3.14 1.3.15 1.3.16 All 67 releases
html-forms / assets / js / admin.js

admin.js in HTML Forms – Simple WordPress Forms Plugin 1.3.13, at assets/js/admin.js

14,196 lines 559.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 (function () { var require = undefined; var module = undefined; var exports = undefined; var define = undefined;(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
2 'use strict';
3
4 Object.defineProperty(exports, "__esModule", {
5 value: true
6 });
7 exports["default"] = void 0;
8
9 function init() {
10 document.body.addEventListener('click', handleClickEvent, true);
11 }
12
13 function handleClickEvent(e) {
14 if (e.target.tagName !== 'A') {
15 return;
16 }
17
18 if (e.target.hasAttribute('data-hf-confirm')) {
19 var sure = confirm(e.target.getAttribute('data-hf-confirm'));
20
21 if (!sure) {
22 e.preventDefault();
23 }
24 }
25 }
26
27 var _default = {
28 init: init
29 };
30 exports["default"] = _default;
31
32 },{}],2:[function(require,module,exports){
33 'use strict';
34
35 var _tabs = _interopRequireDefault(require("./tabs.js"));
36
37 var _formEditor = _interopRequireDefault(require("./form-editor.js"));
38
39 var _formActions = _interopRequireDefault(require("./form-actions.js"));
40
41 var _fieldBuilder = _interopRequireDefault(require("./field-builder.js"));
42
43 var _actionConfirmations = _interopRequireDefault(require("./action-confirmations.js"));
44
45 var _tlite = _interopRequireDefault(require("tlite"));
46
47 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
48
49 window.html_forms = {};
50
51 // init the various components
52 _tabs["default"].init();
53
54 _actionConfirmations["default"].init();
55
56 if (document.getElementById('hf-form-editor')) {
57 _formEditor["default"].init();
58
59 _formActions["default"].init();
60
61 _fieldBuilder["default"].init(_formEditor["default"]);
62 }
63
64 (0, _tlite["default"])(function (el) {
65 return el.className.indexOf('hf-tooltip') > -1;
66 });
67 window.html_forms.FieldBuilder = _fieldBuilder["default"];
68 window.html_forms.Editor = _formEditor["default"]; // tell WP common.js to override the method used for determining hidden columns (screen options)
69
70 if (hf_options.view === 'edit') {
71 window.columns.useCheckboxesForHidden();
72 }
73
74 },{"./action-confirmations.js":1,"./field-builder.js":6,"./form-actions.js":8,"./form-editor.js":9,"./tabs.js":10,"tlite":22}],3:[function(require,module,exports){
75 'use strict';
76
77 Object.defineProperty(exports, "__esModule", {
78 value: true
79 });
80 exports.FieldBuilder = void 0;
81
82 var _preact = require("preact");
83
84 var _decko = require("decko");
85
86 var _fieldConfigurator = require("./field-configurator.js");
87
88 var _class;
89
90 function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
91
92 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
93
94 function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
95
96 function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
97
98 function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }
99
100 function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
101
102 function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
103
104 function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }
105
106 function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
107
108 function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) { var desc = {}; Object.keys(descriptor).forEach(function (key) { desc[key] = descriptor[key]; }); desc.enumerable = !!desc.enumerable; desc.configurable = !!desc.configurable; if ('value' in desc || desc.initializer) { desc.writable = true; } desc = decorators.slice().reverse().reduce(function (desc, decorator) { return decorator(target, property, desc) || desc; }, desc); if (context && desc.initializer !== void 0) { desc.value = desc.initializer ? desc.initializer.call(context) : void 0; desc.initializer = undefined; } if (desc.initializer === void 0) { Object.defineProperty(target, property, desc); desc = null; } return desc; }
109
110 var FieldBuilder = (_class =
111 /*#__PURE__*/
112 function (_Component) {
113 _inherits(FieldBuilder, _Component);
114
115 function FieldBuilder(props) {
116 var _this;
117
118 _classCallCheck(this, FieldBuilder);
119
120 _this = _possibleConstructorReturn(this, _getPrototypeOf(FieldBuilder).call(this, props));
121 _this.state = {
122 activeField: null
123 };
124 return _this;
125 }
126
127 _createClass(FieldBuilder, [{
128 key: "handleCancel",
129 value: function handleCancel() {
130 this.setState({
131 activeField: null
132 });
133 }
134 }, {
135 key: "openFieldConfig",
136 value: function openFieldConfig(e) {
137 var field = this.props.fields[e.target.value];
138
139 if (this.state.activeField === field) {
140 this.setState({
141 activeField: null
142 });
143 } else {
144 this.setState({
145 activeField: field
146 });
147 }
148 }
149 }, {
150 key: "render",
151 value: function render(props, state) {
152 var _this2 = this;
153
154 var fieldButtons = props.fields.map(function (f, i) {
155 return (0, _preact.h)("button", {
156 type: "button",
157 value: i,
158 className: "button " + (state.activeField === f ? "active" : ""),
159 onClick: _this2.openFieldConfig
160 }, f.label);
161 });
162 var fieldType = state.activeField ? state.activeField.key : "";
163 var rows = state.activeField ? state.activeField.configRows : [];
164 return (0, _preact.h)("div", {
165 "class": "hf-field-builder"
166 }, (0, _preact.h)("h4", null, "Add field"), (0, _preact.h)("div", {
167 "class": "available-fields"
168 }, fieldButtons), (0, _preact.h)("div", {
169 style: "max-width: 480px;"
170 }, (0, _preact.h)(_fieldConfigurator.FieldConfigurator, {
171 fieldType: fieldType,
172 rows: rows,
173 onCancel: this.handleCancel
174 })), state.activeField === null ? (0, _preact.h)("p", {
175 "class": "help",
176 style: "margin-bottom: 0;"
177 }, "Use the buttons above to generate your field HTML, or manually modify your form below.") : "");
178 }
179 }]);
180
181 return FieldBuilder;
182 }(_preact.Component), (_applyDecoratedDescriptor(_class.prototype, "handleCancel", [_decko.bind], Object.getOwnPropertyDescriptor(_class.prototype, "handleCancel"), _class.prototype), _applyDecoratedDescriptor(_class.prototype, "openFieldConfig", [_decko.bind], Object.getOwnPropertyDescriptor(_class.prototype, "openFieldConfig"), _class.prototype)), _class);
183 exports.FieldBuilder = FieldBuilder;
184
185 },{"./field-configurator.js":4,"decko":19,"preact":21}],4:[function(require,module,exports){
186 'use strict';
187
188 Object.defineProperty(exports, "__esModule", {
189 value: true
190 });
191 exports.FieldConfigurator = void 0;
192
193 var _preact = require("preact");
194
195 var _decko = require("decko");
196
197 var _html = require("../field-builder/html.js");
198
199 var FS = _interopRequireWildcard(require("./field-settings.js"));
200
201 var _linkstate = _interopRequireDefault(require("linkstate"));
202
203 var _class;
204
205 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
206
207 function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj["default"] = obj; return newObj; } }
208
209 function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
210
211 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
212
213 function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
214
215 function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
216
217 function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }
218
219 function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
220
221 function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
222
223 function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }
224
225 function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
226
227 function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) { var desc = {}; Object.keys(descriptor).forEach(function (key) { desc[key] = descriptor[key]; }); desc.enumerable = !!desc.enumerable; desc.configurable = !!desc.configurable; if ('value' in desc || desc.initializer) { desc.writable = true; } desc = decorators.slice().reverse().reduce(function (desc, decorator) { return decorator(target, property, desc) || desc; }, desc); if (context && desc.initializer !== void 0) { desc.value = desc.initializer ? desc.initializer.call(context) : void 0; desc.initializer = undefined; } if (desc.initializer === void 0) { Object.defineProperty(target, property, desc); desc = null; } return desc; }
228
229 var FieldConfigurator = (_class =
230 /*#__PURE__*/
231 function (_Component) {
232 _inherits(FieldConfigurator, _Component);
233
234 function FieldConfigurator(props) {
235 var _this;
236
237 _classCallCheck(this, FieldConfigurator);
238
239 _this = _possibleConstructorReturn(this, _getPrototypeOf(FieldConfigurator).call(this, props));
240 _this.state = _this.getInitialState();
241 _this.choiceHandlers = {
242 "add": _this.addChoice,
243 "delete": _this.deleteChoice,
244 "changeLabel": _this.changeChoiceLabel,
245 "toggleChecked": _this.toggleChoiceChecked
246 };
247 return _this;
248 }
249
250 _createClass(FieldConfigurator, [{
251 key: "getInitialState",
252 value: function getInitialState() {
253 return {
254 formId: document.querySelector('input[name="form_id"]').value,
255 formSlug: document.querySelector('input[name="form[slug]"]').value,
256 fieldType: "",
257 fieldLabel: "",
258 placeholder: "",
259 value: "",
260 multiple: false,
261 wrap: true,
262 required: false,
263 choices: [{
264 checked: false,
265 label: "One"
266 }, {
267 checked: false,
268 label: "Two"
269 }],
270 accept: ''
271 };
272 }
273 }, {
274 key: "componentWillReceiveProps",
275 value: function componentWillReceiveProps(props) {
276 var newState = {
277 fieldType: props.fieldType
278 }; // when changing from field that accepts multiple values to single-value field, reset all pre-selections
279
280 if (this.state.fieldType === 'checkbox' && props.fieldType !== 'checkbox') {
281 newState.choices = this.state.choices.map(function (c, i) {
282 c.checked = false;
283 return c;
284 });
285 }
286
287 this.setState(newState);
288 }
289 }, {
290 key: "addToForm",
291 value: function addToForm() {
292 var html = (0, _html.htmlgenerate)(this.state);
293 html_forms.Editor.replaceSelection(html);
294 }
295 }, {
296 key: "addChoice",
297 value: function addChoice() {
298 var arr = this.state.choices;
299 arr.push({
300 checked: false,
301 label: "..."
302 });
303 this.setState({
304 choices: arr
305 });
306 }
307 }, {
308 key: "deleteChoice",
309 value: function deleteChoice(e) {
310 var arr = this.state.choices;
311 var index = e.target.parentElement.getAttribute('data-key');
312 arr.splice(index, 1);
313 this.setState({
314 choices: arr
315 });
316 }
317 }, {
318 key: "changeChoiceLabel",
319 value: function changeChoiceLabel(e) {
320 var arr = this.state.choices;
321 var index = e.target.parentElement.getAttribute('data-key');
322 arr[index].label = e.target.value;
323 this.setState({
324 choices: arr
325 });
326 }
327 }, {
328 key: "toggleChoiceChecked",
329 value: function toggleChoiceChecked(e) {
330 var arr = this.state.choices;
331 var index = e.target.parentElement.getAttribute('data-key');
332 arr[index].checked = !arr[index].checked;
333 this.setState({
334 choices: arr
335 });
336 }
337 }, {
338 key: "handleCancel",
339 value: function handleCancel() {
340 // revert back to initial state
341 this.setState(this.getInitialState());
342 this.props.onCancel();
343 }
344 }, {
345 key: "render",
346 value: function render(props, state) {
347 if (props.rows.length === 0) {
348 return "";
349 }
350
351 var formFields = [];
352
353 for (var i = 0; i < props.rows.length; i++) {
354 switch (props.rows[i]) {
355 case "label":
356 formFields.push((0, _preact.h)(FS.Label, {
357 value: state.fieldLabel,
358 onChange: (0, _linkstate["default"])(this, 'fieldLabel')
359 }));
360 break;
361
362 case "placeholder":
363 formFields.push((0, _preact.h)(FS.Placeholder, {
364 value: state.placeholder,
365 onChange: (0, _linkstate["default"])(this, 'placeholder')
366 }));
367 break;
368
369 case "default-value":
370 formFields.push((0, _preact.h)(FS.DefaultValue, {
371 value: state.value,
372 onChange: (0, _linkstate["default"])(this, 'value')
373 }));
374 break;
375
376 case "multiple":
377 formFields.push((0, _preact.h)(FS.Multiple, {
378 checked: state.multiple,
379 onChange: (0, _linkstate["default"])(this, 'multiple')
380 }));
381 break;
382
383 case "required":
384 formFields.push((0, _preact.h)(FS.Required, {
385 checked: state.required,
386 onChange: (0, _linkstate["default"])(this, 'required')
387 }));
388 break;
389
390 case "wrap":
391 formFields.push((0, _preact.h)(FS.Wrap, {
392 checked: state.wrap,
393 onChange: (0, _linkstate["default"])(this, 'wrap')
394 }));
395 break;
396
397 case "add-to-form":
398 formFields.push((0, _preact.h)(FS.AddToForm, {
399 onSubmit: this.addToForm,
400 onCancel: this.handleCancel
401 }));
402 break;
403
404 case "choices":
405 formFields.push((0, _preact.h)(FS.Choices, {
406 multiple: state.fieldType === 'checkbox',
407 choices: state.choices,
408 handlers: this.choiceHandlers
409 }));
410 break;
411
412 case "button-text":
413 formFields.push((0, _preact.h)(FS.ButtonText, {
414 value: state.value,
415 onChange: (0, _linkstate["default"])(this, 'value')
416 }));
417 break;
418
419 case "accept":
420 formFields.push((0, _preact.h)(FS.Accept, {
421 value: state.accept,
422 onChange: (0, _linkstate["default"])(this, 'accept')
423 }));
424 break;
425 }
426 }
427
428 return (0, _preact.h)("div", {
429 "class": "field-config",
430 onKeyPress: FieldConfigurator.handleKeyPress
431 }, formFields);
432 }
433 }], [{
434 key: "handleKeyPress",
435 value: function handleKeyPress(e) {
436 // stop RETURN from submitting the parent form.
437 if (e.keyCode === 13) {
438 e.preventDefault();
439 }
440 }
441 }]);
442
443 return FieldConfigurator;
444 }(_preact.Component), (_applyDecoratedDescriptor(_class.prototype, "addToForm", [_decko.bind], Object.getOwnPropertyDescriptor(_class.prototype, "addToForm"), _class.prototype), _applyDecoratedDescriptor(_class.prototype, "addChoice", [_decko.bind], Object.getOwnPropertyDescriptor(_class.prototype, "addChoice"), _class.prototype), _applyDecoratedDescriptor(_class.prototype, "deleteChoice", [_decko.bind], Object.getOwnPropertyDescriptor(_class.prototype, "deleteChoice"), _class.prototype), _applyDecoratedDescriptor(_class.prototype, "changeChoiceLabel", [_decko.bind], Object.getOwnPropertyDescriptor(_class.prototype, "changeChoiceLabel"), _class.prototype), _applyDecoratedDescriptor(_class.prototype, "toggleChoiceChecked", [_decko.bind], Object.getOwnPropertyDescriptor(_class.prototype, "toggleChoiceChecked"), _class.prototype), _applyDecoratedDescriptor(_class, "handleKeyPress", [_decko.bind], Object.getOwnPropertyDescriptor(_class, "handleKeyPress"), _class), _applyDecoratedDescriptor(_class.prototype, "handleCancel", [_decko.bind], Object.getOwnPropertyDescriptor(_class.prototype, "handleCancel"), _class.prototype)), _class);
445 exports.FieldConfigurator = FieldConfigurator;
446
447 },{"../field-builder/html.js":7,"./field-settings.js":5,"decko":19,"linkstate":20,"preact":21}],5:[function(require,module,exports){
448 'use strict';
449
450 Object.defineProperty(exports, "__esModule", {
451 value: true
452 });
453 exports.AddToForm = AddToForm;
454 exports.Label = Label;
455 exports.Placeholder = Placeholder;
456 exports.DefaultValue = DefaultValue;
457 exports.Wrap = Wrap;
458 exports.Required = Required;
459 exports.Choices = Choices;
460 exports.ButtonText = ButtonText;
461 exports.Accept = Accept;
462 exports.Multiple = Multiple;
463
464 var _preact = require("preact");
465
466 function AddToForm(props) {
467 return (0, _preact.h)("div", {
468 "class": "hf-small-margin"
469 }, (0, _preact.h)("button", {
470 "class": "button",
471 type: "button",
472 onClick: props.onSubmit
473 }, "Add field to form"), " \xA0 ", (0, _preact.h)("a", {
474 href: "javascript:void(0);",
475 "class": "hf-small",
476 style: "vertical-align: middle;",
477 onClick: props.onCancel
478 }, "or close field helper"));
479 }
480
481 function Label(props) {
482 return (0, _preact.h)("div", {
483 "class": "hf-small-margin"
484 }, (0, _preact.h)("label", {
485 "for": "hf-fg-field-label"
486 }, "Field label ", (0, _preact.h)("span", {
487 "class": "hf-required"
488 }, "*")), (0, _preact.h)("input", {
489 id: "hf-fg-field-label",
490 type: "text",
491 value: props.value,
492 onChange: props.onChange
493 }));
494 }
495
496 function Placeholder(props) {
497 return (0, _preact.h)("div", {
498 "class": "hf-small-margin"
499 }, (0, _preact.h)("label", {
500 "for": "hf-fg-placeholder"
501 }, "Placeholder ", (0, _preact.h)("span", {
502 "class": "hf-italic hf-pull-right"
503 }, "Optional")), (0, _preact.h)("input", {
504 id: "hf-fg-placeholder",
505 type: "text",
506 value: props.value,
507 onChange: props.onChange
508 }), (0, _preact.h)("p", {
509 "class": "help"
510 }, "Text to show when field has no value."));
511 }
512
513 function ButtonText(props) {
514 return (0, _preact.h)("div", {
515 "class": "hf-small-margin"
516 }, (0, _preact.h)("label", {
517 "for": "hf-fg-default-value"
518 }, "Button text ", (0, _preact.h)("span", {
519 "class": "hf-required"
520 }, "*")), (0, _preact.h)("input", {
521 id: "hf-fg-default-value",
522 type: "text",
523 value: props.value,
524 onChange: props.onChange
525 }), (0, _preact.h)("p", {
526 "class": "help"
527 }, "Text to show on the button."));
528 }
529
530 function DefaultValue(props) {
531 return (0, _preact.h)("div", {
532 "class": "hf-small-margin"
533 }, (0, _preact.h)("label", {
534 "for": "hf-fg-default-value"
535 }, "Default value ", (0, _preact.h)("span", {
536 "class": "hf-italic hf-pull-right"
537 }, "Optional")), (0, _preact.h)("input", {
538 id: "hf-fg-default-value",
539 type: "text",
540 value: props.value,
541 onChange: props.onChange
542 }), (0, _preact.h)("p", {
543 "class": "help"
544 }, "Text to pre-fill this field with."));
545 }
546
547 function Multiple(props) {
548 return (0, _preact.h)("div", {
549 "class": "hf-small-margin"
550 }, (0, _preact.h)("label", {
551 "class": "inline"
552 }, (0, _preact.h)("input", {
553 type: "checkbox",
554 value: "1",
555 defaultChecked: props.checked,
556 onChange: props.onChange
557 }), "Accept multiple values."));
558 }
559
560 function Wrap(props) {
561 return (0, _preact.h)("div", {
562 "class": "hf-small-margin"
563 }, (0, _preact.h)("label", {
564 "class": "inline"
565 }, (0, _preact.h)("input", {
566 type: "checkbox",
567 value: "1",
568 defaultChecked: props.checked,
569 onChange: props.onChange
570 }), "Wrap this field in paragraph tags."));
571 }
572
573 function Required(props) {
574 return (0, _preact.h)("div", {
575 "class": "hf-small-margin"
576 }, (0, _preact.h)("label", {
577 "class": "inline"
578 }, (0, _preact.h)("input", {
579 type: "checkbox",
580 value: "1",
581 defaultChecked: props.checked,
582 onChange: props.onChange
583 }), "This field is required."));
584 }
585
586 function Choices(props) {
587 var choiceFields = props.choices.map(function (choice, k) {
588 return (0, _preact.h)("div", {
589 "data-key": k
590 }, (0, _preact.h)("input", {
591 type: props.multiple ? "checkbox" : "radio",
592 name: "selected",
593 defaultChecked: choice.checked,
594 onChange: props.handlers.toggleChecked,
595 title: "Pre-select this choice?"
596 }), (0, _preact.h)("input", {
597 type: "text",
598 value: choice.label,
599 placeholder: "Choice label",
600 style: "width: 80%;",
601 onChange: props.handlers.changeLabel
602 }), (0, _preact.h)("a", {
603 href: "javascript:void(0);",
604 onClick: props.handlers["delete"],
605 style: "text-decoration: none;",
606 title: "Delete choice"
607 }, "\u2715"));
608 });
609 return (0, _preact.h)("div", {
610 "class": "hf-small-margin"
611 }, (0, _preact.h)("label", null, "Choices"), choiceFields, (0, _preact.h)("input", {
612 type: props.multiple ? "checkbox" : "radio",
613 style: "visibility: hidden;"
614 }), (0, _preact.h)("a", {
615 href: "javascript:void(0);",
616 onClick: props.handlers.add
617 }, "Add choice"));
618 }
619
620 function Accept(props) {
621 return (0, _preact.h)("div", {
622 "class": "hf-small-margin"
623 }, (0, _preact.h)("label", null, "Accepted file types"), (0, _preact.h)("input", {
624 type: "text",
625 value: props.value,
626 onChange: props.onChange
627 }), (0, _preact.h)("p", {
628 "class": "help"
629 }, "Use a comma-separated list of accepted file extensions, eg ", (0, _preact.h)("code", null, ".pdf"), ". ", (0, _preact.h)("br", null), "Leave empty to accept any file type."));
630 }
631
632 },{"preact":21}],6:[function(require,module,exports){
633 'use strict'; // imports
634
635 Object.defineProperty(exports, "__esModule", {
636 value: true
637 });
638 exports["default"] = void 0;
639
640 var _preact = require("preact");
641
642 var _fieldBuilder = require("./components/field-builder.js");
643
644 // vars
645 var rootElement;
646 var Editor;
647 var fields; // functions
648
649 function Field(key, label, configRows) {
650 this.key = key;
651 this.label = label;
652 this.configRows = configRows || [];
653 }
654
655 function mount() {
656 rootElement = (0, _preact.render)((0, _preact.h)(_fieldBuilder.FieldBuilder, {
657 fields: fields
658 }), document.getElementById('hf-field-builder'), rootElement);
659 } // bootstrap
660
661
662 fields = [new Field("text", "Text", ["label", "placeholder", "default-value", "required", "wrap", "add-to-form"]), new Field("email", "Email", ["label", "placeholder", "default-value", "required", "wrap", "add-to-form"]), new Field("url", "URL", ["label", "placeholder", "default-value", "required", "wrap", "add-to-form"]), new Field("number", "Number", ["label", "placeholder", "default-value", "required", "wrap", "add-to-form"]), new Field("date", "Date", ["label", "default-value", "required", "wrap", "add-to-form"]), new Field("textarea", "Textarea", ["label", "placeholder", "default-value", "required", "wrap", "add-to-form"]), new Field("dropdown", "Dropdown", ["label", "choices", "multiple", "required", "wrap", "add-to-form"]), new Field("checkbox", "Checkboxes", ["label", "choices", "wrap", "add-to-form"]), new Field("radio", "Radio buttons", ["label", "choices", "wrap", "add-to-form"]), new Field("submit", "Submit button", ["button-text", "wrap", "add-to-form"])];
663 var _default = {
664 init: function init() {
665 mount();
666 },
667 registerField: function registerField(key, label, configRows) {
668 fields.push(new Field(key, label, configRows));
669 mount();
670 }
671 };
672 exports["default"] = _default;
673
674 },{"./components/field-builder.js":3,"preact":21}],7:[function(require,module,exports){
675 'use strict';
676
677 Object.defineProperty(exports, "__esModule", {
678 value: true
679 });
680 exports.htmlgenerate = htmlgenerate;
681
682 var _preact = require("preact");
683
684 function htmlgenerate(conf) {
685 var fieldName = namify(conf.fieldLabel);
686 var fieldId = conf.formSlug + '-' + fieldName;
687 var label = conf.fieldLabel.length && conf.fieldType !== 'submit' ? (0, _preact.h)("label", {
688 "for": fieldId
689 }, conf.fieldLabel) : "";
690 var fieldAttr, field;
691
692 switch (conf.fieldType) {
693 case "text":
694 default:
695 fieldAttr = {
696 type: conf.fieldType,
697 name: fieldName,
698 value: conf.value,
699 placeholder: conf.placeholder,
700 required: conf.required,
701 id: fieldId
702 };
703 field = html("input", fieldAttr);
704 break;
705
706 case "textarea":
707 fieldAttr = {
708 name: fieldName,
709 placeholder: conf.placeholder,
710 required: conf.required,
711 id: fieldId
712 };
713 field = html("textarea", fieldAttr, conf.value);
714 break;
715
716 case "dropdown":
717 fieldAttr = {
718 name: fieldName + (conf.multiple ? '[]' : ''),
719 required: conf.required,
720 multiple: conf.multiple,
721 id: fieldId
722 };
723 var opts = conf.choices.map(function (choice) {
724 return html("option", {
725 selected: choice.checked
726 }, choice.label);
727 });
728 field = html("select", fieldAttr, opts);
729 break;
730
731 case "radio":
732 field = conf.choices.map(function (choice) {
733 return html("label", {}, [html("input", {
734 type: "radio",
735 name: fieldName,
736 value: choice.label,
737 selected: choice.checked
738 }), " ", html("span", {}, choice.label)]);
739 });
740 break;
741
742 case "checkbox":
743 field = conf.choices.map(function (choice) {
744 return html("label", {}, [html("input", {
745 type: "checkbox",
746 name: fieldName + "[]",
747 value: choice.label,
748 checked: choice.checked
749 }), " ", html("span", {}, choice.label)]);
750 });
751 break;
752
753 case "file":
754 fieldAttr = {
755 type: "file",
756 name: fieldName,
757 required: conf.required,
758 id: fieldId
759 };
760
761 if (conf['accept']) {
762 fieldAttr['accept'] = conf['accept'];
763 }
764
765 field = html("input", fieldAttr);
766 break;
767
768 case "submit":
769 fieldAttr = {
770 type: "submit",
771 value: conf.value
772 };
773 field = html("input", fieldAttr);
774 break;
775 }
776
777 var str = "";
778
779 if (conf.wrap) {
780 var children = label !== "" ? ["\n\t", label] : [];
781 children = children.concat(["\n\t", field, "\n"]);
782 var tmpl = (0, _preact.h)("p", {}, children);
783 str = renderToString(tmpl);
784 } else {
785 str += renderToString(label);
786 str += "\n";
787 str += renderToString(field);
788 }
789
790 str += "\n";
791 return str;
792 }
793
794 function renderToString(vdom) {
795 var el = document.createElement('div');
796 (0, _preact.render)(vdom, el);
797 return el.innerHTML;
798 }
799
800 function html(tag, attr, children) {
801 attr = filterEmptyObjectValues(attr);
802 return (0, _preact.h)(tag, attr, children);
803 }
804
805 function namify(str) {
806 return str.replace(/ /g, '_').replace(/[^\w\[\]_]*/g, "").toUpperCase();
807 }
808
809 function filterEmptyObjectValues(obj) {
810 var newObj = {};
811
812 for (var propName in obj) {
813 if (obj[propName] !== false && obj[propName] !== "") {
814 newObj[propName] = obj[propName];
815 }
816 }
817
818 return newObj;
819 }
820
821 },{"preact":21}],8:[function(require,module,exports){
822 'use strict';
823
824 Object.defineProperty(exports, "__esModule", {
825 value: true
826 });
827 exports["default"] = void 0;
828 var availableActions, actionTemplates, actions;
829
830 function init() {
831 actions = document.getElementById('hf-form-actions');
832 availableActions = document.getElementById('hf-available-form-actions');
833 actionTemplates = document.getElementById('hf-form-action-templates'); // turn settings into accordions
834
835 [].forEach.call(actions.querySelectorAll('.hf-action-settings'), function (el) {
836 el.parentNode.removeChild(el);
837 var heading = el.getAttribute('data-title');
838 var summary = el.querySelector('.hf-action-summary');
839
840 if (summary) {
841 heading += ' &mdash; <span class="hf-muted">' + summary.innerHTML + '</span>';
842 }
843
844 var wrap = createAccordion(heading, el.innerHTML);
845 actions.appendChild(wrap);
846 actions.querySelector('#hf-form-actions-empty').style.display = 'none';
847 });
848 availableActions.addEventListener('click', addAction, true);
849 }
850
851 function createAccordion(headingHTML, contentHTML) {
852 var wrap = document.createElement('div');
853 wrap.className = "hf-accordion expanded ";
854 var heading = document.createElement('h4');
855 heading.className = "hf-accordion-heading";
856 heading.innerHTML = headingHTML;
857 wrap.appendChild(heading);
858 var content = document.createElement('div');
859 content.className = "hf-accordion-content";
860 content.innerHTML = contentHTML;
861 wrap.appendChild(content);
862 var deleteWrap = document.createElement('p');
863 deleteWrap.style.textAlign = 'right';
864 var deleteLink = document.createElement('a');
865 deleteLink.href = 'javascript:void(0);';
866 deleteLink.className = "danger";
867 deleteLink.innerText = 'Delete this action';
868 deleteWrap.appendChild(deleteLink);
869 content.appendChild(deleteWrap); // bind handlers
870
871 heading.addEventListener('click', createToggleActionHandler(wrap, content));
872 deleteLink.addEventListener('click', createDeleteActionHandler(wrap));
873 return wrap;
874 }
875
876 function addAction(e) {
877 var el = e.target || e.srcElement;
878
879 if (el.tagName !== 'INPUT') {
880 return;
881 }
882
883 var actionType = el.getAttribute('data-action-type');
884 var actionTemplate = actionTemplates.querySelector("#hf-action-type-".concat(actionType, "-template")); // append HTML to actions wrapper
885
886 var index = actions.querySelectorAll('div').length - 1;
887 var wrap = createAccordion(el.value, actionTemplate.innerHTML.replace(/\$index/g, index));
888 actions.appendChild(wrap); // hide "no form actions" message
889
890 actions.querySelector('#hf-form-actions-empty').style.display = 'none';
891 window.html_forms.Editor.updateFieldVariables();
892 }
893
894 function createDeleteActionHandler(wrap) {
895 return function () {
896 actions.removeChild(wrap);
897
898 if (actions.childElementCount === 1) {
899 actions.querySelector('#hf-form-actions-empty').style.display = '';
900 }
901 };
902 }
903
904 function createToggleActionHandler(wrap, content) {
905 return function () {
906 var show = content.offsetParent === null;
907 wrap.className = wrap.className.replace('expanded', '') + (show ? ' expanded' : '');
908 content.style.display = show ? 'block' : 'none';
909 };
910 }
911
912 var _default = {
913 init: init
914 };
915 exports["default"] = _default;
916
917 },{}],9:[function(require,module,exports){
918 'use strict'; // load CodeMirror & plugins
919
920 Object.defineProperty(exports, "__esModule", {
921 value: true
922 });
923 exports["default"] = void 0;
924
925 var CodeMirror = require('codemirror');
926
927 require('codemirror/mode/xml/xml');
928
929 require('codemirror/mode/javascript/javascript');
930
931 require('codemirror/mode/css/css');
932
933 require('codemirror/mode/htmlmixed/htmlmixed');
934
935 require('codemirror/addon/fold/xml-fold');
936
937 require('codemirror/addon/edit/matchtags');
938
939 require('codemirror/addon/edit/closetag.js');
940
941 var editor, element, dom, requiredFieldsInput, emailFieldsInput, previewFrame, previewDom;
942 var templateRegex = /\{\{ *(\w+)(?:\.([\w\.]+))? *(?:\|\| *(\w+))? *\}\}/g;
943
944 function init() {
945 previewFrame = document.getElementById('hf-form-preview');
946 element = document.getElementById('hf-form-editor');
947 dom = document.createElement('form');
948 requiredFieldsInput = document.getElementById('hf-required-fields');
949 emailFieldsInput = document.getElementById('hf-email-fields');
950 dom.innerHTML = element.value;
951 editor = CodeMirror.fromTextArea(element, {
952 selectionPointer: true,
953 matchTags: {
954 bothTags: true
955 },
956 mode: "htmlmixed",
957 htmlMode: true,
958 autoCloseTags: true,
959 autoRefresh: true,
960 styleActiveLine: true,
961 matchBrackets: true
962 });
963 editor.on('changes', debounce(updatePreview, 500));
964 editor.on('changes', debounce(updateShadowDOM, 100));
965 editor.on('changes', debounce(updateFieldVariables, 500));
966 editor.on('blur', updatePreview);
967 editor.on('blur', updateShadowDOM);
968 editor.on('blur', updateFieldVariables);
969 editor.on('blur', updateRequiredFields);
970 editor.on('blur', updateEmailFields);
971 previewFrame.addEventListener('load', setPreviewDom);
972 setPreviewDom();
973 updateFieldVariables();
974 }
975
976 function setPreviewDom() {
977 var frameContent = previewFrame.contentDocument || previewFrame.contentWindow.document;
978 previewDom = frameContent.querySelector('.hf-fields-wrap');
979
980 if (previewDom) {
981 updatePreview();
982 }
983 }
984
985 function getFieldVariableName(f) {
986 return f.name.replace('[]', '').replace(/\[(\w+)\]/g, '.$1');
987 }
988
989 function updateFieldVariables() {
990 var fields = dom.querySelectorAll('input[name], select[name], textarea[name], button[name]');
991 var fieldVariables = uniq([].map.call(fields, function (f) {
992 return '[' + getFieldVariableName(f) + ']';
993 }));
994 var wpbody = document.getElementById('wpbody-content');
995 [].forEach.call(document.querySelectorAll('.hf-field-names'), function (el) {
996 // remove existing variables
997 while (el.firstChild) {
998 el.removeChild(el.firstChild);
999 }
1000
1001 var variableElements = fieldVariables.map(function (n) {
1002 // measure width of actual font size for prettiness
1003 var sizeEl = document.createElement('span');
1004 sizeEl.style.visibility = 'hidden';
1005 sizeEl.innerText = n;
1006 wpbody.appendChild(sizeEl);
1007 var width = sizeEl.offsetWidth;
1008 wpbody.removeChild(sizeEl); // add input el
1009
1010 var el = document.createElement('input');
1011 el.setAttribute('type', 'text');
1012 el.style.maxWidth = width * 1.1 + 14 + 'px';
1013 el.setAttribute('value', n);
1014 el.setAttribute('readonly', true);
1015 el.setAttribute('onfocus', 'this.select()');
1016 return el;
1017 });
1018 variableElements.forEach(function (vel, i, arr) {
1019 el.appendChild(vel);
1020 });
1021 });
1022 }
1023
1024 function updatePreview() {
1025 var markup = editor.getValue(); // replace template tags
1026
1027 markup = markup.replace(templateRegex, function (s, m) {
1028 // if a default value was provided, use that
1029 if (arguments[3]) {
1030 return arguments[3];
1031 }
1032
1033 return '';
1034 }); // update dom
1035
1036 previewDom.innerHTML = markup;
1037 previewDom.dispatchEvent(new Event('hf-refresh'));
1038 }
1039
1040 function updateShadowDOM() {
1041 dom.innerHTML = editor.getValue();
1042 }
1043
1044 function updateRequiredFields() {
1045 var fields = dom.querySelectorAll('[required]');
1046 var fieldNames = [].map.call(fields, getFieldVariableName);
1047 requiredFieldsInput.value = fieldNames.join(',');
1048 }
1049
1050 function updateEmailFields() {
1051 var fields = dom.querySelectorAll('input[type="email"]');
1052 var fieldNames = [].map.call(fields, getFieldVariableName);
1053 emailFieldsInput.value = fieldNames.join(',');
1054 }
1055
1056 function replaceSelection(str) {
1057 editor.replaceSelection(str);
1058 editor.focus();
1059 }
1060
1061 function debounce(func, wait, immediate) {
1062 var timeout;
1063 return function () {
1064 var context = this,
1065 args = arguments;
1066
1067 var later = function later() {
1068 timeout = null;
1069 if (!immediate) func.apply(context, args);
1070 };
1071
1072 var callNow = immediate && !timeout;
1073 clearTimeout(timeout);
1074 timeout = setTimeout(later, wait);
1075 if (callNow) func.apply(context, args);
1076 };
1077 }
1078
1079 ;
1080
1081 function uniq(a) {
1082 var seen = {};
1083 return a.filter(function (item) {
1084 return seen.hasOwnProperty(item) ? false : seen[item] = true;
1085 });
1086 }
1087
1088 var _default = {
1089 init: init,
1090 replaceSelection: replaceSelection,
1091 updateFieldVariables: updateFieldVariables
1092 };
1093 exports["default"] = _default;
1094
1095 },{"codemirror":14,"codemirror/addon/edit/closetag.js":11,"codemirror/addon/edit/matchtags":12,"codemirror/addon/fold/xml-fold":13,"codemirror/mode/css/css":15,"codemirror/mode/htmlmixed/htmlmixed":16,"codemirror/mode/javascript/javascript":17,"codemirror/mode/xml/xml":18}],10:[function(require,module,exports){
1096 'use strict';
1097
1098 Object.defineProperty(exports, "__esModule", {
1099 value: true
1100 });
1101 exports["default"] = void 0;
1102 var tabs, tabNavs;
1103
1104 function init() {
1105 tabs = document.querySelectorAll('.hf-tab');
1106 tabNavs = document.querySelectorAll('#hf-tabs-nav a');
1107
1108 for (var i = 0; i < tabNavs.length; i++) {
1109 tabNavs[i].addEventListener('click', open);
1110 }
1111 }
1112
1113 ;
1114
1115 function open(e) {
1116 var tabTarget = this.getAttribute('data-tab-target');
1117
1118 for (var i = 0; i < tabNavs.length; i++) {
1119 tabNavs[i].classList.toggle('nav-tab-active', tabNavs[i] === this);
1120 }
1121
1122 this.blur();
1123
1124 for (var _i = 0; _i < tabs.length; _i++) {
1125 var tab = tabs[_i];
1126 tab.classList.toggle('hf-tab-active', tab.getAttribute('data-tab') === tabTarget);
1127 }
1128
1129 document.title = document.title.replace(document.title.split(' - ').shift(), this.innerText + " ");
1130
1131 if (window.history) {
1132 var newUrl = window.location.href;
1133 newUrl = newUrl.replace(/\&tab\=\w+/, "");
1134 newUrl += "&tab=" + tabTarget;
1135 window.history.replaceState({
1136 tab: tabTarget
1137 }, document.title, newUrl);
1138 }
1139
1140 e.preventDefault();
1141 }
1142
1143 ;
1144 var _default = {
1145 init: init,
1146 open: open
1147 };
1148 exports["default"] = _default;
1149
1150 },{}],11:[function(require,module,exports){
1151 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1152 // Distributed under an MIT license: https://codemirror.net/LICENSE
1153
1154 /**
1155 * Tag-closer extension for CodeMirror.
1156 *
1157 * This extension adds an "autoCloseTags" option that can be set to
1158 * either true to get the default behavior, or an object to further
1159 * configure its behavior.
1160 *
1161 * These are supported options:
1162 *
1163 * `whenClosing` (default true)
1164 * Whether to autoclose when the '/' of a closing tag is typed.
1165 * `whenOpening` (default true)
1166 * Whether to autoclose the tag when the final '>' of an opening
1167 * tag is typed.
1168 * `dontCloseTags` (default is empty tags for HTML, none for XML)
1169 * An array of tag names that should not be autoclosed.
1170 * `indentTags` (default is block tags for HTML, none for XML)
1171 * An array of tag names that should, when opened, cause a
1172 * blank line to be added inside the tag, and the blank line and
1173 * closing line to be indented.
1174 *
1175 * See demos/closetag.html for a usage example.
1176 */
1177
1178 (function(mod) {
1179 if (typeof exports == "object" && typeof module == "object") // CommonJS
1180 mod(require("../../lib/codemirror"), require("../fold/xml-fold"));
1181 else if (typeof define == "function" && define.amd) // AMD
1182 define(["../../lib/codemirror", "../fold/xml-fold"], mod);
1183 else // Plain browser env
1184 mod(CodeMirror);
1185 })(function(CodeMirror) {
1186 CodeMirror.defineOption("autoCloseTags", false, function(cm, val, old) {
1187 if (old != CodeMirror.Init && old)
1188 cm.removeKeyMap("autoCloseTags");
1189 if (!val) return;
1190 var map = {name: "autoCloseTags"};
1191 if (typeof val != "object" || val.whenClosing)
1192 map["'/'"] = function(cm) { return autoCloseSlash(cm); };
1193 if (typeof val != "object" || val.whenOpening)
1194 map["'>'"] = function(cm) { return autoCloseGT(cm); };
1195 cm.addKeyMap(map);
1196 });
1197
1198 var htmlDontClose = ["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param",
1199 "source", "track", "wbr"];
1200 var htmlIndent = ["applet", "blockquote", "body", "button", "div", "dl", "fieldset", "form", "frameset", "h1", "h2", "h3", "h4",
1201 "h5", "h6", "head", "html", "iframe", "layer", "legend", "object", "ol", "p", "select", "table", "ul"];
1202
1203 function autoCloseGT(cm) {
1204 if (cm.getOption("disableInput")) return CodeMirror.Pass;
1205 var ranges = cm.listSelections(), replacements = [];
1206 var opt = cm.getOption("autoCloseTags");
1207 for (var i = 0; i < ranges.length; i++) {
1208 if (!ranges[i].empty()) return CodeMirror.Pass;
1209 var pos = ranges[i].head, tok = cm.getTokenAt(pos);
1210 var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state;
1211 if (inner.mode.name != "xml" || !state.tagName) return CodeMirror.Pass;
1212
1213 var html = inner.mode.configuration == "html";
1214 var dontCloseTags = (typeof opt == "object" && opt.dontCloseTags) || (html && htmlDontClose);
1215 var indentTags = (typeof opt == "object" && opt.indentTags) || (html && htmlIndent);
1216
1217 var tagName = state.tagName;
1218 if (tok.end > pos.ch) tagName = tagName.slice(0, tagName.length - tok.end + pos.ch);
1219 var lowerTagName = tagName.toLowerCase();
1220 // Don't process the '>' at the end of an end-tag or self-closing tag
1221 if (!tagName ||
1222 tok.type == "string" && (tok.end != pos.ch || !/[\"\']/.test(tok.string.charAt(tok.string.length - 1)) || tok.string.length == 1) ||
1223 tok.type == "tag" && state.type == "closeTag" ||
1224 tok.string.indexOf("/") == (tok.string.length - 1) || // match something like <someTagName />
1225 dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1 ||
1226 closingTagExists(cm, tagName, pos, state, true))
1227 return CodeMirror.Pass;
1228
1229 var indent = indentTags && indexOf(indentTags, lowerTagName) > -1;
1230 replacements[i] = {indent: indent,
1231 text: ">" + (indent ? "\n\n" : "") + "</" + tagName + ">",
1232 newPos: indent ? CodeMirror.Pos(pos.line + 1, 0) : CodeMirror.Pos(pos.line, pos.ch + 1)};
1233 }
1234
1235 var dontIndentOnAutoClose = (typeof opt == "object" && opt.dontIndentOnAutoClose);
1236 for (var i = ranges.length - 1; i >= 0; i--) {
1237 var info = replacements[i];
1238 cm.replaceRange(info.text, ranges[i].head, ranges[i].anchor, "+insert");
1239 var sel = cm.listSelections().slice(0);
1240 sel[i] = {head: info.newPos, anchor: info.newPos};
1241 cm.setSelections(sel);
1242 if (!dontIndentOnAutoClose && info.indent) {
1243 cm.indentLine(info.newPos.line, null, true);
1244 cm.indentLine(info.newPos.line + 1, null, true);
1245 }
1246 }
1247 }
1248
1249 function autoCloseCurrent(cm, typingSlash) {
1250 var ranges = cm.listSelections(), replacements = [];
1251 var head = typingSlash ? "/" : "</";
1252 var opt = cm.getOption("autoCloseTags");
1253 var dontIndentOnAutoClose = (typeof opt == "object" && opt.dontIndentOnSlash);
1254 for (var i = 0; i < ranges.length; i++) {
1255 if (!ranges[i].empty()) return CodeMirror.Pass;
1256 var pos = ranges[i].head, tok = cm.getTokenAt(pos);
1257 var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state;
1258 if (typingSlash && (tok.type == "string" || tok.string.charAt(0) != "<" ||
1259 tok.start != pos.ch - 1))
1260 return CodeMirror.Pass;
1261 // Kludge to get around the fact that we are not in XML mode
1262 // when completing in JS/CSS snippet in htmlmixed mode. Does not
1263 // work for other XML embedded languages (there is no general
1264 // way to go from a mixed mode to its current XML state).
1265 var replacement;
1266 if (inner.mode.name != "xml") {
1267 if (cm.getMode().name == "htmlmixed" && inner.mode.name == "javascript")
1268 replacement = head + "script";
1269 else if (cm.getMode().name == "htmlmixed" && inner.mode.name == "css")
1270 replacement = head + "style";
1271 else
1272 return CodeMirror.Pass;
1273 } else {
1274 if (!state.context || !state.context.tagName ||
1275 closingTagExists(cm, state.context.tagName, pos, state))
1276 return CodeMirror.Pass;
1277 replacement = head + state.context.tagName;
1278 }
1279 if (cm.getLine(pos.line).charAt(tok.end) != ">") replacement += ">";
1280 replacements[i] = replacement;
1281 }
1282 cm.replaceSelections(replacements);
1283 ranges = cm.listSelections();
1284 if (!dontIndentOnAutoClose) {
1285 for (var i = 0; i < ranges.length; i++)
1286 if (i == ranges.length - 1 || ranges[i].head.line < ranges[i + 1].head.line)
1287 cm.indentLine(ranges[i].head.line);
1288 }
1289 }
1290
1291 function autoCloseSlash(cm) {
1292 if (cm.getOption("disableInput")) return CodeMirror.Pass;
1293 return autoCloseCurrent(cm, true);
1294 }
1295
1296 CodeMirror.commands.closeTag = function(cm) { return autoCloseCurrent(cm); };
1297
1298 function indexOf(collection, elt) {
1299 if (collection.indexOf) return collection.indexOf(elt);
1300 for (var i = 0, e = collection.length; i < e; ++i)
1301 if (collection[i] == elt) return i;
1302 return -1;
1303 }
1304
1305 // If xml-fold is loaded, we use its functionality to try and verify
1306 // whether a given tag is actually unclosed.
1307 function closingTagExists(cm, tagName, pos, state, newTag) {
1308 if (!CodeMirror.scanForClosingTag) return false;
1309 var end = Math.min(cm.lastLine() + 1, pos.line + 500);
1310 var nextClose = CodeMirror.scanForClosingTag(cm, pos, null, end);
1311 if (!nextClose || nextClose.tag != tagName) return false;
1312 var cx = state.context;
1313 // If the immediate wrapping context contains onCx instances of
1314 // the same tag, a closing tag only exists if there are at least
1315 // that many closing tags of that type following.
1316 for (var onCx = newTag ? 1 : 0; cx && cx.tagName == tagName; cx = cx.prev) ++onCx;
1317 pos = nextClose.to;
1318 for (var i = 1; i < onCx; i++) {
1319 var next = CodeMirror.scanForClosingTag(cm, pos, null, end);
1320 if (!next || next.tag != tagName) return false;
1321 pos = next.to;
1322 }
1323 return true;
1324 }
1325 });
1326
1327 },{"../../lib/codemirror":14,"../fold/xml-fold":13}],12:[function(require,module,exports){
1328 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1329 // Distributed under an MIT license: https://codemirror.net/LICENSE
1330
1331 (function(mod) {
1332 if (typeof exports == "object" && typeof module == "object") // CommonJS
1333 mod(require("../../lib/codemirror"), require("../fold/xml-fold"));
1334 else if (typeof define == "function" && define.amd) // AMD
1335 define(["../../lib/codemirror", "../fold/xml-fold"], mod);
1336 else // Plain browser env
1337 mod(CodeMirror);
1338 })(function(CodeMirror) {
1339 "use strict";
1340
1341 CodeMirror.defineOption("matchTags", false, function(cm, val, old) {
1342 if (old && old != CodeMirror.Init) {
1343 cm.off("cursorActivity", doMatchTags);
1344 cm.off("viewportChange", maybeUpdateMatch);
1345 clear(cm);
1346 }
1347 if (val) {
1348 cm.state.matchBothTags = typeof val == "object" && val.bothTags;
1349 cm.on("cursorActivity", doMatchTags);
1350 cm.on("viewportChange", maybeUpdateMatch);
1351 doMatchTags(cm);
1352 }
1353 });
1354
1355 function clear(cm) {
1356 if (cm.state.tagHit) cm.state.tagHit.clear();
1357 if (cm.state.tagOther) cm.state.tagOther.clear();
1358 cm.state.tagHit = cm.state.tagOther = null;
1359 }
1360
1361 function doMatchTags(cm) {
1362 cm.state.failedTagMatch = false;
1363 cm.operation(function() {
1364 clear(cm);
1365 if (cm.somethingSelected()) return;
1366 var cur = cm.getCursor(), range = cm.getViewport();
1367 range.from = Math.min(range.from, cur.line); range.to = Math.max(cur.line + 1, range.to);
1368 var match = CodeMirror.findMatchingTag(cm, cur, range);
1369 if (!match) return;
1370 if (cm.state.matchBothTags) {
1371 var hit = match.at == "open" ? match.open : match.close;
1372 if (hit) cm.state.tagHit = cm.markText(hit.from, hit.to, {className: "CodeMirror-matchingtag"});
1373 }
1374 var other = match.at == "close" ? match.open : match.close;
1375 if (other)
1376 cm.state.tagOther = cm.markText(other.from, other.to, {className: "CodeMirror-matchingtag"});
1377 else
1378 cm.state.failedTagMatch = true;
1379 });
1380 }
1381
1382 function maybeUpdateMatch(cm) {
1383 if (cm.state.failedTagMatch) doMatchTags(cm);
1384 }
1385
1386 CodeMirror.commands.toMatchingTag = function(cm) {
1387 var found = CodeMirror.findMatchingTag(cm, cm.getCursor());
1388 if (found) {
1389 var other = found.at == "close" ? found.open : found.close;
1390 if (other) cm.extendSelection(other.to, other.from);
1391 }
1392 };
1393 });
1394
1395 },{"../../lib/codemirror":14,"../fold/xml-fold":13}],13:[function(require,module,exports){
1396 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1397 // Distributed under an MIT license: https://codemirror.net/LICENSE
1398
1399 (function(mod) {
1400 if (typeof exports == "object" && typeof module == "object") // CommonJS
1401 mod(require("../../lib/codemirror"));
1402 else if (typeof define == "function" && define.amd) // AMD
1403 define(["../../lib/codemirror"], mod);
1404 else // Plain browser env
1405 mod(CodeMirror);
1406 })(function(CodeMirror) {
1407 "use strict";
1408
1409 var Pos = CodeMirror.Pos;
1410 function cmp(a, b) { return a.line - b.line || a.ch - b.ch; }
1411
1412 var nameStartChar = "A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD";
1413 var nameChar = nameStartChar + "\-\:\.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040";
1414 var xmlTagStart = new RegExp("<(/?)([" + nameStartChar + "][" + nameChar + "]*)", "g");
1415
1416 function Iter(cm, line, ch, range) {
1417 this.line = line; this.ch = ch;
1418 this.cm = cm; this.text = cm.getLine(line);
1419 this.min = range ? Math.max(range.from, cm.firstLine()) : cm.firstLine();
1420 this.max = range ? Math.min(range.to - 1, cm.lastLine()) : cm.lastLine();
1421 }
1422
1423 function tagAt(iter, ch) {
1424 var type = iter.cm.getTokenTypeAt(Pos(iter.line, ch));
1425 return type && /\btag\b/.test(type);
1426 }
1427
1428 function nextLine(iter) {
1429 if (iter.line >= iter.max) return;
1430 iter.ch = 0;
1431 iter.text = iter.cm.getLine(++iter.line);
1432 return true;
1433 }
1434 function prevLine(iter) {
1435 if (iter.line <= iter.min) return;
1436 iter.text = iter.cm.getLine(--iter.line);
1437 iter.ch = iter.text.length;
1438 return true;
1439 }
1440
1441 function toTagEnd(iter) {
1442 for (;;) {
1443 var gt = iter.text.indexOf(">", iter.ch);
1444 if (gt == -1) { if (nextLine(iter)) continue; else return; }
1445 if (!tagAt(iter, gt + 1)) { iter.ch = gt + 1; continue; }
1446 var lastSlash = iter.text.lastIndexOf("/", gt);
1447 var selfClose = lastSlash > -1 && !/\S/.test(iter.text.slice(lastSlash + 1, gt));
1448 iter.ch = gt + 1;
1449 return selfClose ? "selfClose" : "regular";
1450 }
1451 }
1452 function toTagStart(iter) {
1453 for (;;) {
1454 var lt = iter.ch ? iter.text.lastIndexOf("<", iter.ch - 1) : -1;
1455 if (lt == -1) { if (prevLine(iter)) continue; else return; }
1456 if (!tagAt(iter, lt + 1)) { iter.ch = lt; continue; }
1457 xmlTagStart.lastIndex = lt;
1458 iter.ch = lt;
1459 var match = xmlTagStart.exec(iter.text);
1460 if (match && match.index == lt) return match;
1461 }
1462 }
1463
1464 function toNextTag(iter) {
1465 for (;;) {
1466 xmlTagStart.lastIndex = iter.ch;
1467 var found = xmlTagStart.exec(iter.text);
1468 if (!found) { if (nextLine(iter)) continue; else return; }
1469 if (!tagAt(iter, found.index + 1)) { iter.ch = found.index + 1; continue; }
1470 iter.ch = found.index + found[0].length;
1471 return found;
1472 }
1473 }
1474 function toPrevTag(iter) {
1475 for (;;) {
1476 var gt = iter.ch ? iter.text.lastIndexOf(">", iter.ch - 1) : -1;
1477 if (gt == -1) { if (prevLine(iter)) continue; else return; }
1478 if (!tagAt(iter, gt + 1)) { iter.ch = gt; continue; }
1479 var lastSlash = iter.text.lastIndexOf("/", gt);
1480 var selfClose = lastSlash > -1 && !/\S/.test(iter.text.slice(lastSlash + 1, gt));
1481 iter.ch = gt + 1;
1482 return selfClose ? "selfClose" : "regular";
1483 }
1484 }
1485
1486 function findMatchingClose(iter, tag) {
1487 var stack = [];
1488 for (;;) {
1489 var next = toNextTag(iter), end, startLine = iter.line, startCh = iter.ch - (next ? next[0].length : 0);
1490 if (!next || !(end = toTagEnd(iter))) return;
1491 if (end == "selfClose") continue;
1492 if (next[1]) { // closing tag
1493 for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == next[2]) {
1494 stack.length = i;
1495 break;
1496 }
1497 if (i < 0 && (!tag || tag == next[2])) return {
1498 tag: next[2],
1499 from: Pos(startLine, startCh),
1500 to: Pos(iter.line, iter.ch)
1501 };
1502 } else { // opening tag
1503 stack.push(next[2]);
1504 }
1505 }
1506 }
1507 function findMatchingOpen(iter, tag) {
1508 var stack = [];
1509 for (;;) {
1510 var prev = toPrevTag(iter);
1511 if (!prev) return;
1512 if (prev == "selfClose") { toTagStart(iter); continue; }
1513 var endLine = iter.line, endCh = iter.ch;
1514 var start = toTagStart(iter);
1515 if (!start) return;
1516 if (start[1]) { // closing tag
1517 stack.push(start[2]);
1518 } else { // opening tag
1519 for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == start[2]) {
1520 stack.length = i;
1521 break;
1522 }
1523 if (i < 0 && (!tag || tag == start[2])) return {
1524 tag: start[2],
1525 from: Pos(iter.line, iter.ch),
1526 to: Pos(endLine, endCh)
1527 };
1528 }
1529 }
1530 }
1531
1532 CodeMirror.registerHelper("fold", "xml", function(cm, start) {
1533 var iter = new Iter(cm, start.line, 0);
1534 for (;;) {
1535 var openTag = toNextTag(iter)
1536 if (!openTag || iter.line != start.line) return
1537 var end = toTagEnd(iter)
1538 if (!end) return
1539 if (!openTag[1] && end != "selfClose") {
1540 var startPos = Pos(iter.line, iter.ch);
1541 var endPos = findMatchingClose(iter, openTag[2]);
1542 return endPos && cmp(endPos.from, startPos) > 0 ? {from: startPos, to: endPos.from} : null
1543 }
1544 }
1545 });
1546 CodeMirror.findMatchingTag = function(cm, pos, range) {
1547 var iter = new Iter(cm, pos.line, pos.ch, range);
1548 if (iter.text.indexOf(">") == -1 && iter.text.indexOf("<") == -1) return;
1549 var end = toTagEnd(iter), to = end && Pos(iter.line, iter.ch);
1550 var start = end && toTagStart(iter);
1551 if (!end || !start || cmp(iter, pos) > 0) return;
1552 var here = {from: Pos(iter.line, iter.ch), to: to, tag: start[2]};
1553 if (end == "selfClose") return {open: here, close: null, at: "open"};
1554
1555 if (start[1]) { // closing tag
1556 return {open: findMatchingOpen(iter, start[2]), close: here, at: "close"};
1557 } else { // opening tag
1558 iter = new Iter(cm, to.line, to.ch, range);
1559 return {open: here, close: findMatchingClose(iter, start[2]), at: "open"};
1560 }
1561 };
1562
1563 CodeMirror.findEnclosingTag = function(cm, pos, range, tag) {
1564 var iter = new Iter(cm, pos.line, pos.ch, range);
1565 for (;;) {
1566 var open = findMatchingOpen(iter, tag);
1567 if (!open) break;
1568 var forward = new Iter(cm, pos.line, pos.ch, range);
1569 var close = findMatchingClose(forward, open.tag);
1570 if (close) return {open: open, close: close};
1571 }
1572 };
1573
1574 // Used by addon/edit/closetag.js
1575 CodeMirror.scanForClosingTag = function(cm, pos, name, end) {
1576 var iter = new Iter(cm, pos.line, pos.ch, end ? {from: 0, to: end} : null);
1577 return findMatchingClose(iter, name);
1578 };
1579 });
1580
1581 },{"../../lib/codemirror":14}],14:[function(require,module,exports){
1582 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1583 // Distributed under an MIT license: https://codemirror.net/LICENSE
1584
1585 // This is CodeMirror (https://codemirror.net), a code editor
1586 // implemented in JavaScript on top of the browser's DOM.
1587 //
1588 // You can find some technical background for some of the code below
1589 // at http://marijnhaverbeke.nl/blog/#cm-internals .
1590
1591 (function (global, factory) {
1592 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
1593 typeof define === 'function' && define.amd ? define(factory) :
1594 (global.CodeMirror = factory());
1595 }(this, (function () { 'use strict';
1596
1597 // Kludges for bugs and behavior differences that can't be feature
1598 // detected are enabled based on userAgent etc sniffing.
1599 var userAgent = navigator.userAgent;
1600 var platform = navigator.platform;
1601
1602 var gecko = /gecko\/\d/i.test(userAgent);
1603 var ie_upto10 = /MSIE \d/.test(userAgent);
1604 var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent);
1605 var edge = /Edge\/(\d+)/.exec(userAgent);
1606 var ie = ie_upto10 || ie_11up || edge;
1607 var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1]);
1608 var webkit = !edge && /WebKit\//.test(userAgent);
1609 var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent);
1610 var chrome = !edge && /Chrome\//.test(userAgent);
1611 var presto = /Opera\//.test(userAgent);
1612 var safari = /Apple Computer/.test(navigator.vendor);
1613 var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent);
1614 var phantom = /PhantomJS/.test(userAgent);
1615
1616 var ios = !edge && /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent);
1617 var android = /Android/.test(userAgent);
1618 // This is woefully incomplete. Suggestions for alternative methods welcome.
1619 var mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent);
1620 var mac = ios || /Mac/.test(platform);
1621 var chromeOS = /\bCrOS\b/.test(userAgent);
1622 var windows = /win/i.test(platform);
1623
1624 var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/);
1625 if (presto_version) { presto_version = Number(presto_version[1]); }
1626 if (presto_version && presto_version >= 15) { presto = false; webkit = true; }
1627 // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
1628 var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11));
1629 var captureRightClick = gecko || (ie && ie_version >= 9);
1630
1631 function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") }
1632
1633 var rmClass = function(node, cls) {
1634 var current = node.className;
1635 var match = classTest(cls).exec(current);
1636 if (match) {
1637 var after = current.slice(match.index + match[0].length);
1638 node.className = current.slice(0, match.index) + (after ? match[1] + after : "");
1639 }
1640 };
1641
1642 function removeChildren(e) {
1643 for (var count = e.childNodes.length; count > 0; --count)
1644 { e.removeChild(e.firstChild); }
1645 return e
1646 }
1647
1648 function removeChildrenAndAdd(parent, e) {
1649 return removeChildren(parent).appendChild(e)
1650 }
1651
1652 function elt(tag, content, className, style) {
1653 var e = document.createElement(tag);
1654 if (className) { e.className = className; }
1655 if (style) { e.style.cssText = style; }
1656 if (typeof content == "string") { e.appendChild(document.createTextNode(content)); }
1657 else if (content) { for (var i = 0; i < content.length; ++i) { e.appendChild(content[i]); } }
1658 return e
1659 }
1660 // wrapper for elt, which removes the elt from the accessibility tree
1661 function eltP(tag, content, className, style) {
1662 var e = elt(tag, content, className, style);
1663 e.setAttribute("role", "presentation");
1664 return e
1665 }
1666
1667 var range;
1668 if (document.createRange) { range = function(node, start, end, endNode) {
1669 var r = document.createRange();
1670 r.setEnd(endNode || node, end);
1671 r.setStart(node, start);
1672 return r
1673 }; }
1674 else { range = function(node, start, end) {
1675 var r = document.body.createTextRange();
1676 try { r.moveToElementText(node.parentNode); }
1677 catch(e) { return r }
1678 r.collapse(true);
1679 r.moveEnd("character", end);
1680 r.moveStart("character", start);
1681 return r
1682 }; }
1683
1684 function contains(parent, child) {
1685 if (child.nodeType == 3) // Android browser always returns false when child is a textnode
1686 { child = child.parentNode; }
1687 if (parent.contains)
1688 { return parent.contains(child) }
1689 do {
1690 if (child.nodeType == 11) { child = child.host; }
1691 if (child == parent) { return true }
1692 } while (child = child.parentNode)
1693 }
1694
1695 function activeElt() {
1696 // IE and Edge may throw an "Unspecified Error" when accessing document.activeElement.
1697 // IE < 10 will throw when accessed while the page is loading or in an iframe.
1698 // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable.
1699 var activeElement;
1700 try {
1701 activeElement = document.activeElement;
1702 } catch(e) {
1703 activeElement = document.body || null;
1704 }
1705 while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement)
1706 { activeElement = activeElement.shadowRoot.activeElement; }
1707 return activeElement
1708 }
1709
1710 function addClass(node, cls) {
1711 var current = node.className;
1712 if (!classTest(cls).test(current)) { node.className += (current ? " " : "") + cls; }
1713 }
1714 function joinClasses(a, b) {
1715 var as = a.split(" ");
1716 for (var i = 0; i < as.length; i++)
1717 { if (as[i] && !classTest(as[i]).test(b)) { b += " " + as[i]; } }
1718 return b
1719 }
1720
1721 var selectInput = function(node) { node.select(); };
1722 if (ios) // Mobile Safari apparently has a bug where select() is broken.
1723 { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; }; }
1724 else if (ie) // Suppress mysterious IE10 errors
1725 { selectInput = function(node) { try { node.select(); } catch(_e) {} }; }
1726
1727 function bind(f) {
1728 var args = Array.prototype.slice.call(arguments, 1);
1729 return function(){return f.apply(null, args)}
1730 }
1731
1732 function copyObj(obj, target, overwrite) {
1733 if (!target) { target = {}; }
1734 for (var prop in obj)
1735 { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
1736 { target[prop] = obj[prop]; } }
1737 return target
1738 }
1739
1740 // Counts the column offset in a string, taking tabs into account.
1741 // Used mostly to find indentation.
1742 function countColumn(string, end, tabSize, startIndex, startValue) {
1743 if (end == null) {
1744 end = string.search(/[^\s\u00a0]/);
1745 if (end == -1) { end = string.length; }
1746 }
1747 for (var i = startIndex || 0, n = startValue || 0;;) {
1748 var nextTab = string.indexOf("\t", i);
1749 if (nextTab < 0 || nextTab >= end)
1750 { return n + (end - i) }
1751 n += nextTab - i;
1752 n += tabSize - (n % tabSize);
1753 i = nextTab + 1;
1754 }
1755 }
1756
1757 var Delayed = function() {this.id = null;};
1758 Delayed.prototype.set = function (ms, f) {
1759 clearTimeout(this.id);
1760 this.id = setTimeout(f, ms);
1761 };
1762
1763 function indexOf(array, elt) {
1764 for (var i = 0; i < array.length; ++i)
1765 { if (array[i] == elt) { return i } }
1766 return -1
1767 }
1768
1769 // Number of pixels added to scroller and sizer to hide scrollbar
1770 var scrollerGap = 30;
1771
1772 // Returned or thrown by various protocols to signal 'I'm not
1773 // handling this'.
1774 var Pass = {toString: function(){return "CodeMirror.Pass"}};
1775
1776 // Reused option objects for setSelection & friends
1777 var sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"};
1778
1779 // The inverse of countColumn -- find the offset that corresponds to
1780 // a particular column.
1781 function findColumn(string, goal, tabSize) {
1782 for (var pos = 0, col = 0;;) {
1783 var nextTab = string.indexOf("\t", pos);
1784 if (nextTab == -1) { nextTab = string.length; }
1785 var skipped = nextTab - pos;
1786 if (nextTab == string.length || col + skipped >= goal)
1787 { return pos + Math.min(skipped, goal - col) }
1788 col += nextTab - pos;
1789 col += tabSize - (col % tabSize);
1790 pos = nextTab + 1;
1791 if (col >= goal) { return pos }
1792 }
1793 }
1794
1795 var spaceStrs = [""];
1796 function spaceStr(n) {
1797 while (spaceStrs.length <= n)
1798 { spaceStrs.push(lst(spaceStrs) + " "); }
1799 return spaceStrs[n]
1800 }
1801
1802 function lst(arr) { return arr[arr.length-1] }
1803
1804 function map(array, f) {
1805 var out = [];
1806 for (var i = 0; i < array.length; i++) { out[i] = f(array[i], i); }
1807 return out
1808 }
1809
1810 function insertSorted(array, value, score) {
1811 var pos = 0, priority = score(value);
1812 while (pos < array.length && score(array[pos]) <= priority) { pos++; }
1813 array.splice(pos, 0, value);
1814 }
1815
1816 function nothing() {}
1817
1818 function createObj(base, props) {
1819 var inst;
1820 if (Object.create) {
1821 inst = Object.create(base);
1822 } else {
1823 nothing.prototype = base;
1824 inst = new nothing();
1825 }
1826 if (props) { copyObj(props, inst); }
1827 return inst
1828 }
1829
1830 var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
1831 function isWordCharBasic(ch) {
1832 return /\w/.test(ch) || ch > "\x80" &&
1833 (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch))
1834 }
1835 function isWordChar(ch, helper) {
1836 if (!helper) { return isWordCharBasic(ch) }
1837 if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) { return true }
1838 return helper.test(ch)
1839 }
1840
1841 function isEmpty(obj) {
1842 for (var n in obj) { if (obj.hasOwnProperty(n) && obj[n]) { return false } }
1843 return true
1844 }
1845
1846 // Extending unicode characters. A series of a non-extending char +
1847 // any number of extending chars is treated as a single unit as far
1848 // as editing and measuring is concerned. This is not fully correct,
1849 // since some scripts/fonts/browsers also treat other configurations
1850 // of code points as a group.
1851 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]/;
1852 function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) }
1853
1854 // Returns a number from the range [`0`; `str.length`] unless `pos` is outside that range.
1855 function skipExtendingChars(str, pos, dir) {
1856 while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(pos))) { pos += dir; }
1857 return pos
1858 }
1859
1860 // Returns the value from the range [`from`; `to`] that satisfies
1861 // `pred` and is closest to `from`. Assumes that at least `to`
1862 // satisfies `pred`. Supports `from` being greater than `to`.
1863 function findFirst(pred, from, to) {
1864 // At any point we are certain `to` satisfies `pred`, don't know
1865 // whether `from` does.
1866 var dir = from > to ? -1 : 1;
1867 for (;;) {
1868 if (from == to) { return from }
1869 var midF = (from + to) / 2, mid = dir < 0 ? Math.ceil(midF) : Math.floor(midF);
1870 if (mid == from) { return pred(mid) ? from : to }
1871 if (pred(mid)) { to = mid; }
1872 else { from = mid + dir; }
1873 }
1874 }
1875
1876 // The display handles the DOM integration, both for input reading
1877 // and content drawing. It holds references to DOM nodes and
1878 // display-related state.
1879
1880 function Display(place, doc, input) {
1881 var d = this;
1882 this.input = input;
1883
1884 // Covers bottom-right square when both scrollbars are present.
1885 d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
1886 d.scrollbarFiller.setAttribute("cm-not-content", "true");
1887 // Covers bottom of gutter when coverGutterNextToScrollbar is on
1888 // and h scrollbar is present.
1889 d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler");
1890 d.gutterFiller.setAttribute("cm-not-content", "true");
1891 // Will contain the actual code, positioned to cover the viewport.
1892 d.lineDiv = eltP("div", null, "CodeMirror-code");
1893 // Elements are added to these to represent selection and cursors.
1894 d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
1895 d.cursorDiv = elt("div", null, "CodeMirror-cursors");
1896 // A visibility: hidden element used to find the size of things.
1897 d.measure = elt("div", null, "CodeMirror-measure");
1898 // When lines outside of the viewport are measured, they are drawn in this.
1899 d.lineMeasure = elt("div", null, "CodeMirror-measure");
1900 // Wraps everything that needs to exist inside the vertically-padded coordinate system
1901 d.lineSpace = eltP("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],
1902 null, "position: relative; outline: none");
1903 var lines = eltP("div", [d.lineSpace], "CodeMirror-lines");
1904 // Moved around its parent to cover visible view.
1905 d.mover = elt("div", [lines], null, "position: relative");
1906 // Set to the height of the document, allowing scrolling.
1907 d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
1908 d.sizerWidth = null;
1909 // Behavior of elts with overflow: auto and padding is
1910 // inconsistent across browsers. This is used to ensure the
1911 // scrollable area is big enough.
1912 d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;");
1913 // Will contain the gutters, if any.
1914 d.gutters = elt("div", null, "CodeMirror-gutters");
1915 d.lineGutter = null;
1916 // Actual scrollable element.
1917 d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll");
1918 d.scroller.setAttribute("tabIndex", "-1");
1919 // The element in which the editor lives.
1920 d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror");
1921
1922 // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)
1923 if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
1924 if (!webkit && !(gecko && mobile)) { d.scroller.draggable = true; }
1925
1926 if (place) {
1927 if (place.appendChild) { place.appendChild(d.wrapper); }
1928 else { place(d.wrapper); }
1929 }
1930
1931 // Current rendered range (may be bigger than the view window).
1932 d.viewFrom = d.viewTo = doc.first;
1933 d.reportedViewFrom = d.reportedViewTo = doc.first;
1934 // Information about the rendered lines.
1935 d.view = [];
1936 d.renderedView = null;
1937 // Holds info about a single rendered line when it was rendered
1938 // for measurement, while not in view.
1939 d.externalMeasured = null;
1940 // Empty space (in pixels) above the view
1941 d.viewOffset = 0;
1942 d.lastWrapHeight = d.lastWrapWidth = 0;
1943 d.updateLineNumbers = null;
1944
1945 d.nativeBarWidth = d.barHeight = d.barWidth = 0;
1946 d.scrollbarsClipped = false;
1947
1948 // Used to only resize the line number gutter when necessary (when
1949 // the amount of lines crosses a boundary that makes its width change)
1950 d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
1951 // Set to true when a non-horizontal-scrolling line widget is
1952 // added. As an optimization, line widget aligning is skipped when
1953 // this is false.
1954 d.alignWidgets = false;
1955
1956 d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
1957
1958 // Tracks the maximum line length so that the horizontal scrollbar
1959 // can be kept static when scrolling.
1960 d.maxLine = null;
1961 d.maxLineLength = 0;
1962 d.maxLineChanged = false;
1963
1964 // Used for measuring wheel scrolling granularity
1965 d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
1966
1967 // True when shift is held down.
1968 d.shift = false;
1969
1970 // Used to track whether anything happened since the context menu
1971 // was opened.
1972 d.selForContextMenu = null;
1973
1974 d.activeTouch = null;
1975
1976 input.init(d);
1977 }
1978
1979 // Find the line object corresponding to the given line number.
1980 function getLine(doc, n) {
1981 n -= doc.first;
1982 if (n < 0 || n >= doc.size) { throw new Error("There is no line " + (n + doc.first) + " in the document.") }
1983 var chunk = doc;
1984 while (!chunk.lines) {
1985 for (var i = 0;; ++i) {
1986 var child = chunk.children[i], sz = child.chunkSize();
1987 if (n < sz) { chunk = child; break }
1988 n -= sz;
1989 }
1990 }
1991 return chunk.lines[n]
1992 }
1993
1994 // Get the part of a document between two positions, as an array of
1995 // strings.
1996 function getBetween(doc, start, end) {
1997 var out = [], n = start.line;
1998 doc.iter(start.line, end.line + 1, function (line) {
1999 var text = line.text;
2000 if (n == end.line) { text = text.slice(0, end.ch); }
2001 if (n == start.line) { text = text.slice(start.ch); }
2002 out.push(text);
2003 ++n;
2004 });
2005 return out
2006 }
2007 // Get the lines between from and to, as array of strings.
2008 function getLines(doc, from, to) {
2009 var out = [];
2010 doc.iter(from, to, function (line) { out.push(line.text); }); // iter aborts when callback returns truthy value
2011 return out
2012 }
2013
2014 // Update the height of a line, propagating the height change
2015 // upwards to parent nodes.
2016 function updateLineHeight(line, height) {
2017 var diff = height - line.height;
2018 if (diff) { for (var n = line; n; n = n.parent) { n.height += diff; } }
2019 }
2020
2021 // Given a line object, find its line number by walking up through
2022 // its parent links.
2023 function lineNo(line) {
2024 if (line.parent == null) { return null }
2025 var cur = line.parent, no = indexOf(cur.lines, line);
2026 for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
2027 for (var i = 0;; ++i) {
2028 if (chunk.children[i] == cur) { break }
2029 no += chunk.children[i].chunkSize();
2030 }
2031 }
2032 return no + cur.first
2033 }
2034
2035 // Find the line at the given vertical position, using the height
2036 // information in the document tree.
2037 function lineAtHeight(chunk, h) {
2038 var n = chunk.first;
2039 outer: do {
2040 for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) {
2041 var child = chunk.children[i$1], ch = child.height;
2042 if (h < ch) { chunk = child; continue outer }
2043 h -= ch;
2044 n += child.chunkSize();
2045 }
2046 return n
2047 } while (!chunk.lines)
2048 var i = 0;
2049 for (; i < chunk.lines.length; ++i) {
2050 var line = chunk.lines[i], lh = line.height;
2051 if (h < lh) { break }
2052 h -= lh;
2053 }
2054 return n + i
2055 }
2056
2057 function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size}
2058
2059 function lineNumberFor(options, i) {
2060 return String(options.lineNumberFormatter(i + options.firstLineNumber))
2061 }
2062
2063 // A Pos instance represents a position within the text.
2064 function Pos(line, ch, sticky) {
2065 if ( sticky === void 0 ) sticky = null;
2066
2067 if (!(this instanceof Pos)) { return new Pos(line, ch, sticky) }
2068 this.line = line;
2069 this.ch = ch;
2070 this.sticky = sticky;
2071 }
2072
2073 // Compare two positions, return 0 if they are the same, a negative
2074 // number when a is less, and a positive number otherwise.
2075 function cmp(a, b) { return a.line - b.line || a.ch - b.ch }
2076
2077 function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 }
2078
2079 function copyPos(x) {return Pos(x.line, x.ch)}
2080 function maxPos(a, b) { return cmp(a, b) < 0 ? b : a }
2081 function minPos(a, b) { return cmp(a, b) < 0 ? a : b }
2082
2083 // Most of the external API clips given positions to make sure they
2084 // actually exist within the document.
2085 function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))}
2086 function clipPos(doc, pos) {
2087 if (pos.line < doc.first) { return Pos(doc.first, 0) }
2088 var last = doc.first + doc.size - 1;
2089 if (pos.line > last) { return Pos(last, getLine(doc, last).text.length) }
2090 return clipToLen(pos, getLine(doc, pos.line).text.length)
2091 }
2092 function clipToLen(pos, linelen) {
2093 var ch = pos.ch;
2094 if (ch == null || ch > linelen) { return Pos(pos.line, linelen) }
2095 else if (ch < 0) { return Pos(pos.line, 0) }
2096 else { return pos }
2097 }
2098 function clipPosArray(doc, array) {
2099 var out = [];
2100 for (var i = 0; i < array.length; i++) { out[i] = clipPos(doc, array[i]); }
2101 return out
2102 }
2103
2104 // Optimize some code when these features are not used.
2105 var sawReadOnlySpans = false, sawCollapsedSpans = false;
2106
2107 function seeReadOnlySpans() {
2108 sawReadOnlySpans = true;
2109 }
2110
2111 function seeCollapsedSpans() {
2112 sawCollapsedSpans = true;
2113 }
2114
2115 // TEXTMARKER SPANS
2116
2117 function MarkedSpan(marker, from, to) {
2118 this.marker = marker;
2119 this.from = from; this.to = to;
2120 }
2121
2122 // Search an array of spans for a span matching the given marker.
2123 function getMarkedSpanFor(spans, marker) {
2124 if (spans) { for (var i = 0; i < spans.length; ++i) {
2125 var span = spans[i];
2126 if (span.marker == marker) { return span }
2127 } }
2128 }
2129 // Remove a span from an array, returning undefined if no spans are
2130 // left (we don't store arrays for lines without spans).
2131 function removeMarkedSpan(spans, span) {
2132 var r;
2133 for (var i = 0; i < spans.length; ++i)
2134 { if (spans[i] != span) { (r || (r = [])).push(spans[i]); } }
2135 return r
2136 }
2137 // Add a span to a line.
2138 function addMarkedSpan(line, span) {
2139 line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
2140 span.marker.attachLine(line);
2141 }
2142
2143 // Used for the algorithm that adjusts markers for a change in the
2144 // document. These functions cut an array of spans at a given
2145 // character position, returning an array of remaining chunks (or
2146 // undefined if nothing remains).
2147 function markedSpansBefore(old, startCh, isInsert) {
2148 var nw;
2149 if (old) { for (var i = 0; i < old.length; ++i) {
2150 var span = old[i], marker = span.marker;
2151 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
2152 if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) {
2153 var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh)
2154 ;(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to));
2155 }
2156 } }
2157 return nw
2158 }
2159 function markedSpansAfter(old, endCh, isInsert) {
2160 var nw;
2161 if (old) { for (var i = 0; i < old.length; ++i) {
2162 var span = old[i], marker = span.marker;
2163 var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
2164 if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) {
2165 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh)
2166 ;(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh,
2167 span.to == null ? null : span.to - endCh));
2168 }
2169 } }
2170 return nw
2171 }
2172
2173 // Given a change object, compute the new set of marker spans that
2174 // cover the line in which the change took place. Removes spans
2175 // entirely within the change, reconnects spans belonging to the
2176 // same marker that appear on both sides of the change, and cuts off
2177 // spans partially within the change. Returns an array of span
2178 // arrays with one element for each line in (after) the change.
2179 function stretchSpansOverChange(doc, change) {
2180 if (change.full) { return null }
2181 var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans;
2182 var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans;
2183 if (!oldFirst && !oldLast) { return null }
2184
2185 var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0;
2186 // Get the spans that 'stick out' on both sides
2187 var first = markedSpansBefore(oldFirst, startCh, isInsert);
2188 var last = markedSpansAfter(oldLast, endCh, isInsert);
2189
2190 // Next, merge those two ends
2191 var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0);
2192 if (first) {
2193 // Fix up .to properties of first
2194 for (var i = 0; i < first.length; ++i) {
2195 var span = first[i];
2196 if (span.to == null) {
2197 var found = getMarkedSpanFor(last, span.marker);
2198 if (!found) { span.to = startCh; }
2199 else if (sameLine) { span.to = found.to == null ? null : found.to + offset; }
2200 }
2201 }
2202 }
2203 if (last) {
2204 // Fix up .from in last (or move them into first in case of sameLine)
2205 for (var i$1 = 0; i$1 < last.length; ++i$1) {
2206 var span$1 = last[i$1];
2207 if (span$1.to != null) { span$1.to += offset; }
2208 if (span$1.from == null) {
2209 var found$1 = getMarkedSpanFor(first, span$1.marker);
2210 if (!found$1) {
2211 span$1.from = offset;
2212 if (sameLine) { (first || (first = [])).push(span$1); }
2213 }
2214 } else {
2215 span$1.from += offset;
2216 if (sameLine) { (first || (first = [])).push(span$1); }
2217 }
2218 }
2219 }
2220 // Make sure we didn't create any zero-length spans
2221 if (first) { first = clearEmptySpans(first); }
2222 if (last && last != first) { last = clearEmptySpans(last); }
2223
2224 var newMarkers = [first];
2225 if (!sameLine) {
2226 // Fill gap with whole-line-spans
2227 var gap = change.text.length - 2, gapMarkers;
2228 if (gap > 0 && first)
2229 { for (var i$2 = 0; i$2 < first.length; ++i$2)
2230 { if (first[i$2].to == null)
2231 { (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i$2].marker, null, null)); } } }
2232 for (var i$3 = 0; i$3 < gap; ++i$3)
2233 { newMarkers.push(gapMarkers); }
2234 newMarkers.push(last);
2235 }
2236 return newMarkers
2237 }
2238
2239 // Remove spans that are empty and don't have a clearWhenEmpty
2240 // option of false.
2241 function clearEmptySpans(spans) {
2242 for (var i = 0; i < spans.length; ++i) {
2243 var span = spans[i];
2244 if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)
2245 { spans.splice(i--, 1); }
2246 }
2247 if (!spans.length) { return null }
2248 return spans
2249 }
2250
2251 // Used to 'clip' out readOnly ranges when making a change.
2252 function removeReadOnlyRanges(doc, from, to) {
2253 var markers = null;
2254 doc.iter(from.line, to.line + 1, function (line) {
2255 if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
2256 var mark = line.markedSpans[i].marker;
2257 if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
2258 { (markers || (markers = [])).push(mark); }
2259 } }
2260 });
2261 if (!markers) { return null }
2262 var parts = [{from: from, to: to}];
2263 for (var i = 0; i < markers.length; ++i) {
2264 var mk = markers[i], m = mk.find(0);
2265 for (var j = 0; j < parts.length; ++j) {
2266 var p = parts[j];
2267 if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) { continue }
2268 var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to);
2269 if (dfrom < 0 || !mk.inclusiveLeft && !dfrom)
2270 { newParts.push({from: p.from, to: m.from}); }
2271 if (dto > 0 || !mk.inclusiveRight && !dto)
2272 { newParts.push({from: m.to, to: p.to}); }
2273 parts.splice.apply(parts, newParts);
2274 j += newParts.length - 3;
2275 }
2276 }
2277 return parts
2278 }
2279
2280 // Connect or disconnect spans from a line.
2281 function detachMarkedSpans(line) {
2282 var spans = line.markedSpans;
2283 if (!spans) { return }
2284 for (var i = 0; i < spans.length; ++i)
2285 { spans[i].marker.detachLine(line); }
2286 line.markedSpans = null;
2287 }
2288 function attachMarkedSpans(line, spans) {
2289 if (!spans) { return }
2290 for (var i = 0; i < spans.length; ++i)
2291 { spans[i].marker.attachLine(line); }
2292 line.markedSpans = spans;
2293 }
2294
2295 // Helpers used when computing which overlapping collapsed span
2296 // counts as the larger one.
2297 function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 }
2298 function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 }
2299
2300 // Returns a number indicating which of two overlapping collapsed
2301 // spans is larger (and thus includes the other). Falls back to
2302 // comparing ids when the spans cover exactly the same range.
2303 function compareCollapsedMarkers(a, b) {
2304 var lenDiff = a.lines.length - b.lines.length;
2305 if (lenDiff != 0) { return lenDiff }
2306 var aPos = a.find(), bPos = b.find();
2307 var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b);
2308 if (fromCmp) { return -fromCmp }
2309 var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b);
2310 if (toCmp) { return toCmp }
2311 return b.id - a.id
2312 }
2313
2314 // Find out whether a line ends or starts in a collapsed span. If
2315 // so, return the marker for that span.
2316 function collapsedSpanAtSide(line, start) {
2317 var sps = sawCollapsedSpans && line.markedSpans, found;
2318 if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
2319 sp = sps[i];
2320 if (sp.marker.collapsed && (start ? sp.from : sp.to) == null &&
2321 (!found || compareCollapsedMarkers(found, sp.marker) < 0))
2322 { found = sp.marker; }
2323 } }
2324 return found
2325 }
2326 function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) }
2327 function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) }
2328
2329 function collapsedSpanAround(line, ch) {
2330 var sps = sawCollapsedSpans && line.markedSpans, found;
2331 if (sps) { for (var i = 0; i < sps.length; ++i) {
2332 var sp = sps[i];
2333 if (sp.marker.collapsed && (sp.from == null || sp.from < ch) && (sp.to == null || sp.to > ch) &&
2334 (!found || compareCollapsedMarkers(found, sp.marker) < 0)) { found = sp.marker; }
2335 } }
2336 return found
2337 }
2338
2339 // Test whether there exists a collapsed span that partially
2340 // overlaps (covers the start or end, but not both) of a new span.
2341 // Such overlap is not allowed.
2342 function conflictingCollapsedRange(doc, lineNo$$1, from, to, marker) {
2343 var line = getLine(doc, lineNo$$1);
2344 var sps = sawCollapsedSpans && line.markedSpans;
2345 if (sps) { for (var i = 0; i < sps.length; ++i) {
2346 var sp = sps[i];
2347 if (!sp.marker.collapsed) { continue }
2348 var found = sp.marker.find(0);
2349 var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker);
2350 var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker);
2351 if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue }
2352 if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) ||
2353 fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0))
2354 { return true }
2355 } }
2356 }
2357
2358 // A visual line is a line as drawn on the screen. Folding, for
2359 // example, can cause multiple logical lines to appear on the same
2360 // visual line. This finds the start of the visual line that the
2361 // given line is part of (usually that is the line itself).
2362 function visualLine(line) {
2363 var merged;
2364 while (merged = collapsedSpanAtStart(line))
2365 { line = merged.find(-1, true).line; }
2366 return line
2367 }
2368
2369 function visualLineEnd(line) {
2370 var merged;
2371 while (merged = collapsedSpanAtEnd(line))
2372 { line = merged.find(1, true).line; }
2373 return line
2374 }
2375
2376 // Returns an array of logical lines that continue the visual line
2377 // started by the argument, or undefined if there are no such lines.
2378 function visualLineContinued(line) {
2379 var merged, lines;
2380 while (merged = collapsedSpanAtEnd(line)) {
2381 line = merged.find(1, true).line
2382 ;(lines || (lines = [])).push(line);
2383 }
2384 return lines
2385 }
2386
2387 // Get the line number of the start of the visual line that the
2388 // given line number is part of.
2389 function visualLineNo(doc, lineN) {
2390 var line = getLine(doc, lineN), vis = visualLine(line);
2391 if (line == vis) { return lineN }
2392 return lineNo(vis)
2393 }
2394
2395 // Get the line number of the start of the next visual line after
2396 // the given line.
2397 function visualLineEndNo(doc, lineN) {
2398 if (lineN > doc.lastLine()) { return lineN }
2399 var line = getLine(doc, lineN), merged;
2400 if (!lineIsHidden(doc, line)) { return lineN }
2401 while (merged = collapsedSpanAtEnd(line))
2402 { line = merged.find(1, true).line; }
2403 return lineNo(line) + 1
2404 }
2405
2406 // Compute whether a line is hidden. Lines count as hidden when they
2407 // are part of a visual line that starts with another line, or when
2408 // they are entirely covered by collapsed, non-widget span.
2409 function lineIsHidden(doc, line) {
2410 var sps = sawCollapsedSpans && line.markedSpans;
2411 if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
2412 sp = sps[i];
2413 if (!sp.marker.collapsed) { continue }
2414 if (sp.from == null) { return true }
2415 if (sp.marker.widgetNode) { continue }
2416 if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
2417 { return true }
2418 } }
2419 }
2420 function lineIsHiddenInner(doc, line, span) {
2421 if (span.to == null) {
2422 var end = span.marker.find(1, true);
2423 return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker))
2424 }
2425 if (span.marker.inclusiveRight && span.to == line.text.length)
2426 { return true }
2427 for (var sp = (void 0), i = 0; i < line.markedSpans.length; ++i) {
2428 sp = line.markedSpans[i];
2429 if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to &&
2430 (sp.to == null || sp.to != span.from) &&
2431 (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
2432 lineIsHiddenInner(doc, line, sp)) { return true }
2433 }
2434 }
2435
2436 // Find the height above the given line.
2437 function heightAtLine(lineObj) {
2438 lineObj = visualLine(lineObj);
2439
2440 var h = 0, chunk = lineObj.parent;
2441 for (var i = 0; i < chunk.lines.length; ++i) {
2442 var line = chunk.lines[i];
2443 if (line == lineObj) { break }
2444 else { h += line.height; }
2445 }
2446 for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
2447 for (var i$1 = 0; i$1 < p.children.length; ++i$1) {
2448 var cur = p.children[i$1];
2449 if (cur == chunk) { break }
2450 else { h += cur.height; }
2451 }
2452 }
2453 return h
2454 }
2455
2456 // Compute the character length of a line, taking into account
2457 // collapsed ranges (see markText) that might hide parts, and join
2458 // other lines onto it.
2459 function lineLength(line) {
2460 if (line.height == 0) { return 0 }
2461 var len = line.text.length, merged, cur = line;
2462 while (merged = collapsedSpanAtStart(cur)) {
2463 var found = merged.find(0, true);
2464 cur = found.from.line;
2465 len += found.from.ch - found.to.ch;
2466 }
2467 cur = line;
2468 while (merged = collapsedSpanAtEnd(cur)) {
2469 var found$1 = merged.find(0, true);
2470 len -= cur.text.length - found$1.from.ch;
2471 cur = found$1.to.line;
2472 len += cur.text.length - found$1.to.ch;
2473 }
2474 return len
2475 }
2476
2477 // Find the longest line in the document.
2478 function findMaxLine(cm) {
2479 var d = cm.display, doc = cm.doc;
2480 d.maxLine = getLine(doc, doc.first);
2481 d.maxLineLength = lineLength(d.maxLine);
2482 d.maxLineChanged = true;
2483 doc.iter(function (line) {
2484 var len = lineLength(line);
2485 if (len > d.maxLineLength) {
2486 d.maxLineLength = len;
2487 d.maxLine = line;
2488 }
2489 });
2490 }
2491
2492 // BIDI HELPERS
2493
2494 function iterateBidiSections(order, from, to, f) {
2495 if (!order) { return f(from, to, "ltr", 0) }
2496 var found = false;
2497 for (var i = 0; i < order.length; ++i) {
2498 var part = order[i];
2499 if (part.from < to && part.to > from || from == to && part.to == from) {
2500 f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr", i);
2501 found = true;
2502 }
2503 }
2504 if (!found) { f(from, to, "ltr"); }
2505 }
2506
2507 var bidiOther = null;
2508 function getBidiPartAt(order, ch, sticky) {
2509 var found;
2510 bidiOther = null;
2511 for (var i = 0; i < order.length; ++i) {
2512 var cur = order[i];
2513 if (cur.from < ch && cur.to > ch) { return i }
2514 if (cur.to == ch) {
2515 if (cur.from != cur.to && sticky == "before") { found = i; }
2516 else { bidiOther = i; }
2517 }
2518 if (cur.from == ch) {
2519 if (cur.from != cur.to && sticky != "before") { found = i; }
2520 else { bidiOther = i; }
2521 }
2522 }
2523 return found != null ? found : bidiOther
2524 }
2525
2526 // Bidirectional ordering algorithm
2527 // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
2528 // that this (partially) implements.
2529
2530 // One-char codes used for character types:
2531 // L (L): Left-to-Right
2532 // R (R): Right-to-Left
2533 // r (AL): Right-to-Left Arabic
2534 // 1 (EN): European Number
2535 // + (ES): European Number Separator
2536 // % (ET): European Number Terminator
2537 // n (AN): Arabic Number
2538 // , (CS): Common Number Separator
2539 // m (NSM): Non-Spacing Mark
2540 // b (BN): Boundary Neutral
2541 // s (B): Paragraph Separator
2542 // t (S): Segment Separator
2543 // w (WS): Whitespace
2544 // N (ON): Other Neutrals
2545
2546 // Returns null if characters are ordered as they appear
2547 // (left-to-right), or an array of sections ({from, to, level}
2548 // objects) in the order in which they occur visually.
2549 var bidiOrdering = (function() {
2550 // Character types for codepoints 0 to 0xff
2551 var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN";
2552 // Character types for codepoints 0x600 to 0x6f9
2553 var arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111";
2554 function charType(code) {
2555 if (code <= 0xf7) { return lowTypes.charAt(code) }
2556 else if (0x590 <= code && code <= 0x5f4) { return "R" }
2557 else if (0x600 <= code && code <= 0x6f9) { return arabicTypes.charAt(code - 0x600) }
2558 else if (0x6ee <= code && code <= 0x8ac) { return "r" }
2559 else if (0x2000 <= code && code <= 0x200b) { return "w" }
2560 else if (code == 0x200c) { return "b" }
2561 else { return "L" }
2562 }
2563
2564 var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
2565 var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
2566
2567 function BidiSpan(level, from, to) {
2568 this.level = level;
2569 this.from = from; this.to = to;
2570 }
2571
2572 return function(str, direction) {
2573 var outerType = direction == "ltr" ? "L" : "R";
2574
2575 if (str.length == 0 || direction == "ltr" && !bidiRE.test(str)) { return false }
2576 var len = str.length, types = [];
2577 for (var i = 0; i < len; ++i)
2578 { types.push(charType(str.charCodeAt(i))); }
2579
2580 // W1. Examine each non-spacing mark (NSM) in the level run, and
2581 // change the type of the NSM to the type of the previous
2582 // character. If the NSM is at the start of the level run, it will
2583 // get the type of sor.
2584 for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) {
2585 var type = types[i$1];
2586 if (type == "m") { types[i$1] = prev; }
2587 else { prev = type; }
2588 }
2589
2590 // W2. Search backwards from each instance of a European number
2591 // until the first strong type (R, L, AL, or sor) is found. If an
2592 // AL is found, change the type of the European number to Arabic
2593 // number.
2594 // W3. Change all ALs to R.
2595 for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) {
2596 var type$1 = types[i$2];
2597 if (type$1 == "1" && cur == "r") { types[i$2] = "n"; }
2598 else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == "r") { types[i$2] = "R"; } }
2599 }
2600
2601 // W4. A single European separator between two European numbers
2602 // changes to a European number. A single common separator between
2603 // two numbers of the same type changes to that type.
2604 for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) {
2605 var type$2 = types[i$3];
2606 if (type$2 == "+" && prev$1 == "1" && types[i$3+1] == "1") { types[i$3] = "1"; }
2607 else if (type$2 == "," && prev$1 == types[i$3+1] &&
2608 (prev$1 == "1" || prev$1 == "n")) { types[i$3] = prev$1; }
2609 prev$1 = type$2;
2610 }
2611
2612 // W5. A sequence of European terminators adjacent to European
2613 // numbers changes to all European numbers.
2614 // W6. Otherwise, separators and terminators change to Other
2615 // Neutral.
2616 for (var i$4 = 0; i$4 < len; ++i$4) {
2617 var type$3 = types[i$4];
2618 if (type$3 == ",") { types[i$4] = "N"; }
2619 else if (type$3 == "%") {
2620 var end = (void 0);
2621 for (end = i$4 + 1; end < len && types[end] == "%"; ++end) {}
2622 var replace = (i$4 && types[i$4-1] == "!") || (end < len && types[end] == "1") ? "1" : "N";
2623 for (var j = i$4; j < end; ++j) { types[j] = replace; }
2624 i$4 = end - 1;
2625 }
2626 }
2627
2628 // W7. Search backwards from each instance of a European number
2629 // until the first strong type (R, L, or sor) is found. If an L is
2630 // found, then change the type of the European number to L.
2631 for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) {
2632 var type$4 = types[i$5];
2633 if (cur$1 == "L" && type$4 == "1") { types[i$5] = "L"; }
2634 else if (isStrong.test(type$4)) { cur$1 = type$4; }
2635 }
2636
2637 // N1. A sequence of neutrals takes the direction of the
2638 // surrounding strong text if the text on both sides has the same
2639 // direction. European and Arabic numbers act as if they were R in
2640 // terms of their influence on neutrals. Start-of-level-run (sor)
2641 // and end-of-level-run (eor) are used at level run boundaries.
2642 // N2. Any remaining neutrals take the embedding direction.
2643 for (var i$6 = 0; i$6 < len; ++i$6) {
2644 if (isNeutral.test(types[i$6])) {
2645 var end$1 = (void 0);
2646 for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {}
2647 var before = (i$6 ? types[i$6-1] : outerType) == "L";
2648 var after = (end$1 < len ? types[end$1] : outerType) == "L";
2649 var replace$1 = before == after ? (before ? "L" : "R") : outerType;
2650 for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1; }
2651 i$6 = end$1 - 1;
2652 }
2653 }
2654
2655 // Here we depart from the documented algorithm, in order to avoid
2656 // building up an actual levels array. Since there are only three
2657 // levels (0, 1, 2) in an implementation that doesn't take
2658 // explicit embedding into account, we can build up the order on
2659 // the fly, without following the level-based algorithm.
2660 var order = [], m;
2661 for (var i$7 = 0; i$7 < len;) {
2662 if (countsAsLeft.test(types[i$7])) {
2663 var start = i$7;
2664 for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {}
2665 order.push(new BidiSpan(0, start, i$7));
2666 } else {
2667 var pos = i$7, at = order.length;
2668 for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {}
2669 for (var j$2 = pos; j$2 < i$7;) {
2670 if (countsAsNum.test(types[j$2])) {
2671 if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)); }
2672 var nstart = j$2;
2673 for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {}
2674 order.splice(at, 0, new BidiSpan(2, nstart, j$2));
2675 pos = j$2;
2676 } else { ++j$2; }
2677 }
2678 if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)); }
2679 }
2680 }
2681 if (direction == "ltr") {
2682 if (order[0].level == 1 && (m = str.match(/^\s+/))) {
2683 order[0].from = m[0].length;
2684 order.unshift(new BidiSpan(0, 0, m[0].length));
2685 }
2686 if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
2687 lst(order).to -= m[0].length;
2688 order.push(new BidiSpan(0, len - m[0].length, len));
2689 }
2690 }
2691
2692 return direction == "rtl" ? order.reverse() : order
2693 }
2694 })();
2695
2696 // Get the bidi ordering for the given line (and cache it). Returns
2697 // false for lines that are fully left-to-right, and an array of
2698 // BidiSpan objects otherwise.
2699 function getOrder(line, direction) {
2700 var order = line.order;
2701 if (order == null) { order = line.order = bidiOrdering(line.text, direction); }
2702 return order
2703 }
2704
2705 // EVENT HANDLING
2706
2707 // Lightweight event framework. on/off also work on DOM nodes,
2708 // registering native DOM handlers.
2709
2710 var noHandlers = [];
2711
2712 var on = function(emitter, type, f) {
2713 if (emitter.addEventListener) {
2714 emitter.addEventListener(type, f, false);
2715 } else if (emitter.attachEvent) {
2716 emitter.attachEvent("on" + type, f);
2717 } else {
2718 var map$$1 = emitter._handlers || (emitter._handlers = {});
2719 map$$1[type] = (map$$1[type] || noHandlers).concat(f);
2720 }
2721 };
2722
2723 function getHandlers(emitter, type) {
2724 return emitter._handlers && emitter._handlers[type] || noHandlers
2725 }
2726
2727 function off(emitter, type, f) {
2728 if (emitter.removeEventListener) {
2729 emitter.removeEventListener(type, f, false);
2730 } else if (emitter.detachEvent) {
2731 emitter.detachEvent("on" + type, f);
2732 } else {
2733 var map$$1 = emitter._handlers, arr = map$$1 && map$$1[type];
2734 if (arr) {
2735 var index = indexOf(arr, f);
2736 if (index > -1)
2737 { map$$1[type] = arr.slice(0, index).concat(arr.slice(index + 1)); }
2738 }
2739 }
2740 }
2741
2742 function signal(emitter, type /*, values...*/) {
2743 var handlers = getHandlers(emitter, type);
2744 if (!handlers.length) { return }
2745 var args = Array.prototype.slice.call(arguments, 2);
2746 for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args); }
2747 }
2748
2749 // The DOM events that CodeMirror handles can be overridden by
2750 // registering a (non-DOM) handler on the editor for the event name,
2751 // and preventDefault-ing the event in that handler.
2752 function signalDOMEvent(cm, e, override) {
2753 if (typeof e == "string")
2754 { e = {type: e, preventDefault: function() { this.defaultPrevented = true; }}; }
2755 signal(cm, override || e.type, cm, e);
2756 return e_defaultPrevented(e) || e.codemirrorIgnore
2757 }
2758
2759 function signalCursorActivity(cm) {
2760 var arr = cm._handlers && cm._handlers.cursorActivity;
2761 if (!arr) { return }
2762 var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []);
2763 for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1)
2764 { set.push(arr[i]); } }
2765 }
2766
2767 function hasHandler(emitter, type) {
2768 return getHandlers(emitter, type).length > 0
2769 }
2770
2771 // Add on and off methods to a constructor's prototype, to make
2772 // registering events on such objects more convenient.
2773 function eventMixin(ctor) {
2774 ctor.prototype.on = function(type, f) {on(this, type, f);};
2775 ctor.prototype.off = function(type, f) {off(this, type, f);};
2776 }
2777
2778 // Due to the fact that we still support jurassic IE versions, some
2779 // compatibility wrappers are needed.
2780
2781 function e_preventDefault(e) {
2782 if (e.preventDefault) { e.preventDefault(); }
2783 else { e.returnValue = false; }
2784 }
2785 function e_stopPropagation(e) {
2786 if (e.stopPropagation) { e.stopPropagation(); }
2787 else { e.cancelBubble = true; }
2788 }
2789 function e_defaultPrevented(e) {
2790 return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false
2791 }
2792 function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
2793
2794 function e_target(e) {return e.target || e.srcElement}
2795 function e_button(e) {
2796 var b = e.which;
2797 if (b == null) {
2798 if (e.button & 1) { b = 1; }
2799 else if (e.button & 2) { b = 3; }
2800 else if (e.button & 4) { b = 2; }
2801 }
2802 if (mac && e.ctrlKey && b == 1) { b = 3; }
2803 return b
2804 }
2805
2806 // Detect drag-and-drop
2807 var dragAndDrop = function() {
2808 // There is *some* kind of drag-and-drop support in IE6-8, but I
2809 // couldn't get it to work yet.
2810 if (ie && ie_version < 9) { return false }
2811 var div = elt('div');
2812 return "draggable" in div || "dragDrop" in div
2813 }();
2814
2815 var zwspSupported;
2816 function zeroWidthElement(measure) {
2817 if (zwspSupported == null) {
2818 var test = elt("span", "\u200b");
2819 removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]));
2820 if (measure.firstChild.offsetHeight != 0)
2821 { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8); }
2822 }
2823 var node = zwspSupported ? elt("span", "\u200b") :
2824 elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
2825 node.setAttribute("cm-text", "");
2826 return node
2827 }
2828
2829 // Feature-detect IE's crummy client rect reporting for bidi text
2830 var badBidiRects;
2831 function hasBadBidiRects(measure) {
2832 if (badBidiRects != null) { return badBidiRects }
2833 var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA"));
2834 var r0 = range(txt, 0, 1).getBoundingClientRect();
2835 var r1 = range(txt, 1, 2).getBoundingClientRect();
2836 removeChildren(measure);
2837 if (!r0 || r0.left == r0.right) { return false } // Safari returns null in some cases (#2780)
2838 return badBidiRects = (r1.right - r0.right < 3)
2839 }
2840
2841 // See if "".split is the broken IE version, if so, provide an
2842 // alternative way to split lines.
2843 var splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function (string) {
2844 var pos = 0, result = [], l = string.length;
2845 while (pos <= l) {
2846 var nl = string.indexOf("\n", pos);
2847 if (nl == -1) { nl = string.length; }
2848 var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
2849 var rt = line.indexOf("\r");
2850 if (rt != -1) {
2851 result.push(line.slice(0, rt));
2852 pos += rt + 1;
2853 } else {
2854 result.push(line);
2855 pos = nl + 1;
2856 }
2857 }
2858 return result
2859 } : function (string) { return string.split(/\r\n?|\n/); };
2860
2861 var hasSelection = window.getSelection ? function (te) {
2862 try { return te.selectionStart != te.selectionEnd }
2863 catch(e) { return false }
2864 } : function (te) {
2865 var range$$1;
2866 try {range$$1 = te.ownerDocument.selection.createRange();}
2867 catch(e) {}
2868 if (!range$$1 || range$$1.parentElement() != te) { return false }
2869 return range$$1.compareEndPoints("StartToEnd", range$$1) != 0
2870 };
2871
2872 var hasCopyEvent = (function () {
2873 var e = elt("div");
2874 if ("oncopy" in e) { return true }
2875 e.setAttribute("oncopy", "return;");
2876 return typeof e.oncopy == "function"
2877 })();
2878
2879 var badZoomedRects = null;
2880 function hasBadZoomedRects(measure) {
2881 if (badZoomedRects != null) { return badZoomedRects }
2882 var node = removeChildrenAndAdd(measure, elt("span", "x"));
2883 var normal = node.getBoundingClientRect();
2884 var fromRange = range(node, 0, 1).getBoundingClientRect();
2885 return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1
2886 }
2887
2888 // Known modes, by name and by MIME
2889 var modes = {}, mimeModes = {};
2890
2891 // Extra arguments are stored as the mode's dependencies, which is
2892 // used by (legacy) mechanisms like loadmode.js to automatically
2893 // load a mode. (Preferred mechanism is the require/define calls.)
2894 function defineMode(name, mode) {
2895 if (arguments.length > 2)
2896 { mode.dependencies = Array.prototype.slice.call(arguments, 2); }
2897 modes[name] = mode;
2898 }
2899
2900 function defineMIME(mime, spec) {
2901 mimeModes[mime] = spec;
2902 }
2903
2904 // Given a MIME type, a {name, ...options} config object, or a name
2905 // string, return a mode config object.
2906 function resolveMode(spec) {
2907 if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
2908 spec = mimeModes[spec];
2909 } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
2910 var found = mimeModes[spec.name];
2911 if (typeof found == "string") { found = {name: found}; }
2912 spec = createObj(found, spec);
2913 spec.name = found.name;
2914 } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
2915 return resolveMode("application/xml")
2916 } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) {
2917 return resolveMode("application/json")
2918 }
2919 if (typeof spec == "string") { return {name: spec} }
2920 else { return spec || {name: "null"} }
2921 }
2922
2923 // Given a mode spec (anything that resolveMode accepts), find and
2924 // initialize an actual mode object.
2925 function getMode(options, spec) {
2926 spec = resolveMode(spec);
2927 var mfactory = modes[spec.name];
2928 if (!mfactory) { return getMode(options, "text/plain") }
2929 var modeObj = mfactory(options, spec);
2930 if (modeExtensions.hasOwnProperty(spec.name)) {
2931 var exts = modeExtensions[spec.name];
2932 for (var prop in exts) {
2933 if (!exts.hasOwnProperty(prop)) { continue }
2934 if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop]; }
2935 modeObj[prop] = exts[prop];
2936 }
2937 }
2938 modeObj.name = spec.name;
2939 if (spec.helperType) { modeObj.helperType = spec.helperType; }
2940 if (spec.modeProps) { for (var prop$1 in spec.modeProps)
2941 { modeObj[prop$1] = spec.modeProps[prop$1]; } }
2942
2943 return modeObj
2944 }
2945
2946 // This can be used to attach properties to mode objects from
2947 // outside the actual mode definition.
2948 var modeExtensions = {};
2949 function extendMode(mode, properties) {
2950 var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
2951 copyObj(properties, exts);
2952 }
2953
2954 function copyState(mode, state) {
2955 if (state === true) { return state }
2956 if (mode.copyState) { return mode.copyState(state) }
2957 var nstate = {};
2958 for (var n in state) {
2959 var val = state[n];
2960 if (val instanceof Array) { val = val.concat([]); }
2961 nstate[n] = val;
2962 }
2963 return nstate
2964 }
2965
2966 // Given a mode and a state (for that mode), find the inner mode and
2967 // state at the position that the state refers to.
2968 function innerMode(mode, state) {
2969 var info;
2970 while (mode.innerMode) {
2971 info = mode.innerMode(state);
2972 if (!info || info.mode == mode) { break }
2973 state = info.state;
2974 mode = info.mode;
2975 }
2976 return info || {mode: mode, state: state}
2977 }
2978
2979 function startState(mode, a1, a2) {
2980 return mode.startState ? mode.startState(a1, a2) : true
2981 }
2982
2983 // STRING STREAM
2984
2985 // Fed to the mode parsers, provides helper functions to make
2986 // parsers more succinct.
2987
2988 var StringStream = function(string, tabSize, lineOracle) {
2989 this.pos = this.start = 0;
2990 this.string = string;
2991 this.tabSize = tabSize || 8;
2992 this.lastColumnPos = this.lastColumnValue = 0;
2993 this.lineStart = 0;
2994 this.lineOracle = lineOracle;
2995 };
2996
2997 StringStream.prototype.eol = function () {return this.pos >= this.string.length};
2998 StringStream.prototype.sol = function () {return this.pos == this.lineStart};
2999 StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined};
3000 StringStream.prototype.next = function () {
3001 if (this.pos < this.string.length)
3002 { return this.string.charAt(this.pos++) }
3003 };
3004 StringStream.prototype.eat = function (match) {
3005 var ch = this.string.charAt(this.pos);
3006 var ok;
3007 if (typeof match == "string") { ok = ch == match; }
3008 else { ok = ch && (match.test ? match.test(ch) : match(ch)); }
3009 if (ok) {++this.pos; return ch}
3010 };
3011 StringStream.prototype.eatWhile = function (match) {
3012 var start = this.pos;
3013 while (this.eat(match)){}
3014 return this.pos > start
3015 };
3016 StringStream.prototype.eatSpace = function () {
3017 var this$1 = this;
3018
3019 var start = this.pos;
3020 while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this$1.pos; }
3021 return this.pos > start
3022 };
3023 StringStream.prototype.skipToEnd = function () {this.pos = this.string.length;};
3024 StringStream.prototype.skipTo = function (ch) {
3025 var found = this.string.indexOf(ch, this.pos);
3026 if (found > -1) {this.pos = found; return true}
3027 };
3028 StringStream.prototype.backUp = function (n) {this.pos -= n;};
3029 StringStream.prototype.column = function () {
3030 if (this.lastColumnPos < this.start) {
3031 this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
3032 this.lastColumnPos = this.start;
3033 }
3034 return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
3035 };
3036 StringStream.prototype.indentation = function () {
3037 return countColumn(this.string, null, this.tabSize) -
3038 (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
3039 };
3040 StringStream.prototype.match = function (pattern, consume, caseInsensitive) {
3041 if (typeof pattern == "string") {
3042 var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; };
3043 var substr = this.string.substr(this.pos, pattern.length);
3044 if (cased(substr) == cased(pattern)) {
3045 if (consume !== false) { this.pos += pattern.length; }
3046 return true
3047 }
3048 } else {
3049 var match = this.string.slice(this.pos).match(pattern);
3050 if (match && match.index > 0) { return null }
3051 if (match && consume !== false) { this.pos += match[0].length; }
3052 return match
3053 }
3054 };
3055 StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)};
3056 StringStream.prototype.hideFirstChars = function (n, inner) {
3057 this.lineStart += n;
3058 try { return inner() }
3059 finally { this.lineStart -= n; }
3060 };
3061 StringStream.prototype.lookAhead = function (n) {
3062 var oracle = this.lineOracle;
3063 return oracle && oracle.lookAhead(n)
3064 };
3065 StringStream.prototype.baseToken = function () {
3066 var oracle = this.lineOracle;
3067 return oracle && oracle.baseToken(this.pos)
3068 };
3069
3070 var SavedContext = function(state, lookAhead) {
3071 this.state = state;
3072 this.lookAhead = lookAhead;
3073 };
3074
3075 var Context = function(doc, state, line, lookAhead) {
3076 this.state = state;
3077 this.doc = doc;
3078 this.line = line;
3079 this.maxLookAhead = lookAhead || 0;
3080 this.baseTokens = null;
3081 this.baseTokenPos = 1;
3082 };
3083
3084 Context.prototype.lookAhead = function (n) {
3085 var line = this.doc.getLine(this.line + n);
3086 if (line != null && n > this.maxLookAhead) { this.maxLookAhead = n; }
3087 return line
3088 };
3089
3090 Context.prototype.baseToken = function (n) {
3091 var this$1 = this;
3092
3093 if (!this.baseTokens) { return null }
3094 while (this.baseTokens[this.baseTokenPos] <= n)
3095 { this$1.baseTokenPos += 2; }
3096 var type = this.baseTokens[this.baseTokenPos + 1];
3097 return {type: type && type.replace(/( |^)overlay .*/, ""),
3098 size: this.baseTokens[this.baseTokenPos] - n}
3099 };
3100
3101 Context.prototype.nextLine = function () {
3102 this.line++;
3103 if (this.maxLookAhead > 0) { this.maxLookAhead--; }
3104 };
3105
3106 Context.fromSaved = function (doc, saved, line) {
3107 if (saved instanceof SavedContext)
3108 { return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead) }
3109 else
3110 { return new Context(doc, copyState(doc.mode, saved), line) }
3111 };
3112
3113 Context.prototype.save = function (copy) {
3114 var state = copy !== false ? copyState(this.doc.mode, this.state) : this.state;
3115 return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state
3116 };
3117
3118
3119 // Compute a style array (an array starting with a mode generation
3120 // -- for invalidation -- followed by pairs of end positions and
3121 // style strings), which is used to highlight the tokens on the
3122 // line.
3123 function highlightLine(cm, line, context, forceToEnd) {
3124 // A styles array always starts with a number identifying the
3125 // mode/overlays that it is based on (for easy invalidation).
3126 var st = [cm.state.modeGen], lineClasses = {};
3127 // Compute the base array of styles
3128 runMode(cm, line.text, cm.doc.mode, context, function (end, style) { return st.push(end, style); },
3129 lineClasses, forceToEnd);
3130 var state = context.state;
3131
3132 // Run overlays, adjust style array.
3133 var loop = function ( o ) {
3134 context.baseTokens = st;
3135 var overlay = cm.state.overlays[o], i = 1, at = 0;
3136 context.state = true;
3137 runMode(cm, line.text, overlay.mode, context, function (end, style) {
3138 var start = i;
3139 // Ensure there's a token end at the current position, and that i points at it
3140 while (at < end) {
3141 var i_end = st[i];
3142 if (i_end > end)
3143 { st.splice(i, 1, end, st[i+1], i_end); }
3144 i += 2;
3145 at = Math.min(end, i_end);
3146 }
3147 if (!style) { return }
3148 if (overlay.opaque) {
3149 st.splice(start, i - start, end, "overlay " + style);
3150 i = start + 2;
3151 } else {
3152 for (; start < i; start += 2) {
3153 var cur = st[start+1];
3154 st[start+1] = (cur ? cur + " " : "") + "overlay " + style;
3155 }
3156 }
3157 }, lineClasses);
3158 context.state = state;
3159 context.baseTokens = null;
3160 context.baseTokenPos = 1;
3161 };
3162
3163 for (var o = 0; o < cm.state.overlays.length; ++o) loop( o );
3164
3165 return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null}
3166 }
3167
3168 function getLineStyles(cm, line, updateFrontier) {
3169 if (!line.styles || line.styles[0] != cm.state.modeGen) {
3170 var context = getContextBefore(cm, lineNo(line));
3171 var resetState = line.text.length > cm.options.maxHighlightLength && copyState(cm.doc.mode, context.state);
3172 var result = highlightLine(cm, line, context);
3173 if (resetState) { context.state = resetState; }
3174 line.stateAfter = context.save(!resetState);
3175 line.styles = result.styles;
3176 if (result.classes) { line.styleClasses = result.classes; }
3177 else if (line.styleClasses) { line.styleClasses = null; }
3178 if (updateFrontier === cm.doc.highlightFrontier)
3179 { cm.doc.modeFrontier = Math.max(cm.doc.modeFrontier, ++cm.doc.highlightFrontier); }
3180 }
3181 return line.styles
3182 }
3183
3184 function getContextBefore(cm, n, precise) {
3185 var doc = cm.doc, display = cm.display;
3186 if (!doc.mode.startState) { return new Context(doc, true, n) }
3187 var start = findStartLine(cm, n, precise);
3188 var saved = start > doc.first && getLine(doc, start - 1).stateAfter;
3189 var context = saved ? Context.fromSaved(doc, saved, start) : new Context(doc, startState(doc.mode), start);
3190
3191 doc.iter(start, n, function (line) {
3192 processLine(cm, line.text, context);
3193 var pos = context.line;
3194 line.stateAfter = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo ? context.save() : null;
3195 context.nextLine();
3196 });
3197 if (precise) { doc.modeFrontier = context.line; }
3198 return context
3199 }
3200
3201 // Lightweight form of highlight -- proceed over this line and
3202 // update state, but don't save a style array. Used for lines that
3203 // aren't currently visible.
3204 function processLine(cm, text, context, startAt) {
3205 var mode = cm.doc.mode;
3206 var stream = new StringStream(text, cm.options.tabSize, context);
3207 stream.start = stream.pos = startAt || 0;
3208 if (text == "") { callBlankLine(mode, context.state); }
3209 while (!stream.eol()) {
3210 readToken(mode, stream, context.state);
3211 stream.start = stream.pos;
3212 }
3213 }
3214
3215 function callBlankLine(mode, state) {
3216 if (mode.blankLine) { return mode.blankLine(state) }
3217 if (!mode.innerMode) { return }
3218 var inner = innerMode(mode, state);
3219 if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) }
3220 }
3221
3222 function readToken(mode, stream, state, inner) {
3223 for (var i = 0; i < 10; i++) {
3224 if (inner) { inner[0] = innerMode(mode, state).mode; }
3225 var style = mode.token(stream, state);
3226 if (stream.pos > stream.start) { return style }
3227 }
3228 throw new Error("Mode " + mode.name + " failed to advance stream.")
3229 }
3230
3231 var Token = function(stream, type, state) {
3232 this.start = stream.start; this.end = stream.pos;
3233 this.string = stream.current();
3234 this.type = type || null;
3235 this.state = state;
3236 };
3237
3238 // Utility for getTokenAt and getLineTokens
3239 function takeToken(cm, pos, precise, asArray) {
3240 var doc = cm.doc, mode = doc.mode, style;
3241 pos = clipPos(doc, pos);
3242 var line = getLine(doc, pos.line), context = getContextBefore(cm, pos.line, precise);
3243 var stream = new StringStream(line.text, cm.options.tabSize, context), tokens;
3244 if (asArray) { tokens = []; }
3245 while ((asArray || stream.pos < pos.ch) && !stream.eol()) {
3246 stream.start = stream.pos;
3247 style = readToken(mode, stream, context.state);
3248 if (asArray) { tokens.push(new Token(stream, style, copyState(doc.mode, context.state))); }
3249 }
3250 return asArray ? tokens : new Token(stream, style, context.state)
3251 }
3252
3253 function extractLineClasses(type, output) {
3254 if (type) { for (;;) {
3255 var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/);
3256 if (!lineClass) { break }
3257 type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length);
3258 var prop = lineClass[1] ? "bgClass" : "textClass";
3259 if (output[prop] == null)
3260 { output[prop] = lineClass[2]; }
3261 else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop]))
3262 { output[prop] += " " + lineClass[2]; }
3263 } }
3264 return type
3265 }
3266
3267 // Run the given mode's parser over a line, calling f for each token.
3268 function runMode(cm, text, mode, context, f, lineClasses, forceToEnd) {
3269 var flattenSpans = mode.flattenSpans;
3270 if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans; }
3271 var curStart = 0, curStyle = null;
3272 var stream = new StringStream(text, cm.options.tabSize, context), style;
3273 var inner = cm.options.addModeClass && [null];
3274 if (text == "") { extractLineClasses(callBlankLine(mode, context.state), lineClasses); }
3275 while (!stream.eol()) {
3276 if (stream.pos > cm.options.maxHighlightLength) {
3277 flattenSpans = false;
3278 if (forceToEnd) { processLine(cm, text, context, stream.pos); }
3279 stream.pos = text.length;
3280 style = null;
3281 } else {
3282 style = extractLineClasses(readToken(mode, stream, context.state, inner), lineClasses);
3283 }
3284 if (inner) {
3285 var mName = inner[0].name;
3286 if (mName) { style = "m-" + (style ? mName + " " + style : mName); }
3287 }
3288 if (!flattenSpans || curStyle != style) {
3289 while (curStart < stream.start) {
3290 curStart = Math.min(stream.start, curStart + 5000);
3291 f(curStart, curStyle);
3292 }
3293 curStyle = style;
3294 }
3295 stream.start = stream.pos;
3296 }
3297 while (curStart < stream.pos) {
3298 // Webkit seems to refuse to render text nodes longer than 57444
3299 // characters, and returns inaccurate measurements in nodes
3300 // starting around 5000 chars.
3301 var pos = Math.min(stream.pos, curStart + 5000);
3302 f(pos, curStyle);
3303 curStart = pos;
3304 }
3305 }
3306
3307 // Finds the line to start with when starting a parse. Tries to
3308 // find a line with a stateAfter, so that it can start with a
3309 // valid state. If that fails, it returns the line with the
3310 // smallest indentation, which tends to need the least context to
3311 // parse correctly.
3312 function findStartLine(cm, n, precise) {
3313 var minindent, minline, doc = cm.doc;
3314 var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100);
3315 for (var search = n; search > lim; --search) {
3316 if (search <= doc.first) { return doc.first }
3317 var line = getLine(doc, search - 1), after = line.stateAfter;
3318 if (after && (!precise || search + (after instanceof SavedContext ? after.lookAhead : 0) <= doc.modeFrontier))
3319 { return search }
3320 var indented = countColumn(line.text, null, cm.options.tabSize);
3321 if (minline == null || minindent > indented) {
3322 minline = search - 1;
3323 minindent = indented;
3324 }
3325 }
3326 return minline
3327 }
3328
3329 function retreatFrontier(doc, n) {
3330 doc.modeFrontier = Math.min(doc.modeFrontier, n);
3331 if (doc.highlightFrontier < n - 10) { return }
3332 var start = doc.first;
3333 for (var line = n - 1; line > start; line--) {
3334 var saved = getLine(doc, line).stateAfter;
3335 // change is on 3
3336 // state on line 1 looked ahead 2 -- so saw 3
3337 // test 1 + 2 < 3 should cover this
3338 if (saved && (!(saved instanceof SavedContext) || line + saved.lookAhead < n)) {
3339 start = line + 1;
3340 break
3341 }
3342 }
3343 doc.highlightFrontier = Math.min(doc.highlightFrontier, start);
3344 }
3345
3346 // LINE DATA STRUCTURE
3347
3348 // Line objects. These hold state related to a line, including
3349 // highlighting info (the styles array).
3350 var Line = function(text, markedSpans, estimateHeight) {
3351 this.text = text;
3352 attachMarkedSpans(this, markedSpans);
3353 this.height = estimateHeight ? estimateHeight(this) : 1;
3354 };
3355
3356 Line.prototype.lineNo = function () { return lineNo(this) };
3357 eventMixin(Line);
3358
3359 // Change the content (text, markers) of a line. Automatically
3360 // invalidates cached information and tries to re-estimate the
3361 // line's height.
3362 function updateLine(line, text, markedSpans, estimateHeight) {
3363 line.text = text;
3364 if (line.stateAfter) { line.stateAfter = null; }
3365 if (line.styles) { line.styles = null; }
3366 if (line.order != null) { line.order = null; }
3367 detachMarkedSpans(line);
3368 attachMarkedSpans(line, markedSpans);
3369 var estHeight = estimateHeight ? estimateHeight(line) : 1;
3370 if (estHeight != line.height) { updateLineHeight(line, estHeight); }
3371 }
3372
3373 // Detach a line from the document tree and its markers.
3374 function cleanUpLine(line) {
3375 line.parent = null;
3376 detachMarkedSpans(line);
3377 }
3378
3379 // Convert a style as returned by a mode (either null, or a string
3380 // containing one or more styles) to a CSS style. This is cached,
3381 // and also looks for line-wide styles.
3382 var styleToClassCache = {}, styleToClassCacheWithMode = {};
3383 function interpretTokenStyle(style, options) {
3384 if (!style || /^\s*$/.test(style)) { return null }
3385 var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache;
3386 return cache[style] ||
3387 (cache[style] = style.replace(/\S+/g, "cm-$&"))
3388 }
3389
3390 // Render the DOM representation of the text of a line. Also builds
3391 // up a 'line map', which points at the DOM nodes that represent
3392 // specific stretches of text, and is used by the measuring code.
3393 // The returned object contains the DOM node, this map, and
3394 // information about line-wide styles that were set by the mode.
3395 function buildLineContent(cm, lineView) {
3396 // The padding-right forces the element to have a 'border', which
3397 // is needed on Webkit to be able to get line-level bounding
3398 // rectangles for it (in measureChar).
3399 var content = eltP("span", null, null, webkit ? "padding-right: .1px" : null);
3400 var builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: content,
3401 col: 0, pos: 0, cm: cm,
3402 trailingSpace: false,
3403 splitSpaces: cm.getOption("lineWrapping")};
3404 lineView.measure = {};
3405
3406 // Iterate over the logical lines that make up this visual line.
3407 for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {
3408 var line = i ? lineView.rest[i - 1] : lineView.line, order = (void 0);
3409 builder.pos = 0;
3410 builder.addToken = buildToken;
3411 // Optionally wire in some hacks into the token-rendering
3412 // algorithm, to deal with browser quirks.
3413 if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction)))
3414 { builder.addToken = buildTokenBadBidi(builder.addToken, order); }
3415 builder.map = [];
3416 var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line);
3417 insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate));
3418 if (line.styleClasses) {
3419 if (line.styleClasses.bgClass)
3420 { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || ""); }
3421 if (line.styleClasses.textClass)
3422 { builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || ""); }
3423 }
3424
3425 // Ensure at least a single node is present, for measuring.
3426 if (builder.map.length == 0)
3427 { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))); }
3428
3429 // Store the map and a cache object for the current logical line
3430 if (i == 0) {
3431 lineView.measure.map = builder.map;
3432 lineView.measure.cache = {};
3433 } else {
3434 (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map)
3435 ;(lineView.measure.caches || (lineView.measure.caches = [])).push({});
3436 }
3437 }
3438
3439 // See issue #2901
3440 if (webkit) {
3441 var last = builder.content.lastChild;
3442 if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab")))
3443 { builder.content.className = "cm-tab-wrap-hack"; }
3444 }
3445
3446 signal(cm, "renderLine", cm, lineView.line, builder.pre);
3447 if (builder.pre.className)
3448 { builder.textClass = joinClasses(builder.pre.className, builder.textClass || ""); }
3449
3450 return builder
3451 }
3452
3453 function defaultSpecialCharPlaceholder(ch) {
3454 var token = elt("span", "\u2022", "cm-invalidchar");
3455 token.title = "\\u" + ch.charCodeAt(0).toString(16);
3456 token.setAttribute("aria-label", token.title);
3457 return token
3458 }
3459
3460 // Build up the DOM representation for a single token, and add it to
3461 // the line map. Takes care to render special characters separately.
3462 function buildToken(builder, text, style, startStyle, endStyle, css, attributes) {
3463 if (!text) { return }
3464 var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text;
3465 var special = builder.cm.state.specialChars, mustWrap = false;
3466 var content;
3467 if (!special.test(text)) {
3468 builder.col += text.length;
3469 content = document.createTextNode(displayText);
3470 builder.map.push(builder.pos, builder.pos + text.length, content);
3471 if (ie && ie_version < 9) { mustWrap = true; }
3472 builder.pos += text.length;
3473 } else {
3474 content = document.createDocumentFragment();
3475 var pos = 0;
3476 while (true) {
3477 special.lastIndex = pos;
3478 var m = special.exec(text);
3479 var skipped = m ? m.index - pos : text.length - pos;
3480 if (skipped) {
3481 var txt = document.createTextNode(displayText.slice(pos, pos + skipped));
3482 if (ie && ie_version < 9) { content.appendChild(elt("span", [txt])); }
3483 else { content.appendChild(txt); }
3484 builder.map.push(builder.pos, builder.pos + skipped, txt);
3485 builder.col += skipped;
3486 builder.pos += skipped;
3487 }
3488 if (!m) { break }
3489 pos += skipped + 1;
3490 var txt$1 = (void 0);
3491 if (m[0] == "\t") {
3492 var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
3493 txt$1 = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
3494 txt$1.setAttribute("role", "presentation");
3495 txt$1.setAttribute("cm-text", "\t");
3496 builder.col += tabWidth;
3497 } else if (m[0] == "\r" || m[0] == "\n") {
3498 txt$1 = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar"));
3499 txt$1.setAttribute("cm-text", m[0]);
3500 builder.col += 1;
3501 } else {
3502 txt$1 = builder.cm.options.specialCharPlaceholder(m[0]);
3503 txt$1.setAttribute("cm-text", m[0]);
3504 if (ie && ie_version < 9) { content.appendChild(elt("span", [txt$1])); }
3505 else { content.appendChild(txt$1); }
3506 builder.col += 1;
3507 }
3508 builder.map.push(builder.pos, builder.pos + 1, txt$1);
3509 builder.pos++;
3510 }
3511 }
3512 builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32;
3513 if (style || startStyle || endStyle || mustWrap || css) {
3514 var fullStyle = style || "";
3515 if (startStyle) { fullStyle += startStyle; }
3516 if (endStyle) { fullStyle += endStyle; }
3517 var token = elt("span", [content], fullStyle, css);
3518 if (attributes) {
3519 for (var attr in attributes) { if (attributes.hasOwnProperty(attr) && attr != "style" && attr != "class")
3520 { token.setAttribute(attr, attributes[attr]); } }
3521 }
3522 return builder.content.appendChild(token)
3523 }
3524 builder.content.appendChild(content);
3525 }
3526
3527 // Change some spaces to NBSP to prevent the browser from collapsing
3528 // trailing spaces at the end of a line when rendering text (issue #1362).
3529 function splitSpaces(text, trailingBefore) {
3530 if (text.length > 1 && !/ /.test(text)) { return text }
3531 var spaceBefore = trailingBefore, result = "";
3532 for (var i = 0; i < text.length; i++) {
3533 var ch = text.charAt(i);
3534 if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32))
3535 { ch = "\u00a0"; }
3536 result += ch;
3537 spaceBefore = ch == " ";
3538 }
3539 return result
3540 }
3541
3542 // Work around nonsense dimensions being reported for stretches of
3543 // right-to-left text.
3544 function buildTokenBadBidi(inner, order) {
3545 return function (builder, text, style, startStyle, endStyle, css, attributes) {
3546 style = style ? style + " cm-force-border" : "cm-force-border";
3547 var start = builder.pos, end = start + text.length;
3548 for (;;) {
3549 // Find the part that overlaps with the start of this text
3550 var part = (void 0);
3551 for (var i = 0; i < order.length; i++) {
3552 part = order[i];
3553 if (part.to > start && part.from <= start) { break }
3554 }
3555 if (part.to >= end) { return inner(builder, text, style, startStyle, endStyle, css, attributes) }
3556 inner(builder, text.slice(0, part.to - start), style, startStyle, null, css, attributes);
3557 startStyle = null;
3558 text = text.slice(part.to - start);
3559 start = part.to;
3560 }
3561 }
3562 }
3563
3564 function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
3565 var widget = !ignoreWidget && marker.widgetNode;
3566 if (widget) { builder.map.push(builder.pos, builder.pos + size, widget); }
3567 if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {
3568 if (!widget)
3569 { widget = builder.content.appendChild(document.createElement("span")); }
3570 widget.setAttribute("cm-marker", marker.id);
3571 }
3572 if (widget) {
3573 builder.cm.display.input.setUneditable(widget);
3574 builder.content.appendChild(widget);
3575 }
3576 builder.pos += size;
3577 builder.trailingSpace = false;
3578 }
3579
3580 // Outputs a number of spans to make up a line, taking highlighting
3581 // and marked text into account.
3582 function insertLineContent(line, builder, styles) {
3583 var spans = line.markedSpans, allText = line.text, at = 0;
3584 if (!spans) {
3585 for (var i$1 = 1; i$1 < styles.length; i$1+=2)
3586 { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpretTokenStyle(styles[i$1+1], builder.cm.options)); }
3587 return
3588 }
3589
3590 var len = allText.length, pos = 0, i = 1, text = "", style, css;
3591 var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed, attributes;
3592 for (;;) {
3593 if (nextChange == pos) { // Update current marker set
3594 spanStyle = spanEndStyle = spanStartStyle = css = "";
3595 attributes = null;
3596 collapsed = null; nextChange = Infinity;
3597 var foundBookmarks = [], endStyles = (void 0);
3598 for (var j = 0; j < spans.length; ++j) {
3599 var sp = spans[j], m = sp.marker;
3600 if (m.type == "bookmark" && sp.from == pos && m.widgetNode) {
3601 foundBookmarks.push(m);
3602 } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) {
3603 if (sp.to != null && sp.to != pos && nextChange > sp.to) {
3604 nextChange = sp.to;
3605 spanEndStyle = "";
3606 }
3607 if (m.className) { spanStyle += " " + m.className; }
3608 if (m.css) { css = (css ? css + ";" : "") + m.css; }
3609 if (m.startStyle && sp.from == pos) { spanStartStyle += " " + m.startStyle; }
3610 if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [])).push(m.endStyle, sp.to); }
3611 // support for the old title property
3612 // https://github.com/codemirror/CodeMirror/pull/5673
3613 if (m.title) { (attributes || (attributes = {})).title = m.title; }
3614 if (m.attributes) {
3615 for (var attr in m.attributes)
3616 { (attributes || (attributes = {}))[attr] = m.attributes[attr]; }
3617 }
3618 if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))
3619 { collapsed = sp; }
3620 } else if (sp.from > pos && nextChange > sp.from) {
3621 nextChange = sp.from;
3622 }
3623 }
3624 if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2)
3625 { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += " " + endStyles[j$1]; } } }
3626
3627 if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2)
3628 { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]); } }
3629 if (collapsed && (collapsed.from || 0) == pos) {
3630 buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos,
3631 collapsed.marker, collapsed.from == null);
3632 if (collapsed.to == null) { return }
3633 if (collapsed.to == pos) { collapsed = false; }
3634 }
3635 }
3636 if (pos >= len) { break }
3637
3638 var upto = Math.min(len, nextChange);
3639 while (true) {
3640 if (text) {
3641 var end = pos + text.length;
3642 if (!collapsed) {
3643 var tokenText = end > upto ? text.slice(0, upto - pos) : text;
3644 builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
3645 spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", css, attributes);
3646 }
3647 if (end >= upto) {text = text.slice(upto - pos); pos = upto; break}
3648 pos = end;
3649 spanStartStyle = "";
3650 }
3651 text = allText.slice(at, at = styles[i++]);
3652 style = interpretTokenStyle(styles[i++], builder.cm.options);
3653 }
3654 }
3655 }
3656
3657
3658 // These objects are used to represent the visible (currently drawn)
3659 // part of the document. A LineView may correspond to multiple
3660 // logical lines, if those are connected by collapsed ranges.
3661 function LineView(doc, line, lineN) {
3662 // The starting line
3663 this.line = line;
3664 // Continuing lines, if any
3665 this.rest = visualLineContinued(line);
3666 // Number of logical lines in this visual line
3667 this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1;
3668 this.node = this.text = null;
3669 this.hidden = lineIsHidden(doc, line);
3670 }
3671
3672 // Create a range of LineView objects for the given lines.
3673 function buildViewArray(cm, from, to) {
3674 var array = [], nextPos;
3675 for (var pos = from; pos < to; pos = nextPos) {
3676 var view = new LineView(cm.doc, getLine(cm.doc, pos), pos);
3677 nextPos = pos + view.size;
3678 array.push(view);
3679 }
3680 return array
3681 }
3682
3683 var operationGroup = null;
3684
3685 function pushOperation(op) {
3686 if (operationGroup) {
3687 operationGroup.ops.push(op);
3688 } else {
3689 op.ownsGroup = operationGroup = {
3690 ops: [op],
3691 delayedCallbacks: []
3692 };
3693 }
3694 }
3695
3696 function fireCallbacksForOps(group) {
3697 // Calls delayed callbacks and cursorActivity handlers until no
3698 // new ones appear
3699 var callbacks = group.delayedCallbacks, i = 0;
3700 do {
3701 for (; i < callbacks.length; i++)
3702 { callbacks[i].call(null); }
3703 for (var j = 0; j < group.ops.length; j++) {
3704 var op = group.ops[j];
3705 if (op.cursorActivityHandlers)
3706 { while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
3707 { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm); } }
3708 }
3709 } while (i < callbacks.length)
3710 }
3711
3712 function finishOperation(op, endCb) {
3713 var group = op.ownsGroup;
3714 if (!group) { return }
3715
3716 try { fireCallbacksForOps(group); }
3717 finally {
3718 operationGroup = null;
3719 endCb(group);
3720 }
3721 }
3722
3723 var orphanDelayedCallbacks = null;
3724
3725 // Often, we want to signal events at a point where we are in the
3726 // middle of some work, but don't want the handler to start calling
3727 // other methods on the editor, which might be in an inconsistent
3728 // state or simply not expect any other events to happen.
3729 // signalLater looks whether there are any handlers, and schedules
3730 // them to be executed when the last operation ends, or, if no
3731 // operation is active, when a timeout fires.
3732 function signalLater(emitter, type /*, values...*/) {
3733 var arr = getHandlers(emitter, type);
3734 if (!arr.length) { return }
3735 var args = Array.prototype.slice.call(arguments, 2), list;
3736 if (operationGroup) {
3737 list = operationGroup.delayedCallbacks;
3738 } else if (orphanDelayedCallbacks) {
3739 list = orphanDelayedCallbacks;
3740 } else {
3741 list = orphanDelayedCallbacks = [];
3742 setTimeout(fireOrphanDelayed, 0);
3743 }
3744 var loop = function ( i ) {
3745 list.push(function () { return arr[i].apply(null, args); });
3746 };
3747
3748 for (var i = 0; i < arr.length; ++i)
3749 loop( i );
3750 }
3751
3752 function fireOrphanDelayed() {
3753 var delayed = orphanDelayedCallbacks;
3754 orphanDelayedCallbacks = null;
3755 for (var i = 0; i < delayed.length; ++i) { delayed[i](); }
3756 }
3757
3758 // When an aspect of a line changes, a string is added to
3759 // lineView.changes. This updates the relevant part of the line's
3760 // DOM structure.
3761 function updateLineForChanges(cm, lineView, lineN, dims) {
3762 for (var j = 0; j < lineView.changes.length; j++) {
3763 var type = lineView.changes[j];
3764 if (type == "text") { updateLineText(cm, lineView); }
3765 else if (type == "gutter") { updateLineGutter(cm, lineView, lineN, dims); }
3766 else if (type == "class") { updateLineClasses(cm, lineView); }
3767 else if (type == "widget") { updateLineWidgets(cm, lineView, dims); }
3768 }
3769 lineView.changes = null;
3770 }
3771
3772 // Lines with gutter elements, widgets or a background class need to
3773 // be wrapped, and have the extra elements added to the wrapper div
3774 function ensureLineWrapped(lineView) {
3775 if (lineView.node == lineView.text) {
3776 lineView.node = elt("div", null, null, "position: relative");
3777 if (lineView.text.parentNode)
3778 { lineView.text.parentNode.replaceChild(lineView.node, lineView.text); }
3779 lineView.node.appendChild(lineView.text);
3780 if (ie && ie_version < 8) { lineView.node.style.zIndex = 2; }
3781 }
3782 return lineView.node
3783 }
3784
3785 function updateLineBackground(cm, lineView) {
3786 var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass;
3787 if (cls) { cls += " CodeMirror-linebackground"; }
3788 if (lineView.background) {
3789 if (cls) { lineView.background.className = cls; }
3790 else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; }
3791 } else if (cls) {
3792 var wrap = ensureLineWrapped(lineView);
3793 lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild);
3794 cm.display.input.setUneditable(lineView.background);
3795 }
3796 }
3797
3798 // Wrapper around buildLineContent which will reuse the structure
3799 // in display.externalMeasured when possible.
3800 function getLineContent(cm, lineView) {
3801 var ext = cm.display.externalMeasured;
3802 if (ext && ext.line == lineView.line) {
3803 cm.display.externalMeasured = null;
3804 lineView.measure = ext.measure;
3805 return ext.built
3806 }
3807 return buildLineContent(cm, lineView)
3808 }
3809
3810 // Redraw the line's text. Interacts with the background and text
3811 // classes because the mode may output tokens that influence these
3812 // classes.
3813 function updateLineText(cm, lineView) {
3814 var cls = lineView.text.className;
3815 var built = getLineContent(cm, lineView);
3816 if (lineView.text == lineView.node) { lineView.node = built.pre; }
3817 lineView.text.parentNode.replaceChild(built.pre, lineView.text);
3818 lineView.text = built.pre;
3819 if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {
3820 lineView.bgClass = built.bgClass;
3821 lineView.textClass = built.textClass;
3822 updateLineClasses(cm, lineView);
3823 } else if (cls) {
3824 lineView.text.className = cls;
3825 }
3826 }
3827
3828 function updateLineClasses(cm, lineView) {
3829 updateLineBackground(cm, lineView);
3830 if (lineView.line.wrapClass)
3831 { ensureLineWrapped(lineView).className = lineView.line.wrapClass; }
3832 else if (lineView.node != lineView.text)
3833 { lineView.node.className = ""; }
3834 var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass;
3835 lineView.text.className = textClass || "";
3836 }
3837
3838 function updateLineGutter(cm, lineView, lineN, dims) {
3839 if (lineView.gutter) {
3840 lineView.node.removeChild(lineView.gutter);
3841 lineView.gutter = null;
3842 }
3843 if (lineView.gutterBackground) {
3844 lineView.node.removeChild(lineView.gutterBackground);
3845 lineView.gutterBackground = null;
3846 }
3847 if (lineView.line.gutterClass) {
3848 var wrap = ensureLineWrapped(lineView);
3849 lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass,
3850 ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px; width: " + (dims.gutterTotalWidth) + "px"));
3851 cm.display.input.setUneditable(lineView.gutterBackground);
3852 wrap.insertBefore(lineView.gutterBackground, lineView.text);
3853 }
3854 var markers = lineView.line.gutterMarkers;
3855 if (cm.options.lineNumbers || markers) {
3856 var wrap$1 = ensureLineWrapped(lineView);
3857 var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"));
3858 cm.display.input.setUneditable(gutterWrap);
3859 wrap$1.insertBefore(gutterWrap, lineView.text);
3860 if (lineView.line.gutterClass)
3861 { gutterWrap.className += " " + lineView.line.gutterClass; }
3862 if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
3863 { lineView.lineNumber = gutterWrap.appendChild(
3864 elt("div", lineNumberFor(cm.options, lineN),
3865 "CodeMirror-linenumber CodeMirror-gutter-elt",
3866 ("left: " + (dims.gutterLeft["CodeMirror-linenumbers"]) + "px; width: " + (cm.display.lineNumInnerWidth) + "px"))); }
3867 if (markers) { for (var k = 0; k < cm.options.gutters.length; ++k) {
3868 var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id];
3869 if (found)
3870 { gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt",
3871 ("left: " + (dims.gutterLeft[id]) + "px; width: " + (dims.gutterWidth[id]) + "px"))); }
3872 } }
3873 }
3874 }
3875
3876 function updateLineWidgets(cm, lineView, dims) {
3877 if (lineView.alignable) { lineView.alignable = null; }
3878 for (var node = lineView.node.firstChild, next = (void 0); node; node = next) {
3879 next = node.nextSibling;
3880 if (node.className == "CodeMirror-linewidget")
3881 { lineView.node.removeChild(node); }
3882 }
3883 insertLineWidgets(cm, lineView, dims);
3884 }
3885
3886 // Build a line's DOM representation from scratch
3887 function buildLineElement(cm, lineView, lineN, dims) {
3888 var built = getLineContent(cm, lineView);
3889 lineView.text = lineView.node = built.pre;
3890 if (built.bgClass) { lineView.bgClass = built.bgClass; }
3891 if (built.textClass) { lineView.textClass = built.textClass; }
3892
3893 updateLineClasses(cm, lineView);
3894 updateLineGutter(cm, lineView, lineN, dims);
3895 insertLineWidgets(cm, lineView, dims);
3896 return lineView.node
3897 }
3898
3899 // A lineView may contain multiple logical lines (when merged by
3900 // collapsed spans). The widgets for all of them need to be drawn.
3901 function insertLineWidgets(cm, lineView, dims) {
3902 insertLineWidgetsFor(cm, lineView.line, lineView, dims, true);
3903 if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
3904 { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false); } }
3905 }
3906
3907 function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {
3908 if (!line.widgets) { return }
3909 var wrap = ensureLineWrapped(lineView);
3910 for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
3911 var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
3912 if (!widget.handleMouseEvents) { node.setAttribute("cm-ignore-events", "true"); }
3913 positionLineWidget(widget, node, lineView, dims);
3914 cm.display.input.setUneditable(node);
3915 if (allowAbove && widget.above)
3916 { wrap.insertBefore(node, lineView.gutter || lineView.text); }
3917 else
3918 { wrap.appendChild(node); }
3919 signalLater(widget, "redraw");
3920 }
3921 }
3922
3923 function positionLineWidget(widget, node, lineView, dims) {
3924 if (widget.noHScroll) {
3925 (lineView.alignable || (lineView.alignable = [])).push(node);
3926 var width = dims.wrapperWidth;
3927 node.style.left = dims.fixedPos + "px";
3928 if (!widget.coverGutter) {
3929 width -= dims.gutterTotalWidth;
3930 node.style.paddingLeft = dims.gutterTotalWidth + "px";
3931 }
3932 node.style.width = width + "px";
3933 }
3934 if (widget.coverGutter) {
3935 node.style.zIndex = 5;
3936 node.style.position = "relative";
3937 if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + "px"; }
3938 }
3939 }
3940
3941 function widgetHeight(widget) {
3942 if (widget.height != null) { return widget.height }
3943 var cm = widget.doc.cm;
3944 if (!cm) { return 0 }
3945 if (!contains(document.body, widget.node)) {
3946 var parentStyle = "position: relative;";
3947 if (widget.coverGutter)
3948 { parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;"; }
3949 if (widget.noHScroll)
3950 { parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;"; }
3951 removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle));
3952 }
3953 return widget.height = widget.node.parentNode.offsetHeight
3954 }
3955
3956 // Return true when the given mouse event happened in a widget
3957 function eventInWidget(display, e) {
3958 for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
3959 if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") ||
3960 (n.parentNode == display.sizer && n != display.mover))
3961 { return true }
3962 }
3963 }
3964
3965 // POSITION MEASUREMENT
3966
3967 function paddingTop(display) {return display.lineSpace.offsetTop}
3968 function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight}
3969 function paddingH(display) {
3970 if (display.cachedPaddingH) { return display.cachedPaddingH }
3971 var e = removeChildrenAndAdd(display.measure, elt("pre", "x"));
3972 var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle;
3973 var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)};
3974 if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data; }
3975 return data
3976 }
3977
3978 function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth }
3979 function displayWidth(cm) {
3980 return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth
3981 }
3982 function displayHeight(cm) {
3983 return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight
3984 }
3985
3986 // Ensure the lineView.wrapping.heights array is populated. This is
3987 // an array of bottom offsets for the lines that make up a drawn
3988 // line. When lineWrapping is on, there might be more than one
3989 // height.
3990 function ensureLineHeights(cm, lineView, rect) {
3991 var wrapping = cm.options.lineWrapping;
3992 var curWidth = wrapping && displayWidth(cm);
3993 if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {
3994 var heights = lineView.measure.heights = [];
3995 if (wrapping) {
3996 lineView.measure.width = curWidth;
3997 var rects = lineView.text.firstChild.getClientRects();
3998 for (var i = 0; i < rects.length - 1; i++) {
3999 var cur = rects[i], next = rects[i + 1];
4000 if (Math.abs(cur.bottom - next.bottom) > 2)
4001 { heights.push((cur.bottom + next.top) / 2 - rect.top); }
4002 }
4003 }
4004 heights.push(rect.bottom - rect.top);
4005 }
4006 }
4007
4008 // Find a line map (mapping character offsets to text nodes) and a
4009 // measurement cache for the given line number. (A line view might
4010 // contain multiple lines when collapsed ranges are present.)
4011 function mapFromLineView(lineView, line, lineN) {
4012 if (lineView.line == line)
4013 { return {map: lineView.measure.map, cache: lineView.measure.cache} }
4014 for (var i = 0; i < lineView.rest.length; i++)
4015 { if (lineView.rest[i] == line)
4016 { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } }
4017 for (var i$1 = 0; i$1 < lineView.rest.length; i$1++)
4018 { if (lineNo(lineView.rest[i$1]) > lineN)
4019 { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } }
4020 }
4021
4022 // Render a line into the hidden node display.externalMeasured. Used
4023 // when measurement is needed for a line that's not in the viewport.
4024 function updateExternalMeasurement(cm, line) {
4025 line = visualLine(line);
4026 var lineN = lineNo(line);
4027 var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN);
4028 view.lineN = lineN;
4029 var built = view.built = buildLineContent(cm, view);
4030 view.text = built.pre;
4031 removeChildrenAndAdd(cm.display.lineMeasure, built.pre);
4032 return view
4033 }
4034
4035 // Get a {top, bottom, left, right} box (in line-local coordinates)
4036 // for a given character.
4037 function measureChar(cm, line, ch, bias) {
4038 return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias)
4039 }
4040
4041 // Find a line view that corresponds to the given line number.
4042 function findViewForLine(cm, lineN) {
4043 if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo)
4044 { return cm.display.view[findViewIndex(cm, lineN)] }
4045 var ext = cm.display.externalMeasured;
4046 if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size)
4047 { return ext }
4048 }
4049
4050 // Measurement can be split in two steps, the set-up work that
4051 // applies to the whole line, and the measurement of the actual
4052 // character. Functions like coordsChar, that need to do a lot of
4053 // measurements in a row, can thus ensure that the set-up work is
4054 // only done once.
4055 function prepareMeasureForLine(cm, line) {
4056 var lineN = lineNo(line);
4057 var view = findViewForLine(cm, lineN);
4058 if (view && !view.text) {
4059 view = null;
4060 } else if (view && view.changes) {
4061 updateLineForChanges(cm, view, lineN, getDimensions(cm));
4062 cm.curOp.forceUpdate = true;
4063 }
4064 if (!view)
4065 { view = updateExternalMeasurement(cm, line); }
4066
4067 var info = mapFromLineView(view, line, lineN);
4068 return {
4069 line: line, view: view, rect: null,
4070 map: info.map, cache: info.cache, before: info.before,
4071 hasHeights: false
4072 }
4073 }
4074
4075 // Given a prepared measurement object, measures the position of an
4076 // actual character (or fetches it from the cache).
4077 function measureCharPrepared(cm, prepared, ch, bias, varHeight) {
4078 if (prepared.before) { ch = -1; }
4079 var key = ch + (bias || ""), found;
4080 if (prepared.cache.hasOwnProperty(key)) {
4081 found = prepared.cache[key];
4082 } else {
4083 if (!prepared.rect)
4084 { prepared.rect = prepared.view.text.getBoundingClientRect(); }
4085 if (!prepared.hasHeights) {
4086 ensureLineHeights(cm, prepared.view, prepared.rect);
4087 prepared.hasHeights = true;
4088 }
4089 found = measureCharInner(cm, prepared, ch, bias);
4090 if (!found.bogus) { prepared.cache[key] = found; }
4091 }
4092 return {left: found.left, right: found.right,
4093 top: varHeight ? found.rtop : found.top,
4094 bottom: varHeight ? found.rbottom : found.bottom}
4095 }
4096
4097 var nullRect = {left: 0, right: 0, top: 0, bottom: 0};
4098
4099 function nodeAndOffsetInLineMap(map$$1, ch, bias) {
4100 var node, start, end, collapse, mStart, mEnd;
4101 // First, search the line map for the text node corresponding to,
4102 // or closest to, the target character.
4103 for (var i = 0; i < map$$1.length; i += 3) {
4104 mStart = map$$1[i];
4105 mEnd = map$$1[i + 1];
4106 if (ch < mStart) {
4107 start = 0; end = 1;
4108 collapse = "left";
4109 } else if (ch < mEnd) {
4110 start = ch - mStart;
4111 end = start + 1;
4112 } else if (i == map$$1.length - 3 || ch == mEnd && map$$1[i + 3] > ch) {
4113 end = mEnd - mStart;
4114 start = end - 1;
4115 if (ch >= mEnd) { collapse = "right"; }
4116 }
4117 if (start != null) {
4118 node = map$$1[i + 2];
4119 if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right"))
4120 { collapse = bias; }
4121 if (bias == "left" && start == 0)
4122 { while (i && map$$1[i - 2] == map$$1[i - 3] && map$$1[i - 1].insertLeft) {
4123 node = map$$1[(i -= 3) + 2];
4124 collapse = "left";
4125 } }
4126 if (bias == "right" && start == mEnd - mStart)
4127 { while (i < map$$1.length - 3 && map$$1[i + 3] == map$$1[i + 4] && !map$$1[i + 5].insertLeft) {
4128 node = map$$1[(i += 3) + 2];
4129 collapse = "right";
4130 } }
4131 break
4132 }
4133 }
4134 return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd}
4135 }
4136
4137 function getUsefulRect(rects, bias) {
4138 var rect = nullRect;
4139 if (bias == "left") { for (var i = 0; i < rects.length; i++) {
4140 if ((rect = rects[i]).left != rect.right) { break }
4141 } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) {
4142 if ((rect = rects[i$1]).left != rect.right) { break }
4143 } }
4144 return rect
4145 }
4146
4147 function measureCharInner(cm, prepared, ch, bias) {
4148 var place = nodeAndOffsetInLineMap(prepared.map, ch, bias);
4149 var node = place.node, start = place.start, end = place.end, collapse = place.collapse;
4150
4151 var rect;
4152 if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.
4153 for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonsense rectangles are returned
4154 while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start; }
4155 while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) { ++end; }
4156 if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart)
4157 { rect = node.parentNode.getBoundingClientRect(); }
4158 else
4159 { rect = getUsefulRect(range(node, start, end).getClientRects(), bias); }
4160 if (rect.left || rect.right || start == 0) { break }
4161 end = start;
4162 start = start - 1;
4163 collapse = "right";
4164 }
4165 if (ie && ie_version < 11) { rect = maybeUpdateRectForZooming(cm.display.measure, rect); }
4166 } else { // If it is a widget, simply get the box for the whole widget.
4167 if (start > 0) { collapse = bias = "right"; }
4168 var rects;
4169 if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)
4170 { rect = rects[bias == "right" ? rects.length - 1 : 0]; }
4171 else
4172 { rect = node.getBoundingClientRect(); }
4173 }
4174 if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {
4175 var rSpan = node.parentNode.getClientRects()[0];
4176 if (rSpan)
4177 { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom}; }
4178 else
4179 { rect = nullRect; }
4180 }
4181
4182 var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top;
4183 var mid = (rtop + rbot) / 2;
4184 var heights = prepared.view.measure.heights;
4185 var i = 0;
4186 for (; i < heights.length - 1; i++)
4187 { if (mid < heights[i]) { break } }
4188 var top = i ? heights[i - 1] : 0, bot = heights[i];
4189 var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left,
4190 right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left,
4191 top: top, bottom: bot};
4192 if (!rect.left && !rect.right) { result.bogus = true; }
4193 if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; }
4194
4195 return result
4196 }
4197
4198 // Work around problem with bounding client rects on ranges being
4199 // returned incorrectly when zoomed on IE10 and below.
4200 function maybeUpdateRectForZooming(measure, rect) {
4201 if (!window.screen || screen.logicalXDPI == null ||
4202 screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))
4203 { return rect }
4204 var scaleX = screen.logicalXDPI / screen.deviceXDPI;
4205 var scaleY = screen.logicalYDPI / screen.deviceYDPI;
4206 return {left: rect.left * scaleX, right: rect.right * scaleX,
4207 top: rect.top * scaleY, bottom: rect.bottom * scaleY}
4208 }
4209
4210 function clearLineMeasurementCacheFor(lineView) {
4211 if (lineView.measure) {
4212 lineView.measure.cache = {};
4213 lineView.measure.heights = null;
4214 if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
4215 { lineView.measure.caches[i] = {}; } }
4216 }
4217 }
4218
4219 function clearLineMeasurementCache(cm) {
4220 cm.display.externalMeasure = null;
4221 removeChildren(cm.display.lineMeasure);
4222 for (var i = 0; i < cm.display.view.length; i++)
4223 { clearLineMeasurementCacheFor(cm.display.view[i]); }
4224 }
4225
4226 function clearCaches(cm) {
4227 clearLineMeasurementCache(cm);
4228 cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null;
4229 if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true; }
4230 cm.display.lineNumChars = null;
4231 }
4232
4233 function pageScrollX() {
4234 // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=489206
4235 // which causes page_Offset and bounding client rects to use
4236 // different reference viewports and invalidate our calculations.
4237 if (chrome && android) { return -(document.body.getBoundingClientRect().left - parseInt(getComputedStyle(document.body).marginLeft)) }
4238 return window.pageXOffset || (document.documentElement || document.body).scrollLeft
4239 }
4240 function pageScrollY() {
4241 if (chrome && android) { return -(document.body.getBoundingClientRect().top - parseInt(getComputedStyle(document.body).marginTop)) }
4242 return window.pageYOffset || (document.documentElement || document.body).scrollTop
4243 }
4244
4245 function widgetTopHeight(lineObj) {
4246 var height = 0;
4247 if (lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above)
4248 { height += widgetHeight(lineObj.widgets[i]); } } }
4249 return height
4250 }
4251
4252 // Converts a {top, bottom, left, right} box from line-local
4253 // coordinates into another coordinate system. Context may be one of
4254 // "line", "div" (display.lineDiv), "local"./null (editor), "window",
4255 // or "page".
4256 function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) {
4257 if (!includeWidgets) {
4258 var height = widgetTopHeight(lineObj);
4259 rect.top += height; rect.bottom += height;
4260 }
4261 if (context == "line") { return rect }
4262 if (!context) { context = "local"; }
4263 var yOff = heightAtLine(lineObj);
4264 if (context == "local") { yOff += paddingTop(cm.display); }
4265 else { yOff -= cm.display.viewOffset; }
4266 if (context == "page" || context == "window") {
4267 var lOff = cm.display.lineSpace.getBoundingClientRect();
4268 yOff += lOff.top + (context == "window" ? 0 : pageScrollY());
4269 var xOff = lOff.left + (context == "window" ? 0 : pageScrollX());
4270 rect.left += xOff; rect.right += xOff;
4271 }
4272 rect.top += yOff; rect.bottom += yOff;
4273 return rect
4274 }
4275
4276 // Coverts a box from "div" coords to another coordinate system.
4277 // Context may be "window", "page", "div", or "local"./null.
4278 function fromCoordSystem(cm, coords, context) {
4279 if (context == "div") { return coords }
4280 var left = coords.left, top = coords.top;
4281 // First move into "page" coordinate system
4282 if (context == "page") {
4283 left -= pageScrollX();
4284 top -= pageScrollY();
4285 } else if (context == "local" || !context) {
4286 var localBox = cm.display.sizer.getBoundingClientRect();
4287 left += localBox.left;
4288 top += localBox.top;
4289 }
4290
4291 var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect();
4292 return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top}
4293 }
4294
4295 function charCoords(cm, pos, context, lineObj, bias) {
4296 if (!lineObj) { lineObj = getLine(cm.doc, pos.line); }
4297 return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context)
4298 }
4299
4300 // Returns a box for a given cursor position, which may have an
4301 // 'other' property containing the position of the secondary cursor
4302 // on a bidi boundary.
4303 // A cursor Pos(line, char, "before") is on the same visual line as `char - 1`
4304 // and after `char - 1` in writing order of `char - 1`
4305 // A cursor Pos(line, char, "after") is on the same visual line as `char`
4306 // and before `char` in writing order of `char`
4307 // Examples (upper-case letters are RTL, lower-case are LTR):
4308 // Pos(0, 1, ...)
4309 // before after
4310 // ab a|b a|b
4311 // aB a|B aB|
4312 // Ab |Ab A|b
4313 // AB B|A B|A
4314 // Every position after the last character on a line is considered to stick
4315 // to the last character on the line.
4316 function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
4317 lineObj = lineObj || getLine(cm.doc, pos.line);
4318 if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); }
4319 function get(ch, right) {
4320 var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight);
4321 if (right) { m.left = m.right; } else { m.right = m.left; }
4322 return intoCoordSystem(cm, lineObj, m, context)
4323 }
4324 var order = getOrder(lineObj, cm.doc.direction), ch = pos.ch, sticky = pos.sticky;
4325 if (ch >= lineObj.text.length) {
4326 ch = lineObj.text.length;
4327 sticky = "before";
4328 } else if (ch <= 0) {
4329 ch = 0;
4330 sticky = "after";
4331 }
4332 if (!order) { return get(sticky == "before" ? ch - 1 : ch, sticky == "before") }
4333
4334 function getBidi(ch, partPos, invert) {
4335 var part = order[partPos], right = part.level == 1;
4336 return get(invert ? ch - 1 : ch, right != invert)
4337 }
4338 var partPos = getBidiPartAt(order, ch, sticky);
4339 var other = bidiOther;
4340 var val = getBidi(ch, partPos, sticky == "before");
4341 if (other != null) { val.other = getBidi(ch, other, sticky != "before"); }
4342 return val
4343 }
4344
4345 // Used to cheaply estimate the coordinates for a position. Used for
4346 // intermediate scroll updates.
4347 function estimateCoords(cm, pos) {
4348 var left = 0;
4349 pos = clipPos(cm.doc, pos);
4350 if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch; }
4351 var lineObj = getLine(cm.doc, pos.line);
4352 var top = heightAtLine(lineObj) + paddingTop(cm.display);
4353 return {left: left, right: left, top: top, bottom: top + lineObj.height}
4354 }
4355
4356 // Positions returned by coordsChar contain some extra information.
4357 // xRel is the relative x position of the input coordinates compared
4358 // to the found position (so xRel > 0 means the coordinates are to
4359 // the right of the character position, for example). When outside
4360 // is true, that means the coordinates lie outside the line's
4361 // vertical range.
4362 function PosWithInfo(line, ch, sticky, outside, xRel) {
4363 var pos = Pos(line, ch, sticky);
4364 pos.xRel = xRel;
4365 if (outside) { pos.outside = true; }
4366 return pos
4367 }
4368
4369 // Compute the character position closest to the given coordinates.
4370 // Input must be lineSpace-local ("div" coordinate system).
4371 function coordsChar(cm, x, y) {
4372 var doc = cm.doc;
4373 y += cm.display.viewOffset;
4374 if (y < 0) { return PosWithInfo(doc.first, 0, null, true, -1) }
4375 var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
4376 if (lineN > last)
4377 { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, true, 1) }
4378 if (x < 0) { x = 0; }
4379
4380 var lineObj = getLine(doc, lineN);
4381 for (;;) {
4382 var found = coordsCharInner(cm, lineObj, lineN, x, y);
4383 var collapsed = collapsedSpanAround(lineObj, found.ch + (found.xRel > 0 ? 1 : 0));
4384 if (!collapsed) { return found }
4385 var rangeEnd = collapsed.find(1);
4386 if (rangeEnd.line == lineN) { return rangeEnd }
4387 lineObj = getLine(doc, lineN = rangeEnd.line);
4388 }
4389 }
4390
4391 function wrappedLineExtent(cm, lineObj, preparedMeasure, y) {
4392 y -= widgetTopHeight(lineObj);
4393 var end = lineObj.text.length;
4394 var begin = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch - 1).bottom <= y; }, end, 0);
4395 end = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch).top > y; }, begin, end);
4396 return {begin: begin, end: end}
4397 }
4398
4399 function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) {
4400 if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); }
4401 var targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), "line").top;
4402 return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop)
4403 }
4404
4405 // Returns true if the given side of a box is after the given
4406 // coordinates, in top-to-bottom, left-to-right order.
4407 function boxIsAfter(box, x, y, left) {
4408 return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x
4409 }
4410
4411 function coordsCharInner(cm, lineObj, lineNo$$1, x, y) {
4412 // Move y into line-local coordinate space
4413 y -= heightAtLine(lineObj);
4414 var preparedMeasure = prepareMeasureForLine(cm, lineObj);
4415 // When directly calling `measureCharPrepared`, we have to adjust
4416 // for the widgets at this line.
4417 var widgetHeight$$1 = widgetTopHeight(lineObj);
4418 var begin = 0, end = lineObj.text.length, ltr = true;
4419
4420 var order = getOrder(lineObj, cm.doc.direction);
4421 // If the line isn't plain left-to-right text, first figure out
4422 // which bidi section the coordinates fall into.
4423 if (order) {
4424 var part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart)
4425 (cm, lineObj, lineNo$$1, preparedMeasure, order, x, y);
4426 ltr = part.level != 1;
4427 // The awkward -1 offsets are needed because findFirst (called
4428 // on these below) will treat its first bound as inclusive,
4429 // second as exclusive, but we want to actually address the
4430 // characters in the part's range
4431 begin = ltr ? part.from : part.to - 1;
4432 end = ltr ? part.to : part.from - 1;
4433 }
4434
4435 // A binary search to find the first character whose bounding box
4436 // starts after the coordinates. If we run across any whose box wrap
4437 // the coordinates, store that.
4438 var chAround = null, boxAround = null;
4439 var ch = findFirst(function (ch) {
4440 var box = measureCharPrepared(cm, preparedMeasure, ch);
4441 box.top += widgetHeight$$1; box.bottom += widgetHeight$$1;
4442 if (!boxIsAfter(box, x, y, false)) { return false }
4443 if (box.top <= y && box.left <= x) {
4444 chAround = ch;
4445 boxAround = box;
4446 }
4447 return true
4448 }, begin, end);
4449
4450 var baseX, sticky, outside = false;
4451 // If a box around the coordinates was found, use that
4452 if (boxAround) {
4453 // Distinguish coordinates nearer to the left or right side of the box
4454 var atLeft = x - boxAround.left < boxAround.right - x, atStart = atLeft == ltr;
4455 ch = chAround + (atStart ? 0 : 1);
4456 sticky = atStart ? "after" : "before";
4457 baseX = atLeft ? boxAround.left : boxAround.right;
4458 } else {
4459 // (Adjust for extended bound, if necessary.)
4460 if (!ltr && (ch == end || ch == begin)) { ch++; }
4461 // To determine which side to associate with, get the box to the
4462 // left of the character and compare it's vertical position to the
4463 // coordinates
4464 sticky = ch == 0 ? "after" : ch == lineObj.text.length ? "before" :
4465 (measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight$$1 <= y) == ltr ?
4466 "after" : "before";
4467 // Now get accurate coordinates for this place, in order to get a
4468 // base X position
4469 var coords = cursorCoords(cm, Pos(lineNo$$1, ch, sticky), "line", lineObj, preparedMeasure);
4470 baseX = coords.left;
4471 outside = y < coords.top || y >= coords.bottom;
4472 }
4473
4474 ch = skipExtendingChars(lineObj.text, ch, 1);
4475 return PosWithInfo(lineNo$$1, ch, sticky, outside, x - baseX)
4476 }
4477
4478 function coordsBidiPart(cm, lineObj, lineNo$$1, preparedMeasure, order, x, y) {
4479 // Bidi parts are sorted left-to-right, and in a non-line-wrapping
4480 // situation, we can take this ordering to correspond to the visual
4481 // ordering. This finds the first part whose end is after the given
4482 // coordinates.
4483 var index = findFirst(function (i) {
4484 var part = order[i], ltr = part.level != 1;
4485 return boxIsAfter(cursorCoords(cm, Pos(lineNo$$1, ltr ? part.to : part.from, ltr ? "before" : "after"),
4486 "line", lineObj, preparedMeasure), x, y, true)
4487 }, 0, order.length - 1);
4488 var part = order[index];
4489 // If this isn't the first part, the part's start is also after
4490 // the coordinates, and the coordinates aren't on the same line as
4491 // that start, move one part back.
4492 if (index > 0) {
4493 var ltr = part.level != 1;
4494 var start = cursorCoords(cm, Pos(lineNo$$1, ltr ? part.from : part.to, ltr ? "after" : "before"),
4495 "line", lineObj, preparedMeasure);
4496 if (boxIsAfter(start, x, y, true) && start.top > y)
4497 { part = order[index - 1]; }
4498 }
4499 return part
4500 }
4501
4502 function coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x, y) {
4503 // In a wrapped line, rtl text on wrapping boundaries can do things
4504 // that don't correspond to the ordering in our `order` array at
4505 // all, so a binary search doesn't work, and we want to return a
4506 // part that only spans one line so that the binary search in
4507 // coordsCharInner is safe. As such, we first find the extent of the
4508 // wrapped line, and then do a flat search in which we discard any
4509 // spans that aren't on the line.
4510 var ref = wrappedLineExtent(cm, lineObj, preparedMeasure, y);
4511 var begin = ref.begin;
4512 var end = ref.end;
4513 if (/\s/.test(lineObj.text.charAt(end - 1))) { end--; }
4514 var part = null, closestDist = null;
4515 for (var i = 0; i < order.length; i++) {
4516 var p = order[i];
4517 if (p.from >= end || p.to <= begin) { continue }
4518 var ltr = p.level != 1;
4519 var endX = measureCharPrepared(cm, preparedMeasure, ltr ? Math.min(end, p.to) - 1 : Math.max(begin, p.from)).right;
4520 // Weigh against spans ending before this, so that they are only
4521 // picked if nothing ends after
4522 var dist = endX < x ? x - endX + 1e9 : endX - x;
4523 if (!part || closestDist > dist) {
4524 part = p;
4525 closestDist = dist;
4526 }
4527 }
4528 if (!part) { part = order[order.length - 1]; }
4529 // Clip the part to the wrapped line.
4530 if (part.from < begin) { part = {from: begin, to: part.to, level: part.level}; }
4531 if (part.to > end) { part = {from: part.from, to: end, level: part.level}; }
4532 return part
4533 }
4534
4535 var measureText;
4536 // Compute the default text height.
4537 function textHeight(display) {
4538 if (display.cachedTextHeight != null) { return display.cachedTextHeight }
4539 if (measureText == null) {
4540 measureText = elt("pre");
4541 // Measure a bunch of lines, for browsers that compute
4542 // fractional heights.
4543 for (var i = 0; i < 49; ++i) {
4544 measureText.appendChild(document.createTextNode("x"));
4545 measureText.appendChild(elt("br"));
4546 }
4547 measureText.appendChild(document.createTextNode("x"));
4548 }
4549 removeChildrenAndAdd(display.measure, measureText);
4550 var height = measureText.offsetHeight / 50;
4551 if (height > 3) { display.cachedTextHeight = height; }
4552 removeChildren(display.measure);
4553 return height || 1
4554 }
4555
4556 // Compute the default character width.
4557 function charWidth(display) {
4558 if (display.cachedCharWidth != null) { return display.cachedCharWidth }
4559 var anchor = elt("span", "xxxxxxxxxx");
4560 var pre = elt("pre", [anchor]);
4561 removeChildrenAndAdd(display.measure, pre);
4562 var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10;
4563 if (width > 2) { display.cachedCharWidth = width; }
4564 return width || 10
4565 }
4566
4567 // Do a bulk-read of the DOM positions and sizes needed to draw the
4568 // view, so that we don't interleave reading and writing to the DOM.
4569 function getDimensions(cm) {
4570 var d = cm.display, left = {}, width = {};
4571 var gutterLeft = d.gutters.clientLeft;
4572 for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
4573 left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft;
4574 width[cm.options.gutters[i]] = n.clientWidth;
4575 }
4576 return {fixedPos: compensateForHScroll(d),
4577 gutterTotalWidth: d.gutters.offsetWidth,
4578 gutterLeft: left,
4579 gutterWidth: width,
4580 wrapperWidth: d.wrapper.clientWidth}
4581 }
4582
4583 // Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
4584 // but using getBoundingClientRect to get a sub-pixel-accurate
4585 // result.
4586 function compensateForHScroll(display) {
4587 return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left
4588 }
4589
4590 // Returns a function that estimates the height of a line, to use as
4591 // first approximation until the line becomes visible (and is thus
4592 // properly measurable).
4593 function estimateHeight(cm) {
4594 var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
4595 var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
4596 return function (line) {
4597 if (lineIsHidden(cm.doc, line)) { return 0 }
4598
4599 var widgetsHeight = 0;
4600 if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) {
4601 if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height; }
4602 } }
4603
4604 if (wrapping)
4605 { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th }
4606 else
4607 { return widgetsHeight + th }
4608 }
4609 }
4610
4611 function estimateLineHeights(cm) {
4612 var doc = cm.doc, est = estimateHeight(cm);
4613 doc.iter(function (line) {
4614 var estHeight = est(line);
4615 if (estHeight != line.height) { updateLineHeight(line, estHeight); }
4616 });
4617 }
4618
4619 // Given a mouse event, find the corresponding position. If liberal
4620 // is false, it checks whether a gutter or scrollbar was clicked,
4621 // and returns null if it was. forRect is used by rectangular
4622 // selections, and tries to estimate a character position even for
4623 // coordinates beyond the right of the text.
4624 function posFromMouse(cm, e, liberal, forRect) {
4625 var display = cm.display;
4626 if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") { return null }
4627
4628 var x, y, space = display.lineSpace.getBoundingClientRect();
4629 // Fails unpredictably on IE[67] when mouse is dragged around quickly.
4630 try { x = e.clientX - space.left; y = e.clientY - space.top; }
4631 catch (e) { return null }
4632 var coords = coordsChar(cm, x, y), line;
4633 if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) {
4634 var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length;
4635 coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff));
4636 }
4637 return coords
4638 }
4639
4640 // Find the view element corresponding to a given line. Return null
4641 // when the line isn't visible.
4642 function findViewIndex(cm, n) {
4643 if (n >= cm.display.viewTo) { return null }
4644 n -= cm.display.viewFrom;
4645 if (n < 0) { return null }
4646 var view = cm.display.view;
4647 for (var i = 0; i < view.length; i++) {
4648 n -= view[i].size;
4649 if (n < 0) { return i }
4650 }
4651 }
4652
4653 function updateSelection(cm) {
4654 cm.display.input.showSelection(cm.display.input.prepareSelection());
4655 }
4656
4657 function prepareSelection(cm, primary) {
4658 if ( primary === void 0 ) primary = true;
4659
4660 var doc = cm.doc, result = {};
4661 var curFragment = result.cursors = document.createDocumentFragment();
4662 var selFragment = result.selection = document.createDocumentFragment();
4663
4664 for (var i = 0; i < doc.sel.ranges.length; i++) {
4665 if (!primary && i == doc.sel.primIndex) { continue }
4666 var range$$1 = doc.sel.ranges[i];
4667 if (range$$1.from().line >= cm.display.viewTo || range$$1.to().line < cm.display.viewFrom) { continue }
4668 var collapsed = range$$1.empty();
4669 if (collapsed || cm.options.showCursorWhenSelecting)
4670 { drawSelectionCursor(cm, range$$1.head, curFragment); }
4671 if (!collapsed)
4672 { drawSelectionRange(cm, range$$1, selFragment); }
4673 }
4674 return result
4675 }
4676
4677 // Draws a cursor for the given range
4678 function drawSelectionCursor(cm, head, output) {
4679 var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine);
4680
4681 var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor"));
4682 cursor.style.left = pos.left + "px";
4683 cursor.style.top = pos.top + "px";
4684 cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
4685
4686 if (pos.other) {
4687 // Secondary cursor, shown when on a 'jump' in bi-directional text
4688 var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"));
4689 otherCursor.style.display = "";
4690 otherCursor.style.left = pos.other.left + "px";
4691 otherCursor.style.top = pos.other.top + "px";
4692 otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
4693 }
4694 }
4695
4696 function cmpCoords(a, b) { return a.top - b.top || a.left - b.left }
4697
4698 // Draws the given range as a highlighted selection
4699 function drawSelectionRange(cm, range$$1, output) {
4700 var display = cm.display, doc = cm.doc;
4701 var fragment = document.createDocumentFragment();
4702 var padding = paddingH(cm.display), leftSide = padding.left;
4703 var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right;
4704 var docLTR = doc.direction == "ltr";
4705
4706 function add(left, top, width, bottom) {
4707 if (top < 0) { top = 0; }
4708 top = Math.round(top);
4709 bottom = Math.round(bottom);
4710 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")));
4711 }
4712
4713 function drawForLine(line, fromArg, toArg) {
4714 var lineObj = getLine(doc, line);
4715 var lineLen = lineObj.text.length;
4716 var start, end;
4717 function coords(ch, bias) {
4718 return charCoords(cm, Pos(line, ch), "div", lineObj, bias)
4719 }
4720
4721 function wrapX(pos, dir, side) {
4722 var extent = wrappedLineExtentChar(cm, lineObj, null, pos);
4723 var prop = (dir == "ltr") == (side == "after") ? "left" : "right";
4724 var ch = side == "after" ? extent.begin : extent.end - (/\s/.test(lineObj.text.charAt(extent.end - 1)) ? 2 : 1);
4725 return coords(ch, prop)[prop]
4726 }
4727
4728 var order = getOrder(lineObj, doc.direction);
4729 iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir, i) {
4730 var ltr = dir == "ltr";
4731 var fromPos = coords(from, ltr ? "left" : "right");
4732 var toPos = coords(to - 1, ltr ? "right" : "left");
4733
4734 var openStart = fromArg == null && from == 0, openEnd = toArg == null && to == lineLen;
4735 var first = i == 0, last = !order || i == order.length - 1;
4736 if (toPos.top - fromPos.top <= 3) { // Single line
4737 var openLeft = (docLTR ? openStart : openEnd) && first;
4738 var openRight = (docLTR ? openEnd : openStart) && last;
4739 var left = openLeft ? leftSide : (ltr ? fromPos : toPos).left;
4740 var right = openRight ? rightSide : (ltr ? toPos : fromPos).right;
4741 add(left, fromPos.top, right - left, fromPos.bottom);
4742 } else { // Multiple lines
4743 var topLeft, topRight, botLeft, botRight;
4744 if (ltr) {
4745 topLeft = docLTR && openStart && first ? leftSide : fromPos.left;
4746 topRight = docLTR ? rightSide : wrapX(from, dir, "before");
4747 botLeft = docLTR ? leftSide : wrapX(to, dir, "after");
4748 botRight = docLTR && openEnd && last ? rightSide : toPos.right;
4749 } else {
4750 topLeft = !docLTR ? leftSide : wrapX(from, dir, "before");
4751 topRight = !docLTR && openStart && first ? rightSide : fromPos.right;
4752 botLeft = !docLTR && openEnd && last ? leftSide : toPos.left;
4753 botRight = !docLTR ? rightSide : wrapX(to, dir, "after");
4754 }
4755 add(topLeft, fromPos.top, topRight - topLeft, fromPos.bottom);
4756 if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top); }
4757 add(botLeft, toPos.top, botRight - botLeft, toPos.bottom);
4758 }
4759
4760 if (!start || cmpCoords(fromPos, start) < 0) { start = fromPos; }
4761 if (cmpCoords(toPos, start) < 0) { start = toPos; }
4762 if (!end || cmpCoords(fromPos, end) < 0) { end = fromPos; }
4763 if (cmpCoords(toPos, end) < 0) { end = toPos; }
4764 });
4765 return {start: start, end: end}
4766 }
4767
4768 var sFrom = range$$1.from(), sTo = range$$1.to();
4769 if (sFrom.line == sTo.line) {
4770 drawForLine(sFrom.line, sFrom.ch, sTo.ch);
4771 } else {
4772 var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line);
4773 var singleVLine = visualLine(fromLine) == visualLine(toLine);
4774 var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end;
4775 var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start;
4776 if (singleVLine) {
4777 if (leftEnd.top < rightStart.top - 2) {
4778 add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);
4779 add(leftSide, rightStart.top, rightStart.left, rightStart.bottom);
4780 } else {
4781 add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);
4782 }
4783 }
4784 if (leftEnd.bottom < rightStart.top)
4785 { add(leftSide, leftEnd.bottom, null, rightStart.top); }
4786 }
4787
4788 output.appendChild(fragment);
4789 }
4790
4791 // Cursor-blinking
4792 function restartBlink(cm) {
4793 if (!cm.state.focused) { return }
4794 var display = cm.display;
4795 clearInterval(display.blinker);
4796 var on = true;
4797 display.cursorDiv.style.visibility = "";
4798 if (cm.options.cursorBlinkRate > 0)
4799 { display.blinker = setInterval(function () { return display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; },
4800 cm.options.cursorBlinkRate); }
4801 else if (cm.options.cursorBlinkRate < 0)
4802 { display.cursorDiv.style.visibility = "hidden"; }
4803 }
4804
4805 function ensureFocus(cm) {
4806 if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); }
4807 }
4808
4809 function delayBlurEvent(cm) {
4810 cm.state.delayingBlurEvent = true;
4811 setTimeout(function () { if (cm.state.delayingBlurEvent) {
4812 cm.state.delayingBlurEvent = false;
4813 onBlur(cm);
4814 } }, 100);
4815 }
4816
4817 function onFocus(cm, e) {
4818 if (cm.state.delayingBlurEvent) { cm.state.delayingBlurEvent = false; }
4819
4820 if (cm.options.readOnly == "nocursor") { return }
4821 if (!cm.state.focused) {
4822 signal(cm, "focus", cm, e);
4823 cm.state.focused = true;
4824 addClass(cm.display.wrapper, "CodeMirror-focused");
4825 // This test prevents this from firing when a context
4826 // menu is closed (since the input reset would kill the
4827 // select-all detection hack)
4828 if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {
4829 cm.display.input.reset();
4830 if (webkit) { setTimeout(function () { return cm.display.input.reset(true); }, 20); } // Issue #1730
4831 }
4832 cm.display.input.receivedFocus();
4833 }
4834 restartBlink(cm);
4835 }
4836 function onBlur(cm, e) {
4837 if (cm.state.delayingBlurEvent) { return }
4838
4839 if (cm.state.focused) {
4840 signal(cm, "blur", cm, e);
4841 cm.state.focused = false;
4842 rmClass(cm.display.wrapper, "CodeMirror-focused");
4843 }
4844 clearInterval(cm.display.blinker);
4845 setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false; } }, 150);
4846 }
4847
4848 // Read the actual heights of the rendered lines, and update their
4849 // stored heights to match.
4850 function updateHeightsInViewport(cm) {
4851 var display = cm.display;
4852 var prevBottom = display.lineDiv.offsetTop;
4853 for (var i = 0; i < display.view.length; i++) {
4854 var cur = display.view[i], wrapping = cm.options.lineWrapping;
4855 var height = (void 0), width = 0;
4856 if (cur.hidden) { continue }
4857 if (ie && ie_version < 8) {
4858 var bot = cur.node.offsetTop + cur.node.offsetHeight;
4859 height = bot - prevBottom;
4860 prevBottom = bot;
4861 } else {
4862 var box = cur.node.getBoundingClientRect();
4863 height = box.bottom - box.top;
4864 // Check that lines don't extend past the right of the current
4865 // editor width
4866 if (!wrapping && cur.text.firstChild)
4867 { width = cur.text.firstChild.getBoundingClientRect().right - box.left - 1; }
4868 }
4869 var diff = cur.line.height - height;
4870 if (diff > .005 || diff < -.005) {
4871 updateLineHeight(cur.line, height);
4872 updateWidgetHeight(cur.line);
4873 if (cur.rest) { for (var j = 0; j < cur.rest.length; j++)
4874 { updateWidgetHeight(cur.rest[j]); } }
4875 }
4876 if (width > cm.display.sizerWidth) {
4877 var chWidth = Math.ceil(width / charWidth(cm.display));
4878 if (chWidth > cm.display.maxLineLength) {
4879 cm.display.maxLineLength = chWidth;
4880 cm.display.maxLine = cur.line;
4881 cm.display.maxLineChanged = true;
4882 }
4883 }
4884 }
4885 }
4886
4887 // Read and store the height of line widgets associated with the
4888 // given line.
4889 function updateWidgetHeight(line) {
4890 if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i) {
4891 var w = line.widgets[i], parent = w.node.parentNode;
4892 if (parent) { w.height = parent.offsetHeight; }
4893 } }
4894 }
4895
4896 // Compute the lines that are visible in a given viewport (defaults
4897 // the the current scroll position). viewport may contain top,
4898 // height, and ensure (see op.scrollToPos) properties.
4899 function visibleLines(display, doc, viewport) {
4900 var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop;
4901 top = Math.floor(top - paddingTop(display));
4902 var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight;
4903
4904 var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom);
4905 // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
4906 // forces those lines into the viewport (if possible).
4907 if (viewport && viewport.ensure) {
4908 var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line;
4909 if (ensureFrom < from) {
4910 from = ensureFrom;
4911 to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight);
4912 } else if (Math.min(ensureTo, doc.lastLine()) >= to) {
4913 from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight);
4914 to = ensureTo;
4915 }
4916 }
4917 return {from: from, to: Math.max(to, from + 1)}
4918 }
4919
4920 // Re-align line numbers and gutter marks to compensate for
4921 // horizontal scrolling.
4922 function alignHorizontally(cm) {
4923 var display = cm.display, view = display.view;
4924 if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) { return }
4925 var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;
4926 var gutterW = display.gutters.offsetWidth, left = comp + "px";
4927 for (var i = 0; i < view.length; i++) { if (!view[i].hidden) {
4928 if (cm.options.fixedGutter) {
4929 if (view[i].gutter)
4930 { view[i].gutter.style.left = left; }
4931 if (view[i].gutterBackground)
4932 { view[i].gutterBackground.style.left = left; }
4933 }
4934 var align = view[i].alignable;
4935 if (align) { for (var j = 0; j < align.length; j++)
4936 { align[j].style.left = left; } }
4937 } }
4938 if (cm.options.fixedGutter)
4939 { display.gutters.style.left = (comp + gutterW) + "px"; }
4940 }
4941
4942 // Used to ensure that the line number gutter is still the right
4943 // size for the current document size. Returns true when an update
4944 // is needed.
4945 function maybeUpdateLineNumberWidth(cm) {
4946 if (!cm.options.lineNumbers) { return false }
4947 var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;
4948 if (last.length != display.lineNumChars) {
4949 var test = display.measure.appendChild(elt("div", [elt("div", last)],
4950 "CodeMirror-linenumber CodeMirror-gutter-elt"));
4951 var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
4952 display.lineGutter.style.width = "";
4953 display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1;
4954 display.lineNumWidth = display.lineNumInnerWidth + padding;
4955 display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
4956 display.lineGutter.style.width = display.lineNumWidth + "px";
4957 updateGutterSpace(cm);
4958 return true
4959 }
4960 return false
4961 }
4962
4963 // SCROLLING THINGS INTO VIEW
4964
4965 // If an editor sits on the top or bottom of the window, partially
4966 // scrolled out of view, this ensures that the cursor is visible.
4967 function maybeScrollWindow(cm, rect) {
4968 if (signalDOMEvent(cm, "scrollCursorIntoView")) { return }
4969
4970 var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;
4971 if (rect.top + box.top < 0) { doScroll = true; }
4972 else if (rect.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) { doScroll = false; }
4973 if (doScroll != null && !phantom) {
4974 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;"));
4975 cm.display.lineSpace.appendChild(scrollNode);
4976 scrollNode.scrollIntoView(doScroll);
4977 cm.display.lineSpace.removeChild(scrollNode);
4978 }
4979 }
4980
4981 // Scroll a given position into view (immediately), verifying that
4982 // it actually became visible (as line heights are accurately
4983 // measured, the position of something may 'drift' during drawing).
4984 function scrollPosIntoView(cm, pos, end, margin) {
4985 if (margin == null) { margin = 0; }
4986 var rect;
4987 if (!cm.options.lineWrapping && pos == end) {
4988 // Set pos and end to the cursor positions around the character pos sticks to
4989 // If pos.sticky == "before", that is around pos.ch - 1, otherwise around pos.ch
4990 // If pos == Pos(_, 0, "before"), pos and end are unchanged
4991 pos = pos.ch ? Pos(pos.line, pos.sticky == "before" ? pos.ch - 1 : pos.ch, "after") : pos;
4992 end = pos.sticky == "before" ? Pos(pos.line, pos.ch + 1, "before") : pos;
4993 }
4994 for (var limit = 0; limit < 5; limit++) {
4995 var changed = false;
4996 var coords = cursorCoords(cm, pos);
4997 var endCoords = !end || end == pos ? coords : cursorCoords(cm, end);
4998 rect = {left: Math.min(coords.left, endCoords.left),
4999 top: Math.min(coords.top, endCoords.top) - margin,
5000 right: Math.max(coords.left, endCoords.left),
5001 bottom: Math.max(coords.bottom, endCoords.bottom) + margin};
5002 var scrollPos = calculateScrollPos(cm, rect);
5003 var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
5004 if (scrollPos.scrollTop != null) {
5005 updateScrollTop(cm, scrollPos.scrollTop);
5006 if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true; }
5007 }
5008 if (scrollPos.scrollLeft != null) {
5009 setScrollLeft(cm, scrollPos.scrollLeft);
5010 if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true; }
5011 }
5012 if (!changed) { break }
5013 }
5014 return rect
5015 }
5016
5017 // Scroll a given set of coordinates into view (immediately).
5018 function scrollIntoView(cm, rect) {
5019 var scrollPos = calculateScrollPos(cm, rect);
5020 if (scrollPos.scrollTop != null) { updateScrollTop(cm, scrollPos.scrollTop); }
5021 if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft); }
5022 }
5023
5024 // Calculate a new scroll position needed to scroll the given
5025 // rectangle into view. Returns an object with scrollTop and
5026 // scrollLeft properties. When these are undefined, the
5027 // vertical/horizontal position does not need to be adjusted.
5028 function calculateScrollPos(cm, rect) {
5029 var display = cm.display, snapMargin = textHeight(cm.display);
5030 if (rect.top < 0) { rect.top = 0; }
5031 var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop;
5032 var screen = displayHeight(cm), result = {};
5033 if (rect.bottom - rect.top > screen) { rect.bottom = rect.top + screen; }
5034 var docBottom = cm.doc.height + paddingVert(display);
5035 var atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMargin;
5036 if (rect.top < screentop) {
5037 result.scrollTop = atTop ? 0 : rect.top;
5038 } else if (rect.bottom > screentop + screen) {
5039 var newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen);
5040 if (newTop != screentop) { result.scrollTop = newTop; }
5041 }
5042
5043 var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft;
5044 var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0);
5045 var tooWide = rect.right - rect.left > screenw;
5046 if (tooWide) { rect.right = rect.left + screenw; }
5047 if (rect.left < 10)
5048 { result.scrollLeft = 0; }
5049 else if (rect.left < screenleft)
5050 { result.scrollLeft = Math.max(0, rect.left - (tooWide ? 0 : 10)); }
5051 else if (rect.right > screenw + screenleft - 3)
5052 { result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw; }
5053 return result
5054 }
5055
5056 // Store a relative adjustment to the scroll position in the current
5057 // operation (to be applied when the operation finishes).
5058 function addToScrollTop(cm, top) {
5059 if (top == null) { return }
5060 resolveScrollToPos(cm);
5061 cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top;
5062 }
5063
5064 // Make sure that at the end of the operation the current cursor is
5065 // shown.
5066 function ensureCursorVisible(cm) {
5067 resolveScrollToPos(cm);
5068 var cur = cm.getCursor();
5069 cm.curOp.scrollToPos = {from: cur, to: cur, margin: cm.options.cursorScrollMargin};
5070 }
5071
5072 function scrollToCoords(cm, x, y) {
5073 if (x != null || y != null) { resolveScrollToPos(cm); }
5074 if (x != null) { cm.curOp.scrollLeft = x; }
5075 if (y != null) { cm.curOp.scrollTop = y; }
5076 }
5077
5078 function scrollToRange(cm, range$$1) {
5079 resolveScrollToPos(cm);
5080 cm.curOp.scrollToPos = range$$1;
5081 }
5082
5083 // When an operation has its scrollToPos property set, and another
5084 // scroll action is applied before the end of the operation, this
5085 // 'simulates' scrolling that position into view in a cheap way, so
5086 // that the effect of intermediate scroll commands is not ignored.
5087 function resolveScrollToPos(cm) {
5088 var range$$1 = cm.curOp.scrollToPos;
5089 if (range$$1) {
5090 cm.curOp.scrollToPos = null;
5091 var from = estimateCoords(cm, range$$1.from), to = estimateCoords(cm, range$$1.to);
5092 scrollToCoordsRange(cm, from, to, range$$1.margin);
5093 }
5094 }
5095
5096 function scrollToCoordsRange(cm, from, to, margin) {
5097 var sPos = calculateScrollPos(cm, {
5098 left: Math.min(from.left, to.left),
5099 top: Math.min(from.top, to.top) - margin,
5100 right: Math.max(from.right, to.right),
5101 bottom: Math.max(from.bottom, to.bottom) + margin
5102 });
5103 scrollToCoords(cm, sPos.scrollLeft, sPos.scrollTop);
5104 }
5105
5106 // Sync the scrollable area and scrollbars, ensure the viewport
5107 // covers the visible area.
5108 function updateScrollTop(cm, val) {
5109 if (Math.abs(cm.doc.scrollTop - val) < 2) { return }
5110 if (!gecko) { updateDisplaySimple(cm, {top: val}); }
5111 setScrollTop(cm, val, true);
5112 if (gecko) { updateDisplaySimple(cm); }
5113 startWorker(cm, 100);
5114 }
5115
5116 function setScrollTop(cm, val, forceScroll) {
5117 val = Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val);
5118 if (cm.display.scroller.scrollTop == val && !forceScroll) { return }
5119 cm.doc.scrollTop = val;
5120 cm.display.scrollbars.setScrollTop(val);
5121 if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = val; }
5122 }
5123
5124 // Sync scroller and scrollbar, ensure the gutter elements are
5125 // aligned.
5126 function setScrollLeft(cm, val, isScroller, forceScroll) {
5127 val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth);
5128 if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) { return }
5129 cm.doc.scrollLeft = val;
5130 alignHorizontally(cm);
5131 if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val; }
5132 cm.display.scrollbars.setScrollLeft(val);
5133 }
5134
5135 // SCROLLBARS
5136
5137 // Prepare DOM reads needed to update the scrollbars. Done in one
5138 // shot to minimize update/measure roundtrips.
5139 function measureForScrollbars(cm) {
5140 var d = cm.display, gutterW = d.gutters.offsetWidth;
5141 var docH = Math.round(cm.doc.height + paddingVert(cm.display));
5142 return {
5143 clientHeight: d.scroller.clientHeight,
5144 viewHeight: d.wrapper.clientHeight,
5145 scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,
5146 viewWidth: d.wrapper.clientWidth,
5147 barLeft: cm.options.fixedGutter ? gutterW : 0,
5148 docHeight: docH,
5149 scrollHeight: docH + scrollGap(cm) + d.barHeight,
5150 nativeBarWidth: d.nativeBarWidth,
5151 gutterWidth: gutterW
5152 }
5153 }
5154
5155 var NativeScrollbars = function(place, scroll, cm) {
5156 this.cm = cm;
5157 var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar");
5158 var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar");
5159 vert.tabIndex = horiz.tabIndex = -1;
5160 place(vert); place(horiz);
5161
5162 on(vert, "scroll", function () {
5163 if (vert.clientHeight) { scroll(vert.scrollTop, "vertical"); }
5164 });
5165 on(horiz, "scroll", function () {
5166 if (horiz.clientWidth) { scroll(horiz.scrollLeft, "horizontal"); }
5167 });
5168
5169 this.checkedZeroWidth = false;
5170 // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
5171 if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = "18px"; }
5172 };
5173
5174 NativeScrollbars.prototype.update = function (measure) {
5175 var needsH = measure.scrollWidth > measure.clientWidth + 1;
5176 var needsV = measure.scrollHeight > measure.clientHeight + 1;
5177 var sWidth = measure.nativeBarWidth;
5178
5179 if (needsV) {
5180 this.vert.style.display = "block";
5181 this.vert.style.bottom = needsH ? sWidth + "px" : "0";
5182 var totalHeight = measure.viewHeight - (needsH ? sWidth : 0);
5183 // A bug in IE8 can cause this value to be negative, so guard it.
5184 this.vert.firstChild.style.height =
5185 Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px";
5186 } else {
5187 this.vert.style.display = "";
5188 this.vert.firstChild.style.height = "0";
5189 }
5190
5191 if (needsH) {
5192 this.horiz.style.display = "block";
5193 this.horiz.style.right = needsV ? sWidth + "px" : "0";
5194 this.horiz.style.left = measure.barLeft + "px";
5195 var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0);
5196 this.horiz.firstChild.style.width =
5197 Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + "px";
5198 } else {
5199 this.horiz.style.display = "";
5200 this.horiz.firstChild.style.width = "0";
5201 }
5202
5203 if (!this.checkedZeroWidth && measure.clientHeight > 0) {
5204 if (sWidth == 0) { this.zeroWidthHack(); }
5205 this.checkedZeroWidth = true;
5206 }
5207
5208 return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0}
5209 };
5210
5211 NativeScrollbars.prototype.setScrollLeft = function (pos) {
5212 if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos; }
5213 if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz, "horiz"); }
5214 };
5215
5216 NativeScrollbars.prototype.setScrollTop = function (pos) {
5217 if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos; }
5218 if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert, "vert"); }
5219 };
5220
5221 NativeScrollbars.prototype.zeroWidthHack = function () {
5222 var w = mac && !mac_geMountainLion ? "12px" : "18px";
5223 this.horiz.style.height = this.vert.style.width = w;
5224 this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none";
5225 this.disableHoriz = new Delayed;
5226 this.disableVert = new Delayed;
5227 };
5228
5229 NativeScrollbars.prototype.enableZeroWidthBar = function (bar, delay, type) {
5230 bar.style.pointerEvents = "auto";
5231 function maybeDisable() {
5232 // To find out whether the scrollbar is still visible, we
5233 // check whether the element under the pixel in the bottom
5234 // right corner of the scrollbar box is the scrollbar box
5235 // itself (when the bar is still visible) or its filler child
5236 // (when the bar is hidden). If it is still visible, we keep
5237 // it enabled, if it's hidden, we disable pointer events.
5238 var box = bar.getBoundingClientRect();
5239 var elt$$1 = type == "vert" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2)
5240 : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1);
5241 if (elt$$1 != bar) { bar.style.pointerEvents = "none"; }
5242 else { delay.set(1000, maybeDisable); }
5243 }
5244 delay.set(1000, maybeDisable);
5245 };
5246
5247 NativeScrollbars.prototype.clear = function () {
5248 var parent = this.horiz.parentNode;
5249 parent.removeChild(this.horiz);
5250 parent.removeChild(this.vert);
5251 };
5252
5253 var NullScrollbars = function () {};
5254
5255 NullScrollbars.prototype.update = function () { return {bottom: 0, right: 0} };
5256 NullScrollbars.prototype.setScrollLeft = function () {};
5257 NullScrollbars.prototype.setScrollTop = function () {};
5258 NullScrollbars.prototype.clear = function () {};
5259
5260 function updateScrollbars(cm, measure) {
5261 if (!measure) { measure = measureForScrollbars(cm); }
5262 var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight;
5263 updateScrollbarsInner(cm, measure);
5264 for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) {
5265 if (startWidth != cm.display.barWidth && cm.options.lineWrapping)
5266 { updateHeightsInViewport(cm); }
5267 updateScrollbarsInner(cm, measureForScrollbars(cm));
5268 startWidth = cm.display.barWidth; startHeight = cm.display.barHeight;
5269 }
5270 }
5271
5272 // Re-synchronize the fake scrollbars with the actual size of the
5273 // content.
5274 function updateScrollbarsInner(cm, measure) {
5275 var d = cm.display;
5276 var sizes = d.scrollbars.update(measure);
5277
5278 d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px";
5279 d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px";
5280 d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent";
5281
5282 if (sizes.right && sizes.bottom) {
5283 d.scrollbarFiller.style.display = "block";
5284 d.scrollbarFiller.style.height = sizes.bottom + "px";
5285 d.scrollbarFiller.style.width = sizes.right + "px";
5286 } else { d.scrollbarFiller.style.display = ""; }
5287 if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
5288 d.gutterFiller.style.display = "block";
5289 d.gutterFiller.style.height = sizes.bottom + "px";
5290 d.gutterFiller.style.width = measure.gutterWidth + "px";
5291 } else { d.gutterFiller.style.display = ""; }
5292 }
5293
5294 var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars};
5295
5296 function initScrollbars(cm) {
5297 if (cm.display.scrollbars) {
5298 cm.display.scrollbars.clear();
5299 if (cm.display.scrollbars.addClass)
5300 { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass); }
5301 }
5302
5303 cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) {
5304 cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller);
5305 // Prevent clicks in the scrollbars from killing focus
5306 on(node, "mousedown", function () {
5307 if (cm.state.focused) { setTimeout(function () { return cm.display.input.focus(); }, 0); }
5308 });
5309 node.setAttribute("cm-not-content", "true");
5310 }, function (pos, axis) {
5311 if (axis == "horizontal") { setScrollLeft(cm, pos); }
5312 else { updateScrollTop(cm, pos); }
5313 }, cm);
5314 if (cm.display.scrollbars.addClass)
5315 { addClass(cm.display.wrapper, cm.display.scrollbars.addClass); }
5316 }
5317
5318 // Operations are used to wrap a series of changes to the editor
5319 // state in such a way that each change won't have to update the
5320 // cursor and display (which would be awkward, slow, and
5321 // error-prone). Instead, display updates are batched and then all
5322 // combined and executed at once.
5323
5324 var nextOpId = 0;
5325 // Start a new operation.
5326 function startOperation(cm) {
5327 cm.curOp = {
5328 cm: cm,
5329 viewChanged: false, // Flag that indicates that lines might need to be redrawn
5330 startHeight: cm.doc.height, // Used to detect need to update scrollbar
5331 forceUpdate: false, // Used to force a redraw
5332 updateInput: 0, // Whether to reset the input textarea
5333 typing: false, // Whether this reset should be careful to leave existing text (for compositing)
5334 changeObjs: null, // Accumulated changes, for firing change events
5335 cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on
5336 cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
5337 selectionChanged: false, // Whether the selection needs to be redrawn
5338 updateMaxLine: false, // Set when the widest line needs to be determined anew
5339 scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
5340 scrollToPos: null, // Used to scroll to a specific position
5341 focus: false,
5342 id: ++nextOpId // Unique ID
5343 };
5344 pushOperation(cm.curOp);
5345 }
5346
5347 // Finish an operation, updating the display and signalling delayed events
5348 function endOperation(cm) {
5349 var op = cm.curOp;
5350 if (op) { finishOperation(op, function (group) {
5351 for (var i = 0; i < group.ops.length; i++)
5352 { group.ops[i].cm.curOp = null; }
5353 endOperations(group);
5354 }); }
5355 }
5356
5357 // The DOM updates done when an operation finishes are batched so
5358 // that the minimum number of relayouts are required.
5359 function endOperations(group) {
5360 var ops = group.ops;
5361 for (var i = 0; i < ops.length; i++) // Read DOM
5362 { endOperation_R1(ops[i]); }
5363 for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe)
5364 { endOperation_W1(ops[i$1]); }
5365 for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM
5366 { endOperation_R2(ops[i$2]); }
5367 for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe)
5368 { endOperation_W2(ops[i$3]); }
5369 for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM
5370 { endOperation_finish(ops[i$4]); }
5371 }
5372
5373 function endOperation_R1(op) {
5374 var cm = op.cm, display = cm.display;
5375 maybeClipScrollbars(cm);
5376 if (op.updateMaxLine) { findMaxLine(cm); }
5377
5378 op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||
5379 op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
5380 op.scrollToPos.to.line >= display.viewTo) ||
5381 display.maxLineChanged && cm.options.lineWrapping;
5382 op.update = op.mustUpdate &&
5383 new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate);
5384 }
5385
5386 function endOperation_W1(op) {
5387 op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update);
5388 }
5389
5390 function endOperation_R2(op) {
5391 var cm = op.cm, display = cm.display;
5392 if (op.updatedDisplay) { updateHeightsInViewport(cm); }
5393
5394 op.barMeasure = measureForScrollbars(cm);
5395
5396 // If the max line changed since it was last measured, measure it,
5397 // and ensure the document's width matches it.
5398 // updateDisplay_W2 will use these properties to do the actual resizing
5399 if (display.maxLineChanged && !cm.options.lineWrapping) {
5400 op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3;
5401 cm.display.sizerWidth = op.adjustWidthTo;
5402 op.barMeasure.scrollWidth =
5403 Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth);
5404 op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm));
5405 }
5406
5407 if (op.updatedDisplay || op.selectionChanged)
5408 { op.preparedSelection = display.input.prepareSelection(); }
5409 }
5410
5411 function endOperation_W2(op) {
5412 var cm = op.cm;
5413
5414 if (op.adjustWidthTo != null) {
5415 cm.display.sizer.style.minWidth = op.adjustWidthTo + "px";
5416 if (op.maxScrollLeft < cm.doc.scrollLeft)
5417 { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true); }
5418 cm.display.maxLineChanged = false;
5419 }
5420
5421 var takeFocus = op.focus && op.focus == activeElt();
5422 if (op.preparedSelection)
5423 { cm.display.input.showSelection(op.preparedSelection, takeFocus); }
5424 if (op.updatedDisplay || op.startHeight != cm.doc.height)
5425 { updateScrollbars(cm, op.barMeasure); }
5426 if (op.updatedDisplay)
5427 { setDocumentHeight(cm, op.barMeasure); }
5428
5429 if (op.selectionChanged) { restartBlink(cm); }
5430
5431 if (cm.state.focused && op.updateInput)
5432 { cm.display.input.reset(op.typing); }
5433 if (takeFocus) { ensureFocus(op.cm); }
5434 }
5435
5436 function endOperation_finish(op) {
5437 var cm = op.cm, display = cm.display, doc = cm.doc;
5438
5439 if (op.updatedDisplay) { postUpdateDisplay(cm, op.update); }
5440
5441 // Abort mouse wheel delta measurement, when scrolling explicitly
5442 if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))
5443 { display.wheelStartX = display.wheelStartY = null; }
5444
5445 // Propagate the scroll position to the actual DOM scroller
5446 if (op.scrollTop != null) { setScrollTop(cm, op.scrollTop, op.forceScroll); }
5447
5448 if (op.scrollLeft != null) { setScrollLeft(cm, op.scrollLeft, true, true); }
5449 // If we need to scroll a specific position into view, do so.
5450 if (op.scrollToPos) {
5451 var rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
5452 clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin);
5453 maybeScrollWindow(cm, rect);
5454 }
5455
5456 // Fire events for markers that are hidden/unidden by editing or
5457 // undoing
5458 var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
5459 if (hidden) { for (var i = 0; i < hidden.length; ++i)
5460 { if (!hidden[i].lines.length) { signal(hidden[i], "hide"); } } }
5461 if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1)
5462 { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], "unhide"); } } }
5463
5464 if (display.wrapper.offsetHeight)
5465 { doc.scrollTop = cm.display.scroller.scrollTop; }
5466
5467 // Fire change events, and delayed event handlers
5468 if (op.changeObjs)
5469 { signal(cm, "changes", cm, op.changeObjs); }
5470 if (op.update)
5471 { op.update.finish(); }
5472 }
5473
5474 // Run the given function in an operation
5475 function runInOp(cm, f) {
5476 if (cm.curOp) { return f() }
5477 startOperation(cm);
5478 try { return f() }
5479 finally { endOperation(cm); }
5480 }
5481 // Wraps a function in an operation. Returns the wrapped function.
5482 function operation(cm, f) {
5483 return function() {
5484 if (cm.curOp) { return f.apply(cm, arguments) }
5485 startOperation(cm);
5486 try { return f.apply(cm, arguments) }
5487 finally { endOperation(cm); }
5488 }
5489 }
5490 // Used to add methods to editor and doc instances, wrapping them in
5491 // operations.
5492 function methodOp(f) {
5493 return function() {
5494 if (this.curOp) { return f.apply(this, arguments) }
5495 startOperation(this);
5496 try { return f.apply(this, arguments) }
5497 finally { endOperation(this); }
5498 }
5499 }
5500 function docMethodOp(f) {
5501 return function() {
5502 var cm = this.cm;
5503 if (!cm || cm.curOp) { return f.apply(this, arguments) }
5504 startOperation(cm);
5505 try { return f.apply(this, arguments) }
5506 finally { endOperation(cm); }
5507 }
5508 }
5509
5510 // Updates the display.view data structure for a given change to the
5511 // document. From and to are in pre-change coordinates. Lendiff is
5512 // the amount of lines added or subtracted by the change. This is
5513 // used for changes that span multiple lines, or change the way
5514 // lines are divided into visual lines. regLineChange (below)
5515 // registers single-line changes.
5516 function regChange(cm, from, to, lendiff) {
5517 if (from == null) { from = cm.doc.first; }
5518 if (to == null) { to = cm.doc.first + cm.doc.size; }
5519 if (!lendiff) { lendiff = 0; }
5520
5521 var display = cm.display;
5522 if (lendiff && to < display.viewTo &&
5523 (display.updateLineNumbers == null || display.updateLineNumbers > from))
5524 { display.updateLineNumbers = from; }
5525
5526 cm.curOp.viewChanged = true;
5527
5528 if (from >= display.viewTo) { // Change after
5529 if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)
5530 { resetView(cm); }
5531 } else if (to <= display.viewFrom) { // Change before
5532 if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {
5533 resetView(cm);
5534 } else {
5535 display.viewFrom += lendiff;
5536 display.viewTo += lendiff;
5537 }
5538 } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap
5539 resetView(cm);
5540 } else if (from <= display.viewFrom) { // Top overlap
5541 var cut = viewCuttingPoint(cm, to, to + lendiff, 1);
5542 if (cut) {
5543 display.view = display.view.slice(cut.index);
5544 display.viewFrom = cut.lineN;
5545 display.viewTo += lendiff;
5546 } else {
5547 resetView(cm);
5548 }
5549 } else if (to >= display.viewTo) { // Bottom overlap
5550 var cut$1 = viewCuttingPoint(cm, from, from, -1);
5551 if (cut$1) {
5552 display.view = display.view.slice(0, cut$1.index);
5553 display.viewTo = cut$1.lineN;
5554 } else {
5555 resetView(cm);
5556 }
5557 } else { // Gap in the middle
5558 var cutTop = viewCuttingPoint(cm, from, from, -1);
5559 var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1);
5560 if (cutTop && cutBot) {
5561 display.view = display.view.slice(0, cutTop.index)
5562 .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))
5563 .concat(display.view.slice(cutBot.index));
5564 display.viewTo += lendiff;
5565 } else {
5566 resetView(cm);
5567 }
5568 }
5569
5570 var ext = display.externalMeasured;
5571 if (ext) {
5572 if (to < ext.lineN)
5573 { ext.lineN += lendiff; }
5574 else if (from < ext.lineN + ext.size)
5575 { display.externalMeasured = null; }
5576 }
5577 }
5578
5579 // Register a change to a single line. Type must be one of "text",
5580 // "gutter", "class", "widget"
5581 function regLineChange(cm, line, type) {
5582 cm.curOp.viewChanged = true;
5583 var display = cm.display, ext = cm.display.externalMeasured;
5584 if (ext && line >= ext.lineN && line < ext.lineN + ext.size)
5585 { display.externalMeasured = null; }
5586
5587 if (line < display.viewFrom || line >= display.viewTo) { return }
5588 var lineView = display.view[findViewIndex(cm, line)];
5589 if (lineView.node == null) { return }
5590 var arr = lineView.changes || (lineView.changes = []);
5591 if (indexOf(arr, type) == -1) { arr.push(type); }
5592 }
5593
5594 // Clear the view.
5595 function resetView(cm) {
5596 cm.display.viewFrom = cm.display.viewTo = cm.doc.first;
5597 cm.display.view = [];
5598 cm.display.viewOffset = 0;
5599 }
5600
5601 function viewCuttingPoint(cm, oldN, newN, dir) {
5602 var index = findViewIndex(cm, oldN), diff, view = cm.display.view;
5603 if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)
5604 { return {index: index, lineN: newN} }
5605 var n = cm.display.viewFrom;
5606 for (var i = 0; i < index; i++)
5607 { n += view[i].size; }
5608 if (n != oldN) {
5609 if (dir > 0) {
5610 if (index == view.length - 1) { return null }
5611 diff = (n + view[index].size) - oldN;
5612 index++;
5613 } else {
5614 diff = n - oldN;
5615 }
5616 oldN += diff; newN += diff;
5617 }
5618 while (visualLineNo(cm.doc, newN) != newN) {
5619 if (index == (dir < 0 ? 0 : view.length - 1)) { return null }
5620 newN += dir * view[index - (dir < 0 ? 1 : 0)].size;
5621 index += dir;
5622 }
5623 return {index: index, lineN: newN}
5624 }
5625
5626 // Force the view to cover a given range, adding empty view element
5627 // or clipping off existing ones as needed.
5628 function adjustView(cm, from, to) {
5629 var display = cm.display, view = display.view;
5630 if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {
5631 display.view = buildViewArray(cm, from, to);
5632 display.viewFrom = from;
5633 } else {
5634 if (display.viewFrom > from)
5635 { display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view); }
5636 else if (display.viewFrom < from)
5637 { display.view = display.view.slice(findViewIndex(cm, from)); }
5638 display.viewFrom = from;
5639 if (display.viewTo < to)
5640 { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)); }
5641 else if (display.viewTo > to)
5642 { display.view = display.view.slice(0, findViewIndex(cm, to)); }
5643 }
5644 display.viewTo = to;
5645 }
5646
5647 // Count the number of lines in the view whose DOM representation is
5648 // out of date (or nonexistent).
5649 function countDirtyView(cm) {
5650 var view = cm.display.view, dirty = 0;
5651 for (var i = 0; i < view.length; i++) {
5652 var lineView = view[i];
5653 if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty; }
5654 }
5655 return dirty
5656 }
5657
5658 // HIGHLIGHT WORKER
5659
5660 function startWorker(cm, time) {
5661 if (cm.doc.highlightFrontier < cm.display.viewTo)
5662 { cm.state.highlight.set(time, bind(highlightWorker, cm)); }
5663 }
5664
5665 function highlightWorker(cm) {
5666 var doc = cm.doc;
5667 if (doc.highlightFrontier >= cm.display.viewTo) { return }
5668 var end = +new Date + cm.options.workTime;
5669 var context = getContextBefore(cm, doc.highlightFrontier);
5670 var changedLines = [];
5671
5672 doc.iter(context.line, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function (line) {
5673 if (context.line >= cm.display.viewFrom) { // Visible
5674 var oldStyles = line.styles;
5675 var resetState = line.text.length > cm.options.maxHighlightLength ? copyState(doc.mode, context.state) : null;
5676 var highlighted = highlightLine(cm, line, context, true);
5677 if (resetState) { context.state = resetState; }
5678 line.styles = highlighted.styles;
5679 var oldCls = line.styleClasses, newCls = highlighted.classes;
5680 if (newCls) { line.styleClasses = newCls; }
5681 else if (oldCls) { line.styleClasses = null; }
5682 var ischange = !oldStyles || oldStyles.length != line.styles.length ||
5683 oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass);
5684 for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldStyles[i] != line.styles[i]; }
5685 if (ischange) { changedLines.push(context.line); }
5686 line.stateAfter = context.save();
5687 context.nextLine();
5688 } else {
5689 if (line.text.length <= cm.options.maxHighlightLength)
5690 { processLine(cm, line.text, context); }
5691 line.stateAfter = context.line % 5 == 0 ? context.save() : null;
5692 context.nextLine();
5693 }
5694 if (+new Date > end) {
5695 startWorker(cm, cm.options.workDelay);
5696 return true
5697 }
5698 });
5699 doc.highlightFrontier = context.line;
5700 doc.modeFrontier = Math.max(doc.modeFrontier, context.line);
5701 if (changedLines.length) { runInOp(cm, function () {
5702 for (var i = 0; i < changedLines.length; i++)
5703 { regLineChange(cm, changedLines[i], "text"); }
5704 }); }
5705 }
5706
5707 // DISPLAY DRAWING
5708
5709 var DisplayUpdate = function(cm, viewport, force) {
5710 var display = cm.display;
5711
5712 this.viewport = viewport;
5713 // Store some values that we'll need later (but don't want to force a relayout for)
5714 this.visible = visibleLines(display, cm.doc, viewport);
5715 this.editorIsHidden = !display.wrapper.offsetWidth;
5716 this.wrapperHeight = display.wrapper.clientHeight;
5717 this.wrapperWidth = display.wrapper.clientWidth;
5718 this.oldDisplayWidth = displayWidth(cm);
5719 this.force = force;
5720 this.dims = getDimensions(cm);
5721 this.events = [];
5722 };
5723
5724 DisplayUpdate.prototype.signal = function (emitter, type) {
5725 if (hasHandler(emitter, type))
5726 { this.events.push(arguments); }
5727 };
5728 DisplayUpdate.prototype.finish = function () {
5729 var this$1 = this;
5730
5731 for (var i = 0; i < this.events.length; i++)
5732 { signal.apply(null, this$1.events[i]); }
5733 };
5734
5735 function maybeClipScrollbars(cm) {
5736 var display = cm.display;
5737 if (!display.scrollbarsClipped && display.scroller.offsetWidth) {
5738 display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth;
5739 display.heightForcer.style.height = scrollGap(cm) + "px";
5740 display.sizer.style.marginBottom = -display.nativeBarWidth + "px";
5741 display.sizer.style.borderRightWidth = scrollGap(cm) + "px";
5742 display.scrollbarsClipped = true;
5743 }
5744 }
5745
5746 function selectionSnapshot(cm) {
5747 if (cm.hasFocus()) { return null }
5748 var active = activeElt();
5749 if (!active || !contains(cm.display.lineDiv, active)) { return null }
5750 var result = {activeElt: active};
5751 if (window.getSelection) {
5752 var sel = window.getSelection();
5753 if (sel.anchorNode && sel.extend && contains(cm.display.lineDiv, sel.anchorNode)) {
5754 result.anchorNode = sel.anchorNode;
5755 result.anchorOffset = sel.anchorOffset;
5756 result.focusNode = sel.focusNode;
5757 result.focusOffset = sel.focusOffset;
5758 }
5759 }
5760 return result
5761 }
5762
5763 function restoreSelection(snapshot) {
5764 if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt()) { return }
5765 snapshot.activeElt.focus();
5766 if (snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) {
5767 var sel = window.getSelection(), range$$1 = document.createRange();
5768 range$$1.setEnd(snapshot.anchorNode, snapshot.anchorOffset);
5769 range$$1.collapse(false);
5770 sel.removeAllRanges();
5771 sel.addRange(range$$1);
5772 sel.extend(snapshot.focusNode, snapshot.focusOffset);
5773 }
5774 }
5775
5776 // Does the actual updating of the line display. Bails out
5777 // (returning false) when there is nothing to be done and forced is
5778 // false.
5779 function updateDisplayIfNeeded(cm, update) {
5780 var display = cm.display, doc = cm.doc;
5781
5782 if (update.editorIsHidden) {
5783 resetView(cm);
5784 return false
5785 }
5786
5787 // Bail out if the visible area is already rendered and nothing changed.
5788 if (!update.force &&
5789 update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&
5790 (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&
5791 display.renderedView == display.view && countDirtyView(cm) == 0)
5792 { return false }
5793
5794 if (maybeUpdateLineNumberWidth(cm)) {
5795 resetView(cm);
5796 update.dims = getDimensions(cm);
5797 }
5798
5799 // Compute a suitable new viewport (from & to)
5800 var end = doc.first + doc.size;
5801 var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first);
5802 var to = Math.min(end, update.visible.to + cm.options.viewportMargin);
5803 if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max(doc.first, display.viewFrom); }
5804 if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, display.viewTo); }
5805 if (sawCollapsedSpans) {
5806 from = visualLineNo(cm.doc, from);
5807 to = visualLineEndNo(cm.doc, to);
5808 }
5809
5810 var different = from != display.viewFrom || to != display.viewTo ||
5811 display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth;
5812 adjustView(cm, from, to);
5813
5814 display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom));
5815 // Position the mover div to align with the current scroll position
5816 cm.display.mover.style.top = display.viewOffset + "px";
5817
5818 var toUpdate = countDirtyView(cm);
5819 if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view &&
5820 (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))
5821 { return false }
5822
5823 // For big changes, we hide the enclosing element during the
5824 // update, since that speeds up the operations on most browsers.
5825 var selSnapshot = selectionSnapshot(cm);
5826 if (toUpdate > 4) { display.lineDiv.style.display = "none"; }
5827 patchDisplay(cm, display.updateLineNumbers, update.dims);
5828 if (toUpdate > 4) { display.lineDiv.style.display = ""; }
5829 display.renderedView = display.view;
5830 // There might have been a widget with a focused element that got
5831 // hidden or updated, if so re-focus it.
5832 restoreSelection(selSnapshot);
5833
5834 // Prevent selection and cursors from interfering with the scroll
5835 // width and height.
5836 removeChildren(display.cursorDiv);
5837 removeChildren(display.selectionDiv);
5838 display.gutters.style.height = display.sizer.style.minHeight = 0;
5839
5840 if (different) {
5841 display.lastWrapHeight = update.wrapperHeight;
5842 display.lastWrapWidth = update.wrapperWidth;
5843 startWorker(cm, 400);
5844 }
5845
5846 display.updateLineNumbers = null;
5847
5848 return true
5849 }
5850
5851 function postUpdateDisplay(cm, update) {
5852 var viewport = update.viewport;
5853
5854 for (var first = true;; first = false) {
5855 if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) {
5856 // Clip forced viewport to actual scrollable area.
5857 if (viewport && viewport.top != null)
5858 { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)}; }
5859 // Updated line heights might result in the drawn area not
5860 // actually covering the viewport. Keep looping until it does.
5861 update.visible = visibleLines(cm.display, cm.doc, viewport);
5862 if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)
5863 { break }
5864 }
5865 if (!updateDisplayIfNeeded(cm, update)) { break }
5866 updateHeightsInViewport(cm);
5867 var barMeasure = measureForScrollbars(cm);
5868 updateSelection(cm);
5869 updateScrollbars(cm, barMeasure);
5870 setDocumentHeight(cm, barMeasure);
5871 update.force = false;
5872 }
5873
5874 update.signal(cm, "update", cm);
5875 if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {
5876 update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo);
5877 cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo;
5878 }
5879 }
5880
5881 function updateDisplaySimple(cm, viewport) {
5882 var update = new DisplayUpdate(cm, viewport);
5883 if (updateDisplayIfNeeded(cm, update)) {
5884 updateHeightsInViewport(cm);
5885 postUpdateDisplay(cm, update);
5886 var barMeasure = measureForScrollbars(cm);
5887 updateSelection(cm);
5888 updateScrollbars(cm, barMeasure);
5889 setDocumentHeight(cm, barMeasure);
5890 update.finish();
5891 }
5892 }
5893
5894 // Sync the actual display DOM structure with display.view, removing
5895 // nodes for lines that are no longer in view, and creating the ones
5896 // that are not there yet, and updating the ones that are out of
5897 // date.
5898 function patchDisplay(cm, updateNumbersFrom, dims) {
5899 var display = cm.display, lineNumbers = cm.options.lineNumbers;
5900 var container = display.lineDiv, cur = container.firstChild;
5901
5902 function rm(node) {
5903 var next = node.nextSibling;
5904 // Works around a throw-scroll bug in OS X Webkit
5905 if (webkit && mac && cm.display.currentWheelTarget == node)
5906 { node.style.display = "none"; }
5907 else
5908 { node.parentNode.removeChild(node); }
5909 return next
5910 }
5911
5912 var view = display.view, lineN = display.viewFrom;
5913 // Loop over the elements in the view, syncing cur (the DOM nodes
5914 // in display.lineDiv) with the view as we go.
5915 for (var i = 0; i < view.length; i++) {
5916 var lineView = view[i];
5917 if (lineView.hidden) ; else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet
5918 var node = buildLineElement(cm, lineView, lineN, dims);
5919 container.insertBefore(node, cur);
5920 } else { // Already drawn
5921 while (cur != lineView.node) { cur = rm(cur); }
5922 var updateNumber = lineNumbers && updateNumbersFrom != null &&
5923 updateNumbersFrom <= lineN && lineView.lineNumber;
5924 if (lineView.changes) {
5925 if (indexOf(lineView.changes, "gutter") > -1) { updateNumber = false; }
5926 updateLineForChanges(cm, lineView, lineN, dims);
5927 }
5928 if (updateNumber) {
5929 removeChildren(lineView.lineNumber);
5930 lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN)));
5931 }
5932 cur = lineView.node.nextSibling;
5933 }
5934 lineN += lineView.size;
5935 }
5936 while (cur) { cur = rm(cur); }
5937 }
5938
5939 function updateGutterSpace(cm) {
5940 var width = cm.display.gutters.offsetWidth;
5941 cm.display.sizer.style.marginLeft = width + "px";
5942 }
5943
5944 function setDocumentHeight(cm, measure) {
5945 cm.display.sizer.style.minHeight = measure.docHeight + "px";
5946 cm.display.heightForcer.style.top = measure.docHeight + "px";
5947 cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px";
5948 }
5949
5950 // Rebuild the gutter elements, ensure the margin to the left of the
5951 // code matches their width.
5952 function updateGutters(cm) {
5953 var gutters = cm.display.gutters, specs = cm.options.gutters;
5954 removeChildren(gutters);
5955 var i = 0;
5956 for (; i < specs.length; ++i) {
5957 var gutterClass = specs[i];
5958 var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass));
5959 if (gutterClass == "CodeMirror-linenumbers") {
5960 cm.display.lineGutter = gElt;
5961 gElt.style.width = (cm.display.lineNumWidth || 1) + "px";
5962 }
5963 }
5964 gutters.style.display = i ? "" : "none";
5965 updateGutterSpace(cm);
5966 }
5967
5968 // Make sure the gutters options contains the element
5969 // "CodeMirror-linenumbers" when the lineNumbers option is true.
5970 function setGuttersForLineNumbers(options) {
5971 var found = indexOf(options.gutters, "CodeMirror-linenumbers");
5972 if (found == -1 && options.lineNumbers) {
5973 options.gutters = options.gutters.concat(["CodeMirror-linenumbers"]);
5974 } else if (found > -1 && !options.lineNumbers) {
5975 options.gutters = options.gutters.slice(0);
5976 options.gutters.splice(found, 1);
5977 }
5978 }
5979
5980 // Since the delta values reported on mouse wheel events are
5981 // unstandardized between browsers and even browser versions, and
5982 // generally horribly unpredictable, this code starts by measuring
5983 // the scroll effect that the first few mouse wheel events have,
5984 // and, from that, detects the way it can convert deltas to pixel
5985 // offsets afterwards.
5986 //
5987 // The reason we want to know the amount a wheel event will scroll
5988 // is that it gives us a chance to update the display before the
5989 // actual scrolling happens, reducing flickering.
5990
5991 var wheelSamples = 0, wheelPixelsPerUnit = null;
5992 // Fill in a browser-detected starting value on browsers where we
5993 // know one. These don't have to be accurate -- the result of them
5994 // being wrong would just be a slight flicker on the first wheel
5995 // scroll (if it is large enough).
5996 if (ie) { wheelPixelsPerUnit = -.53; }
5997 else if (gecko) { wheelPixelsPerUnit = 15; }
5998 else if (chrome) { wheelPixelsPerUnit = -.7; }
5999 else if (safari) { wheelPixelsPerUnit = -1/3; }
6000
6001 function wheelEventDelta(e) {
6002 var dx = e.wheelDeltaX, dy = e.wheelDeltaY;
6003 if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail; }
6004 if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail; }
6005 else if (dy == null) { dy = e.wheelDelta; }
6006 return {x: dx, y: dy}
6007 }
6008 function wheelEventPixels(e) {
6009 var delta = wheelEventDelta(e);
6010 delta.x *= wheelPixelsPerUnit;
6011 delta.y *= wheelPixelsPerUnit;
6012 return delta
6013 }
6014
6015 function onScrollWheel(cm, e) {
6016 var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y;
6017
6018 var display = cm.display, scroll = display.scroller;
6019 // Quit if there's nothing to scroll here
6020 var canScrollX = scroll.scrollWidth > scroll.clientWidth;
6021 var canScrollY = scroll.scrollHeight > scroll.clientHeight;
6022 if (!(dx && canScrollX || dy && canScrollY)) { return }
6023
6024 // Webkit browsers on OS X abort momentum scrolls when the target
6025 // of the scroll event is removed from the scrollable element.
6026 // This hack (see related code in patchDisplay) makes sure the
6027 // element is kept around.
6028 if (dy && mac && webkit) {
6029 outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {
6030 for (var i = 0; i < view.length; i++) {
6031 if (view[i].node == cur) {
6032 cm.display.currentWheelTarget = cur;
6033 break outer
6034 }
6035 }
6036 }
6037 }
6038
6039 // On some browsers, horizontal scrolling will cause redraws to
6040 // happen before the gutter has been realigned, causing it to
6041 // wriggle around in a most unseemly way. When we have an
6042 // estimated pixels/delta value, we just handle horizontal
6043 // scrolling entirely here. It'll be slightly off from native, but
6044 // better than glitching out.
6045 if (dx && !gecko && !presto && wheelPixelsPerUnit != null) {
6046 if (dy && canScrollY)
6047 { updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * wheelPixelsPerUnit)); }
6048 setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * wheelPixelsPerUnit));
6049 // Only prevent default scrolling if vertical scrolling is
6050 // actually possible. Otherwise, it causes vertical scroll
6051 // jitter on OSX trackpads when deltaX is small and deltaY
6052 // is large (issue #3579)
6053 if (!dy || (dy && canScrollY))
6054 { e_preventDefault(e); }
6055 display.wheelStartX = null; // Abort measurement, if in progress
6056 return
6057 }
6058
6059 // 'Project' the visible viewport to cover the area that is being
6060 // scrolled into view (if we know enough to estimate it).
6061 if (dy && wheelPixelsPerUnit != null) {
6062 var pixels = dy * wheelPixelsPerUnit;
6063 var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
6064 if (pixels < 0) { top = Math.max(0, top + pixels - 50); }
6065 else { bot = Math.min(cm.doc.height, bot + pixels + 50); }
6066 updateDisplaySimple(cm, {top: top, bottom: bot});
6067 }
6068
6069 if (wheelSamples < 20) {
6070 if (display.wheelStartX == null) {
6071 display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;
6072 display.wheelDX = dx; display.wheelDY = dy;
6073 setTimeout(function () {
6074 if (display.wheelStartX == null) { return }
6075 var movedX = scroll.scrollLeft - display.wheelStartX;
6076 var movedY = scroll.scrollTop - display.wheelStartY;
6077 var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
6078 (movedX && display.wheelDX && movedX / display.wheelDX);
6079 display.wheelStartX = display.wheelStartY = null;
6080 if (!sample) { return }
6081 wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
6082 ++wheelSamples;
6083 }, 200);
6084 } else {
6085 display.wheelDX += dx; display.wheelDY += dy;
6086 }
6087 }
6088 }
6089
6090 // Selection objects are immutable. A new one is created every time
6091 // the selection changes. A selection is one or more non-overlapping
6092 // (and non-touching) ranges, sorted, and an integer that indicates
6093 // which one is the primary selection (the one that's scrolled into
6094 // view, that getCursor returns, etc).
6095 var Selection = function(ranges, primIndex) {
6096 this.ranges = ranges;
6097 this.primIndex = primIndex;
6098 };
6099
6100 Selection.prototype.primary = function () { return this.ranges[this.primIndex] };
6101
6102 Selection.prototype.equals = function (other) {
6103 var this$1 = this;
6104
6105 if (other == this) { return true }
6106 if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false }
6107 for (var i = 0; i < this.ranges.length; i++) {
6108 var here = this$1.ranges[i], there = other.ranges[i];
6109 if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) { return false }
6110 }
6111 return true
6112 };
6113
6114 Selection.prototype.deepCopy = function () {
6115 var this$1 = this;
6116
6117 var out = [];
6118 for (var i = 0; i < this.ranges.length; i++)
6119 { out[i] = new Range(copyPos(this$1.ranges[i].anchor), copyPos(this$1.ranges[i].head)); }
6120 return new Selection(out, this.primIndex)
6121 };
6122
6123 Selection.prototype.somethingSelected = function () {
6124 var this$1 = this;
6125
6126 for (var i = 0; i < this.ranges.length; i++)
6127 { if (!this$1.ranges[i].empty()) { return true } }
6128 return false
6129 };
6130
6131 Selection.prototype.contains = function (pos, end) {
6132 var this$1 = this;
6133
6134 if (!end) { end = pos; }
6135 for (var i = 0; i < this.ranges.length; i++) {
6136 var range = this$1.ranges[i];
6137 if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)
6138 { return i }
6139 }
6140 return -1
6141 };
6142
6143 var Range = function(anchor, head) {
6144 this.anchor = anchor; this.head = head;
6145 };
6146
6147 Range.prototype.from = function () { return minPos(this.anchor, this.head) };
6148 Range.prototype.to = function () { return maxPos(this.anchor, this.head) };
6149 Range.prototype.empty = function () { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch };
6150
6151 // Take an unsorted, potentially overlapping set of ranges, and
6152 // build a selection out of it. 'Consumes' ranges array (modifying
6153 // it).
6154 function normalizeSelection(cm, ranges, primIndex) {
6155 var mayTouch = cm && cm.options.selectionsMayTouch;
6156 var prim = ranges[primIndex];
6157 ranges.sort(function (a, b) { return cmp(a.from(), b.from()); });
6158 primIndex = indexOf(ranges, prim);
6159 for (var i = 1; i < ranges.length; i++) {
6160 var cur = ranges[i], prev = ranges[i - 1];
6161 var diff = cmp(prev.to(), cur.from());
6162 if (mayTouch && !cur.empty() ? diff > 0 : diff >= 0) {
6163 var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to());
6164 var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head;
6165 if (i <= primIndex) { --primIndex; }
6166 ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to));
6167 }
6168 }
6169 return new Selection(ranges, primIndex)
6170 }
6171
6172 function simpleSelection(anchor, head) {
6173 return new Selection([new Range(anchor, head || anchor)], 0)
6174 }
6175
6176 // Compute the position of the end of a change (its 'to' property
6177 // refers to the pre-change end).
6178 function changeEnd(change) {
6179 if (!change.text) { return change.to }
6180 return Pos(change.from.line + change.text.length - 1,
6181 lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0))
6182 }
6183
6184 // Adjust a position to refer to the post-change position of the
6185 // same text, or the end of the change if the change covers it.
6186 function adjustForChange(pos, change) {
6187 if (cmp(pos, change.from) < 0) { return pos }
6188 if (cmp(pos, change.to) <= 0) { return changeEnd(change) }
6189
6190 var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;
6191 if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch; }
6192 return Pos(line, ch)
6193 }
6194
6195 function computeSelAfterChange(doc, change) {
6196 var out = [];
6197 for (var i = 0; i < doc.sel.ranges.length; i++) {
6198 var range = doc.sel.ranges[i];
6199 out.push(new Range(adjustForChange(range.anchor, change),
6200 adjustForChange(range.head, change)));
6201 }
6202 return normalizeSelection(doc.cm, out, doc.sel.primIndex)
6203 }
6204
6205 function offsetPos(pos, old, nw) {
6206 if (pos.line == old.line)
6207 { return Pos(nw.line, pos.ch - old.ch + nw.ch) }
6208 else
6209 { return Pos(nw.line + (pos.line - old.line), pos.ch) }
6210 }
6211
6212 // Used by replaceSelections to allow moving the selection to the
6213 // start or around the replaced test. Hint may be "start" or "around".
6214 function computeReplacedSel(doc, changes, hint) {
6215 var out = [];
6216 var oldPrev = Pos(doc.first, 0), newPrev = oldPrev;
6217 for (var i = 0; i < changes.length; i++) {
6218 var change = changes[i];
6219 var from = offsetPos(change.from, oldPrev, newPrev);
6220 var to = offsetPos(changeEnd(change), oldPrev, newPrev);
6221 oldPrev = change.to;
6222 newPrev = to;
6223 if (hint == "around") {
6224 var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0;
6225 out[i] = new Range(inv ? to : from, inv ? from : to);
6226 } else {
6227 out[i] = new Range(from, from);
6228 }
6229 }
6230 return new Selection(out, doc.sel.primIndex)
6231 }
6232
6233 // Used to get the editor into a consistent state again when options change.
6234
6235 function loadMode(cm) {
6236 cm.doc.mode = getMode(cm.options, cm.doc.modeOption);
6237 resetModeState(cm);
6238 }
6239
6240 function resetModeState(cm) {
6241 cm.doc.iter(function (line) {
6242 if (line.stateAfter) { line.stateAfter = null; }
6243 if (line.styles) { line.styles = null; }
6244 });
6245 cm.doc.modeFrontier = cm.doc.highlightFrontier = cm.doc.first;
6246 startWorker(cm, 100);
6247 cm.state.modeGen++;
6248 if (cm.curOp) { regChange(cm); }
6249 }
6250
6251 // DOCUMENT DATA STRUCTURE
6252
6253 // By default, updates that start and end at the beginning of a line
6254 // are treated specially, in order to make the association of line
6255 // widgets and marker elements with the text behave more intuitive.
6256 function isWholeLineUpdate(doc, change) {
6257 return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" &&
6258 (!doc.cm || doc.cm.options.wholeLineUpdateBefore)
6259 }
6260
6261 // Perform a change on the document data structure.
6262 function updateDoc(doc, change, markedSpans, estimateHeight$$1) {
6263 function spansFor(n) {return markedSpans ? markedSpans[n] : null}
6264 function update(line, text, spans) {
6265 updateLine(line, text, spans, estimateHeight$$1);
6266 signalLater(line, "change", line, change);
6267 }
6268 function linesFor(start, end) {
6269 var result = [];
6270 for (var i = start; i < end; ++i)
6271 { result.push(new Line(text[i], spansFor(i), estimateHeight$$1)); }
6272 return result
6273 }
6274
6275 var from = change.from, to = change.to, text = change.text;
6276 var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
6277 var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;
6278
6279 // Adjust the line structure
6280 if (change.full) {
6281 doc.insert(0, linesFor(0, text.length));
6282 doc.remove(text.length, doc.size - text.length);
6283 } else if (isWholeLineUpdate(doc, change)) {
6284 // This is a whole-line replace. Treated specially to make
6285 // sure line objects move the way they are supposed to.
6286 var added = linesFor(0, text.length - 1);
6287 update(lastLine, lastLine.text, lastSpans);
6288 if (nlines) { doc.remove(from.line, nlines); }
6289 if (added.length) { doc.insert(from.line, added); }
6290 } else if (firstLine == lastLine) {
6291 if (text.length == 1) {
6292 update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
6293 } else {
6294 var added$1 = linesFor(1, text.length - 1);
6295 added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight$$1));
6296 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
6297 doc.insert(from.line + 1, added$1);
6298 }
6299 } else if (text.length == 1) {
6300 update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0));
6301 doc.remove(from.line + 1, nlines);
6302 } else {
6303 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
6304 update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
6305 var added$2 = linesFor(1, text.length - 1);
6306 if (nlines > 1) { doc.remove(from.line + 1, nlines - 1); }
6307 doc.insert(from.line + 1, added$2);
6308 }
6309
6310 signalLater(doc, "change", doc, change);
6311 }
6312
6313 // Call f for all linked documents.
6314 function linkedDocs(doc, f, sharedHistOnly) {
6315 function propagate(doc, skip, sharedHist) {
6316 if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) {
6317 var rel = doc.linked[i];
6318 if (rel.doc == skip) { continue }
6319 var shared = sharedHist && rel.sharedHist;
6320 if (sharedHistOnly && !shared) { continue }
6321 f(rel.doc, shared);
6322 propagate(rel.doc, doc, shared);
6323 } }
6324 }
6325 propagate(doc, null, true);
6326 }
6327
6328 // Attach a document to an editor.
6329 function attachDoc(cm, doc) {
6330 if (doc.cm) { throw new Error("This document is already in use.") }
6331 cm.doc = doc;
6332 doc.cm = cm;
6333 estimateLineHeights(cm);
6334 loadMode(cm);
6335 setDirectionClass(cm);
6336 if (!cm.options.lineWrapping) { findMaxLine(cm); }
6337 cm.options.mode = doc.modeOption;
6338 regChange(cm);
6339 }
6340
6341 function setDirectionClass(cm) {
6342 (cm.doc.direction == "rtl" ? addClass : rmClass)(cm.display.lineDiv, "CodeMirror-rtl");
6343 }
6344
6345 function directionChanged(cm) {
6346 runInOp(cm, function () {
6347 setDirectionClass(cm);
6348 regChange(cm);
6349 });
6350 }
6351
6352 function History(startGen) {
6353 // Arrays of change events and selections. Doing something adds an
6354 // event to done and clears undo. Undoing moves events from done
6355 // to undone, redoing moves them in the other direction.
6356 this.done = []; this.undone = [];
6357 this.undoDepth = Infinity;
6358 // Used to track when changes can be merged into a single undo
6359 // event
6360 this.lastModTime = this.lastSelTime = 0;
6361 this.lastOp = this.lastSelOp = null;
6362 this.lastOrigin = this.lastSelOrigin = null;
6363 // Used by the isClean() method
6364 this.generation = this.maxGeneration = startGen || 1;
6365 }
6366
6367 // Create a history change event from an updateDoc-style change
6368 // object.
6369 function historyChangeFromChange(doc, change) {
6370 var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
6371 attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
6372 linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); }, true);
6373 return histChange
6374 }
6375
6376 // Pop all selection events off the end of a history array. Stop at
6377 // a change event.
6378 function clearSelectionEvents(array) {
6379 while (array.length) {
6380 var last = lst(array);
6381 if (last.ranges) { array.pop(); }
6382 else { break }
6383 }
6384 }
6385
6386 // Find the top change event in the history. Pop off selection
6387 // events that are in the way.
6388 function lastChangeEvent(hist, force) {
6389 if (force) {
6390 clearSelectionEvents(hist.done);
6391 return lst(hist.done)
6392 } else if (hist.done.length && !lst(hist.done).ranges) {
6393 return lst(hist.done)
6394 } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) {
6395 hist.done.pop();
6396 return lst(hist.done)
6397 }
6398 }
6399
6400 // Register a change in the history. Merges changes that are within
6401 // a single operation, or are close together with an origin that
6402 // allows merging (starting with "+") into a single event.
6403 function addChangeToHistory(doc, change, selAfter, opId) {
6404 var hist = doc.history;
6405 hist.undone.length = 0;
6406 var time = +new Date, cur;
6407 var last;
6408
6409 if ((hist.lastOp == opId ||
6410 hist.lastOrigin == change.origin && change.origin &&
6411 ((change.origin.charAt(0) == "+" && hist.lastModTime > time - (doc.cm ? doc.cm.options.historyEventDelay : 500)) ||
6412 change.origin.charAt(0) == "*")) &&
6413 (cur = lastChangeEvent(hist, hist.lastOp == opId))) {
6414 // Merge this change into the last event
6415 last = lst(cur.changes);
6416 if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) {
6417 // Optimized case for simple insertion -- don't want to add
6418 // new changesets for every character typed
6419 last.to = changeEnd(change);
6420 } else {
6421 // Add new sub-event
6422 cur.changes.push(historyChangeFromChange(doc, change));
6423 }
6424 } else {
6425 // Can not be merged, start a new event.
6426 var before = lst(hist.done);
6427 if (!before || !before.ranges)
6428 { pushSelectionToHistory(doc.sel, hist.done); }
6429 cur = {changes: [historyChangeFromChange(doc, change)],
6430 generation: hist.generation};
6431 hist.done.push(cur);
6432 while (hist.done.length > hist.undoDepth) {
6433 hist.done.shift();
6434 if (!hist.done[0].ranges) { hist.done.shift(); }
6435 }
6436 }
6437 hist.done.push(selAfter);
6438 hist.generation = ++hist.maxGeneration;
6439 hist.lastModTime = hist.lastSelTime = time;
6440 hist.lastOp = hist.lastSelOp = opId;
6441 hist.lastOrigin = hist.lastSelOrigin = change.origin;
6442
6443 if (!last) { signal(doc, "historyAdded"); }
6444 }
6445
6446 function selectionEventCanBeMerged(doc, origin, prev, sel) {
6447 var ch = origin.charAt(0);
6448 return ch == "*" ||
6449 ch == "+" &&
6450 prev.ranges.length == sel.ranges.length &&
6451 prev.somethingSelected() == sel.somethingSelected() &&
6452 new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500)
6453 }
6454
6455 // Called whenever the selection changes, sets the new selection as
6456 // the pending selection in the history, and pushes the old pending
6457 // selection into the 'done' array when it was significantly
6458 // different (in number of selected ranges, emptiness, or time).
6459 function addSelectionToHistory(doc, sel, opId, options) {
6460 var hist = doc.history, origin = options && options.origin;
6461
6462 // A new event is started when the previous origin does not match
6463 // the current, or the origins don't allow matching. Origins
6464 // starting with * are always merged, those starting with + are
6465 // merged when similar and close together in time.
6466 if (opId == hist.lastSelOp ||
6467 (origin && hist.lastSelOrigin == origin &&
6468 (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||
6469 selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))
6470 { hist.done[hist.done.length - 1] = sel; }
6471 else
6472 { pushSelectionToHistory(sel, hist.done); }
6473
6474 hist.lastSelTime = +new Date;
6475 hist.lastSelOrigin = origin;
6476 hist.lastSelOp = opId;
6477 if (options && options.clearRedo !== false)
6478 { clearSelectionEvents(hist.undone); }
6479 }
6480
6481 function pushSelectionToHistory(sel, dest) {
6482 var top = lst(dest);
6483 if (!(top && top.ranges && top.equals(sel)))
6484 { dest.push(sel); }
6485 }
6486
6487 // Used to store marked span information in the history.
6488 function attachLocalSpans(doc, change, from, to) {
6489 var existing = change["spans_" + doc.id], n = 0;
6490 doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function (line) {
6491 if (line.markedSpans)
6492 { (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans; }
6493 ++n;
6494 });
6495 }
6496
6497 // When un/re-doing restores text containing marked spans, those
6498 // that have been explicitly cleared should not be restored.
6499 function removeClearedSpans(spans) {
6500 if (!spans) { return null }
6501 var out;
6502 for (var i = 0; i < spans.length; ++i) {
6503 if (spans[i].marker.explicitlyCleared) { if (!out) { out = spans.slice(0, i); } }
6504 else if (out) { out.push(spans[i]); }
6505 }
6506 return !out ? spans : out.length ? out : null
6507 }
6508
6509 // Retrieve and filter the old marked spans stored in a change event.
6510 function getOldSpans(doc, change) {
6511 var found = change["spans_" + doc.id];
6512 if (!found) { return null }
6513 var nw = [];
6514 for (var i = 0; i < change.text.length; ++i)
6515 { nw.push(removeClearedSpans(found[i])); }
6516 return nw
6517 }
6518
6519 // Used for un/re-doing changes from the history. Combines the
6520 // result of computing the existing spans with the set of spans that
6521 // existed in the history (so that deleting around a span and then
6522 // undoing brings back the span).
6523 function mergeOldSpans(doc, change) {
6524 var old = getOldSpans(doc, change);
6525 var stretched = stretchSpansOverChange(doc, change);
6526 if (!old) { return stretched }
6527 if (!stretched) { return old }
6528
6529 for (var i = 0; i < old.length; ++i) {
6530 var oldCur = old[i], stretchCur = stretched[i];
6531 if (oldCur && stretchCur) {
6532 spans: for (var j = 0; j < stretchCur.length; ++j) {
6533 var span = stretchCur[j];
6534 for (var k = 0; k < oldCur.length; ++k)
6535 { if (oldCur[k].marker == span.marker) { continue spans } }
6536 oldCur.push(span);
6537 }
6538 } else if (stretchCur) {
6539 old[i] = stretchCur;
6540 }
6541 }
6542 return old
6543 }
6544
6545 // Used both to provide a JSON-safe object in .getHistory, and, when
6546 // detaching a document, to split the history in two
6547 function copyHistoryArray(events, newGroup, instantiateSel) {
6548 var copy = [];
6549 for (var i = 0; i < events.length; ++i) {
6550 var event = events[i];
6551 if (event.ranges) {
6552 copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event);
6553 continue
6554 }
6555 var changes = event.changes, newChanges = [];
6556 copy.push({changes: newChanges});
6557 for (var j = 0; j < changes.length; ++j) {
6558 var change = changes[j], m = (void 0);
6559 newChanges.push({from: change.from, to: change.to, text: change.text});
6560 if (newGroup) { for (var prop in change) { if (m = prop.match(/^spans_(\d+)$/)) {
6561 if (indexOf(newGroup, Number(m[1])) > -1) {
6562 lst(newChanges)[prop] = change[prop];
6563 delete change[prop];
6564 }
6565 } } }
6566 }
6567 }
6568 return copy
6569 }
6570
6571 // The 'scroll' parameter given to many of these indicated whether
6572 // the new cursor position should be scrolled into view after
6573 // modifying the selection.
6574
6575 // If shift is held or the extend flag is set, extends a range to
6576 // include a given position (and optionally a second position).
6577 // Otherwise, simply returns the range between the given positions.
6578 // Used for cursor motion and such.
6579 function extendRange(range, head, other, extend) {
6580 if (extend) {
6581 var anchor = range.anchor;
6582 if (other) {
6583 var posBefore = cmp(head, anchor) < 0;
6584 if (posBefore != (cmp(other, anchor) < 0)) {
6585 anchor = head;
6586 head = other;
6587 } else if (posBefore != (cmp(head, other) < 0)) {
6588 head = other;
6589 }
6590 }
6591 return new Range(anchor, head)
6592 } else {
6593 return new Range(other || head, head)
6594 }
6595 }
6596
6597 // Extend the primary selection range, discard the rest.
6598 function extendSelection(doc, head, other, options, extend) {
6599 if (extend == null) { extend = doc.cm && (doc.cm.display.shift || doc.extend); }
6600 setSelection(doc, new Selection([extendRange(doc.sel.primary(), head, other, extend)], 0), options);
6601 }
6602
6603 // Extend all selections (pos is an array of selections with length
6604 // equal the number of selections)
6605 function extendSelections(doc, heads, options) {
6606 var out = [];
6607 var extend = doc.cm && (doc.cm.display.shift || doc.extend);
6608 for (var i = 0; i < doc.sel.ranges.length; i++)
6609 { out[i] = extendRange(doc.sel.ranges[i], heads[i], null, extend); }
6610 var newSel = normalizeSelection(doc.cm, out, doc.sel.primIndex);
6611 setSelection(doc, newSel, options);
6612 }
6613
6614 // Updates a single range in the selection.
6615 function replaceOneSelection(doc, i, range, options) {
6616 var ranges = doc.sel.ranges.slice(0);
6617 ranges[i] = range;
6618 setSelection(doc, normalizeSelection(doc.cm, ranges, doc.sel.primIndex), options);
6619 }
6620
6621 // Reset the selection to a single range.
6622 function setSimpleSelection(doc, anchor, head, options) {
6623 setSelection(doc, simpleSelection(anchor, head), options);
6624 }
6625
6626 // Give beforeSelectionChange handlers a change to influence a
6627 // selection update.
6628 function filterSelectionChange(doc, sel, options) {
6629 var obj = {
6630 ranges: sel.ranges,
6631 update: function(ranges) {
6632 var this$1 = this;
6633
6634 this.ranges = [];
6635 for (var i = 0; i < ranges.length; i++)
6636 { this$1.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),
6637 clipPos(doc, ranges[i].head)); }
6638 },
6639 origin: options && options.origin
6640 };
6641 signal(doc, "beforeSelectionChange", doc, obj);
6642 if (doc.cm) { signal(doc.cm, "beforeSelectionChange", doc.cm, obj); }
6643 if (obj.ranges != sel.ranges) { return normalizeSelection(doc.cm, obj.ranges, obj.ranges.length - 1) }
6644 else { return sel }
6645 }
6646
6647 function setSelectionReplaceHistory(doc, sel, options) {
6648 var done = doc.history.done, last = lst(done);
6649 if (last && last.ranges) {
6650 done[done.length - 1] = sel;
6651 setSelectionNoUndo(doc, sel, options);
6652 } else {
6653 setSelection(doc, sel, options);
6654 }
6655 }
6656
6657 // Set a new selection.
6658 function setSelection(doc, sel, options) {
6659 setSelectionNoUndo(doc, sel, options);
6660 addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options);
6661 }
6662
6663 function setSelectionNoUndo(doc, sel, options) {
6664 if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange"))
6665 { sel = filterSelectionChange(doc, sel, options); }
6666
6667 var bias = options && options.bias ||
6668 (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1);
6669 setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true));
6670
6671 if (!(options && options.scroll === false) && doc.cm)
6672 { ensureCursorVisible(doc.cm); }
6673 }
6674
6675 function setSelectionInner(doc, sel) {
6676 if (sel.equals(doc.sel)) { return }
6677
6678 doc.sel = sel;
6679
6680 if (doc.cm) {
6681 doc.cm.curOp.updateInput = 1;
6682 doc.cm.curOp.selectionChanged = true;
6683 signalCursorActivity(doc.cm);
6684 }
6685 signalLater(doc, "cursorActivity", doc);
6686 }
6687
6688 // Verify that the selection does not partially select any atomic
6689 // marked ranges.
6690 function reCheckSelection(doc) {
6691 setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false));
6692 }
6693
6694 // Return a selection that does not partially select any atomic
6695 // ranges.
6696 function skipAtomicInSelection(doc, sel, bias, mayClear) {
6697 var out;
6698 for (var i = 0; i < sel.ranges.length; i++) {
6699 var range = sel.ranges[i];
6700 var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i];
6701 var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear);
6702 var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear);
6703 if (out || newAnchor != range.anchor || newHead != range.head) {
6704 if (!out) { out = sel.ranges.slice(0, i); }
6705 out[i] = new Range(newAnchor, newHead);
6706 }
6707 }
6708 return out ? normalizeSelection(doc.cm, out, sel.primIndex) : sel
6709 }
6710
6711 function skipAtomicInner(doc, pos, oldPos, dir, mayClear) {
6712 var line = getLine(doc, pos.line);
6713 if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
6714 var sp = line.markedSpans[i], m = sp.marker;
6715 if ((sp.from == null || (m.inclusiveLeft ? sp.from <= pos.ch : sp.from < pos.ch)) &&
6716 (sp.to == null || (m.inclusiveRight ? sp.to >= pos.ch : sp.to > pos.ch))) {
6717 if (mayClear) {
6718 signal(m, "beforeCursorEnter");
6719 if (m.explicitlyCleared) {
6720 if (!line.markedSpans) { break }
6721 else {--i; continue}
6722 }
6723 }
6724 if (!m.atomic) { continue }
6725
6726 if (oldPos) {
6727 var near = m.find(dir < 0 ? 1 : -1), diff = (void 0);
6728 if (dir < 0 ? m.inclusiveRight : m.inclusiveLeft)
6729 { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null); }
6730 if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0))
6731 { return skipAtomicInner(doc, near, pos, dir, mayClear) }
6732 }
6733
6734 var far = m.find(dir < 0 ? -1 : 1);
6735 if (dir < 0 ? m.inclusiveLeft : m.inclusiveRight)
6736 { far = movePos(doc, far, dir, far.line == pos.line ? line : null); }
6737 return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null
6738 }
6739 } }
6740 return pos
6741 }
6742
6743 // Ensure a given position is not inside an atomic range.
6744 function skipAtomic(doc, pos, oldPos, bias, mayClear) {
6745 var dir = bias || 1;
6746 var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) ||
6747 (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) ||
6748 skipAtomicInner(doc, pos, oldPos, -dir, mayClear) ||
6749 (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true));
6750 if (!found) {
6751 doc.cantEdit = true;
6752 return Pos(doc.first, 0)
6753 }
6754 return found
6755 }
6756
6757 function movePos(doc, pos, dir, line) {
6758 if (dir < 0 && pos.ch == 0) {
6759 if (pos.line > doc.first) { return clipPos(doc, Pos(pos.line - 1)) }
6760 else { return null }
6761 } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) {
6762 if (pos.line < doc.first + doc.size - 1) { return Pos(pos.line + 1, 0) }
6763 else { return null }
6764 } else {
6765 return new Pos(pos.line, pos.ch + dir)
6766 }
6767 }
6768
6769 function selectAll(cm) {
6770 cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll);
6771 }
6772
6773 // UPDATING
6774
6775 // Allow "beforeChange" event handlers to influence a change
6776 function filterChange(doc, change, update) {
6777 var obj = {
6778 canceled: false,
6779 from: change.from,
6780 to: change.to,
6781 text: change.text,
6782 origin: change.origin,
6783 cancel: function () { return obj.canceled = true; }
6784 };
6785 if (update) { obj.update = function (from, to, text, origin) {
6786 if (from) { obj.from = clipPos(doc, from); }
6787 if (to) { obj.to = clipPos(doc, to); }
6788 if (text) { obj.text = text; }
6789 if (origin !== undefined) { obj.origin = origin; }
6790 }; }
6791 signal(doc, "beforeChange", doc, obj);
6792 if (doc.cm) { signal(doc.cm, "beforeChange", doc.cm, obj); }
6793
6794 if (obj.canceled) {
6795 if (doc.cm) { doc.cm.curOp.updateInput = 2; }
6796 return null
6797 }
6798 return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin}
6799 }
6800
6801 // Apply a change to a document, and add it to the document's
6802 // history, and propagating it to all linked documents.
6803 function makeChange(doc, change, ignoreReadOnly) {
6804 if (doc.cm) {
6805 if (!doc.cm.curOp) { return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) }
6806 if (doc.cm.state.suppressEdits) { return }
6807 }
6808
6809 if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
6810 change = filterChange(doc, change, true);
6811 if (!change) { return }
6812 }
6813
6814 // Possibly split or suppress the update based on the presence
6815 // of read-only spans in its range.
6816 var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);
6817 if (split) {
6818 for (var i = split.length - 1; i >= 0; --i)
6819 { makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text, origin: change.origin}); }
6820 } else {
6821 makeChangeInner(doc, change);
6822 }
6823 }
6824
6825 function makeChangeInner(doc, change) {
6826 if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) { return }
6827 var selAfter = computeSelAfterChange(doc, change);
6828 addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);
6829
6830 makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));
6831 var rebased = [];
6832
6833 linkedDocs(doc, function (doc, sharedHist) {
6834 if (!sharedHist && indexOf(rebased, doc.history) == -1) {
6835 rebaseHist(doc.history, change);
6836 rebased.push(doc.history);
6837 }
6838 makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change));
6839 });
6840 }
6841
6842 // Revert a change stored in a document's history.
6843 function makeChangeFromHistory(doc, type, allowSelectionOnly) {
6844 var suppress = doc.cm && doc.cm.state.suppressEdits;
6845 if (suppress && !allowSelectionOnly) { return }
6846
6847 var hist = doc.history, event, selAfter = doc.sel;
6848 var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done;
6849
6850 // Verify that there is a useable event (so that ctrl-z won't
6851 // needlessly clear selection events)
6852 var i = 0;
6853 for (; i < source.length; i++) {
6854 event = source[i];
6855 if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges)
6856 { break }
6857 }
6858 if (i == source.length) { return }
6859 hist.lastOrigin = hist.lastSelOrigin = null;
6860
6861 for (;;) {
6862 event = source.pop();
6863 if (event.ranges) {
6864 pushSelectionToHistory(event, dest);
6865 if (allowSelectionOnly && !event.equals(doc.sel)) {
6866 setSelection(doc, event, {clearRedo: false});
6867 return
6868 }
6869 selAfter = event;
6870 } else if (suppress) {
6871 source.push(event);
6872 return
6873 } else { break }
6874 }
6875
6876 // Build up a reverse change object to add to the opposite history
6877 // stack (redo when undoing, and vice versa).
6878 var antiChanges = [];
6879 pushSelectionToHistory(selAfter, dest);
6880 dest.push({changes: antiChanges, generation: hist.generation});
6881 hist.generation = event.generation || ++hist.maxGeneration;
6882
6883 var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange");
6884
6885 var loop = function ( i ) {
6886 var change = event.changes[i];
6887 change.origin = type;
6888 if (filter && !filterChange(doc, change, false)) {
6889 source.length = 0;
6890 return {}
6891 }
6892
6893 antiChanges.push(historyChangeFromChange(doc, change));
6894
6895 var after = i ? computeSelAfterChange(doc, change) : lst(source);
6896 makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
6897 if (!i && doc.cm) { doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}); }
6898 var rebased = [];
6899
6900 // Propagate to the linked documents
6901 linkedDocs(doc, function (doc, sharedHist) {
6902 if (!sharedHist && indexOf(rebased, doc.history) == -1) {
6903 rebaseHist(doc.history, change);
6904 rebased.push(doc.history);
6905 }
6906 makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change));
6907 });
6908 };
6909
6910 for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) {
6911 var returned = loop( i$1 );
6912
6913 if ( returned ) return returned.v;
6914 }
6915 }
6916
6917 // Sub-views need their line numbers shifted when text is added
6918 // above or below them in the parent document.
6919 function shiftDoc(doc, distance) {
6920 if (distance == 0) { return }
6921 doc.first += distance;
6922 doc.sel = new Selection(map(doc.sel.ranges, function (range) { return new Range(
6923 Pos(range.anchor.line + distance, range.anchor.ch),
6924 Pos(range.head.line + distance, range.head.ch)
6925 ); }), doc.sel.primIndex);
6926 if (doc.cm) {
6927 regChange(doc.cm, doc.first, doc.first - distance, distance);
6928 for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++)
6929 { regLineChange(doc.cm, l, "gutter"); }
6930 }
6931 }
6932
6933 // More lower-level change function, handling only a single document
6934 // (not linked ones).
6935 function makeChangeSingleDoc(doc, change, selAfter, spans) {
6936 if (doc.cm && !doc.cm.curOp)
6937 { return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) }
6938
6939 if (change.to.line < doc.first) {
6940 shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line));
6941 return
6942 }
6943 if (change.from.line > doc.lastLine()) { return }
6944
6945 // Clip the change to the size of this doc
6946 if (change.from.line < doc.first) {
6947 var shift = change.text.length - 1 - (doc.first - change.from.line);
6948 shiftDoc(doc, shift);
6949 change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
6950 text: [lst(change.text)], origin: change.origin};
6951 }
6952 var last = doc.lastLine();
6953 if (change.to.line > last) {
6954 change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
6955 text: [change.text[0]], origin: change.origin};
6956 }
6957
6958 change.removed = getBetween(doc, change.from, change.to);
6959
6960 if (!selAfter) { selAfter = computeSelAfterChange(doc, change); }
6961 if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans); }
6962 else { updateDoc(doc, change, spans); }
6963 setSelectionNoUndo(doc, selAfter, sel_dontScroll);
6964 }
6965
6966 // Handle the interaction of a change to a document with the editor
6967 // that this document is part of.
6968 function makeChangeSingleDocInEditor(cm, change, spans) {
6969 var doc = cm.doc, display = cm.display, from = change.from, to = change.to;
6970
6971 var recomputeMaxLength = false, checkWidthStart = from.line;
6972 if (!cm.options.lineWrapping) {
6973 checkWidthStart = lineNo(visualLine(getLine(doc, from.line)));
6974 doc.iter(checkWidthStart, to.line + 1, function (line) {
6975 if (line == display.maxLine) {
6976 recomputeMaxLength = true;
6977 return true
6978 }
6979 });
6980 }
6981
6982 if (doc.sel.contains(change.from, change.to) > -1)
6983 { signalCursorActivity(cm); }
6984
6985 updateDoc(doc, change, spans, estimateHeight(cm));
6986
6987 if (!cm.options.lineWrapping) {
6988 doc.iter(checkWidthStart, from.line + change.text.length, function (line) {
6989 var len = lineLength(line);
6990 if (len > display.maxLineLength) {
6991 display.maxLine = line;
6992 display.maxLineLength = len;
6993 display.maxLineChanged = true;
6994 recomputeMaxLength = false;
6995 }
6996 });
6997 if (recomputeMaxLength) { cm.curOp.updateMaxLine = true; }
6998 }
6999
7000 retreatFrontier(doc, from.line);
7001 startWorker(cm, 400);
7002
7003 var lendiff = change.text.length - (to.line - from.line) - 1;
7004 // Remember that these lines changed, for updating the display
7005 if (change.full)
7006 { regChange(cm); }
7007 else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change))
7008 { regLineChange(cm, from.line, "text"); }
7009 else
7010 { regChange(cm, from.line, to.line + 1, lendiff); }
7011
7012 var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change");
7013 if (changeHandler || changesHandler) {
7014 var obj = {
7015 from: from, to: to,
7016 text: change.text,
7017 removed: change.removed,
7018 origin: change.origin
7019 };
7020 if (changeHandler) { signalLater(cm, "change", cm, obj); }
7021 if (changesHandler) { (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj); }
7022 }
7023 cm.display.selForContextMenu = null;
7024 }
7025
7026 function replaceRange(doc, code, from, to, origin) {
7027 var assign;
7028
7029 if (!to) { to = from; }
7030 if (cmp(to, from) < 0) { (assign = [to, from], from = assign[0], to = assign[1]); }
7031 if (typeof code == "string") { code = doc.splitLines(code); }
7032 makeChange(doc, {from: from, to: to, text: code, origin: origin});
7033 }
7034
7035 // Rebasing/resetting history to deal with externally-sourced changes
7036
7037 function rebaseHistSelSingle(pos, from, to, diff) {
7038 if (to < pos.line) {
7039 pos.line += diff;
7040 } else if (from < pos.line) {
7041 pos.line = from;
7042 pos.ch = 0;
7043 }
7044 }
7045
7046 // Tries to rebase an array of history events given a change in the
7047 // document. If the change touches the same lines as the event, the
7048 // event, and everything 'behind' it, is discarded. If the change is
7049 // before the event, the event's positions are updated. Uses a
7050 // copy-on-write scheme for the positions, to avoid having to
7051 // reallocate them all on every rebase, but also avoid problems with
7052 // shared position objects being unsafely updated.
7053 function rebaseHistArray(array, from, to, diff) {
7054 for (var i = 0; i < array.length; ++i) {
7055 var sub = array[i], ok = true;
7056 if (sub.ranges) {
7057 if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; }
7058 for (var j = 0; j < sub.ranges.length; j++) {
7059 rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff);
7060 rebaseHistSelSingle(sub.ranges[j].head, from, to, diff);
7061 }
7062 continue
7063 }
7064 for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) {
7065 var cur = sub.changes[j$1];
7066 if (to < cur.from.line) {
7067 cur.from = Pos(cur.from.line + diff, cur.from.ch);
7068 cur.to = Pos(cur.to.line + diff, cur.to.ch);
7069 } else if (from <= cur.to.line) {
7070 ok = false;
7071 break
7072 }
7073 }
7074 if (!ok) {
7075 array.splice(0, i + 1);
7076 i = 0;
7077 }
7078 }
7079 }
7080
7081 function rebaseHist(hist, change) {
7082 var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1;
7083 rebaseHistArray(hist.done, from, to, diff);
7084 rebaseHistArray(hist.undone, from, to, diff);
7085 }
7086
7087 // Utility for applying a change to a line by handle or number,
7088 // returning the number and optionally registering the line as
7089 // changed.
7090 function changeLine(doc, handle, changeType, op) {
7091 var no = handle, line = handle;
7092 if (typeof handle == "number") { line = getLine(doc, clipLine(doc, handle)); }
7093 else { no = lineNo(handle); }
7094 if (no == null) { return null }
7095 if (op(line, no) && doc.cm) { regLineChange(doc.cm, no, changeType); }
7096 return line
7097 }
7098
7099 // The document is represented as a BTree consisting of leaves, with
7100 // chunk of lines in them, and branches, with up to ten leaves or
7101 // other branch nodes below them. The top node is always a branch
7102 // node, and is the document object itself (meaning it has
7103 // additional methods and properties).
7104 //
7105 // All nodes have parent links. The tree is used both to go from
7106 // line numbers to line objects, and to go from objects to numbers.
7107 // It also indexes by height, and is used to convert between height
7108 // and line object, and to find the total height of the document.
7109 //
7110 // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html
7111
7112 function LeafChunk(lines) {
7113 var this$1 = this;
7114
7115 this.lines = lines;
7116 this.parent = null;
7117 var height = 0;
7118 for (var i = 0; i < lines.length; ++i) {
7119 lines[i].parent = this$1;
7120 height += lines[i].height;
7121 }
7122 this.height = height;
7123 }
7124
7125 LeafChunk.prototype = {
7126 chunkSize: function() { return this.lines.length },
7127
7128 // Remove the n lines at offset 'at'.
7129 removeInner: function(at, n) {
7130 var this$1 = this;
7131
7132 for (var i = at, e = at + n; i < e; ++i) {
7133 var line = this$1.lines[i];
7134 this$1.height -= line.height;
7135 cleanUpLine(line);
7136 signalLater(line, "delete");
7137 }
7138 this.lines.splice(at, n);
7139 },
7140
7141 // Helper used to collapse a small branch into a single leaf.
7142 collapse: function(lines) {
7143 lines.push.apply(lines, this.lines);
7144 },
7145
7146 // Insert the given array of lines at offset 'at', count them as
7147 // having the given height.
7148 insertInner: function(at, lines, height) {
7149 var this$1 = this;
7150
7151 this.height += height;
7152 this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
7153 for (var i = 0; i < lines.length; ++i) { lines[i].parent = this$1; }
7154 },
7155
7156 // Used to iterate over a part of the tree.
7157 iterN: function(at, n, op) {
7158 var this$1 = this;
7159
7160 for (var e = at + n; at < e; ++at)
7161 { if (op(this$1.lines[at])) { return true } }
7162 }
7163 };
7164
7165 function BranchChunk(children) {
7166 var this$1 = this;
7167
7168 this.children = children;
7169 var size = 0, height = 0;
7170 for (var i = 0; i < children.length; ++i) {
7171 var ch = children[i];
7172 size += ch.chunkSize(); height += ch.height;
7173 ch.parent = this$1;
7174 }
7175 this.size = size;
7176 this.height = height;
7177 this.parent = null;
7178 }
7179
7180 BranchChunk.prototype = {
7181 chunkSize: function() { return this.size },
7182
7183 removeInner: function(at, n) {
7184 var this$1 = this;
7185
7186 this.size -= n;
7187 for (var i = 0; i < this.children.length; ++i) {
7188 var child = this$1.children[i], sz = child.chunkSize();
7189 if (at < sz) {
7190 var rm = Math.min(n, sz - at), oldHeight = child.height;
7191 child.removeInner(at, rm);
7192 this$1.height -= oldHeight - child.height;
7193 if (sz == rm) { this$1.children.splice(i--, 1); child.parent = null; }
7194 if ((n -= rm) == 0) { break }
7195 at = 0;
7196 } else { at -= sz; }
7197 }
7198 // If the result is smaller than 25 lines, ensure that it is a
7199 // single leaf node.
7200 if (this.size - n < 25 &&
7201 (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) {
7202 var lines = [];
7203 this.collapse(lines);
7204 this.children = [new LeafChunk(lines)];
7205 this.children[0].parent = this;
7206 }
7207 },
7208
7209 collapse: function(lines) {
7210 var this$1 = this;
7211
7212 for (var i = 0; i < this.children.length; ++i) { this$1.children[i].collapse(lines); }
7213 },
7214
7215 insertInner: function(at, lines, height) {
7216 var this$1 = this;
7217
7218 this.size += lines.length;
7219 this.height += height;
7220 for (var i = 0; i < this.children.length; ++i) {
7221 var child = this$1.children[i], sz = child.chunkSize();
7222 if (at <= sz) {
7223 child.insertInner(at, lines, height);
7224 if (child.lines && child.lines.length > 50) {
7225 // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced.
7226 // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest.
7227 var remaining = child.lines.length % 25 + 25;
7228 for (var pos = remaining; pos < child.lines.length;) {
7229 var leaf = new LeafChunk(child.lines.slice(pos, pos += 25));
7230 child.height -= leaf.height;
7231 this$1.children.splice(++i, 0, leaf);
7232 leaf.parent = this$1;
7233 }
7234 child.lines = child.lines.slice(0, remaining);
7235 this$1.maybeSpill();
7236 }
7237 break
7238 }
7239 at -= sz;
7240 }
7241 },
7242
7243 // When a node has grown, check whether it should be split.
7244 maybeSpill: function() {
7245 if (this.children.length <= 10) { return }
7246 var me = this;
7247 do {
7248 var spilled = me.children.splice(me.children.length - 5, 5);
7249 var sibling = new BranchChunk(spilled);
7250 if (!me.parent) { // Become the parent node
7251 var copy = new BranchChunk(me.children);
7252 copy.parent = me;
7253 me.children = [copy, sibling];
7254 me = copy;
7255 } else {
7256 me.size -= sibling.size;
7257 me.height -= sibling.height;
7258 var myIndex = indexOf(me.parent.children, me);
7259 me.parent.children.splice(myIndex + 1, 0, sibling);
7260 }
7261 sibling.parent = me.parent;
7262 } while (me.children.length > 10)
7263 me.parent.maybeSpill();
7264 },
7265
7266 iterN: function(at, n, op) {
7267 var this$1 = this;
7268
7269 for (var i = 0; i < this.children.length; ++i) {
7270 var child = this$1.children[i], sz = child.chunkSize();
7271 if (at < sz) {
7272 var used = Math.min(n, sz - at);
7273 if (child.iterN(at, used, op)) { return true }
7274 if ((n -= used) == 0) { break }
7275 at = 0;
7276 } else { at -= sz; }
7277 }
7278 }
7279 };
7280
7281 // Line widgets are block elements displayed above or below a line.
7282
7283 var LineWidget = function(doc, node, options) {
7284 var this$1 = this;
7285
7286 if (options) { for (var opt in options) { if (options.hasOwnProperty(opt))
7287 { this$1[opt] = options[opt]; } } }
7288 this.doc = doc;
7289 this.node = node;
7290 };
7291
7292 LineWidget.prototype.clear = function () {
7293 var this$1 = this;
7294
7295 var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line);
7296 if (no == null || !ws) { return }
7297 for (var i = 0; i < ws.length; ++i) { if (ws[i] == this$1) { ws.splice(i--, 1); } }
7298 if (!ws.length) { line.widgets = null; }
7299 var height = widgetHeight(this);
7300 updateLineHeight(line, Math.max(0, line.height - height));
7301 if (cm) {
7302 runInOp(cm, function () {
7303 adjustScrollWhenAboveVisible(cm, line, -height);
7304 regLineChange(cm, no, "widget");
7305 });
7306 signalLater(cm, "lineWidgetCleared", cm, this, no);
7307 }
7308 };
7309
7310 LineWidget.prototype.changed = function () {
7311 var this$1 = this;
7312
7313 var oldH = this.height, cm = this.doc.cm, line = this.line;
7314 this.height = null;
7315 var diff = widgetHeight(this) - oldH;
7316 if (!diff) { return }
7317 if (!lineIsHidden(this.doc, line)) { updateLineHeight(line, line.height + diff); }
7318 if (cm) {
7319 runInOp(cm, function () {
7320 cm.curOp.forceUpdate = true;
7321 adjustScrollWhenAboveVisible(cm, line, diff);
7322 signalLater(cm, "lineWidgetChanged", cm, this$1, lineNo(line));
7323 });
7324 }
7325 };
7326 eventMixin(LineWidget);
7327
7328 function adjustScrollWhenAboveVisible(cm, line, diff) {
7329 if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))
7330 { addToScrollTop(cm, diff); }
7331 }
7332
7333 function addLineWidget(doc, handle, node, options) {
7334 var widget = new LineWidget(doc, node, options);
7335 var cm = doc.cm;
7336 if (cm && widget.noHScroll) { cm.display.alignWidgets = true; }
7337 changeLine(doc, handle, "widget", function (line) {
7338 var widgets = line.widgets || (line.widgets = []);
7339 if (widget.insertAt == null) { widgets.push(widget); }
7340 else { widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget); }
7341 widget.line = line;
7342 if (cm && !lineIsHidden(doc, line)) {
7343 var aboveVisible = heightAtLine(line) < doc.scrollTop;
7344 updateLineHeight(line, line.height + widgetHeight(widget));
7345 if (aboveVisible) { addToScrollTop(cm, widget.height); }
7346 cm.curOp.forceUpdate = true;
7347 }
7348 return true
7349 });
7350 if (cm) { signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle)); }
7351 return widget
7352 }
7353
7354 // TEXTMARKERS
7355
7356 // Created with markText and setBookmark methods. A TextMarker is a
7357 // handle that can be used to clear or find a marked position in the
7358 // document. Line objects hold arrays (markedSpans) containing
7359 // {from, to, marker} object pointing to such marker objects, and
7360 // indicating that such a marker is present on that line. Multiple
7361 // lines may point to the same marker when it spans across lines.
7362 // The spans will have null for their from/to properties when the
7363 // marker continues beyond the start/end of the line. Markers have
7364 // links back to the lines they currently touch.
7365
7366 // Collapsed markers have unique ids, in order to be able to order
7367 // them, which is needed for uniquely determining an outer marker
7368 // when they overlap (they may nest, but not partially overlap).
7369 var nextMarkerId = 0;
7370
7371 var TextMarker = function(doc, type) {
7372 this.lines = [];
7373 this.type = type;
7374 this.doc = doc;
7375 this.id = ++nextMarkerId;
7376 };
7377
7378 // Clear the marker.
7379 TextMarker.prototype.clear = function () {
7380 var this$1 = this;
7381
7382 if (this.explicitlyCleared) { return }
7383 var cm = this.doc.cm, withOp = cm && !cm.curOp;
7384 if (withOp) { startOperation(cm); }
7385 if (hasHandler(this, "clear")) {
7386 var found = this.find();
7387 if (found) { signalLater(this, "clear", found.from, found.to); }
7388 }
7389 var min = null, max = null;
7390 for (var i = 0; i < this.lines.length; ++i) {
7391 var line = this$1.lines[i];
7392 var span = getMarkedSpanFor(line.markedSpans, this$1);
7393 if (cm && !this$1.collapsed) { regLineChange(cm, lineNo(line), "text"); }
7394 else if (cm) {
7395 if (span.to != null) { max = lineNo(line); }
7396 if (span.from != null) { min = lineNo(line); }
7397 }
7398 line.markedSpans = removeMarkedSpan(line.markedSpans, span);
7399 if (span.from == null && this$1.collapsed && !lineIsHidden(this$1.doc, line) && cm)
7400 { updateLineHeight(line, textHeight(cm.display)); }
7401 }
7402 if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) {
7403 var visual = visualLine(this$1.lines[i$1]), len = lineLength(visual);
7404 if (len > cm.display.maxLineLength) {
7405 cm.display.maxLine = visual;
7406 cm.display.maxLineLength = len;
7407 cm.display.maxLineChanged = true;
7408 }
7409 } }
7410
7411 if (min != null && cm && this.collapsed) { regChange(cm, min, max + 1); }
7412 this.lines.length = 0;
7413 this.explicitlyCleared = true;
7414 if (this.atomic && this.doc.cantEdit) {
7415 this.doc.cantEdit = false;
7416 if (cm) { reCheckSelection(cm.doc); }
7417 }
7418 if (cm) { signalLater(cm, "markerCleared", cm, this, min, max); }
7419 if (withOp) { endOperation(cm); }
7420 if (this.parent) { this.parent.clear(); }
7421 };
7422
7423 // Find the position of the marker in the document. Returns a {from,
7424 // to} object by default. Side can be passed to get a specific side
7425 // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the
7426 // Pos objects returned contain a line object, rather than a line
7427 // number (used to prevent looking up the same line twice).
7428 TextMarker.prototype.find = function (side, lineObj) {
7429 var this$1 = this;
7430
7431 if (side == null && this.type == "bookmark") { side = 1; }
7432 var from, to;
7433 for (var i = 0; i < this.lines.length; ++i) {
7434 var line = this$1.lines[i];
7435 var span = getMarkedSpanFor(line.markedSpans, this$1);
7436 if (span.from != null) {
7437 from = Pos(lineObj ? line : lineNo(line), span.from);
7438 if (side == -1) { return from }
7439 }
7440 if (span.to != null) {
7441 to = Pos(lineObj ? line : lineNo(line), span.to);
7442 if (side == 1) { return to }
7443 }
7444 }
7445 return from && {from: from, to: to}
7446 };
7447
7448 // Signals that the marker's widget changed, and surrounding layout
7449 // should be recomputed.
7450 TextMarker.prototype.changed = function () {
7451 var this$1 = this;
7452
7453 var pos = this.find(-1, true), widget = this, cm = this.doc.cm;
7454 if (!pos || !cm) { return }
7455 runInOp(cm, function () {
7456 var line = pos.line, lineN = lineNo(pos.line);
7457 var view = findViewForLine(cm, lineN);
7458 if (view) {
7459 clearLineMeasurementCacheFor(view);
7460 cm.curOp.selectionChanged = cm.curOp.forceUpdate = true;
7461 }
7462 cm.curOp.updateMaxLine = true;
7463 if (!lineIsHidden(widget.doc, line) && widget.height != null) {
7464 var oldHeight = widget.height;
7465 widget.height = null;
7466 var dHeight = widgetHeight(widget) - oldHeight;
7467 if (dHeight)
7468 { updateLineHeight(line, line.height + dHeight); }
7469 }
7470 signalLater(cm, "markerChanged", cm, this$1);
7471 });
7472 };
7473
7474 TextMarker.prototype.attachLine = function (line) {
7475 if (!this.lines.length && this.doc.cm) {
7476 var op = this.doc.cm.curOp;
7477 if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
7478 { (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this); }
7479 }
7480 this.lines.push(line);
7481 };
7482
7483 TextMarker.prototype.detachLine = function (line) {
7484 this.lines.splice(indexOf(this.lines, line), 1);
7485 if (!this.lines.length && this.doc.cm) {
7486 var op = this.doc.cm.curOp
7487 ;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this);
7488 }
7489 };
7490 eventMixin(TextMarker);
7491
7492 // Create a marker, wire it up to the right lines, and
7493 function markText(doc, from, to, options, type) {
7494 // Shared markers (across linked documents) are handled separately
7495 // (markTextShared will call out to this again, once per
7496 // document).
7497 if (options && options.shared) { return markTextShared(doc, from, to, options, type) }
7498 // Ensure we are in an operation.
7499 if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, markText)(doc, from, to, options, type) }
7500
7501 var marker = new TextMarker(doc, type), diff = cmp(from, to);
7502 if (options) { copyObj(options, marker, false); }
7503 // Don't connect empty markers unless clearWhenEmpty is false
7504 if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false)
7505 { return marker }
7506 if (marker.replacedWith) {
7507 // Showing up as a widget implies collapsed (widget replaces text)
7508 marker.collapsed = true;
7509 marker.widgetNode = eltP("span", [marker.replacedWith], "CodeMirror-widget");
7510 if (!options.handleMouseEvents) { marker.widgetNode.setAttribute("cm-ignore-events", "true"); }
7511 if (options.insertLeft) { marker.widgetNode.insertLeft = true; }
7512 }
7513 if (marker.collapsed) {
7514 if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||
7515 from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker))
7516 { throw new Error("Inserting collapsed marker partially overlapping an existing one") }
7517 seeCollapsedSpans();
7518 }
7519
7520 if (marker.addToHistory)
7521 { addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN); }
7522
7523 var curLine = from.line, cm = doc.cm, updateMaxLine;
7524 doc.iter(curLine, to.line + 1, function (line) {
7525 if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine)
7526 { updateMaxLine = true; }
7527 if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0); }
7528 addMarkedSpan(line, new MarkedSpan(marker,
7529 curLine == from.line ? from.ch : null,
7530 curLine == to.line ? to.ch : null));
7531 ++curLine;
7532 });
7533 // lineIsHidden depends on the presence of the spans, so needs a second pass
7534 if (marker.collapsed) { doc.iter(from.line, to.line + 1, function (line) {
7535 if (lineIsHidden(doc, line)) { updateLineHeight(line, 0); }
7536 }); }
7537
7538 if (marker.clearOnEnter) { on(marker, "beforeCursorEnter", function () { return marker.clear(); }); }
7539
7540 if (marker.readOnly) {
7541 seeReadOnlySpans();
7542 if (doc.history.done.length || doc.history.undone.length)
7543 { doc.clearHistory(); }
7544 }
7545 if (marker.collapsed) {
7546 marker.id = ++nextMarkerId;
7547 marker.atomic = true;
7548 }
7549 if (cm) {
7550 // Sync editor state
7551 if (updateMaxLine) { cm.curOp.updateMaxLine = true; }
7552 if (marker.collapsed)
7553 { regChange(cm, from.line, to.line + 1); }
7554 else if (marker.className || marker.startStyle || marker.endStyle || marker.css ||
7555 marker.attributes || marker.title)
7556 { for (var i = from.line; i <= to.line; i++) { regLineChange(cm, i, "text"); } }
7557 if (marker.atomic) { reCheckSelection(cm.doc); }
7558 signalLater(cm, "markerAdded", cm, marker);
7559 }
7560 return marker
7561 }
7562
7563 // SHARED TEXTMARKERS
7564
7565 // A shared marker spans multiple linked documents. It is
7566 // implemented as a meta-marker-object controlling multiple normal
7567 // markers.
7568 var SharedTextMarker = function(markers, primary) {
7569 var this$1 = this;
7570
7571 this.markers = markers;
7572 this.primary = primary;
7573 for (var i = 0; i < markers.length; ++i)
7574 { markers[i].parent = this$1; }
7575 };
7576
7577 SharedTextMarker.prototype.clear = function () {
7578 var this$1 = this;
7579
7580 if (this.explicitlyCleared) { return }
7581 this.explicitlyCleared = true;
7582 for (var i = 0; i < this.markers.length; ++i)
7583 { this$1.markers[i].clear(); }
7584 signalLater(this, "clear");
7585 };
7586
7587 SharedTextMarker.prototype.find = function (side, lineObj) {
7588 return this.primary.find(side, lineObj)
7589 };
7590 eventMixin(SharedTextMarker);
7591
7592 function markTextShared(doc, from, to, options, type) {
7593 options = copyObj(options);
7594 options.shared = false;
7595 var markers = [markText(doc, from, to, options, type)], primary = markers[0];
7596 var widget = options.widgetNode;
7597 linkedDocs(doc, function (doc) {
7598 if (widget) { options.widgetNode = widget.cloneNode(true); }
7599 markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));
7600 for (var i = 0; i < doc.linked.length; ++i)
7601 { if (doc.linked[i].isParent) { return } }
7602 primary = lst(markers);
7603 });
7604 return new SharedTextMarker(markers, primary)
7605 }
7606
7607 function findSharedMarkers(doc) {
7608 return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function (m) { return m.parent; })
7609 }
7610
7611 function copySharedMarkers(doc, markers) {
7612 for (var i = 0; i < markers.length; i++) {
7613 var marker = markers[i], pos = marker.find();
7614 var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to);
7615 if (cmp(mFrom, mTo)) {
7616 var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type);
7617 marker.markers.push(subMark);
7618 subMark.parent = marker;
7619 }
7620 }
7621 }
7622
7623 function detachSharedMarkers(markers) {
7624 var loop = function ( i ) {
7625 var marker = markers[i], linked = [marker.primary.doc];
7626 linkedDocs(marker.primary.doc, function (d) { return linked.push(d); });
7627 for (var j = 0; j < marker.markers.length; j++) {
7628 var subMarker = marker.markers[j];
7629 if (indexOf(linked, subMarker.doc) == -1) {
7630 subMarker.parent = null;
7631 marker.markers.splice(j--, 1);
7632 }
7633 }
7634 };
7635
7636 for (var i = 0; i < markers.length; i++) loop( i );
7637 }
7638
7639 var nextDocId = 0;
7640 var Doc = function(text, mode, firstLine, lineSep, direction) {
7641 if (!(this instanceof Doc)) { return new Doc(text, mode, firstLine, lineSep, direction) }
7642 if (firstLine == null) { firstLine = 0; }
7643
7644 BranchChunk.call(this, [new LeafChunk([new Line("", null)])]);
7645 this.first = firstLine;
7646 this.scrollTop = this.scrollLeft = 0;
7647 this.cantEdit = false;
7648 this.cleanGeneration = 1;
7649 this.modeFrontier = this.highlightFrontier = firstLine;
7650 var start = Pos(firstLine, 0);
7651 this.sel = simpleSelection(start);
7652 this.history = new History(null);
7653 this.id = ++nextDocId;
7654 this.modeOption = mode;
7655 this.lineSep = lineSep;
7656 this.direction = (direction == "rtl") ? "rtl" : "ltr";
7657 this.extend = false;
7658
7659 if (typeof text == "string") { text = this.splitLines(text); }
7660 updateDoc(this, {from: start, to: start, text: text});
7661 setSelection(this, simpleSelection(start), sel_dontScroll);
7662 };
7663
7664 Doc.prototype = createObj(BranchChunk.prototype, {
7665 constructor: Doc,
7666 // Iterate over the document. Supports two forms -- with only one
7667 // argument, it calls that for each line in the document. With
7668 // three, it iterates over the range given by the first two (with
7669 // the second being non-inclusive).
7670 iter: function(from, to, op) {
7671 if (op) { this.iterN(from - this.first, to - from, op); }
7672 else { this.iterN(this.first, this.first + this.size, from); }
7673 },
7674
7675 // Non-public interface for adding and removing lines.
7676 insert: function(at, lines) {
7677 var height = 0;
7678 for (var i = 0; i < lines.length; ++i) { height += lines[i].height; }
7679 this.insertInner(at - this.first, lines, height);
7680 },
7681 remove: function(at, n) { this.removeInner(at - this.first, n); },
7682
7683 // From here, the methods are part of the public interface. Most
7684 // are also available from CodeMirror (editor) instances.
7685
7686 getValue: function(lineSep) {
7687 var lines = getLines(this, this.first, this.first + this.size);
7688 if (lineSep === false) { return lines }
7689 return lines.join(lineSep || this.lineSeparator())
7690 },
7691 setValue: docMethodOp(function(code) {
7692 var top = Pos(this.first, 0), last = this.first + this.size - 1;
7693 makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
7694 text: this.splitLines(code), origin: "setValue", full: true}, true);
7695 if (this.cm) { scrollToCoords(this.cm, 0, 0); }
7696 setSelection(this, simpleSelection(top), sel_dontScroll);
7697 }),
7698 replaceRange: function(code, from, to, origin) {
7699 from = clipPos(this, from);
7700 to = to ? clipPos(this, to) : from;
7701 replaceRange(this, code, from, to, origin);
7702 },
7703 getRange: function(from, to, lineSep) {
7704 var lines = getBetween(this, clipPos(this, from), clipPos(this, to));
7705 if (lineSep === false) { return lines }
7706 return lines.join(lineSep || this.lineSeparator())
7707 },
7708
7709 getLine: function(line) {var l = this.getLineHandle(line); return l && l.text},
7710
7711 getLineHandle: function(line) {if (isLine(this, line)) { return getLine(this, line) }},
7712 getLineNumber: function(line) {return lineNo(line)},
7713
7714 getLineHandleVisualStart: function(line) {
7715 if (typeof line == "number") { line = getLine(this, line); }
7716 return visualLine(line)
7717 },
7718
7719 lineCount: function() {return this.size},
7720 firstLine: function() {return this.first},
7721 lastLine: function() {return this.first + this.size - 1},
7722
7723 clipPos: function(pos) {return clipPos(this, pos)},
7724
7725 getCursor: function(start) {
7726 var range$$1 = this.sel.primary(), pos;
7727 if (start == null || start == "head") { pos = range$$1.head; }
7728 else if (start == "anchor") { pos = range$$1.anchor; }
7729 else if (start == "end" || start == "to" || start === false) { pos = range$$1.to(); }
7730 else { pos = range$$1.from(); }
7731 return pos
7732 },
7733 listSelections: function() { return this.sel.ranges },
7734 somethingSelected: function() {return this.sel.somethingSelected()},
7735
7736 setCursor: docMethodOp(function(line, ch, options) {
7737 setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options);
7738 }),
7739 setSelection: docMethodOp(function(anchor, head, options) {
7740 setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options);
7741 }),
7742 extendSelection: docMethodOp(function(head, other, options) {
7743 extendSelection(this, clipPos(this, head), other && clipPos(this, other), options);
7744 }),
7745 extendSelections: docMethodOp(function(heads, options) {
7746 extendSelections(this, clipPosArray(this, heads), options);
7747 }),
7748 extendSelectionsBy: docMethodOp(function(f, options) {
7749 var heads = map(this.sel.ranges, f);
7750 extendSelections(this, clipPosArray(this, heads), options);
7751 }),
7752 setSelections: docMethodOp(function(ranges, primary, options) {
7753 var this$1 = this;
7754
7755 if (!ranges.length) { return }
7756 var out = [];
7757 for (var i = 0; i < ranges.length; i++)
7758 { out[i] = new Range(clipPos(this$1, ranges[i].anchor),
7759 clipPos(this$1, ranges[i].head)); }
7760 if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex); }
7761 setSelection(this, normalizeSelection(this.cm, out, primary), options);
7762 }),
7763 addSelection: docMethodOp(function(anchor, head, options) {
7764 var ranges = this.sel.ranges.slice(0);
7765 ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)));
7766 setSelection(this, normalizeSelection(this.cm, ranges, ranges.length - 1), options);
7767 }),
7768
7769 getSelection: function(lineSep) {
7770 var this$1 = this;
7771
7772 var ranges = this.sel.ranges, lines;
7773 for (var i = 0; i < ranges.length; i++) {
7774 var sel = getBetween(this$1, ranges[i].from(), ranges[i].to());
7775 lines = lines ? lines.concat(sel) : sel;
7776 }
7777 if (lineSep === false) { return lines }
7778 else { return lines.join(lineSep || this.lineSeparator()) }
7779 },
7780 getSelections: function(lineSep) {
7781 var this$1 = this;
7782
7783 var parts = [], ranges = this.sel.ranges;
7784 for (var i = 0; i < ranges.length; i++) {
7785 var sel = getBetween(this$1, ranges[i].from(), ranges[i].to());
7786 if (lineSep !== false) { sel = sel.join(lineSep || this$1.lineSeparator()); }
7787 parts[i] = sel;
7788 }
7789 return parts
7790 },
7791 replaceSelection: function(code, collapse, origin) {
7792 var dup = [];
7793 for (var i = 0; i < this.sel.ranges.length; i++)
7794 { dup[i] = code; }
7795 this.replaceSelections(dup, collapse, origin || "+input");
7796 },
7797 replaceSelections: docMethodOp(function(code, collapse, origin) {
7798 var this$1 = this;
7799
7800 var changes = [], sel = this.sel;
7801 for (var i = 0; i < sel.ranges.length; i++) {
7802 var range$$1 = sel.ranges[i];
7803 changes[i] = {from: range$$1.from(), to: range$$1.to(), text: this$1.splitLines(code[i]), origin: origin};
7804 }
7805 var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse);
7806 for (var i$1 = changes.length - 1; i$1 >= 0; i$1--)
7807 { makeChange(this$1, changes[i$1]); }
7808 if (newSel) { setSelectionReplaceHistory(this, newSel); }
7809 else if (this.cm) { ensureCursorVisible(this.cm); }
7810 }),
7811 undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}),
7812 redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}),
7813 undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}),
7814 redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true);}),
7815
7816 setExtending: function(val) {this.extend = val;},
7817 getExtending: function() {return this.extend},
7818
7819 historySize: function() {
7820 var hist = this.history, done = 0, undone = 0;
7821 for (var i = 0; i < hist.done.length; i++) { if (!hist.done[i].ranges) { ++done; } }
7822 for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].ranges) { ++undone; } }
7823 return {undo: done, redo: undone}
7824 },
7825 clearHistory: function() {this.history = new History(this.history.maxGeneration);},
7826
7827 markClean: function() {
7828 this.cleanGeneration = this.changeGeneration(true);
7829 },
7830 changeGeneration: function(forceSplit) {
7831 if (forceSplit)
7832 { this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null; }
7833 return this.history.generation
7834 },
7835 isClean: function (gen) {
7836 return this.history.generation == (gen || this.cleanGeneration)
7837 },
7838
7839 getHistory: function() {
7840 return {done: copyHistoryArray(this.history.done),
7841 undone: copyHistoryArray(this.history.undone)}
7842 },
7843 setHistory: function(histData) {
7844 var hist = this.history = new History(this.history.maxGeneration);
7845 hist.done = copyHistoryArray(histData.done.slice(0), null, true);
7846 hist.undone = copyHistoryArray(histData.undone.slice(0), null, true);
7847 },
7848
7849 setGutterMarker: docMethodOp(function(line, gutterID, value) {
7850 return changeLine(this, line, "gutter", function (line) {
7851 var markers = line.gutterMarkers || (line.gutterMarkers = {});
7852 markers[gutterID] = value;
7853 if (!value && isEmpty(markers)) { line.gutterMarkers = null; }
7854 return true
7855 })
7856 }),
7857
7858 clearGutter: docMethodOp(function(gutterID) {
7859 var this$1 = this;
7860
7861 this.iter(function (line) {
7862 if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
7863 changeLine(this$1, line, "gutter", function () {
7864 line.gutterMarkers[gutterID] = null;
7865 if (isEmpty(line.gutterMarkers)) { line.gutterMarkers = null; }
7866 return true
7867 });
7868 }
7869 });
7870 }),
7871
7872 lineInfo: function(line) {
7873 var n;
7874 if (typeof line == "number") {
7875 if (!isLine(this, line)) { return null }
7876 n = line;
7877 line = getLine(this, line);
7878 if (!line) { return null }
7879 } else {
7880 n = lineNo(line);
7881 if (n == null) { return null }
7882 }
7883 return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
7884 textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
7885 widgets: line.widgets}
7886 },
7887
7888 addLineClass: docMethodOp(function(handle, where, cls) {
7889 return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
7890 var prop = where == "text" ? "textClass"
7891 : where == "background" ? "bgClass"
7892 : where == "gutter" ? "gutterClass" : "wrapClass";
7893 if (!line[prop]) { line[prop] = cls; }
7894 else if (classTest(cls).test(line[prop])) { return false }
7895 else { line[prop] += " " + cls; }
7896 return true
7897 })
7898 }),
7899 removeLineClass: docMethodOp(function(handle, where, cls) {
7900 return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
7901 var prop = where == "text" ? "textClass"
7902 : where == "background" ? "bgClass"
7903 : where == "gutter" ? "gutterClass" : "wrapClass";
7904 var cur = line[prop];
7905 if (!cur) { return false }
7906 else if (cls == null) { line[prop] = null; }
7907 else {
7908 var found = cur.match(classTest(cls));
7909 if (!found) { return false }
7910 var end = found.index + found[0].length;
7911 line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null;
7912 }
7913 return true
7914 })
7915 }),
7916
7917 addLineWidget: docMethodOp(function(handle, node, options) {
7918 return addLineWidget(this, handle, node, options)
7919 }),
7920 removeLineWidget: function(widget) { widget.clear(); },
7921
7922 markText: function(from, to, options) {
7923 return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range")
7924 },
7925 setBookmark: function(pos, options) {
7926 var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
7927 insertLeft: options && options.insertLeft,
7928 clearWhenEmpty: false, shared: options && options.shared,
7929 handleMouseEvents: options && options.handleMouseEvents};
7930 pos = clipPos(this, pos);
7931 return markText(this, pos, pos, realOpts, "bookmark")
7932 },
7933 findMarksAt: function(pos) {
7934 pos = clipPos(this, pos);
7935 var markers = [], spans = getLine(this, pos.line).markedSpans;
7936 if (spans) { for (var i = 0; i < spans.length; ++i) {
7937 var span = spans[i];
7938 if ((span.from == null || span.from <= pos.ch) &&
7939 (span.to == null || span.to >= pos.ch))
7940 { markers.push(span.marker.parent || span.marker); }
7941 } }
7942 return markers
7943 },
7944 findMarks: function(from, to, filter) {
7945 from = clipPos(this, from); to = clipPos(this, to);
7946 var found = [], lineNo$$1 = from.line;
7947 this.iter(from.line, to.line + 1, function (line) {
7948 var spans = line.markedSpans;
7949 if (spans) { for (var i = 0; i < spans.length; i++) {
7950 var span = spans[i];
7951 if (!(span.to != null && lineNo$$1 == from.line && from.ch >= span.to ||
7952 span.from == null && lineNo$$1 != from.line ||
7953 span.from != null && lineNo$$1 == to.line && span.from >= to.ch) &&
7954 (!filter || filter(span.marker)))
7955 { found.push(span.marker.parent || span.marker); }
7956 } }
7957 ++lineNo$$1;
7958 });
7959 return found
7960 },
7961 getAllMarks: function() {
7962 var markers = [];
7963 this.iter(function (line) {
7964 var sps = line.markedSpans;
7965 if (sps) { for (var i = 0; i < sps.length; ++i)
7966 { if (sps[i].from != null) { markers.push(sps[i].marker); } } }
7967 });
7968 return markers
7969 },
7970
7971 posFromIndex: function(off) {
7972 var ch, lineNo$$1 = this.first, sepSize = this.lineSeparator().length;
7973 this.iter(function (line) {
7974 var sz = line.text.length + sepSize;
7975 if (sz > off) { ch = off; return true }
7976 off -= sz;
7977 ++lineNo$$1;
7978 });
7979 return clipPos(this, Pos(lineNo$$1, ch))
7980 },
7981 indexFromPos: function (coords) {
7982 coords = clipPos(this, coords);
7983 var index = coords.ch;
7984 if (coords.line < this.first || coords.ch < 0) { return 0 }
7985 var sepSize = this.lineSeparator().length;
7986 this.iter(this.first, coords.line, function (line) { // iter aborts when callback returns a truthy value
7987 index += line.text.length + sepSize;
7988 });
7989 return index
7990 },
7991
7992 copy: function(copyHistory) {
7993 var doc = new Doc(getLines(this, this.first, this.first + this.size),
7994 this.modeOption, this.first, this.lineSep, this.direction);
7995 doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;
7996 doc.sel = this.sel;
7997 doc.extend = false;
7998 if (copyHistory) {
7999 doc.history.undoDepth = this.history.undoDepth;
8000 doc.setHistory(this.getHistory());
8001 }
8002 return doc
8003 },
8004
8005 linkedDoc: function(options) {
8006 if (!options) { options = {}; }
8007 var from = this.first, to = this.first + this.size;
8008 if (options.from != null && options.from > from) { from = options.from; }
8009 if (options.to != null && options.to < to) { to = options.to; }
8010 var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep, this.direction);
8011 if (options.sharedHist) { copy.history = this.history
8012 ; }(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});
8013 copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];
8014 copySharedMarkers(copy, findSharedMarkers(this));
8015 return copy
8016 },
8017 unlinkDoc: function(other) {
8018 var this$1 = this;
8019
8020 if (other instanceof CodeMirror) { other = other.doc; }
8021 if (this.linked) { for (var i = 0; i < this.linked.length; ++i) {
8022 var link = this$1.linked[i];
8023 if (link.doc != other) { continue }
8024 this$1.linked.splice(i, 1);
8025 other.unlinkDoc(this$1);
8026 detachSharedMarkers(findSharedMarkers(this$1));
8027 break
8028 } }
8029 // If the histories were shared, split them again
8030 if (other.history == this.history) {
8031 var splitIds = [other.id];
8032 linkedDocs(other, function (doc) { return splitIds.push(doc.id); }, true);
8033 other.history = new History(null);
8034 other.history.done = copyHistoryArray(this.history.done, splitIds);
8035 other.history.undone = copyHistoryArray(this.history.undone, splitIds);
8036 }
8037 },
8038 iterLinkedDocs: function(f) {linkedDocs(this, f);},
8039
8040 getMode: function() {return this.mode},
8041 getEditor: function() {return this.cm},
8042
8043 splitLines: function(str) {
8044 if (this.lineSep) { return str.split(this.lineSep) }
8045 return splitLinesAuto(str)
8046 },
8047 lineSeparator: function() { return this.lineSep || "\n" },
8048
8049 setDirection: docMethodOp(function (dir) {
8050 if (dir != "rtl") { dir = "ltr"; }
8051 if (dir == this.direction) { return }
8052 this.direction = dir;
8053 this.iter(function (line) { return line.order = null; });
8054 if (this.cm) { directionChanged(this.cm); }
8055 })
8056 });
8057
8058 // Public alias.
8059 Doc.prototype.eachLine = Doc.prototype.iter;
8060
8061 // Kludge to work around strange IE behavior where it'll sometimes
8062 // re-fire a series of drag-related events right after the drop (#1551)
8063 var lastDrop = 0;
8064
8065 function onDrop(e) {
8066 var cm = this;
8067 clearDragCursor(cm);
8068 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))
8069 { return }
8070 e_preventDefault(e);
8071 if (ie) { lastDrop = +new Date; }
8072 var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
8073 if (!pos || cm.isReadOnly()) { return }
8074 // Might be a file drop, in which case we simply extract the text
8075 // and insert it.
8076 if (files && files.length && window.FileReader && window.File) {
8077 var n = files.length, text = Array(n), read = 0;
8078 var loadFile = function (file, i) {
8079 if (cm.options.allowDropFileTypes &&
8080 indexOf(cm.options.allowDropFileTypes, file.type) == -1)
8081 { return }
8082
8083 var reader = new FileReader;
8084 reader.onload = operation(cm, function () {
8085 var content = reader.result;
8086 if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) { content = ""; }
8087 text[i] = content;
8088 if (++read == n) {
8089 pos = clipPos(cm.doc, pos);
8090 var change = {from: pos, to: pos,
8091 text: cm.doc.splitLines(text.join(cm.doc.lineSeparator())),
8092 origin: "paste"};
8093 makeChange(cm.doc, change);
8094 setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change)));
8095 }
8096 });
8097 reader.readAsText(file);
8098 };
8099 for (var i = 0; i < n; ++i) { loadFile(files[i], i); }
8100 } else { // Normal drop
8101 // Don't do a replace if the drop happened inside of the selected text.
8102 if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {
8103 cm.state.draggingText(e);
8104 // Ensure the editor is re-focused
8105 setTimeout(function () { return cm.display.input.focus(); }, 20);
8106 return
8107 }
8108 try {
8109 var text$1 = e.dataTransfer.getData("Text");
8110 if (text$1) {
8111 var selected;
8112 if (cm.state.draggingText && !cm.state.draggingText.copy)
8113 { selected = cm.listSelections(); }
8114 setSelectionNoUndo(cm.doc, simpleSelection(pos, pos));
8115 if (selected) { for (var i$1 = 0; i$1 < selected.length; ++i$1)
8116 { replaceRange(cm.doc, "", selected[i$1].anchor, selected[i$1].head, "drag"); } }
8117 cm.replaceSelection(text$1, "around", "paste");
8118 cm.display.input.focus();
8119 }
8120 }
8121 catch(e){}
8122 }
8123 }
8124
8125 function onDragStart(cm, e) {
8126 if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return }
8127 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return }
8128
8129 e.dataTransfer.setData("Text", cm.getSelection());
8130 e.dataTransfer.effectAllowed = "copyMove";
8131
8132 // Use dummy image instead of default browsers image.
8133 // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
8134 if (e.dataTransfer.setDragImage && !safari) {
8135 var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
8136 img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
8137 if (presto) {
8138 img.width = img.height = 1;
8139 cm.display.wrapper.appendChild(img);
8140 // Force a relayout, or Opera won't use our image for some obscure reason
8141 img._top = img.offsetTop;
8142 }
8143 e.dataTransfer.setDragImage(img, 0, 0);
8144 if (presto) { img.parentNode.removeChild(img); }
8145 }
8146 }
8147
8148 function onDragOver(cm, e) {
8149 var pos = posFromMouse(cm, e);
8150 if (!pos) { return }
8151 var frag = document.createDocumentFragment();
8152 drawSelectionCursor(cm, pos, frag);
8153 if (!cm.display.dragCursor) {
8154 cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors");
8155 cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv);
8156 }
8157 removeChildrenAndAdd(cm.display.dragCursor, frag);
8158 }
8159
8160 function clearDragCursor(cm) {
8161 if (cm.display.dragCursor) {
8162 cm.display.lineSpace.removeChild(cm.display.dragCursor);
8163 cm.display.dragCursor = null;
8164 }
8165 }
8166
8167 // These must be handled carefully, because naively registering a
8168 // handler for each editor will cause the editors to never be
8169 // garbage collected.
8170
8171 function forEachCodeMirror(f) {
8172 if (!document.getElementsByClassName) { return }
8173 var byClass = document.getElementsByClassName("CodeMirror"), editors = [];
8174 for (var i = 0; i < byClass.length; i++) {
8175 var cm = byClass[i].CodeMirror;
8176 if (cm) { editors.push(cm); }
8177 }
8178 if (editors.length) { editors[0].operation(function () {
8179 for (var i = 0; i < editors.length; i++) { f(editors[i]); }
8180 }); }
8181 }
8182
8183 var globalsRegistered = false;
8184 function ensureGlobalHandlers() {
8185 if (globalsRegistered) { return }
8186 registerGlobalHandlers();
8187 globalsRegistered = true;
8188 }
8189 function registerGlobalHandlers() {
8190 // When the window resizes, we need to refresh active editors.
8191 var resizeTimer;
8192 on(window, "resize", function () {
8193 if (resizeTimer == null) { resizeTimer = setTimeout(function () {
8194 resizeTimer = null;
8195 forEachCodeMirror(onResize);
8196 }, 100); }
8197 });
8198 // When the window loses focus, we want to show the editor as blurred
8199 on(window, "blur", function () { return forEachCodeMirror(onBlur); });
8200 }
8201 // Called when the window resizes
8202 function onResize(cm) {
8203 var d = cm.display;
8204 // Might be a text scaling operation, clear size caches.
8205 d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
8206 d.scrollbarsClipped = false;
8207 cm.setSize();
8208 }
8209
8210 var keyNames = {
8211 3: "Pause", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
8212 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
8213 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
8214 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod",
8215 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 127: "Delete", 145: "ScrollLock",
8216 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
8217 221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete",
8218 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert"
8219 };
8220
8221 // Number keys
8222 for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i); }
8223 // Alphabetic keys
8224 for (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1); }
8225 // Function keys
8226 for (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235] = "F" + i$2; }
8227
8228 var keyMap = {};
8229
8230 keyMap.basic = {
8231 "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
8232 "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
8233 "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore",
8234 "Tab": "defaultTab", "Shift-Tab": "indentAuto",
8235 "Enter": "newlineAndIndent", "Insert": "toggleOverwrite",
8236 "Esc": "singleSelection"
8237 };
8238 // Note that the save and find-related commands aren't defined by
8239 // default. User code or addons can define them. Unknown commands
8240 // are simply ignored.
8241 keyMap.pcDefault = {
8242 "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
8243 "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown",
8244 "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
8245 "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
8246 "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
8247 "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
8248 "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection",
8249 "fallthrough": "basic"
8250 };
8251 // Very basic readline/emacs-style bindings, which are standard on Mac.
8252 keyMap.emacsy = {
8253 "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
8254 "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
8255 "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
8256 "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars",
8257 "Ctrl-O": "openLine"
8258 };
8259 keyMap.macDefault = {
8260 "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
8261 "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
8262 "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore",
8263 "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
8264 "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
8265 "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight",
8266 "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd",
8267 "fallthrough": ["basic", "emacsy"]
8268 };
8269 keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
8270
8271 // KEYMAP DISPATCH
8272
8273 function normalizeKeyName(name) {
8274 var parts = name.split(/-(?!$)/);
8275 name = parts[parts.length - 1];
8276 var alt, ctrl, shift, cmd;
8277 for (var i = 0; i < parts.length - 1; i++) {
8278 var mod = parts[i];
8279 if (/^(cmd|meta|m)$/i.test(mod)) { cmd = true; }
8280 else if (/^a(lt)?$/i.test(mod)) { alt = true; }
8281 else if (/^(c|ctrl|control)$/i.test(mod)) { ctrl = true; }
8282 else if (/^s(hift)?$/i.test(mod)) { shift = true; }
8283 else { throw new Error("Unrecognized modifier name: " + mod) }
8284 }
8285 if (alt) { name = "Alt-" + name; }
8286 if (ctrl) { name = "Ctrl-" + name; }
8287 if (cmd) { name = "Cmd-" + name; }
8288 if (shift) { name = "Shift-" + name; }
8289 return name
8290 }
8291
8292 // This is a kludge to keep keymaps mostly working as raw objects
8293 // (backwards compatibility) while at the same time support features
8294 // like normalization and multi-stroke key bindings. It compiles a
8295 // new normalized keymap, and then updates the old object to reflect
8296 // this.
8297 function normalizeKeyMap(keymap) {
8298 var copy = {};
8299 for (var keyname in keymap) { if (keymap.hasOwnProperty(keyname)) {
8300 var value = keymap[keyname];
8301 if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) { continue }
8302 if (value == "...") { delete keymap[keyname]; continue }
8303
8304 var keys = map(keyname.split(" "), normalizeKeyName);
8305 for (var i = 0; i < keys.length; i++) {
8306 var val = (void 0), name = (void 0);
8307 if (i == keys.length - 1) {
8308 name = keys.join(" ");
8309 val = value;
8310 } else {
8311 name = keys.slice(0, i + 1).join(" ");
8312 val = "...";
8313 }
8314 var prev = copy[name];
8315 if (!prev) { copy[name] = val; }
8316 else if (prev != val) { throw new Error("Inconsistent bindings for " + name) }
8317 }
8318 delete keymap[keyname];
8319 } }
8320 for (var prop in copy) { keymap[prop] = copy[prop]; }
8321 return keymap
8322 }
8323
8324 function lookupKey(key, map$$1, handle, context) {
8325 map$$1 = getKeyMap(map$$1);
8326 var found = map$$1.call ? map$$1.call(key, context) : map$$1[key];
8327 if (found === false) { return "nothing" }
8328 if (found === "...") { return "multi" }
8329 if (found != null && handle(found)) { return "handled" }
8330
8331 if (map$$1.fallthrough) {
8332 if (Object.prototype.toString.call(map$$1.fallthrough) != "[object Array]")
8333 { return lookupKey(key, map$$1.fallthrough, handle, context) }
8334 for (var i = 0; i < map$$1.fallthrough.length; i++) {
8335 var result = lookupKey(key, map$$1.fallthrough[i], handle, context);
8336 if (result) { return result }
8337 }
8338 }
8339 }
8340
8341 // Modifier key presses don't count as 'real' key presses for the
8342 // purpose of keymap fallthrough.
8343 function isModifierKey(value) {
8344 var name = typeof value == "string" ? value : keyNames[value.keyCode];
8345 return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod"
8346 }
8347
8348 function addModifierNames(name, event, noShift) {
8349 var base = name;
8350 if (event.altKey && base != "Alt") { name = "Alt-" + name; }
8351 if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") { name = "Ctrl-" + name; }
8352 if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") { name = "Cmd-" + name; }
8353 if (!noShift && event.shiftKey && base != "Shift") { name = "Shift-" + name; }
8354 return name
8355 }
8356
8357 // Look up the name of a key as indicated by an event object.
8358 function keyName(event, noShift) {
8359 if (presto && event.keyCode == 34 && event["char"]) { return false }
8360 var name = keyNames[event.keyCode];
8361 if (name == null || event.altGraphKey) { return false }
8362 // Ctrl-ScrollLock has keyCode 3, same as Ctrl-Pause,
8363 // so we'll use event.code when available (Chrome 48+, FF 38+, Safari 10.1+)
8364 if (event.keyCode == 3 && event.code) { name = event.code; }
8365 return addModifierNames(name, event, noShift)
8366 }
8367
8368 function getKeyMap(val) {
8369 return typeof val == "string" ? keyMap[val] : val
8370 }
8371
8372 // Helper for deleting text near the selection(s), used to implement
8373 // backspace, delete, and similar functionality.
8374 function deleteNearSelection(cm, compute) {
8375 var ranges = cm.doc.sel.ranges, kill = [];
8376 // Build up a set of ranges to kill first, merging overlapping
8377 // ranges.
8378 for (var i = 0; i < ranges.length; i++) {
8379 var toKill = compute(ranges[i]);
8380 while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {
8381 var replaced = kill.pop();
8382 if (cmp(replaced.from, toKill.from) < 0) {
8383 toKill.from = replaced.from;
8384 break
8385 }
8386 }
8387 kill.push(toKill);
8388 }
8389 // Next, remove those actual ranges.
8390 runInOp(cm, function () {
8391 for (var i = kill.length - 1; i >= 0; i--)
8392 { replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete"); }
8393 ensureCursorVisible(cm);
8394 });
8395 }
8396
8397 function moveCharLogically(line, ch, dir) {
8398 var target = skipExtendingChars(line.text, ch + dir, dir);
8399 return target < 0 || target > line.text.length ? null : target
8400 }
8401
8402 function moveLogically(line, start, dir) {
8403 var ch = moveCharLogically(line, start.ch, dir);
8404 return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before")
8405 }
8406
8407 function endOfLine(visually, cm, lineObj, lineNo, dir) {
8408 if (visually) {
8409 var order = getOrder(lineObj, cm.doc.direction);
8410 if (order) {
8411 var part = dir < 0 ? lst(order) : order[0];
8412 var moveInStorageOrder = (dir < 0) == (part.level == 1);
8413 var sticky = moveInStorageOrder ? "after" : "before";
8414 var ch;
8415 // With a wrapped rtl chunk (possibly spanning multiple bidi parts),
8416 // it could be that the last bidi part is not on the last visual line,
8417 // since visual lines contain content order-consecutive chunks.
8418 // Thus, in rtl, we are looking for the first (content-order) character
8419 // in the rtl chunk that is on the last line (that is, the same line
8420 // as the last (content-order) character).
8421 if (part.level > 0 || cm.doc.direction == "rtl") {
8422 var prep = prepareMeasureForLine(cm, lineObj);
8423 ch = dir < 0 ? lineObj.text.length - 1 : 0;
8424 var targetTop = measureCharPrepared(cm, prep, ch).top;
8425 ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch);
8426 if (sticky == "before") { ch = moveCharLogically(lineObj, ch, 1); }
8427 } else { ch = dir < 0 ? part.to : part.from; }
8428 return new Pos(lineNo, ch, sticky)
8429 }
8430 }
8431 return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after")
8432 }
8433
8434 function moveVisually(cm, line, start, dir) {
8435 var bidi = getOrder(line, cm.doc.direction);
8436 if (!bidi) { return moveLogically(line, start, dir) }
8437 if (start.ch >= line.text.length) {
8438 start.ch = line.text.length;
8439 start.sticky = "before";
8440 } else if (start.ch <= 0) {
8441 start.ch = 0;
8442 start.sticky = "after";
8443 }
8444 var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos];
8445 if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) {
8446 // Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines,
8447 // nothing interesting happens.
8448 return moveLogically(line, start, dir)
8449 }
8450
8451 var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); };
8452 var prep;
8453 var getWrappedLineExtent = function (ch) {
8454 if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} }
8455 prep = prep || prepareMeasureForLine(cm, line);
8456 return wrappedLineExtentChar(cm, line, prep, ch)
8457 };
8458 var wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch);
8459
8460 if (cm.doc.direction == "rtl" || part.level == 1) {
8461 var moveInStorageOrder = (part.level == 1) == (dir < 0);
8462 var ch = mv(start, moveInStorageOrder ? 1 : -1);
8463 if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) {
8464 // Case 2: We move within an rtl part or in an rtl editor on the same visual line
8465 var sticky = moveInStorageOrder ? "before" : "after";
8466 return new Pos(start.line, ch, sticky)
8467 }
8468 }
8469
8470 // Case 3: Could not move within this bidi part in this visual line, so leave
8471 // the current bidi part
8472
8473 var searchInVisualLine = function (partPos, dir, wrappedLineExtent) {
8474 var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder
8475 ? new Pos(start.line, mv(ch, 1), "before")
8476 : new Pos(start.line, ch, "after"); };
8477
8478 for (; partPos >= 0 && partPos < bidi.length; partPos += dir) {
8479 var part = bidi[partPos];
8480 var moveInStorageOrder = (dir > 0) == (part.level != 1);
8481 var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1);
8482 if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) }
8483 ch = moveInStorageOrder ? part.from : mv(part.to, -1);
8484 if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) }
8485 }
8486 };
8487
8488 // Case 3a: Look for other bidi parts on the same visual line
8489 var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent);
8490 if (res) { return res }
8491
8492 // Case 3b: Look for other bidi parts on the next visual line
8493 var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1);
8494 if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) {
8495 res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh));
8496 if (res) { return res }
8497 }
8498
8499 // Case 4: Nowhere to move
8500 return null
8501 }
8502
8503 // Commands are parameter-less actions that can be performed on an
8504 // editor, mostly used for keybindings.
8505 var commands = {
8506 selectAll: selectAll,
8507 singleSelection: function (cm) { return cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll); },
8508 killLine: function (cm) { return deleteNearSelection(cm, function (range) {
8509 if (range.empty()) {
8510 var len = getLine(cm.doc, range.head.line).text.length;
8511 if (range.head.ch == len && range.head.line < cm.lastLine())
8512 { return {from: range.head, to: Pos(range.head.line + 1, 0)} }
8513 else
8514 { return {from: range.head, to: Pos(range.head.line, len)} }
8515 } else {
8516 return {from: range.from(), to: range.to()}
8517 }
8518 }); },
8519 deleteLine: function (cm) { return deleteNearSelection(cm, function (range) { return ({
8520 from: Pos(range.from().line, 0),
8521 to: clipPos(cm.doc, Pos(range.to().line + 1, 0))
8522 }); }); },
8523 delLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { return ({
8524 from: Pos(range.from().line, 0), to: range.from()
8525 }); }); },
8526 delWrappedLineLeft: function (cm) { return deleteNearSelection(cm, function (range) {
8527 var top = cm.charCoords(range.head, "div").top + 5;
8528 var leftPos = cm.coordsChar({left: 0, top: top}, "div");
8529 return {from: leftPos, to: range.from()}
8530 }); },
8531 delWrappedLineRight: function (cm) { return deleteNearSelection(cm, function (range) {
8532 var top = cm.charCoords(range.head, "div").top + 5;
8533 var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div");
8534 return {from: range.from(), to: rightPos }
8535 }); },
8536 undo: function (cm) { return cm.undo(); },
8537 redo: function (cm) { return cm.redo(); },
8538 undoSelection: function (cm) { return cm.undoSelection(); },
8539 redoSelection: function (cm) { return cm.redoSelection(); },
8540 goDocStart: function (cm) { return cm.extendSelection(Pos(cm.firstLine(), 0)); },
8541 goDocEnd: function (cm) { return cm.extendSelection(Pos(cm.lastLine())); },
8542 goLineStart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStart(cm, range.head.line); },
8543 {origin: "+move", bias: 1}
8544 ); },
8545 goLineStartSmart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStartSmart(cm, range.head); },
8546 {origin: "+move", bias: 1}
8547 ); },
8548 goLineEnd: function (cm) { return cm.extendSelectionsBy(function (range) { return lineEnd(cm, range.head.line); },
8549 {origin: "+move", bias: -1}
8550 ); },
8551 goLineRight: function (cm) { return cm.extendSelectionsBy(function (range) {
8552 var top = cm.cursorCoords(range.head, "div").top + 5;
8553 return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div")
8554 }, sel_move); },
8555 goLineLeft: function (cm) { return cm.extendSelectionsBy(function (range) {
8556 var top = cm.cursorCoords(range.head, "div").top + 5;
8557 return cm.coordsChar({left: 0, top: top}, "div")
8558 }, sel_move); },
8559 goLineLeftSmart: function (cm) { return cm.extendSelectionsBy(function (range) {
8560 var top = cm.cursorCoords(range.head, "div").top + 5;
8561 var pos = cm.coordsChar({left: 0, top: top}, "div");
8562 if (pos.ch < cm.getLine(pos.line).search(/\S/)) { return lineStartSmart(cm, range.head) }
8563 return pos
8564 }, sel_move); },
8565 goLineUp: function (cm) { return cm.moveV(-1, "line"); },
8566 goLineDown: function (cm) { return cm.moveV(1, "line"); },
8567 goPageUp: function (cm) { return cm.moveV(-1, "page"); },
8568 goPageDown: function (cm) { return cm.moveV(1, "page"); },
8569 goCharLeft: function (cm) { return cm.moveH(-1, "char"); },
8570 goCharRight: function (cm) { return cm.moveH(1, "char"); },
8571 goColumnLeft: function (cm) { return cm.moveH(-1, "column"); },
8572 goColumnRight: function (cm) { return cm.moveH(1, "column"); },
8573 goWordLeft: function (cm) { return cm.moveH(-1, "word"); },
8574 goGroupRight: function (cm) { return cm.moveH(1, "group"); },
8575 goGroupLeft: function (cm) { return cm.moveH(-1, "group"); },
8576 goWordRight: function (cm) { return cm.moveH(1, "word"); },
8577 delCharBefore: function (cm) { return cm.deleteH(-1, "char"); },
8578 delCharAfter: function (cm) { return cm.deleteH(1, "char"); },
8579 delWordBefore: function (cm) { return cm.deleteH(-1, "word"); },
8580 delWordAfter: function (cm) { return cm.deleteH(1, "word"); },
8581 delGroupBefore: function (cm) { return cm.deleteH(-1, "group"); },
8582 delGroupAfter: function (cm) { return cm.deleteH(1, "group"); },
8583 indentAuto: function (cm) { return cm.indentSelection("smart"); },
8584 indentMore: function (cm) { return cm.indentSelection("add"); },
8585 indentLess: function (cm) { return cm.indentSelection("subtract"); },
8586 insertTab: function (cm) { return cm.replaceSelection("\t"); },
8587 insertSoftTab: function (cm) {
8588 var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize;
8589 for (var i = 0; i < ranges.length; i++) {
8590 var pos = ranges[i].from();
8591 var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize);
8592 spaces.push(spaceStr(tabSize - col % tabSize));
8593 }
8594 cm.replaceSelections(spaces);
8595 },
8596 defaultTab: function (cm) {
8597 if (cm.somethingSelected()) { cm.indentSelection("add"); }
8598 else { cm.execCommand("insertTab"); }
8599 },
8600 // Swap the two chars left and right of each selection's head.
8601 // Move cursor behind the two swapped characters afterwards.
8602 //
8603 // Doesn't consider line feeds a character.
8604 // Doesn't scan more than one line above to find a character.
8605 // Doesn't do anything on an empty line.
8606 // Doesn't do anything with non-empty selections.
8607 transposeChars: function (cm) { return runInOp(cm, function () {
8608 var ranges = cm.listSelections(), newSel = [];
8609 for (var i = 0; i < ranges.length; i++) {
8610 if (!ranges[i].empty()) { continue }
8611 var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text;
8612 if (line) {
8613 if (cur.ch == line.length) { cur = new Pos(cur.line, cur.ch - 1); }
8614 if (cur.ch > 0) {
8615 cur = new Pos(cur.line, cur.ch + 1);
8616 cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),
8617 Pos(cur.line, cur.ch - 2), cur, "+transpose");
8618 } else if (cur.line > cm.doc.first) {
8619 var prev = getLine(cm.doc, cur.line - 1).text;
8620 if (prev) {
8621 cur = new Pos(cur.line, 1);
8622 cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() +
8623 prev.charAt(prev.length - 1),
8624 Pos(cur.line - 1, prev.length - 1), cur, "+transpose");
8625 }
8626 }
8627 }
8628 newSel.push(new Range(cur, cur));
8629 }
8630 cm.setSelections(newSel);
8631 }); },
8632 newlineAndIndent: function (cm) { return runInOp(cm, function () {
8633 var sels = cm.listSelections();
8634 for (var i = sels.length - 1; i >= 0; i--)
8635 { cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input"); }
8636 sels = cm.listSelections();
8637 for (var i$1 = 0; i$1 < sels.length; i$1++)
8638 { cm.indentLine(sels[i$1].from().line, null, true); }
8639 ensureCursorVisible(cm);
8640 }); },
8641 openLine: function (cm) { return cm.replaceSelection("\n", "start"); },
8642 toggleOverwrite: function (cm) { return cm.toggleOverwrite(); }
8643 };
8644
8645
8646 function lineStart(cm, lineN) {
8647 var line = getLine(cm.doc, lineN);
8648 var visual = visualLine(line);
8649 if (visual != line) { lineN = lineNo(visual); }
8650 return endOfLine(true, cm, visual, lineN, 1)
8651 }
8652 function lineEnd(cm, lineN) {
8653 var line = getLine(cm.doc, lineN);
8654 var visual = visualLineEnd(line);
8655 if (visual != line) { lineN = lineNo(visual); }
8656 return endOfLine(true, cm, line, lineN, -1)
8657 }
8658 function lineStartSmart(cm, pos) {
8659 var start = lineStart(cm, pos.line);
8660 var line = getLine(cm.doc, start.line);
8661 var order = getOrder(line, cm.doc.direction);
8662 if (!order || order[0].level == 0) {
8663 var firstNonWS = Math.max(0, line.text.search(/\S/));
8664 var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch;
8665 return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky)
8666 }
8667 return start
8668 }
8669
8670 // Run a handler that was bound to a key.
8671 function doHandleBinding(cm, bound, dropShift) {
8672 if (typeof bound == "string") {
8673 bound = commands[bound];
8674 if (!bound) { return false }
8675 }
8676 // Ensure previous input has been read, so that the handler sees a
8677 // consistent view of the document
8678 cm.display.input.ensurePolled();
8679 var prevShift = cm.display.shift, done = false;
8680 try {
8681 if (cm.isReadOnly()) { cm.state.suppressEdits = true; }
8682 if (dropShift) { cm.display.shift = false; }
8683 done = bound(cm) != Pass;
8684 } finally {
8685 cm.display.shift = prevShift;
8686 cm.state.suppressEdits = false;
8687 }
8688 return done
8689 }
8690
8691 function lookupKeyForEditor(cm, name, handle) {
8692 for (var i = 0; i < cm.state.keyMaps.length; i++) {
8693 var result = lookupKey(name, cm.state.keyMaps[i], handle, cm);
8694 if (result) { return result }
8695 }
8696 return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm))
8697 || lookupKey(name, cm.options.keyMap, handle, cm)
8698 }
8699
8700 // Note that, despite the name, this function is also used to check
8701 // for bound mouse clicks.
8702
8703 var stopSeq = new Delayed;
8704
8705 function dispatchKey(cm, name, e, handle) {
8706 var seq = cm.state.keySeq;
8707 if (seq) {
8708 if (isModifierKey(name)) { return "handled" }
8709 if (/\'$/.test(name))
8710 { cm.state.keySeq = null; }
8711 else
8712 { stopSeq.set(50, function () {
8713 if (cm.state.keySeq == seq) {
8714 cm.state.keySeq = null;
8715 cm.display.input.reset();
8716 }
8717 }); }
8718 if (dispatchKeyInner(cm, seq + " " + name, e, handle)) { return true }
8719 }
8720 return dispatchKeyInner(cm, name, e, handle)
8721 }
8722
8723 function dispatchKeyInner(cm, name, e, handle) {
8724 var result = lookupKeyForEditor(cm, name, handle);
8725
8726 if (result == "multi")
8727 { cm.state.keySeq = name; }
8728 if (result == "handled")
8729 { signalLater(cm, "keyHandled", cm, name, e); }
8730
8731 if (result == "handled" || result == "multi") {
8732 e_preventDefault(e);
8733 restartBlink(cm);
8734 }
8735
8736 return !!result
8737 }
8738
8739 // Handle a key from the keydown event.
8740 function handleKeyBinding(cm, e) {
8741 var name = keyName(e, true);
8742 if (!name) { return false }
8743
8744 if (e.shiftKey && !cm.state.keySeq) {
8745 // First try to resolve full name (including 'Shift-'). Failing
8746 // that, see if there is a cursor-motion command (starting with
8747 // 'go') bound to the keyname without 'Shift-'.
8748 return dispatchKey(cm, "Shift-" + name, e, function (b) { return doHandleBinding(cm, b, true); })
8749 || dispatchKey(cm, name, e, function (b) {
8750 if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
8751 { return doHandleBinding(cm, b) }
8752 })
8753 } else {
8754 return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b); })
8755 }
8756 }
8757
8758 // Handle a key from the keypress event
8759 function handleCharBinding(cm, e, ch) {
8760 return dispatchKey(cm, "'" + ch + "'", e, function (b) { return doHandleBinding(cm, b, true); })
8761 }
8762
8763 var lastStoppedKey = null;
8764 function onKeyDown(e) {
8765 var cm = this;
8766 cm.curOp.focus = activeElt();
8767 if (signalDOMEvent(cm, e)) { return }
8768 // IE does strange things with escape.
8769 if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false; }
8770 var code = e.keyCode;
8771 cm.display.shift = code == 16 || e.shiftKey;
8772 var handled = handleKeyBinding(cm, e);
8773 if (presto) {
8774 lastStoppedKey = handled ? code : null;
8775 // Opera has no cut event... we try to at least catch the key combo
8776 if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
8777 { cm.replaceSelection("", null, "cut"); }
8778 }
8779
8780 // Turn mouse into crosshair when Alt is held on Mac.
8781 if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className))
8782 { showCrossHair(cm); }
8783 }
8784
8785 function showCrossHair(cm) {
8786 var lineDiv = cm.display.lineDiv;
8787 addClass(lineDiv, "CodeMirror-crosshair");
8788
8789 function up(e) {
8790 if (e.keyCode == 18 || !e.altKey) {
8791 rmClass(lineDiv, "CodeMirror-crosshair");
8792 off(document, "keyup", up);
8793 off(document, "mouseover", up);
8794 }
8795 }
8796 on(document, "keyup", up);
8797 on(document, "mouseover", up);
8798 }
8799
8800 function onKeyUp(e) {
8801 if (e.keyCode == 16) { this.doc.sel.shift = false; }
8802 signalDOMEvent(this, e);
8803 }
8804
8805 function onKeyPress(e) {
8806 var cm = this;
8807 if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return }
8808 var keyCode = e.keyCode, charCode = e.charCode;
8809 if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return}
8810 if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) { return }
8811 var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
8812 // Some browsers fire keypress events for backspace
8813 if (ch == "\x08") { return }
8814 if (handleCharBinding(cm, e, ch)) { return }
8815 cm.display.input.onKeyPress(e);
8816 }
8817
8818 var DOUBLECLICK_DELAY = 400;
8819
8820 var PastClick = function(time, pos, button) {
8821 this.time = time;
8822 this.pos = pos;
8823 this.button = button;
8824 };
8825
8826 PastClick.prototype.compare = function (time, pos, button) {
8827 return this.time + DOUBLECLICK_DELAY > time &&
8828 cmp(pos, this.pos) == 0 && button == this.button
8829 };
8830
8831 var lastClick, lastDoubleClick;
8832 function clickRepeat(pos, button) {
8833 var now = +new Date;
8834 if (lastDoubleClick && lastDoubleClick.compare(now, pos, button)) {
8835 lastClick = lastDoubleClick = null;
8836 return "triple"
8837 } else if (lastClick && lastClick.compare(now, pos, button)) {
8838 lastDoubleClick = new PastClick(now, pos, button);
8839 lastClick = null;
8840 return "double"
8841 } else {
8842 lastClick = new PastClick(now, pos, button);
8843 lastDoubleClick = null;
8844 return "single"
8845 }
8846 }
8847
8848 // A mouse down can be a single click, double click, triple click,
8849 // start of selection drag, start of text drag, new cursor
8850 // (ctrl-click), rectangle drag (alt-drag), or xwin
8851 // middle-click-paste. Or it might be a click on something we should
8852 // not interfere with, such as a scrollbar or widget.
8853 function onMouseDown(e) {
8854 var cm = this, display = cm.display;
8855 if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) { return }
8856 display.input.ensurePolled();
8857 display.shift = e.shiftKey;
8858
8859 if (eventInWidget(display, e)) {
8860 if (!webkit) {
8861 // Briefly turn off draggability, to allow widgets to do
8862 // normal dragging things.
8863 display.scroller.draggable = false;
8864 setTimeout(function () { return display.scroller.draggable = true; }, 100);
8865 }
8866 return
8867 }
8868 if (clickInGutter(cm, e)) { return }
8869 var pos = posFromMouse(cm, e), button = e_button(e), repeat = pos ? clickRepeat(pos, button) : "single";
8870 window.focus();
8871
8872 // #3261: make sure, that we're not starting a second selection
8873 if (button == 1 && cm.state.selectingText)
8874 { cm.state.selectingText(e); }
8875
8876 if (pos && handleMappedButton(cm, button, pos, repeat, e)) { return }
8877
8878 if (button == 1) {
8879 if (pos) { leftButtonDown(cm, pos, repeat, e); }
8880 else if (e_target(e) == display.scroller) { e_preventDefault(e); }
8881 } else if (button == 2) {
8882 if (pos) { extendSelection(cm.doc, pos); }
8883 setTimeout(function () { return display.input.focus(); }, 20);
8884 } else if (button == 3) {
8885 if (captureRightClick) { cm.display.input.onContextMenu(e); }
8886 else { delayBlurEvent(cm); }
8887 }
8888 }
8889
8890 function handleMappedButton(cm, button, pos, repeat, event) {
8891 var name = "Click";
8892 if (repeat == "double") { name = "Double" + name; }
8893 else if (repeat == "triple") { name = "Triple" + name; }
8894 name = (button == 1 ? "Left" : button == 2 ? "Middle" : "Right") + name;
8895
8896 return dispatchKey(cm, addModifierNames(name, event), event, function (bound) {
8897 if (typeof bound == "string") { bound = commands[bound]; }
8898 if (!bound) { return false }
8899 var done = false;
8900 try {
8901 if (cm.isReadOnly()) { cm.state.suppressEdits = true; }
8902 done = bound(cm, pos) != Pass;
8903 } finally {
8904 cm.state.suppressEdits = false;
8905 }
8906 return done
8907 })
8908 }
8909
8910 function configureMouse(cm, repeat, event) {
8911 var option = cm.getOption("configureMouse");
8912 var value = option ? option(cm, repeat, event) : {};
8913 if (value.unit == null) {
8914 var rect = chromeOS ? event.shiftKey && event.metaKey : event.altKey;
8915 value.unit = rect ? "rectangle" : repeat == "single" ? "char" : repeat == "double" ? "word" : "line";
8916 }
8917 if (value.extend == null || cm.doc.extend) { value.extend = cm.doc.extend || event.shiftKey; }
8918 if (value.addNew == null) { value.addNew = mac ? event.metaKey : event.ctrlKey; }
8919 if (value.moveOnDrag == null) { value.moveOnDrag = !(mac ? event.altKey : event.ctrlKey); }
8920 return value
8921 }
8922
8923 function leftButtonDown(cm, pos, repeat, event) {
8924 if (ie) { setTimeout(bind(ensureFocus, cm), 0); }
8925 else { cm.curOp.focus = activeElt(); }
8926
8927 var behavior = configureMouse(cm, repeat, event);
8928
8929 var sel = cm.doc.sel, contained;
8930 if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() &&
8931 repeat == "single" && (contained = sel.contains(pos)) > -1 &&
8932 (cmp((contained = sel.ranges[contained]).from(), pos) < 0 || pos.xRel > 0) &&
8933 (cmp(contained.to(), pos) > 0 || pos.xRel < 0))
8934 { leftButtonStartDrag(cm, event, pos, behavior); }
8935 else
8936 { leftButtonSelect(cm, event, pos, behavior); }
8937 }
8938
8939 // Start a text drag. When it ends, see if any dragging actually
8940 // happen, and treat as a click if it didn't.
8941 function leftButtonStartDrag(cm, event, pos, behavior) {
8942 var display = cm.display, moved = false;
8943 var dragEnd = operation(cm, function (e) {
8944 if (webkit) { display.scroller.draggable = false; }
8945 cm.state.draggingText = false;
8946 off(display.wrapper.ownerDocument, "mouseup", dragEnd);
8947 off(display.wrapper.ownerDocument, "mousemove", mouseMove);
8948 off(display.scroller, "dragstart", dragStart);
8949 off(display.scroller, "drop", dragEnd);
8950 if (!moved) {
8951 e_preventDefault(e);
8952 if (!behavior.addNew)
8953 { extendSelection(cm.doc, pos, null, null, behavior.extend); }
8954 // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)
8955 if (webkit || ie && ie_version == 9)
8956 { setTimeout(function () {display.wrapper.ownerDocument.body.focus(); display.input.focus();}, 20); }
8957 else
8958 { display.input.focus(); }
8959 }
8960 });
8961 var mouseMove = function(e2) {
8962 moved = moved || Math.abs(event.clientX - e2.clientX) + Math.abs(event.clientY - e2.clientY) >= 10;
8963 };
8964 var dragStart = function () { return moved = true; };
8965 // Let the drag handler handle this.
8966 if (webkit) { display.scroller.draggable = true; }
8967 cm.state.draggingText = dragEnd;
8968 dragEnd.copy = !behavior.moveOnDrag;
8969 // IE's approach to draggable
8970 if (display.scroller.dragDrop) { display.scroller.dragDrop(); }
8971 on(display.wrapper.ownerDocument, "mouseup", dragEnd);
8972 on(display.wrapper.ownerDocument, "mousemove", mouseMove);
8973 on(display.scroller, "dragstart", dragStart);
8974 on(display.scroller, "drop", dragEnd);
8975
8976 delayBlurEvent(cm);
8977 setTimeout(function () { return display.input.focus(); }, 20);
8978 }
8979
8980 function rangeForUnit(cm, pos, unit) {
8981 if (unit == "char") { return new Range(pos, pos) }
8982 if (unit == "word") { return cm.findWordAt(pos) }
8983 if (unit == "line") { return new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) }
8984 var result = unit(cm, pos);
8985 return new Range(result.from, result.to)
8986 }
8987
8988 // Normal selection, as opposed to text dragging.
8989 function leftButtonSelect(cm, event, start, behavior) {
8990 var display = cm.display, doc = cm.doc;
8991 e_preventDefault(event);
8992
8993 var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges;
8994 if (behavior.addNew && !behavior.extend) {
8995 ourIndex = doc.sel.contains(start);
8996 if (ourIndex > -1)
8997 { ourRange = ranges[ourIndex]; }
8998 else
8999 { ourRange = new Range(start, start); }
9000 } else {
9001 ourRange = doc.sel.primary();
9002 ourIndex = doc.sel.primIndex;
9003 }
9004
9005 if (behavior.unit == "rectangle") {
9006 if (!behavior.addNew) { ourRange = new Range(start, start); }
9007 start = posFromMouse(cm, event, true, true);
9008 ourIndex = -1;
9009 } else {
9010 var range$$1 = rangeForUnit(cm, start, behavior.unit);
9011 if (behavior.extend)
9012 { ourRange = extendRange(ourRange, range$$1.anchor, range$$1.head, behavior.extend); }
9013 else
9014 { ourRange = range$$1; }
9015 }
9016
9017 if (!behavior.addNew) {
9018 ourIndex = 0;
9019 setSelection(doc, new Selection([ourRange], 0), sel_mouse);
9020 startSel = doc.sel;
9021 } else if (ourIndex == -1) {
9022 ourIndex = ranges.length;
9023 setSelection(doc, normalizeSelection(cm, ranges.concat([ourRange]), ourIndex),
9024 {scroll: false, origin: "*mouse"});
9025 } else if (ranges.length > 1 && ranges[ourIndex].empty() && behavior.unit == "char" && !behavior.extend) {
9026 setSelection(doc, normalizeSelection(cm, ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0),
9027 {scroll: false, origin: "*mouse"});
9028 startSel = doc.sel;
9029 } else {
9030 replaceOneSelection(doc, ourIndex, ourRange, sel_mouse);
9031 }
9032
9033 var lastPos = start;
9034 function extendTo(pos) {
9035 if (cmp(lastPos, pos) == 0) { return }
9036 lastPos = pos;
9037
9038 if (behavior.unit == "rectangle") {
9039 var ranges = [], tabSize = cm.options.tabSize;
9040 var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize);
9041 var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize);
9042 var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol);
9043 for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line));
9044 line <= end; line++) {
9045 var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize);
9046 if (left == right)
9047 { ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))); }
9048 else if (text.length > leftPos)
9049 { ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))); }
9050 }
9051 if (!ranges.length) { ranges.push(new Range(start, start)); }
9052 setSelection(doc, normalizeSelection(cm, startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex),
9053 {origin: "*mouse", scroll: false});
9054 cm.scrollIntoView(pos);
9055 } else {
9056 var oldRange = ourRange;
9057 var range$$1 = rangeForUnit(cm, pos, behavior.unit);
9058 var anchor = oldRange.anchor, head;
9059 if (cmp(range$$1.anchor, anchor) > 0) {
9060 head = range$$1.head;
9061 anchor = minPos(oldRange.from(), range$$1.anchor);
9062 } else {
9063 head = range$$1.anchor;
9064 anchor = maxPos(oldRange.to(), range$$1.head);
9065 }
9066 var ranges$1 = startSel.ranges.slice(0);
9067 ranges$1[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc, anchor), head));
9068 setSelection(doc, normalizeSelection(cm, ranges$1, ourIndex), sel_mouse);
9069 }
9070 }
9071
9072 var editorSize = display.wrapper.getBoundingClientRect();
9073 // Used to ensure timeout re-tries don't fire when another extend
9074 // happened in the meantime (clearTimeout isn't reliable -- at
9075 // least on Chrome, the timeouts still happen even when cleared,
9076 // if the clear happens after their scheduled firing time).
9077 var counter = 0;
9078
9079 function extend(e) {
9080 var curCount = ++counter;
9081 var cur = posFromMouse(cm, e, true, behavior.unit == "rectangle");
9082 if (!cur) { return }
9083 if (cmp(cur, lastPos) != 0) {
9084 cm.curOp.focus = activeElt();
9085 extendTo(cur);
9086 var visible = visibleLines(display, doc);
9087 if (cur.line >= visible.to || cur.line < visible.from)
9088 { setTimeout(operation(cm, function () {if (counter == curCount) { extend(e); }}), 150); }
9089 } else {
9090 var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;
9091 if (outside) { setTimeout(operation(cm, function () {
9092 if (counter != curCount) { return }
9093 display.scroller.scrollTop += outside;
9094 extend(e);
9095 }), 50); }
9096 }
9097 }
9098
9099 function done(e) {
9100 cm.state.selectingText = false;
9101 counter = Infinity;
9102 e_preventDefault(e);
9103 display.input.focus();
9104 off(display.wrapper.ownerDocument, "mousemove", move);
9105 off(display.wrapper.ownerDocument, "mouseup", up);
9106 doc.history.lastSelOrigin = null;
9107 }
9108
9109 var move = operation(cm, function (e) {
9110 if (e.buttons === 0 || !e_button(e)) { done(e); }
9111 else { extend(e); }
9112 });
9113 var up = operation(cm, done);
9114 cm.state.selectingText = up;
9115 on(display.wrapper.ownerDocument, "mousemove", move);
9116 on(display.wrapper.ownerDocument, "mouseup", up);
9117 }
9118
9119 // Used when mouse-selecting to adjust the anchor to the proper side
9120 // of a bidi jump depending on the visual position of the head.
9121 function bidiSimplify(cm, range$$1) {
9122 var anchor = range$$1.anchor;
9123 var head = range$$1.head;
9124 var anchorLine = getLine(cm.doc, anchor.line);
9125 if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) { return range$$1 }
9126 var order = getOrder(anchorLine);
9127 if (!order) { return range$$1 }
9128 var index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index];
9129 if (part.from != anchor.ch && part.to != anchor.ch) { return range$$1 }
9130 var boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1);
9131 if (boundary == 0 || boundary == order.length) { return range$$1 }
9132
9133 // Compute the relative visual position of the head compared to the
9134 // anchor (<0 is to the left, >0 to the right)
9135 var leftSide;
9136 if (head.line != anchor.line) {
9137 leftSide = (head.line - anchor.line) * (cm.doc.direction == "ltr" ? 1 : -1) > 0;
9138 } else {
9139 var headIndex = getBidiPartAt(order, head.ch, head.sticky);
9140 var dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1);
9141 if (headIndex == boundary - 1 || headIndex == boundary)
9142 { leftSide = dir < 0; }
9143 else
9144 { leftSide = dir > 0; }
9145 }
9146
9147 var usePart = order[boundary + (leftSide ? -1 : 0)];
9148 var from = leftSide == (usePart.level == 1);
9149 var ch = from ? usePart.from : usePart.to, sticky = from ? "after" : "before";
9150 return anchor.ch == ch && anchor.sticky == sticky ? range$$1 : new Range(new Pos(anchor.line, ch, sticky), head)
9151 }
9152
9153
9154 // Determines whether an event happened in the gutter, and fires the
9155 // handlers for the corresponding event.
9156 function gutterEvent(cm, e, type, prevent) {
9157 var mX, mY;
9158 if (e.touches) {
9159 mX = e.touches[0].clientX;
9160 mY = e.touches[0].clientY;
9161 } else {
9162 try { mX = e.clientX; mY = e.clientY; }
9163 catch(e) { return false }
9164 }
9165 if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false }
9166 if (prevent) { e_preventDefault(e); }
9167
9168 var display = cm.display;
9169 var lineBox = display.lineDiv.getBoundingClientRect();
9170
9171 if (mY > lineBox.bottom || !hasHandler(cm, type)) { return e_defaultPrevented(e) }
9172 mY -= lineBox.top - display.viewOffset;
9173
9174 for (var i = 0; i < cm.options.gutters.length; ++i) {
9175 var g = display.gutters.childNodes[i];
9176 if (g && g.getBoundingClientRect().right >= mX) {
9177 var line = lineAtHeight(cm.doc, mY);
9178 var gutter = cm.options.gutters[i];
9179 signal(cm, type, cm, line, gutter, e);
9180 return e_defaultPrevented(e)
9181 }
9182 }
9183 }
9184
9185 function clickInGutter(cm, e) {
9186 return gutterEvent(cm, e, "gutterClick", true)
9187 }
9188
9189 // CONTEXT MENU HANDLING
9190
9191 // To make the context menu work, we need to briefly unhide the
9192 // textarea (making it as unobtrusive as possible) to let the
9193 // right-click take effect on it.
9194 function onContextMenu(cm, e) {
9195 if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) { return }
9196 if (signalDOMEvent(cm, e, "contextmenu")) { return }
9197 if (!captureRightClick) { cm.display.input.onContextMenu(e); }
9198 }
9199
9200 function contextMenuInGutter(cm, e) {
9201 if (!hasHandler(cm, "gutterContextMenu")) { return false }
9202 return gutterEvent(cm, e, "gutterContextMenu", false)
9203 }
9204
9205 function themeChanged(cm) {
9206 cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
9207 cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
9208 clearCaches(cm);
9209 }
9210
9211 var Init = {toString: function(){return "CodeMirror.Init"}};
9212
9213 var defaults = {};
9214 var optionHandlers = {};
9215
9216 function defineOptions(CodeMirror) {
9217 var optionHandlers = CodeMirror.optionHandlers;
9218
9219 function option(name, deflt, handle, notOnInit) {
9220 CodeMirror.defaults[name] = deflt;
9221 if (handle) { optionHandlers[name] =
9222 notOnInit ? function (cm, val, old) {if (old != Init) { handle(cm, val, old); }} : handle; }
9223 }
9224
9225 CodeMirror.defineOption = option;
9226
9227 // Passed to option handlers when there is no old value.
9228 CodeMirror.Init = Init;
9229
9230 // These two are, on init, called from the constructor because they
9231 // have to be initialized before the editor can start at all.
9232 option("value", "", function (cm, val) { return cm.setValue(val); }, true);
9233 option("mode", null, function (cm, val) {
9234 cm.doc.modeOption = val;
9235 loadMode(cm);
9236 }, true);
9237
9238 option("indentUnit", 2, loadMode, true);
9239 option("indentWithTabs", false);
9240 option("smartIndent", true);
9241 option("tabSize", 4, function (cm) {
9242 resetModeState(cm);
9243 clearCaches(cm);
9244 regChange(cm);
9245 }, true);
9246
9247 option("lineSeparator", null, function (cm, val) {
9248 cm.doc.lineSep = val;
9249 if (!val) { return }
9250 var newBreaks = [], lineNo = cm.doc.first;
9251 cm.doc.iter(function (line) {
9252 for (var pos = 0;;) {
9253 var found = line.text.indexOf(val, pos);
9254 if (found == -1) { break }
9255 pos = found + val.length;
9256 newBreaks.push(Pos(lineNo, found));
9257 }
9258 lineNo++;
9259 });
9260 for (var i = newBreaks.length - 1; i >= 0; i--)
9261 { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)); }
9262 });
9263 option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff]/g, function (cm, val, old) {
9264 cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g");
9265 if (old != Init) { cm.refresh(); }
9266 });
9267 option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function (cm) { return cm.refresh(); }, true);
9268 option("electricChars", true);
9269 option("inputStyle", mobile ? "contenteditable" : "textarea", function () {
9270 throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME
9271 }, true);
9272 option("spellcheck", false, function (cm, val) { return cm.getInputField().spellcheck = val; }, true);
9273 option("autocorrect", false, function (cm, val) { return cm.getInputField().autocorrect = val; }, true);
9274 option("autocapitalize", false, function (cm, val) { return cm.getInputField().autocapitalize = val; }, true);
9275 option("rtlMoveVisually", !windows);
9276 option("wholeLineUpdateBefore", true);
9277
9278 option("theme", "default", function (cm) {
9279 themeChanged(cm);
9280 guttersChanged(cm);
9281 }, true);
9282 option("keyMap", "default", function (cm, val, old) {
9283 var next = getKeyMap(val);
9284 var prev = old != Init && getKeyMap(old);
9285 if (prev && prev.detach) { prev.detach(cm, next); }
9286 if (next.attach) { next.attach(cm, prev || null); }
9287 });
9288 option("extraKeys", null);
9289 option("configureMouse", null);
9290
9291 option("lineWrapping", false, wrappingChanged, true);
9292 option("gutters", [], function (cm) {
9293 setGuttersForLineNumbers(cm.options);
9294 guttersChanged(cm);
9295 }, true);
9296 option("fixedGutter", true, function (cm, val) {
9297 cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
9298 cm.refresh();
9299 }, true);
9300 option("coverGutterNextToScrollbar", false, function (cm) { return updateScrollbars(cm); }, true);
9301 option("scrollbarStyle", "native", function (cm) {
9302 initScrollbars(cm);
9303 updateScrollbars(cm);
9304 cm.display.scrollbars.setScrollTop(cm.doc.scrollTop);
9305 cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft);
9306 }, true);
9307 option("lineNumbers", false, function (cm) {
9308 setGuttersForLineNumbers(cm.options);
9309 guttersChanged(cm);
9310 }, true);
9311 option("firstLineNumber", 1, guttersChanged, true);
9312 option("lineNumberFormatter", function (integer) { return integer; }, guttersChanged, true);
9313 option("showCursorWhenSelecting", false, updateSelection, true);
9314
9315 option("resetSelectionOnContextMenu", true);
9316 option("lineWiseCopyCut", true);
9317 option("pasteLinesPerSelection", true);
9318 option("selectionsMayTouch", false);
9319
9320 option("readOnly", false, function (cm, val) {
9321 if (val == "nocursor") {
9322 onBlur(cm);
9323 cm.display.input.blur();
9324 }
9325 cm.display.input.readOnlyChanged(val);
9326 });
9327 option("disableInput", false, function (cm, val) {if (!val) { cm.display.input.reset(); }}, true);
9328 option("dragDrop", true, dragDropChanged);
9329 option("allowDropFileTypes", null);
9330
9331 option("cursorBlinkRate", 530);
9332 option("cursorScrollMargin", 0);
9333 option("cursorHeight", 1, updateSelection, true);
9334 option("singleCursorHeightPerLine", true, updateSelection, true);
9335 option("workTime", 100);
9336 option("workDelay", 100);
9337 option("flattenSpans", true, resetModeState, true);
9338 option("addModeClass", false, resetModeState, true);
9339 option("pollInterval", 100);
9340 option("undoDepth", 200, function (cm, val) { return cm.doc.history.undoDepth = val; });
9341 option("historyEventDelay", 1250);
9342 option("viewportMargin", 10, function (cm) { return cm.refresh(); }, true);
9343 option("maxHighlightLength", 10000, resetModeState, true);
9344 option("moveInputWithCursor", true, function (cm, val) {
9345 if (!val) { cm.display.input.resetPosition(); }
9346 });
9347
9348 option("tabindex", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || ""; });
9349 option("autofocus", null);
9350 option("direction", "ltr", function (cm, val) { return cm.doc.setDirection(val); }, true);
9351 option("phrases", null);
9352 }
9353
9354 function guttersChanged(cm) {
9355 updateGutters(cm);
9356 regChange(cm);
9357 alignHorizontally(cm);
9358 }
9359
9360 function dragDropChanged(cm, value, old) {
9361 var wasOn = old && old != Init;
9362 if (!value != !wasOn) {
9363 var funcs = cm.display.dragFunctions;
9364 var toggle = value ? on : off;
9365 toggle(cm.display.scroller, "dragstart", funcs.start);
9366 toggle(cm.display.scroller, "dragenter", funcs.enter);
9367 toggle(cm.display.scroller, "dragover", funcs.over);
9368 toggle(cm.display.scroller, "dragleave", funcs.leave);
9369 toggle(cm.display.scroller, "drop", funcs.drop);
9370 }
9371 }
9372
9373 function wrappingChanged(cm) {
9374 if (cm.options.lineWrapping) {
9375 addClass(cm.display.wrapper, "CodeMirror-wrap");
9376 cm.display.sizer.style.minWidth = "";
9377 cm.display.sizerWidth = null;
9378 } else {
9379 rmClass(cm.display.wrapper, "CodeMirror-wrap");
9380 findMaxLine(cm);
9381 }
9382 estimateLineHeights(cm);
9383 regChange(cm);
9384 clearCaches(cm);
9385 setTimeout(function () { return updateScrollbars(cm); }, 100);
9386 }
9387
9388 // A CodeMirror instance represents an editor. This is the object
9389 // that user code is usually dealing with.
9390
9391 function CodeMirror(place, options) {
9392 var this$1 = this;
9393
9394 if (!(this instanceof CodeMirror)) { return new CodeMirror(place, options) }
9395
9396 this.options = options = options ? copyObj(options) : {};
9397 // Determine effective options based on given values and defaults.
9398 copyObj(defaults, options, false);
9399 setGuttersForLineNumbers(options);
9400
9401 var doc = options.value;
9402 if (typeof doc == "string") { doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction); }
9403 else if (options.mode) { doc.modeOption = options.mode; }
9404 this.doc = doc;
9405
9406 var input = new CodeMirror.inputStyles[options.inputStyle](this);
9407 var display = this.display = new Display(place, doc, input);
9408 display.wrapper.CodeMirror = this;
9409 updateGutters(this);
9410 themeChanged(this);
9411 if (options.lineWrapping)
9412 { this.display.wrapper.className += " CodeMirror-wrap"; }
9413 initScrollbars(this);
9414
9415 this.state = {
9416 keyMaps: [], // stores maps added by addKeyMap
9417 overlays: [], // highlighting overlays, as added by addOverlay
9418 modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info
9419 overwrite: false,
9420 delayingBlurEvent: false,
9421 focused: false,
9422 suppressEdits: false, // used to disable editing during key handlers when in readOnly mode
9423 pasteIncoming: -1, cutIncoming: -1, // help recognize paste/cut edits in input.poll
9424 selectingText: false,
9425 draggingText: false,
9426 highlight: new Delayed(), // stores highlight worker timeout
9427 keySeq: null, // Unfinished key sequence
9428 specialChars: null
9429 };
9430
9431 if (options.autofocus && !mobile) { display.input.focus(); }
9432
9433 // Override magic textarea content restore that IE sometimes does
9434 // on our hidden textarea on reload
9435 if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20); }
9436
9437 registerEventHandlers(this);
9438 ensureGlobalHandlers();
9439
9440 startOperation(this);
9441 this.curOp.forceUpdate = true;
9442 attachDoc(this, doc);
9443
9444 if ((options.autofocus && !mobile) || this.hasFocus())
9445 { setTimeout(bind(onFocus, this), 20); }
9446 else
9447 { onBlur(this); }
9448
9449 for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt))
9450 { optionHandlers[opt](this$1, options[opt], Init); } }
9451 maybeUpdateLineNumberWidth(this);
9452 if (options.finishInit) { options.finishInit(this); }
9453 for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this$1); }
9454 endOperation(this);
9455 // Suppress optimizelegibility in Webkit, since it breaks text
9456 // measuring on line wrapping boundaries.
9457 if (webkit && options.lineWrapping &&
9458 getComputedStyle(display.lineDiv).textRendering == "optimizelegibility")
9459 { display.lineDiv.style.textRendering = "auto"; }
9460 }
9461
9462 // The default configuration options.
9463 CodeMirror.defaults = defaults;
9464 // Functions to run when options are changed.
9465 CodeMirror.optionHandlers = optionHandlers;
9466
9467 // Attach the necessary event handlers when initializing the editor
9468 function registerEventHandlers(cm) {
9469 var d = cm.display;
9470 on(d.scroller, "mousedown", operation(cm, onMouseDown));
9471 // Older IE's will not fire a second mousedown for a double click
9472 if (ie && ie_version < 11)
9473 { on(d.scroller, "dblclick", operation(cm, function (e) {
9474 if (signalDOMEvent(cm, e)) { return }
9475 var pos = posFromMouse(cm, e);
9476 if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) { return }
9477 e_preventDefault(e);
9478 var word = cm.findWordAt(pos);
9479 extendSelection(cm.doc, word.anchor, word.head);
9480 })); }
9481 else
9482 { on(d.scroller, "dblclick", function (e) { return signalDOMEvent(cm, e) || e_preventDefault(e); }); }
9483 // Some browsers fire contextmenu *after* opening the menu, at
9484 // which point we can't mess with it anymore. Context menu is
9485 // handled in onMouseDown for these browsers.
9486 on(d.scroller, "contextmenu", function (e) { return onContextMenu(cm, e); });
9487
9488 // Used to suppress mouse event handling when a touch happens
9489 var touchFinished, prevTouch = {end: 0};
9490 function finishTouch() {
9491 if (d.activeTouch) {
9492 touchFinished = setTimeout(function () { return d.activeTouch = null; }, 1000);
9493 prevTouch = d.activeTouch;
9494 prevTouch.end = +new Date;
9495 }
9496 }
9497 function isMouseLikeTouchEvent(e) {
9498 if (e.touches.length != 1) { return false }
9499 var touch = e.touches[0];
9500 return touch.radiusX <= 1 && touch.radiusY <= 1
9501 }
9502 function farAway(touch, other) {
9503 if (other.left == null) { return true }
9504 var dx = other.left - touch.left, dy = other.top - touch.top;
9505 return dx * dx + dy * dy > 20 * 20
9506 }
9507 on(d.scroller, "touchstart", function (e) {
9508 if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e) && !clickInGutter(cm, e)) {
9509 d.input.ensurePolled();
9510 clearTimeout(touchFinished);
9511 var now = +new Date;
9512 d.activeTouch = {start: now, moved: false,
9513 prev: now - prevTouch.end <= 300 ? prevTouch : null};
9514 if (e.touches.length == 1) {
9515 d.activeTouch.left = e.touches[0].pageX;
9516 d.activeTouch.top = e.touches[0].pageY;
9517 }
9518 }
9519 });
9520 on(d.scroller, "touchmove", function () {
9521 if (d.activeTouch) { d.activeTouch.moved = true; }
9522 });
9523 on(d.scroller, "touchend", function (e) {
9524 var touch = d.activeTouch;
9525 if (touch && !eventInWidget(d, e) && touch.left != null &&
9526 !touch.moved && new Date - touch.start < 300) {
9527 var pos = cm.coordsChar(d.activeTouch, "page"), range;
9528 if (!touch.prev || farAway(touch, touch.prev)) // Single tap
9529 { range = new Range(pos, pos); }
9530 else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap
9531 { range = cm.findWordAt(pos); }
9532 else // Triple tap
9533 { range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))); }
9534 cm.setSelection(range.anchor, range.head);
9535 cm.focus();
9536 e_preventDefault(e);
9537 }
9538 finishTouch();
9539 });
9540 on(d.scroller, "touchcancel", finishTouch);
9541
9542 // Sync scrolling between fake scrollbars and real scrollable
9543 // area, ensure viewport is updated when scrolling.
9544 on(d.scroller, "scroll", function () {
9545 if (d.scroller.clientHeight) {
9546 updateScrollTop(cm, d.scroller.scrollTop);
9547 setScrollLeft(cm, d.scroller.scrollLeft, true);
9548 signal(cm, "scroll", cm);
9549 }
9550 });
9551
9552 // Listen to wheel events in order to try and update the viewport on time.
9553 on(d.scroller, "mousewheel", function (e) { return onScrollWheel(cm, e); });
9554 on(d.scroller, "DOMMouseScroll", function (e) { return onScrollWheel(cm, e); });
9555
9556 // Prevent wrapper from ever scrolling
9557 on(d.wrapper, "scroll", function () { return d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
9558
9559 d.dragFunctions = {
9560 enter: function (e) {if (!signalDOMEvent(cm, e)) { e_stop(e); }},
9561 over: function (e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e); }},
9562 start: function (e) { return onDragStart(cm, e); },
9563 drop: operation(cm, onDrop),
9564 leave: function (e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm); }}
9565 };
9566
9567 var inp = d.input.getField();
9568 on(inp, "keyup", function (e) { return onKeyUp.call(cm, e); });
9569 on(inp, "keydown", operation(cm, onKeyDown));
9570 on(inp, "keypress", operation(cm, onKeyPress));
9571 on(inp, "focus", function (e) { return onFocus(cm, e); });
9572 on(inp, "blur", function (e) { return onBlur(cm, e); });
9573 }
9574
9575 var initHooks = [];
9576 CodeMirror.defineInitHook = function (f) { return initHooks.push(f); };
9577
9578 // Indent the given line. The how parameter can be "smart",
9579 // "add"/null, "subtract", or "prev". When aggressive is false
9580 // (typically set to true for forced single-line indents), empty
9581 // lines are not indented, and places where the mode returns Pass
9582 // are left alone.
9583 function indentLine(cm, n, how, aggressive) {
9584 var doc = cm.doc, state;
9585 if (how == null) { how = "add"; }
9586 if (how == "smart") {
9587 // Fall back to "prev" when the mode doesn't have an indentation
9588 // method.
9589 if (!doc.mode.indent) { how = "prev"; }
9590 else { state = getContextBefore(cm, n).state; }
9591 }
9592
9593 var tabSize = cm.options.tabSize;
9594 var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
9595 if (line.stateAfter) { line.stateAfter = null; }
9596 var curSpaceString = line.text.match(/^\s*/)[0], indentation;
9597 if (!aggressive && !/\S/.test(line.text)) {
9598 indentation = 0;
9599 how = "not";
9600 } else if (how == "smart") {
9601 indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
9602 if (indentation == Pass || indentation > 150) {
9603 if (!aggressive) { return }
9604 how = "prev";
9605 }
9606 }
9607 if (how == "prev") {
9608 if (n > doc.first) { indentation = countColumn(getLine(doc, n-1).text, null, tabSize); }
9609 else { indentation = 0; }
9610 } else if (how == "add") {
9611 indentation = curSpace + cm.options.indentUnit;
9612 } else if (how == "subtract") {
9613 indentation = curSpace - cm.options.indentUnit;
9614 } else if (typeof how == "number") {
9615 indentation = curSpace + how;
9616 }
9617 indentation = Math.max(0, indentation);
9618
9619 var indentString = "", pos = 0;
9620 if (cm.options.indentWithTabs)
9621 { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";} }
9622 if (pos < indentation) { indentString += spaceStr(indentation - pos); }
9623
9624 if (indentString != curSpaceString) {
9625 replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
9626 line.stateAfter = null;
9627 return true
9628 } else {
9629 // Ensure that, if the cursor was in the whitespace at the start
9630 // of the line, it is moved to the end of that space.
9631 for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) {
9632 var range = doc.sel.ranges[i$1];
9633 if (range.head.line == n && range.head.ch < curSpaceString.length) {
9634 var pos$1 = Pos(n, curSpaceString.length);
9635 replaceOneSelection(doc, i$1, new Range(pos$1, pos$1));
9636 break
9637 }
9638 }
9639 }
9640 }
9641
9642 // This will be set to a {lineWise: bool, text: [string]} object, so
9643 // that, when pasting, we know what kind of selections the copied
9644 // text was made out of.
9645 var lastCopied = null;
9646
9647 function setLastCopied(newLastCopied) {
9648 lastCopied = newLastCopied;
9649 }
9650
9651 function applyTextInput(cm, inserted, deleted, sel, origin) {
9652 var doc = cm.doc;
9653 cm.display.shift = false;
9654 if (!sel) { sel = doc.sel; }
9655
9656 var recent = +new Date - 200;
9657 var paste = origin == "paste" || cm.state.pasteIncoming > recent;
9658 var textLines = splitLinesAuto(inserted), multiPaste = null;
9659 // When pasting N lines into N selections, insert one line per selection
9660 if (paste && sel.ranges.length > 1) {
9661 if (lastCopied && lastCopied.text.join("\n") == inserted) {
9662 if (sel.ranges.length % lastCopied.text.length == 0) {
9663 multiPaste = [];
9664 for (var i = 0; i < lastCopied.text.length; i++)
9665 { multiPaste.push(doc.splitLines(lastCopied.text[i])); }
9666 }
9667 } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) {
9668 multiPaste = map(textLines, function (l) { return [l]; });
9669 }
9670 }
9671
9672 var updateInput = cm.curOp.updateInput;
9673 // Normal behavior is to insert the new text into every selection
9674 for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) {
9675 var range$$1 = sel.ranges[i$1];
9676 var from = range$$1.from(), to = range$$1.to();
9677 if (range$$1.empty()) {
9678 if (deleted && deleted > 0) // Handle deletion
9679 { from = Pos(from.line, from.ch - deleted); }
9680 else if (cm.state.overwrite && !paste) // Handle overwrite
9681 { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); }
9682 else if (paste && lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == inserted)
9683 { from = to = Pos(from.line, 0); }
9684 }
9685 var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines,
9686 origin: origin || (paste ? "paste" : cm.state.cutIncoming > recent ? "cut" : "+input")};
9687 makeChange(cm.doc, changeEvent);
9688 signalLater(cm, "inputRead", cm, changeEvent);
9689 }
9690 if (inserted && !paste)
9691 { triggerElectric(cm, inserted); }
9692
9693 ensureCursorVisible(cm);
9694 if (cm.curOp.updateInput < 2) { cm.curOp.updateInput = updateInput; }
9695 cm.curOp.typing = true;
9696 cm.state.pasteIncoming = cm.state.cutIncoming = -1;
9697 }
9698
9699 function handlePaste(e, cm) {
9700 var pasted = e.clipboardData && e.clipboardData.getData("Text");
9701 if (pasted) {
9702 e.preventDefault();
9703 if (!cm.isReadOnly() && !cm.options.disableInput)
9704 { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, "paste"); }); }
9705 return true
9706 }
9707 }
9708
9709 function triggerElectric(cm, inserted) {
9710 // When an 'electric' character is inserted, immediately trigger a reindent
9711 if (!cm.options.electricChars || !cm.options.smartIndent) { return }
9712 var sel = cm.doc.sel;
9713
9714 for (var i = sel.ranges.length - 1; i >= 0; i--) {
9715 var range$$1 = sel.ranges[i];
9716 if (range$$1.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range$$1.head.line)) { continue }
9717 var mode = cm.getModeAt(range$$1.head);
9718 var indented = false;
9719 if (mode.electricChars) {
9720 for (var j = 0; j < mode.electricChars.length; j++)
9721 { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
9722 indented = indentLine(cm, range$$1.head.line, "smart");
9723 break
9724 } }
9725 } else if (mode.electricInput) {
9726 if (mode.electricInput.test(getLine(cm.doc, range$$1.head.line).text.slice(0, range$$1.head.ch)))
9727 { indented = indentLine(cm, range$$1.head.line, "smart"); }
9728 }
9729 if (indented) { signalLater(cm, "electricInput", cm, range$$1.head.line); }
9730 }
9731 }
9732
9733 function copyableRanges(cm) {
9734 var text = [], ranges = [];
9735 for (var i = 0; i < cm.doc.sel.ranges.length; i++) {
9736 var line = cm.doc.sel.ranges[i].head.line;
9737 var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)};
9738 ranges.push(lineRange);
9739 text.push(cm.getRange(lineRange.anchor, lineRange.head));
9740 }
9741 return {text: text, ranges: ranges}
9742 }
9743
9744 function disableBrowserMagic(field, spellcheck, autocorrect, autocapitalize) {
9745 field.setAttribute("autocorrect", !!autocorrect);
9746 field.setAttribute("autocapitalize", !!autocapitalize);
9747 field.setAttribute("spellcheck", !!spellcheck);
9748 }
9749
9750 function hiddenTextarea() {
9751 var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none");
9752 var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
9753 // The textarea is kept positioned near the cursor to prevent the
9754 // fact that it'll be scrolled into view on input from scrolling
9755 // our fake cursor out of view. On webkit, when wrap=off, paste is
9756 // very slow. So make the area wide instead.
9757 if (webkit) { te.style.width = "1000px"; }
9758 else { te.setAttribute("wrap", "off"); }
9759 // If border: 0; -- iOS fails to open keyboard (issue #1287)
9760 if (ios) { te.style.border = "1px solid black"; }
9761 disableBrowserMagic(te);
9762 return div
9763 }
9764
9765 // The publicly visible API. Note that methodOp(f) means
9766 // 'wrap f in an operation, performed on its `this` parameter'.
9767
9768 // This is not the complete set of editor methods. Most of the
9769 // methods defined on the Doc type are also injected into
9770 // CodeMirror.prototype, for backwards compatibility and
9771 // convenience.
9772
9773 function addEditorMethods(CodeMirror) {
9774 var optionHandlers = CodeMirror.optionHandlers;
9775
9776 var helpers = CodeMirror.helpers = {};
9777
9778 CodeMirror.prototype = {
9779 constructor: CodeMirror,
9780 focus: function(){window.focus(); this.display.input.focus();},
9781
9782 setOption: function(option, value) {
9783 var options = this.options, old = options[option];
9784 if (options[option] == value && option != "mode") { return }
9785 options[option] = value;
9786 if (optionHandlers.hasOwnProperty(option))
9787 { operation(this, optionHandlers[option])(this, value, old); }
9788 signal(this, "optionChange", this, option);
9789 },
9790
9791 getOption: function(option) {return this.options[option]},
9792 getDoc: function() {return this.doc},
9793
9794 addKeyMap: function(map$$1, bottom) {
9795 this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map$$1));
9796 },
9797 removeKeyMap: function(map$$1) {
9798 var maps = this.state.keyMaps;
9799 for (var i = 0; i < maps.length; ++i)
9800 { if (maps[i] == map$$1 || maps[i].name == map$$1) {
9801 maps.splice(i, 1);
9802 return true
9803 } }
9804 },
9805
9806 addOverlay: methodOp(function(spec, options) {
9807 var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
9808 if (mode.startState) { throw new Error("Overlays may not be stateful.") }
9809 insertSorted(this.state.overlays,
9810 {mode: mode, modeSpec: spec, opaque: options && options.opaque,
9811 priority: (options && options.priority) || 0},
9812 function (overlay) { return overlay.priority; });
9813 this.state.modeGen++;
9814 regChange(this);
9815 }),
9816 removeOverlay: methodOp(function(spec) {
9817 var this$1 = this;
9818
9819 var overlays = this.state.overlays;
9820 for (var i = 0; i < overlays.length; ++i) {
9821 var cur = overlays[i].modeSpec;
9822 if (cur == spec || typeof spec == "string" && cur.name == spec) {
9823 overlays.splice(i, 1);
9824 this$1.state.modeGen++;
9825 regChange(this$1);
9826 return
9827 }
9828 }
9829 }),
9830
9831 indentLine: methodOp(function(n, dir, aggressive) {
9832 if (typeof dir != "string" && typeof dir != "number") {
9833 if (dir == null) { dir = this.options.smartIndent ? "smart" : "prev"; }
9834 else { dir = dir ? "add" : "subtract"; }
9835 }
9836 if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive); }
9837 }),
9838 indentSelection: methodOp(function(how) {
9839 var this$1 = this;
9840
9841 var ranges = this.doc.sel.ranges, end = -1;
9842 for (var i = 0; i < ranges.length; i++) {
9843 var range$$1 = ranges[i];
9844 if (!range$$1.empty()) {
9845 var from = range$$1.from(), to = range$$1.to();
9846 var start = Math.max(end, from.line);
9847 end = Math.min(this$1.lastLine(), to.line - (to.ch ? 0 : 1)) + 1;
9848 for (var j = start; j < end; ++j)
9849 { indentLine(this$1, j, how); }
9850 var newRanges = this$1.doc.sel.ranges;
9851 if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0)
9852 { replaceOneSelection(this$1.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll); }
9853 } else if (range$$1.head.line > end) {
9854 indentLine(this$1, range$$1.head.line, how, true);
9855 end = range$$1.head.line;
9856 if (i == this$1.doc.sel.primIndex) { ensureCursorVisible(this$1); }
9857 }
9858 }
9859 }),
9860
9861 // Fetch the parser token for a given character. Useful for hacks
9862 // that want to inspect the mode state (say, for completion).
9863 getTokenAt: function(pos, precise) {
9864 return takeToken(this, pos, precise)
9865 },
9866
9867 getLineTokens: function(line, precise) {
9868 return takeToken(this, Pos(line), precise, true)
9869 },
9870
9871 getTokenTypeAt: function(pos) {
9872 pos = clipPos(this.doc, pos);
9873 var styles = getLineStyles(this, getLine(this.doc, pos.line));
9874 var before = 0, after = (styles.length - 1) / 2, ch = pos.ch;
9875 var type;
9876 if (ch == 0) { type = styles[2]; }
9877 else { for (;;) {
9878 var mid = (before + after) >> 1;
9879 if ((mid ? styles[mid * 2 - 1] : 0) >= ch) { after = mid; }
9880 else if (styles[mid * 2 + 1] < ch) { before = mid + 1; }
9881 else { type = styles[mid * 2 + 2]; break }
9882 } }
9883 var cut = type ? type.indexOf("overlay ") : -1;
9884 return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1)
9885 },
9886
9887 getModeAt: function(pos) {
9888 var mode = this.doc.mode;
9889 if (!mode.innerMode) { return mode }
9890 return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode
9891 },
9892
9893 getHelper: function(pos, type) {
9894 return this.getHelpers(pos, type)[0]
9895 },
9896
9897 getHelpers: function(pos, type) {
9898 var this$1 = this;
9899
9900 var found = [];
9901 if (!helpers.hasOwnProperty(type)) { return found }
9902 var help = helpers[type], mode = this.getModeAt(pos);
9903 if (typeof mode[type] == "string") {
9904 if (help[mode[type]]) { found.push(help[mode[type]]); }
9905 } else if (mode[type]) {
9906 for (var i = 0; i < mode[type].length; i++) {
9907 var val = help[mode[type][i]];
9908 if (val) { found.push(val); }
9909 }
9910 } else if (mode.helperType && help[mode.helperType]) {
9911 found.push(help[mode.helperType]);
9912 } else if (help[mode.name]) {
9913 found.push(help[mode.name]);
9914 }
9915 for (var i$1 = 0; i$1 < help._global.length; i$1++) {
9916 var cur = help._global[i$1];
9917 if (cur.pred(mode, this$1) && indexOf(found, cur.val) == -1)
9918 { found.push(cur.val); }
9919 }
9920 return found
9921 },
9922
9923 getStateAfter: function(line, precise) {
9924 var doc = this.doc;
9925 line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);
9926 return getContextBefore(this, line + 1, precise).state
9927 },
9928
9929 cursorCoords: function(start, mode) {
9930 var pos, range$$1 = this.doc.sel.primary();
9931 if (start == null) { pos = range$$1.head; }
9932 else if (typeof start == "object") { pos = clipPos(this.doc, start); }
9933 else { pos = start ? range$$1.from() : range$$1.to(); }
9934 return cursorCoords(this, pos, mode || "page")
9935 },
9936
9937 charCoords: function(pos, mode) {
9938 return charCoords(this, clipPos(this.doc, pos), mode || "page")
9939 },
9940
9941 coordsChar: function(coords, mode) {
9942 coords = fromCoordSystem(this, coords, mode || "page");
9943 return coordsChar(this, coords.left, coords.top)
9944 },
9945
9946 lineAtHeight: function(height, mode) {
9947 height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top;
9948 return lineAtHeight(this.doc, height + this.display.viewOffset)
9949 },
9950 heightAtLine: function(line, mode, includeWidgets) {
9951 var end = false, lineObj;
9952 if (typeof line == "number") {
9953 var last = this.doc.first + this.doc.size - 1;
9954 if (line < this.doc.first) { line = this.doc.first; }
9955 else if (line > last) { line = last; end = true; }
9956 lineObj = getLine(this.doc, line);
9957 } else {
9958 lineObj = line;
9959 }
9960 return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page", includeWidgets || end).top +
9961 (end ? this.doc.height - heightAtLine(lineObj) : 0)
9962 },
9963
9964 defaultTextHeight: function() { return textHeight(this.display) },
9965 defaultCharWidth: function() { return charWidth(this.display) },
9966
9967 getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}},
9968
9969 addWidget: function(pos, node, scroll, vert, horiz) {
9970 var display = this.display;
9971 pos = cursorCoords(this, clipPos(this.doc, pos));
9972 var top = pos.bottom, left = pos.left;
9973 node.style.position = "absolute";
9974 node.setAttribute("cm-ignore-events", "true");
9975 this.display.input.setUneditable(node);
9976 display.sizer.appendChild(node);
9977 if (vert == "over") {
9978 top = pos.top;
9979 } else if (vert == "above" || vert == "near") {
9980 var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
9981 hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
9982 // Default to positioning above (if specified and possible); otherwise default to positioning below
9983 if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
9984 { top = pos.top - node.offsetHeight; }
9985 else if (pos.bottom + node.offsetHeight <= vspace)
9986 { top = pos.bottom; }
9987 if (left + node.offsetWidth > hspace)
9988 { left = hspace - node.offsetWidth; }
9989 }
9990 node.style.top = top + "px";
9991 node.style.left = node.style.right = "";
9992 if (horiz == "right") {
9993 left = display.sizer.clientWidth - node.offsetWidth;
9994 node.style.right = "0px";
9995 } else {
9996 if (horiz == "left") { left = 0; }
9997 else if (horiz == "middle") { left = (display.sizer.clientWidth - node.offsetWidth) / 2; }
9998 node.style.left = left + "px";
9999 }
10000 if (scroll)
10001 { scrollIntoView(this, {left: left, top: top, right: left + node.offsetWidth, bottom: top + node.offsetHeight}); }
10002 },
10003
10004 triggerOnKeyDown: methodOp(onKeyDown),
10005 triggerOnKeyPress: methodOp(onKeyPress),
10006 triggerOnKeyUp: onKeyUp,
10007 triggerOnMouseDown: methodOp(onMouseDown),
10008
10009 execCommand: function(cmd) {
10010 if (commands.hasOwnProperty(cmd))
10011 { return commands[cmd].call(null, this) }
10012 },
10013
10014 triggerElectric: methodOp(function(text) { triggerElectric(this, text); }),
10015
10016 findPosH: function(from, amount, unit, visually) {
10017 var this$1 = this;
10018
10019 var dir = 1;
10020 if (amount < 0) { dir = -1; amount = -amount; }
10021 var cur = clipPos(this.doc, from);
10022 for (var i = 0; i < amount; ++i) {
10023 cur = findPosH(this$1.doc, cur, dir, unit, visually);
10024 if (cur.hitSide) { break }
10025 }
10026 return cur
10027 },
10028
10029 moveH: methodOp(function(dir, unit) {
10030 var this$1 = this;
10031
10032 this.extendSelectionsBy(function (range$$1) {
10033 if (this$1.display.shift || this$1.doc.extend || range$$1.empty())
10034 { return findPosH(this$1.doc, range$$1.head, dir, unit, this$1.options.rtlMoveVisually) }
10035 else
10036 { return dir < 0 ? range$$1.from() : range$$1.to() }
10037 }, sel_move);
10038 }),
10039
10040 deleteH: methodOp(function(dir, unit) {
10041 var sel = this.doc.sel, doc = this.doc;
10042 if (sel.somethingSelected())
10043 { doc.replaceSelection("", null, "+delete"); }
10044 else
10045 { deleteNearSelection(this, function (range$$1) {
10046 var other = findPosH(doc, range$$1.head, dir, unit, false);
10047 return dir < 0 ? {from: other, to: range$$1.head} : {from: range$$1.head, to: other}
10048 }); }
10049 }),
10050
10051 findPosV: function(from, amount, unit, goalColumn) {
10052 var this$1 = this;
10053
10054 var dir = 1, x = goalColumn;
10055 if (amount < 0) { dir = -1; amount = -amount; }
10056 var cur = clipPos(this.doc, from);
10057 for (var i = 0; i < amount; ++i) {
10058 var coords = cursorCoords(this$1, cur, "div");
10059 if (x == null) { x = coords.left; }
10060 else { coords.left = x; }
10061 cur = findPosV(this$1, coords, dir, unit);
10062 if (cur.hitSide) { break }
10063 }
10064 return cur
10065 },
10066
10067 moveV: methodOp(function(dir, unit) {
10068 var this$1 = this;
10069
10070 var doc = this.doc, goals = [];
10071 var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected();
10072 doc.extendSelectionsBy(function (range$$1) {
10073 if (collapse)
10074 { return dir < 0 ? range$$1.from() : range$$1.to() }
10075 var headPos = cursorCoords(this$1, range$$1.head, "div");
10076 if (range$$1.goalColumn != null) { headPos.left = range$$1.goalColumn; }
10077 goals.push(headPos.left);
10078 var pos = findPosV(this$1, headPos, dir, unit);
10079 if (unit == "page" && range$$1 == doc.sel.primary())
10080 { addToScrollTop(this$1, charCoords(this$1, pos, "div").top - headPos.top); }
10081 return pos
10082 }, sel_move);
10083 if (goals.length) { for (var i = 0; i < doc.sel.ranges.length; i++)
10084 { doc.sel.ranges[i].goalColumn = goals[i]; } }
10085 }),
10086
10087 // Find the word at the given position (as returned by coordsChar).
10088 findWordAt: function(pos) {
10089 var doc = this.doc, line = getLine(doc, pos.line).text;
10090 var start = pos.ch, end = pos.ch;
10091 if (line) {
10092 var helper = this.getHelper(pos, "wordChars");
10093 if ((pos.sticky == "before" || end == line.length) && start) { --start; } else { ++end; }
10094 var startChar = line.charAt(start);
10095 var check = isWordChar(startChar, helper)
10096 ? function (ch) { return isWordChar(ch, helper); }
10097 : /\s/.test(startChar) ? function (ch) { return /\s/.test(ch); }
10098 : function (ch) { return (!/\s/.test(ch) && !isWordChar(ch)); };
10099 while (start > 0 && check(line.charAt(start - 1))) { --start; }
10100 while (end < line.length && check(line.charAt(end))) { ++end; }
10101 }
10102 return new Range(Pos(pos.line, start), Pos(pos.line, end))
10103 },
10104
10105 toggleOverwrite: function(value) {
10106 if (value != null && value == this.state.overwrite) { return }
10107 if (this.state.overwrite = !this.state.overwrite)
10108 { addClass(this.display.cursorDiv, "CodeMirror-overwrite"); }
10109 else
10110 { rmClass(this.display.cursorDiv, "CodeMirror-overwrite"); }
10111
10112 signal(this, "overwriteToggle", this, this.state.overwrite);
10113 },
10114 hasFocus: function() { return this.display.input.getField() == activeElt() },
10115 isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) },
10116
10117 scrollTo: methodOp(function (x, y) { scrollToCoords(this, x, y); }),
10118 getScrollInfo: function() {
10119 var scroller = this.display.scroller;
10120 return {left: scroller.scrollLeft, top: scroller.scrollTop,
10121 height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight,
10122 width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth,
10123 clientHeight: displayHeight(this), clientWidth: displayWidth(this)}
10124 },
10125
10126 scrollIntoView: methodOp(function(range$$1, margin) {
10127 if (range$$1 == null) {
10128 range$$1 = {from: this.doc.sel.primary().head, to: null};
10129 if (margin == null) { margin = this.options.cursorScrollMargin; }
10130 } else if (typeof range$$1 == "number") {
10131 range$$1 = {from: Pos(range$$1, 0), to: null};
10132 } else if (range$$1.from == null) {
10133 range$$1 = {from: range$$1, to: null};
10134 }
10135 if (!range$$1.to) { range$$1.to = range$$1.from; }
10136 range$$1.margin = margin || 0;
10137
10138 if (range$$1.from.line != null) {
10139 scrollToRange(this, range$$1);
10140 } else {
10141 scrollToCoordsRange(this, range$$1.from, range$$1.to, range$$1.margin);
10142 }
10143 }),
10144
10145 setSize: methodOp(function(width, height) {
10146 var this$1 = this;
10147
10148 var interpret = function (val) { return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; };
10149 if (width != null) { this.display.wrapper.style.width = interpret(width); }
10150 if (height != null) { this.display.wrapper.style.height = interpret(height); }
10151 if (this.options.lineWrapping) { clearLineMeasurementCache(this); }
10152 var lineNo$$1 = this.display.viewFrom;
10153 this.doc.iter(lineNo$$1, this.display.viewTo, function (line) {
10154 if (line.widgets) { for (var i = 0; i < line.widgets.length; i++)
10155 { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo$$1, "widget"); break } } }
10156 ++lineNo$$1;
10157 });
10158 this.curOp.forceUpdate = true;
10159 signal(this, "refresh", this);
10160 }),
10161
10162 operation: function(f){return runInOp(this, f)},
10163 startOperation: function(){return startOperation(this)},
10164 endOperation: function(){return endOperation(this)},
10165
10166 refresh: methodOp(function() {
10167 var oldHeight = this.display.cachedTextHeight;
10168 regChange(this);
10169 this.curOp.forceUpdate = true;
10170 clearCaches(this);
10171 scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop);
10172 updateGutterSpace(this);
10173 if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5)
10174 { estimateLineHeights(this); }
10175 signal(this, "refresh", this);
10176 }),
10177
10178 swapDoc: methodOp(function(doc) {
10179 var old = this.doc;
10180 old.cm = null;
10181 attachDoc(this, doc);
10182 clearCaches(this);
10183 this.display.input.reset();
10184 scrollToCoords(this, doc.scrollLeft, doc.scrollTop);
10185 this.curOp.forceScroll = true;
10186 signalLater(this, "swapDoc", this, old);
10187 return old
10188 }),
10189
10190 phrase: function(phraseText) {
10191 var phrases = this.options.phrases;
10192 return phrases && Object.prototype.hasOwnProperty.call(phrases, phraseText) ? phrases[phraseText] : phraseText
10193 },
10194
10195 getInputField: function(){return this.display.input.getField()},
10196 getWrapperElement: function(){return this.display.wrapper},
10197 getScrollerElement: function(){return this.display.scroller},
10198 getGutterElement: function(){return this.display.gutters}
10199 };
10200 eventMixin(CodeMirror);
10201
10202 CodeMirror.registerHelper = function(type, name, value) {
10203 if (!helpers.hasOwnProperty(type)) { helpers[type] = CodeMirror[type] = {_global: []}; }
10204 helpers[type][name] = value;
10205 };
10206 CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {
10207 CodeMirror.registerHelper(type, name, value);
10208 helpers[type]._global.push({pred: predicate, val: value});
10209 };
10210 }
10211
10212 // Used for horizontal relative motion. Dir is -1 or 1 (left or
10213 // right), unit can be "char", "column" (like char, but doesn't
10214 // cross line boundaries), "word" (across next word), or "group" (to
10215 // the start of next group of word or non-word-non-whitespace
10216 // chars). The visually param controls whether, in right-to-left
10217 // text, direction 1 means to move towards the next index in the
10218 // string, or towards the character to the right of the current
10219 // position. The resulting position will have a hitSide=true
10220 // property if it reached the end of the document.
10221 function findPosH(doc, pos, dir, unit, visually) {
10222 var oldPos = pos;
10223 var origDir = dir;
10224 var lineObj = getLine(doc, pos.line);
10225 function findNextLine() {
10226 var l = pos.line + dir;
10227 if (l < doc.first || l >= doc.first + doc.size) { return false }
10228 pos = new Pos(l, pos.ch, pos.sticky);
10229 return lineObj = getLine(doc, l)
10230 }
10231 function moveOnce(boundToLine) {
10232 var next;
10233 if (visually) {
10234 next = moveVisually(doc.cm, lineObj, pos, dir);
10235 } else {
10236 next = moveLogically(lineObj, pos, dir);
10237 }
10238 if (next == null) {
10239 if (!boundToLine && findNextLine())
10240 { pos = endOfLine(visually, doc.cm, lineObj, pos.line, dir); }
10241 else
10242 { return false }
10243 } else {
10244 pos = next;
10245 }
10246 return true
10247 }
10248
10249 if (unit == "char") {
10250 moveOnce();
10251 } else if (unit == "column") {
10252 moveOnce(true);
10253 } else if (unit == "word" || unit == "group") {
10254 var sawType = null, group = unit == "group";
10255 var helper = doc.cm && doc.cm.getHelper(pos, "wordChars");
10256 for (var first = true;; first = false) {
10257 if (dir < 0 && !moveOnce(!first)) { break }
10258 var cur = lineObj.text.charAt(pos.ch) || "\n";
10259 var type = isWordChar(cur, helper) ? "w"
10260 : group && cur == "\n" ? "n"
10261 : !group || /\s/.test(cur) ? null
10262 : "p";
10263 if (group && !first && !type) { type = "s"; }
10264 if (sawType && sawType != type) {
10265 if (dir < 0) {dir = 1; moveOnce(); pos.sticky = "after";}
10266 break
10267 }
10268
10269 if (type) { sawType = type; }
10270 if (dir > 0 && !moveOnce(!first)) { break }
10271 }
10272 }
10273 var result = skipAtomic(doc, pos, oldPos, origDir, true);
10274 if (equalCursorPos(oldPos, result)) { result.hitSide = true; }
10275 return result
10276 }
10277
10278 // For relative vertical movement. Dir may be -1 or 1. Unit can be
10279 // "page" or "line". The resulting position will have a hitSide=true
10280 // property if it reached the end of the document.
10281 function findPosV(cm, pos, dir, unit) {
10282 var doc = cm.doc, x = pos.left, y;
10283 if (unit == "page") {
10284 var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
10285 var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3);
10286 y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount;
10287
10288 } else if (unit == "line") {
10289 y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
10290 }
10291 var target;
10292 for (;;) {
10293 target = coordsChar(cm, x, y);
10294 if (!target.outside) { break }
10295 if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break }
10296 y += dir * 5;
10297 }
10298 return target
10299 }
10300
10301 // CONTENTEDITABLE INPUT STYLE
10302
10303 var ContentEditableInput = function(cm) {
10304 this.cm = cm;
10305 this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null;
10306 this.polling = new Delayed();
10307 this.composing = null;
10308 this.gracePeriod = false;
10309 this.readDOMTimeout = null;
10310 };
10311
10312 ContentEditableInput.prototype.init = function (display) {
10313 var this$1 = this;
10314
10315 var input = this, cm = input.cm;
10316 var div = input.div = display.lineDiv;
10317 disableBrowserMagic(div, cm.options.spellcheck, cm.options.autocorrect, cm.options.autocapitalize);
10318
10319 on(div, "paste", function (e) {
10320 if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
10321 // IE doesn't fire input events, so we schedule a read for the pasted content in this way
10322 if (ie_version <= 11) { setTimeout(operation(cm, function () { return this$1.updateFromDOM(); }), 20); }
10323 });
10324
10325 on(div, "compositionstart", function (e) {
10326 this$1.composing = {data: e.data, done: false};
10327 });
10328 on(div, "compositionupdate", function (e) {
10329 if (!this$1.composing) { this$1.composing = {data: e.data, done: false}; }
10330 });
10331 on(div, "compositionend", function (e) {
10332 if (this$1.composing) {
10333 if (e.data != this$1.composing.data) { this$1.readFromDOMSoon(); }
10334 this$1.composing.done = true;
10335 }
10336 });
10337
10338 on(div, "touchstart", function () { return input.forceCompositionEnd(); });
10339
10340 on(div, "input", function () {
10341 if (!this$1.composing) { this$1.readFromDOMSoon(); }
10342 });
10343
10344 function onCopyCut(e) {
10345 if (signalDOMEvent(cm, e)) { return }
10346 if (cm.somethingSelected()) {
10347 setLastCopied({lineWise: false, text: cm.getSelections()});
10348 if (e.type == "cut") { cm.replaceSelection("", null, "cut"); }
10349 } else if (!cm.options.lineWiseCopyCut) {
10350 return
10351 } else {
10352 var ranges = copyableRanges(cm);
10353 setLastCopied({lineWise: true, text: ranges.text});
10354 if (e.type == "cut") {
10355 cm.operation(function () {
10356 cm.setSelections(ranges.ranges, 0, sel_dontScroll);
10357 cm.replaceSelection("", null, "cut");
10358 });
10359 }
10360 }
10361 if (e.clipboardData) {
10362 e.clipboardData.clearData();
10363 var content = lastCopied.text.join("\n");
10364 // iOS exposes the clipboard API, but seems to discard content inserted into it
10365 e.clipboardData.setData("Text", content);
10366 if (e.clipboardData.getData("Text") == content) {
10367 e.preventDefault();
10368 return
10369 }
10370 }
10371 // Old-fashioned briefly-focus-a-textarea hack
10372 var kludge = hiddenTextarea(), te = kludge.firstChild;
10373 cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild);
10374 te.value = lastCopied.text.join("\n");
10375 var hadFocus = document.activeElement;
10376 selectInput(te);
10377 setTimeout(function () {
10378 cm.display.lineSpace.removeChild(kludge);
10379 hadFocus.focus();
10380 if (hadFocus == div) { input.showPrimarySelection(); }
10381 }, 50);
10382 }
10383 on(div, "copy", onCopyCut);
10384 on(div, "cut", onCopyCut);
10385 };
10386
10387 ContentEditableInput.prototype.prepareSelection = function () {
10388 var result = prepareSelection(this.cm, false);
10389 result.focus = this.cm.state.focused;
10390 return result
10391 };
10392
10393 ContentEditableInput.prototype.showSelection = function (info, takeFocus) {
10394 if (!info || !this.cm.display.view.length) { return }
10395 if (info.focus || takeFocus) { this.showPrimarySelection(); }
10396 this.showMultipleSelections(info);
10397 };
10398
10399 ContentEditableInput.prototype.getSelection = function () {
10400 return this.cm.display.wrapper.ownerDocument.getSelection()
10401 };
10402
10403 ContentEditableInput.prototype.showPrimarySelection = function () {
10404 var sel = this.getSelection(), cm = this.cm, prim = cm.doc.sel.primary();
10405 var from = prim.from(), to = prim.to();
10406
10407 if (cm.display.viewTo == cm.display.viewFrom || from.line >= cm.display.viewTo || to.line < cm.display.viewFrom) {
10408 sel.removeAllRanges();
10409 return
10410 }
10411
10412 var curAnchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
10413 var curFocus = domToPos(cm, sel.focusNode, sel.focusOffset);
10414 if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad &&
10415 cmp(minPos(curAnchor, curFocus), from) == 0 &&
10416 cmp(maxPos(curAnchor, curFocus), to) == 0)
10417 { return }
10418
10419 var view = cm.display.view;
10420 var start = (from.line >= cm.display.viewFrom && posToDOM(cm, from)) ||
10421 {node: view[0].measure.map[2], offset: 0};
10422 var end = to.line < cm.display.viewTo && posToDOM(cm, to);
10423 if (!end) {
10424 var measure = view[view.length - 1].measure;
10425 var map$$1 = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map;
10426 end = {node: map$$1[map$$1.length - 1], offset: map$$1[map$$1.length - 2] - map$$1[map$$1.length - 3]};
10427 }
10428
10429 if (!start || !end) {
10430 sel.removeAllRanges();
10431 return
10432 }
10433
10434 var old = sel.rangeCount && sel.getRangeAt(0), rng;
10435 try { rng = range(start.node, start.offset, end.offset, end.node); }
10436 catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible
10437 if (rng) {
10438 if (!gecko && cm.state.focused) {
10439 sel.collapse(start.node, start.offset);
10440 if (!rng.collapsed) {
10441 sel.removeAllRanges();
10442 sel.addRange(rng);
10443 }
10444 } else {
10445 sel.removeAllRanges();
10446 sel.addRange(rng);
10447 }
10448 if (old && sel.anchorNode == null) { sel.addRange(old); }
10449 else if (gecko) { this.startGracePeriod(); }
10450 }
10451 this.rememberSelection();
10452 };
10453
10454 ContentEditableInput.prototype.startGracePeriod = function () {
10455 var this$1 = this;
10456
10457 clearTimeout(this.gracePeriod);
10458 this.gracePeriod = setTimeout(function () {
10459 this$1.gracePeriod = false;
10460 if (this$1.selectionChanged())
10461 { this$1.cm.operation(function () { return this$1.cm.curOp.selectionChanged = true; }); }
10462 }, 20);
10463 };
10464
10465 ContentEditableInput.prototype.showMultipleSelections = function (info) {
10466 removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors);
10467 removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection);
10468 };
10469
10470 ContentEditableInput.prototype.rememberSelection = function () {
10471 var sel = this.getSelection();
10472 this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset;
10473 this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset;
10474 };
10475
10476 ContentEditableInput.prototype.selectionInEditor = function () {
10477 var sel = this.getSelection();
10478 if (!sel.rangeCount) { return false }
10479 var node = sel.getRangeAt(0).commonAncestorContainer;
10480 return contains(this.div, node)
10481 };
10482
10483 ContentEditableInput.prototype.focus = function () {
10484 if (this.cm.options.readOnly != "nocursor") {
10485 if (!this.selectionInEditor())
10486 { this.showSelection(this.prepareSelection(), true); }
10487 this.div.focus();
10488 }
10489 };
10490 ContentEditableInput.prototype.blur = function () { this.div.blur(); };
10491 ContentEditableInput.prototype.getField = function () { return this.div };
10492
10493 ContentEditableInput.prototype.supportsTouch = function () { return true };
10494
10495 ContentEditableInput.prototype.receivedFocus = function () {
10496 var input = this;
10497 if (this.selectionInEditor())
10498 { this.pollSelection(); }
10499 else
10500 { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }); }
10501
10502 function poll() {
10503 if (input.cm.state.focused) {
10504 input.pollSelection();
10505 input.polling.set(input.cm.options.pollInterval, poll);
10506 }
10507 }
10508 this.polling.set(this.cm.options.pollInterval, poll);
10509 };
10510
10511 ContentEditableInput.prototype.selectionChanged = function () {
10512 var sel = this.getSelection();
10513 return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||
10514 sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset
10515 };
10516
10517 ContentEditableInput.prototype.pollSelection = function () {
10518 if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) { return }
10519 var sel = this.getSelection(), cm = this.cm;
10520 // On Android Chrome (version 56, at least), backspacing into an
10521 // uneditable block element will put the cursor in that element,
10522 // and then, because it's not editable, hide the virtual keyboard.
10523 // Because Android doesn't allow us to actually detect backspace
10524 // presses in a sane way, this code checks for when that happens
10525 // and simulates a backspace press in this case.
10526 if (android && chrome && this.cm.options.gutters.length && isInGutter(sel.anchorNode)) {
10527 this.cm.triggerOnKeyDown({type: "keydown", keyCode: 8, preventDefault: Math.abs});
10528 this.blur();
10529 this.focus();
10530 return
10531 }
10532 if (this.composing) { return }
10533 this.rememberSelection();
10534 var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
10535 var head = domToPos(cm, sel.focusNode, sel.focusOffset);
10536 if (anchor && head) { runInOp(cm, function () {
10537 setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll);
10538 if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true; }
10539 }); }
10540 };
10541
10542 ContentEditableInput.prototype.pollContent = function () {
10543 if (this.readDOMTimeout != null) {
10544 clearTimeout(this.readDOMTimeout);
10545 this.readDOMTimeout = null;
10546 }
10547
10548 var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary();
10549 var from = sel.from(), to = sel.to();
10550 if (from.ch == 0 && from.line > cm.firstLine())
10551 { from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length); }
10552 if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine())
10553 { to = Pos(to.line + 1, 0); }
10554 if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return false }
10555
10556 var fromIndex, fromLine, fromNode;
10557 if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) {
10558 fromLine = lineNo(display.view[0].line);
10559 fromNode = display.view[0].node;
10560 } else {
10561 fromLine = lineNo(display.view[fromIndex].line);
10562 fromNode = display.view[fromIndex - 1].node.nextSibling;
10563 }
10564 var toIndex = findViewIndex(cm, to.line);
10565 var toLine, toNode;
10566 if (toIndex == display.view.length - 1) {
10567 toLine = display.viewTo - 1;
10568 toNode = display.lineDiv.lastChild;
10569 } else {
10570 toLine = lineNo(display.view[toIndex + 1].line) - 1;
10571 toNode = display.view[toIndex + 1].node.previousSibling;
10572 }
10573
10574 if (!fromNode) { return false }
10575 var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine));
10576 var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length));
10577 while (newText.length > 1 && oldText.length > 1) {
10578 if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; }
10579 else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; }
10580 else { break }
10581 }
10582
10583 var cutFront = 0, cutEnd = 0;
10584 var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length);
10585 while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront))
10586 { ++cutFront; }
10587 var newBot = lst(newText), oldBot = lst(oldText);
10588 var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0),
10589 oldBot.length - (oldText.length == 1 ? cutFront : 0));
10590 while (cutEnd < maxCutEnd &&
10591 newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1))
10592 { ++cutEnd; }
10593 // Try to move start of change to start of selection if ambiguous
10594 if (newText.length == 1 && oldText.length == 1 && fromLine == from.line) {
10595 while (cutFront && cutFront > from.ch &&
10596 newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) {
10597 cutFront--;
10598 cutEnd++;
10599 }
10600 }
10601
10602 newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, "");
10603 newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, "");
10604
10605 var chFrom = Pos(fromLine, cutFront);
10606 var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0);
10607 if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {
10608 replaceRange(cm.doc, newText, chFrom, chTo, "+input");
10609 return true
10610 }
10611 };
10612
10613 ContentEditableInput.prototype.ensurePolled = function () {
10614 this.forceCompositionEnd();
10615 };
10616 ContentEditableInput.prototype.reset = function () {
10617 this.forceCompositionEnd();
10618 };
10619 ContentEditableInput.prototype.forceCompositionEnd = function () {
10620 if (!this.composing) { return }
10621 clearTimeout(this.readDOMTimeout);
10622 this.composing = null;
10623 this.updateFromDOM();
10624 this.div.blur();
10625 this.div.focus();
10626 };
10627 ContentEditableInput.prototype.readFromDOMSoon = function () {
10628 var this$1 = this;
10629
10630 if (this.readDOMTimeout != null) { return }
10631 this.readDOMTimeout = setTimeout(function () {
10632 this$1.readDOMTimeout = null;
10633 if (this$1.composing) {
10634 if (this$1.composing.done) { this$1.composing = null; }
10635 else { return }
10636 }
10637 this$1.updateFromDOM();
10638 }, 80);
10639 };
10640
10641 ContentEditableInput.prototype.updateFromDOM = function () {
10642 var this$1 = this;
10643
10644 if (this.cm.isReadOnly() || !this.pollContent())
10645 { runInOp(this.cm, function () { return regChange(this$1.cm); }); }
10646 };
10647
10648 ContentEditableInput.prototype.setUneditable = function (node) {
10649 node.contentEditable = "false";
10650 };
10651
10652 ContentEditableInput.prototype.onKeyPress = function (e) {
10653 if (e.charCode == 0 || this.composing) { return }
10654 e.preventDefault();
10655 if (!this.cm.isReadOnly())
10656 { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0); }
10657 };
10658
10659 ContentEditableInput.prototype.readOnlyChanged = function (val) {
10660 this.div.contentEditable = String(val != "nocursor");
10661 };
10662
10663 ContentEditableInput.prototype.onContextMenu = function () {};
10664 ContentEditableInput.prototype.resetPosition = function () {};
10665
10666 ContentEditableInput.prototype.needsContentAttribute = true;
10667
10668 function posToDOM(cm, pos) {
10669 var view = findViewForLine(cm, pos.line);
10670 if (!view || view.hidden) { return null }
10671 var line = getLine(cm.doc, pos.line);
10672 var info = mapFromLineView(view, line, pos.line);
10673
10674 var order = getOrder(line, cm.doc.direction), side = "left";
10675 if (order) {
10676 var partPos = getBidiPartAt(order, pos.ch);
10677 side = partPos % 2 ? "right" : "left";
10678 }
10679 var result = nodeAndOffsetInLineMap(info.map, pos.ch, side);
10680 result.offset = result.collapse == "right" ? result.end : result.start;
10681 return result
10682 }
10683
10684 function isInGutter(node) {
10685 for (var scan = node; scan; scan = scan.parentNode)
10686 { if (/CodeMirror-gutter-wrapper/.test(scan.className)) { return true } }
10687 return false
10688 }
10689
10690 function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos }
10691
10692 function domTextBetween(cm, from, to, fromLine, toLine) {
10693 var text = "", closing = false, lineSep = cm.doc.lineSeparator(), extraLinebreak = false;
10694 function recognizeMarker(id) { return function (marker) { return marker.id == id; } }
10695 function close() {
10696 if (closing) {
10697 text += lineSep;
10698 if (extraLinebreak) { text += lineSep; }
10699 closing = extraLinebreak = false;
10700 }
10701 }
10702 function addText(str) {
10703 if (str) {
10704 close();
10705 text += str;
10706 }
10707 }
10708 function walk(node) {
10709 if (node.nodeType == 1) {
10710 var cmText = node.getAttribute("cm-text");
10711 if (cmText) {
10712 addText(cmText);
10713 return
10714 }
10715 var markerID = node.getAttribute("cm-marker"), range$$1;
10716 if (markerID) {
10717 var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID));
10718 if (found.length && (range$$1 = found[0].find(0)))
10719 { addText(getBetween(cm.doc, range$$1.from, range$$1.to).join(lineSep)); }
10720 return
10721 }
10722 if (node.getAttribute("contenteditable") == "false") { return }
10723 var isBlock = /^(pre|div|p|li|table|br)$/i.test(node.nodeName);
10724 if (!/^br$/i.test(node.nodeName) && node.textContent.length == 0) { return }
10725
10726 if (isBlock) { close(); }
10727 for (var i = 0; i < node.childNodes.length; i++)
10728 { walk(node.childNodes[i]); }
10729
10730 if (/^(pre|p)$/i.test(node.nodeName)) { extraLinebreak = true; }
10731 if (isBlock) { closing = true; }
10732 } else if (node.nodeType == 3) {
10733 addText(node.nodeValue.replace(/\u200b/g, "").replace(/\u00a0/g, " "));
10734 }
10735 }
10736 for (;;) {
10737 walk(from);
10738 if (from == to) { break }
10739 from = from.nextSibling;
10740 extraLinebreak = false;
10741 }
10742 return text
10743 }
10744
10745 function domToPos(cm, node, offset) {
10746 var lineNode;
10747 if (node == cm.display.lineDiv) {
10748 lineNode = cm.display.lineDiv.childNodes[offset];
10749 if (!lineNode) { return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) }
10750 node = null; offset = 0;
10751 } else {
10752 for (lineNode = node;; lineNode = lineNode.parentNode) {
10753 if (!lineNode || lineNode == cm.display.lineDiv) { return null }
10754 if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) { break }
10755 }
10756 }
10757 for (var i = 0; i < cm.display.view.length; i++) {
10758 var lineView = cm.display.view[i];
10759 if (lineView.node == lineNode)
10760 { return locateNodeInLineView(lineView, node, offset) }
10761 }
10762 }
10763
10764 function locateNodeInLineView(lineView, node, offset) {
10765 var wrapper = lineView.text.firstChild, bad = false;
10766 if (!node || !contains(wrapper, node)) { return badPos(Pos(lineNo(lineView.line), 0), true) }
10767 if (node == wrapper) {
10768 bad = true;
10769 node = wrapper.childNodes[offset];
10770 offset = 0;
10771 if (!node) {
10772 var line = lineView.rest ? lst(lineView.rest) : lineView.line;
10773 return badPos(Pos(lineNo(line), line.text.length), bad)
10774 }
10775 }
10776
10777 var textNode = node.nodeType == 3 ? node : null, topNode = node;
10778 if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {
10779 textNode = node.firstChild;
10780 if (offset) { offset = textNode.nodeValue.length; }
10781 }
10782 while (topNode.parentNode != wrapper) { topNode = topNode.parentNode; }
10783 var measure = lineView.measure, maps = measure.maps;
10784
10785 function find(textNode, topNode, offset) {
10786 for (var i = -1; i < (maps ? maps.length : 0); i++) {
10787 var map$$1 = i < 0 ? measure.map : maps[i];
10788 for (var j = 0; j < map$$1.length; j += 3) {
10789 var curNode = map$$1[j + 2];
10790 if (curNode == textNode || curNode == topNode) {
10791 var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]);
10792 var ch = map$$1[j] + offset;
10793 if (offset < 0 || curNode != textNode) { ch = map$$1[j + (offset ? 1 : 0)]; }
10794 return Pos(line, ch)
10795 }
10796 }
10797 }
10798 }
10799 var found = find(textNode, topNode, offset);
10800 if (found) { return badPos(found, bad) }
10801
10802 // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems
10803 for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) {
10804 found = find(after, after.firstChild, 0);
10805 if (found)
10806 { return badPos(Pos(found.line, found.ch - dist), bad) }
10807 else
10808 { dist += after.textContent.length; }
10809 }
10810 for (var before = topNode.previousSibling, dist$1 = offset; before; before = before.previousSibling) {
10811 found = find(before, before.firstChild, -1);
10812 if (found)
10813 { return badPos(Pos(found.line, found.ch + dist$1), bad) }
10814 else
10815 { dist$1 += before.textContent.length; }
10816 }
10817 }
10818
10819 // TEXTAREA INPUT STYLE
10820
10821 var TextareaInput = function(cm) {
10822 this.cm = cm;
10823 // See input.poll and input.reset
10824 this.prevInput = "";
10825
10826 // Flag that indicates whether we expect input to appear real soon
10827 // now (after some event like 'keypress' or 'input') and are
10828 // polling intensively.
10829 this.pollingFast = false;
10830 // Self-resetting timeout for the poller
10831 this.polling = new Delayed();
10832 // Used to work around IE issue with selection being forgotten when focus moves away from textarea
10833 this.hasSelection = false;
10834 this.composing = null;
10835 };
10836
10837 TextareaInput.prototype.init = function (display) {
10838 var this$1 = this;
10839
10840 var input = this, cm = this.cm;
10841 this.createField(display);
10842 var te = this.textarea;
10843
10844 display.wrapper.insertBefore(this.wrapper, display.wrapper.firstChild);
10845
10846 // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)
10847 if (ios) { te.style.width = "0px"; }
10848
10849 on(te, "input", function () {
10850 if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = null; }
10851 input.poll();
10852 });
10853
10854 on(te, "paste", function (e) {
10855 if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
10856
10857 cm.state.pasteIncoming = +new Date;
10858 input.fastPoll();
10859 });
10860
10861 function prepareCopyCut(e) {
10862 if (signalDOMEvent(cm, e)) { return }
10863 if (cm.somethingSelected()) {
10864 setLastCopied({lineWise: false, text: cm.getSelections()});
10865 } else if (!cm.options.lineWiseCopyCut) {
10866 return
10867 } else {
10868 var ranges = copyableRanges(cm);
10869 setLastCopied({lineWise: true, text: ranges.text});
10870 if (e.type == "cut") {
10871 cm.setSelections(ranges.ranges, null, sel_dontScroll);
10872 } else {
10873 input.prevInput = "";
10874 te.value = ranges.text.join("\n");
10875 selectInput(te);
10876 }
10877 }
10878 if (e.type == "cut") { cm.state.cutIncoming = +new Date; }
10879 }
10880 on(te, "cut", prepareCopyCut);
10881 on(te, "copy", prepareCopyCut);
10882
10883 on(display.scroller, "paste", function (e) {
10884 if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return }
10885 if (!te.dispatchEvent) {
10886 cm.state.pasteIncoming = +new Date;
10887 input.focus();
10888 return
10889 }
10890
10891 // Pass the `paste` event to the textarea so it's handled by its event listener.
10892 var event = new Event("paste");
10893 event.clipboardData = e.clipboardData;
10894 te.dispatchEvent(event);
10895 });
10896
10897 // Prevent normal selection in the editor (we handle our own)
10898 on(display.lineSpace, "selectstart", function (e) {
10899 if (!eventInWidget(display, e)) { e_preventDefault(e); }
10900 });
10901
10902 on(te, "compositionstart", function () {
10903 var start = cm.getCursor("from");
10904 if (input.composing) { input.composing.range.clear(); }
10905 input.composing = {
10906 start: start,
10907 range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"})
10908 };
10909 });
10910 on(te, "compositionend", function () {
10911 if (input.composing) {
10912 input.poll();
10913 input.composing.range.clear();
10914 input.composing = null;
10915 }
10916 });
10917 };
10918
10919 TextareaInput.prototype.createField = function (_display) {
10920 // Wraps and hides input textarea
10921 this.wrapper = hiddenTextarea();
10922 // The semihidden textarea that is focused when the editor is
10923 // focused, and receives input.
10924 this.textarea = this.wrapper.firstChild;
10925 };
10926
10927 TextareaInput.prototype.prepareSelection = function () {
10928 // Redraw the selection and/or cursor
10929 var cm = this.cm, display = cm.display, doc = cm.doc;
10930 var result = prepareSelection(cm);
10931
10932 // Move the hidden textarea near the cursor to prevent scrolling artifacts
10933 if (cm.options.moveInputWithCursor) {
10934 var headPos = cursorCoords(cm, doc.sel.primary().head, "div");
10935 var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();
10936 result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
10937 headPos.top + lineOff.top - wrapOff.top));
10938 result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
10939 headPos.left + lineOff.left - wrapOff.left));
10940 }
10941
10942 return result
10943 };
10944
10945 TextareaInput.prototype.showSelection = function (drawn) {
10946 var cm = this.cm, display = cm.display;
10947 removeChildrenAndAdd(display.cursorDiv, drawn.cursors);
10948 removeChildrenAndAdd(display.selectionDiv, drawn.selection);
10949 if (drawn.teTop != null) {
10950 this.wrapper.style.top = drawn.teTop + "px";
10951 this.wrapper.style.left = drawn.teLeft + "px";
10952 }
10953 };
10954
10955 // Reset the input to correspond to the selection (or to be empty,
10956 // when not typing and nothing is selected)
10957 TextareaInput.prototype.reset = function (typing) {
10958 if (this.contextMenuPending || this.composing) { return }
10959 var cm = this.cm;
10960 if (cm.somethingSelected()) {
10961 this.prevInput = "";
10962 var content = cm.getSelection();
10963 this.textarea.value = content;
10964 if (cm.state.focused) { selectInput(this.textarea); }
10965 if (ie && ie_version >= 9) { this.hasSelection = content; }
10966 } else if (!typing) {
10967 this.prevInput = this.textarea.value = "";
10968 if (ie && ie_version >= 9) { this.hasSelection = null; }
10969 }
10970 };
10971
10972 TextareaInput.prototype.getField = function () { return this.textarea };
10973
10974 TextareaInput.prototype.supportsTouch = function () { return false };
10975
10976 TextareaInput.prototype.focus = function () {
10977 if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) {
10978 try { this.textarea.focus(); }
10979 catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM
10980 }
10981 };
10982
10983 TextareaInput.prototype.blur = function () { this.textarea.blur(); };
10984
10985 TextareaInput.prototype.resetPosition = function () {
10986 this.wrapper.style.top = this.wrapper.style.left = 0;
10987 };
10988
10989 TextareaInput.prototype.receivedFocus = function () { this.slowPoll(); };
10990
10991 // Poll for input changes, using the normal rate of polling. This
10992 // runs as long as the editor is focused.
10993 TextareaInput.prototype.slowPoll = function () {
10994 var this$1 = this;
10995
10996 if (this.pollingFast) { return }
10997 this.polling.set(this.cm.options.pollInterval, function () {
10998 this$1.poll();
10999 if (this$1.cm.state.focused) { this$1.slowPoll(); }
11000 });
11001 };
11002
11003 // When an event has just come in that is likely to add or change
11004 // something in the input textarea, we poll faster, to ensure that
11005 // the change appears on the screen quickly.
11006 TextareaInput.prototype.fastPoll = function () {
11007 var missed = false, input = this;
11008 input.pollingFast = true;
11009 function p() {
11010 var changed = input.poll();
11011 if (!changed && !missed) {missed = true; input.polling.set(60, p);}
11012 else {input.pollingFast = false; input.slowPoll();}
11013 }
11014 input.polling.set(20, p);
11015 };
11016
11017 // Read input from the textarea, and update the document to match.
11018 // When something is selected, it is present in the textarea, and
11019 // selected (unless it is huge, in which case a placeholder is
11020 // used). When nothing is selected, the cursor sits after previously
11021 // seen text (can be empty), which is stored in prevInput (we must
11022 // not reset the textarea when typing, because that breaks IME).
11023 TextareaInput.prototype.poll = function () {
11024 var this$1 = this;
11025
11026 var cm = this.cm, input = this.textarea, prevInput = this.prevInput;
11027 // Since this is called a *lot*, try to bail out as cheaply as
11028 // possible when it is clear that nothing happened. hasSelection
11029 // will be the case when there is a lot of text in the textarea,
11030 // in which case reading its value would be expensive.
11031 if (this.contextMenuPending || !cm.state.focused ||
11032 (hasSelection(input) && !prevInput && !this.composing) ||
11033 cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq)
11034 { return false }
11035
11036 var text = input.value;
11037 // If nothing changed, bail.
11038 if (text == prevInput && !cm.somethingSelected()) { return false }
11039 // Work around nonsensical selection resetting in IE9/10, and
11040 // inexplicable appearance of private area unicode characters on
11041 // some key combos in Mac (#2689).
11042 if (ie && ie_version >= 9 && this.hasSelection === text ||
11043 mac && /[\uf700-\uf7ff]/.test(text)) {
11044 cm.display.input.reset();
11045 return false
11046 }
11047
11048 if (cm.doc.sel == cm.display.selForContextMenu) {
11049 var first = text.charCodeAt(0);
11050 if (first == 0x200b && !prevInput) { prevInput = "\u200b"; }
11051 if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") }
11052 }
11053 // Find the part of the input that is actually new
11054 var same = 0, l = Math.min(prevInput.length, text.length);
11055 while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same; }
11056
11057 runInOp(cm, function () {
11058 applyTextInput(cm, text.slice(same), prevInput.length - same,
11059 null, this$1.composing ? "*compose" : null);
11060
11061 // Don't leave long text in the textarea, since it makes further polling slow
11062 if (text.length > 1000 || text.indexOf("\n") > -1) { input.value = this$1.prevInput = ""; }
11063 else { this$1.prevInput = text; }
11064
11065 if (this$1.composing) {
11066 this$1.composing.range.clear();
11067 this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor("to"),
11068 {className: "CodeMirror-composing"});
11069 }
11070 });
11071 return true
11072 };
11073
11074 TextareaInput.prototype.ensurePolled = function () {
11075 if (this.pollingFast && this.poll()) { this.pollingFast = false; }
11076 };
11077
11078 TextareaInput.prototype.onKeyPress = function () {
11079 if (ie && ie_version >= 9) { this.hasSelection = null; }
11080 this.fastPoll();
11081 };
11082
11083 TextareaInput.prototype.onContextMenu = function (e) {
11084 var input = this, cm = input.cm, display = cm.display, te = input.textarea;
11085 if (input.contextMenuPending) { input.contextMenuPending(); }
11086 var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
11087 if (!pos || presto) { return } // Opera is difficult.
11088
11089 // Reset the current text selection only if the click is done outside of the selection
11090 // and 'resetSelectionOnContextMenu' option is true.
11091 var reset = cm.options.resetSelectionOnContextMenu;
11092 if (reset && cm.doc.sel.contains(pos) == -1)
11093 { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll); }
11094
11095 var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText;
11096 var wrapperBox = input.wrapper.offsetParent.getBoundingClientRect();
11097 input.wrapper.style.cssText = "position: static";
11098 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);";
11099 var oldScrollY;
11100 if (webkit) { oldScrollY = window.scrollY; } // Work around Chrome issue (#2712)
11101 display.input.focus();
11102 if (webkit) { window.scrollTo(null, oldScrollY); }
11103 display.input.reset();
11104 // Adds "Select all" to context menu in FF
11105 if (!cm.somethingSelected()) { te.value = input.prevInput = " "; }
11106 input.contextMenuPending = rehide;
11107 display.selForContextMenu = cm.doc.sel;
11108 clearTimeout(display.detectingSelectAll);
11109
11110 // Select-all will be greyed out if there's nothing to select, so
11111 // this adds a zero-width space so that we can later check whether
11112 // it got selected.
11113 function prepareSelectAllHack() {
11114 if (te.selectionStart != null) {
11115 var selected = cm.somethingSelected();
11116 var extval = "\u200b" + (selected ? te.value : "");
11117 te.value = "\u21da"; // Used to catch context-menu undo
11118 te.value = extval;
11119 input.prevInput = selected ? "" : "\u200b";
11120 te.selectionStart = 1; te.selectionEnd = extval.length;
11121 // Re-set this, in case some other handler touched the
11122 // selection in the meantime.
11123 display.selForContextMenu = cm.doc.sel;
11124 }
11125 }
11126 function rehide() {
11127 if (input.contextMenuPending != rehide) { return }
11128 input.contextMenuPending = false;
11129 input.wrapper.style.cssText = oldWrapperCSS;
11130 te.style.cssText = oldCSS;
11131 if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos); }
11132
11133 // Try to detect the user choosing select-all
11134 if (te.selectionStart != null) {
11135 if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack(); }
11136 var i = 0, poll = function () {
11137 if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 &&
11138 te.selectionEnd > 0 && input.prevInput == "\u200b") {
11139 operation(cm, selectAll)(cm);
11140 } else if (i++ < 10) {
11141 display.detectingSelectAll = setTimeout(poll, 500);
11142 } else {
11143 display.selForContextMenu = null;
11144 display.input.reset();
11145 }
11146 };
11147 display.detectingSelectAll = setTimeout(poll, 200);
11148 }
11149 }
11150
11151 if (ie && ie_version >= 9) { prepareSelectAllHack(); }
11152 if (captureRightClick) {
11153 e_stop(e);
11154 var mouseup = function () {
11155 off(window, "mouseup", mouseup);
11156 setTimeout(rehide, 20);
11157 };
11158 on(window, "mouseup", mouseup);
11159 } else {
11160 setTimeout(rehide, 50);
11161 }
11162 };
11163
11164 TextareaInput.prototype.readOnlyChanged = function (val) {
11165 if (!val) { this.reset(); }
11166 this.textarea.disabled = val == "nocursor";
11167 };
11168
11169 TextareaInput.prototype.setUneditable = function () {};
11170
11171 TextareaInput.prototype.needsContentAttribute = false;
11172
11173 function fromTextArea(textarea, options) {
11174 options = options ? copyObj(options) : {};
11175 options.value = textarea.value;
11176 if (!options.tabindex && textarea.tabIndex)
11177 { options.tabindex = textarea.tabIndex; }
11178 if (!options.placeholder && textarea.placeholder)
11179 { options.placeholder = textarea.placeholder; }
11180 // Set autofocus to true if this textarea is focused, or if it has
11181 // autofocus and no other element is focused.
11182 if (options.autofocus == null) {
11183 var hasFocus = activeElt();
11184 options.autofocus = hasFocus == textarea ||
11185 textarea.getAttribute("autofocus") != null && hasFocus == document.body;
11186 }
11187
11188 function save() {textarea.value = cm.getValue();}
11189
11190 var realSubmit;
11191 if (textarea.form) {
11192 on(textarea.form, "submit", save);
11193 // Deplorable hack to make the submit method do the right thing.
11194 if (!options.leaveSubmitMethodAlone) {
11195 var form = textarea.form;
11196 realSubmit = form.submit;
11197 try {
11198 var wrappedSubmit = form.submit = function () {
11199 save();
11200 form.submit = realSubmit;
11201 form.submit();
11202 form.submit = wrappedSubmit;
11203 };
11204 } catch(e) {}
11205 }
11206 }
11207
11208 options.finishInit = function (cm) {
11209 cm.save = save;
11210 cm.getTextArea = function () { return textarea; };
11211 cm.toTextArea = function () {
11212 cm.toTextArea = isNaN; // Prevent this from being ran twice
11213 save();
11214 textarea.parentNode.removeChild(cm.getWrapperElement());
11215 textarea.style.display = "";
11216 if (textarea.form) {
11217 off(textarea.form, "submit", save);
11218 if (typeof textarea.form.submit == "function")
11219 { textarea.form.submit = realSubmit; }
11220 }
11221 };
11222 };
11223
11224 textarea.style.display = "none";
11225 var cm = CodeMirror(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); },
11226 options);
11227 return cm
11228 }
11229
11230 function addLegacyProps(CodeMirror) {
11231 CodeMirror.off = off;
11232 CodeMirror.on = on;
11233 CodeMirror.wheelEventPixels = wheelEventPixels;
11234 CodeMirror.Doc = Doc;
11235 CodeMirror.splitLines = splitLinesAuto;
11236 CodeMirror.countColumn = countColumn;
11237 CodeMirror.findColumn = findColumn;
11238 CodeMirror.isWordChar = isWordCharBasic;
11239 CodeMirror.Pass = Pass;
11240 CodeMirror.signal = signal;
11241 CodeMirror.Line = Line;
11242 CodeMirror.changeEnd = changeEnd;
11243 CodeMirror.scrollbarModel = scrollbarModel;
11244 CodeMirror.Pos = Pos;
11245 CodeMirror.cmpPos = cmp;
11246 CodeMirror.modes = modes;
11247 CodeMirror.mimeModes = mimeModes;
11248 CodeMirror.resolveMode = resolveMode;
11249 CodeMirror.getMode = getMode;
11250 CodeMirror.modeExtensions = modeExtensions;
11251 CodeMirror.extendMode = extendMode;
11252 CodeMirror.copyState = copyState;
11253 CodeMirror.startState = startState;
11254 CodeMirror.innerMode = innerMode;
11255 CodeMirror.commands = commands;
11256 CodeMirror.keyMap = keyMap;
11257 CodeMirror.keyName = keyName;
11258 CodeMirror.isModifierKey = isModifierKey;
11259 CodeMirror.lookupKey = lookupKey;
11260 CodeMirror.normalizeKeyMap = normalizeKeyMap;
11261 CodeMirror.StringStream = StringStream;
11262 CodeMirror.SharedTextMarker = SharedTextMarker;
11263 CodeMirror.TextMarker = TextMarker;
11264 CodeMirror.LineWidget = LineWidget;
11265 CodeMirror.e_preventDefault = e_preventDefault;
11266 CodeMirror.e_stopPropagation = e_stopPropagation;
11267 CodeMirror.e_stop = e_stop;
11268 CodeMirror.addClass = addClass;
11269 CodeMirror.contains = contains;
11270 CodeMirror.rmClass = rmClass;
11271 CodeMirror.keyNames = keyNames;
11272 }
11273
11274 // EDITOR CONSTRUCTOR
11275
11276 defineOptions(CodeMirror);
11277
11278 addEditorMethods(CodeMirror);
11279
11280 // Set up methods on CodeMirror's prototype to redirect to the editor's document.
11281 var dontDelegate = "iter insert remove copy getEditor constructor".split(" ");
11282 for (var prop in Doc.prototype) { if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
11283 { CodeMirror.prototype[prop] = (function(method) {
11284 return function() {return method.apply(this.doc, arguments)}
11285 })(Doc.prototype[prop]); } }
11286
11287 eventMixin(Doc);
11288 CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput};
11289
11290 // Extra arguments are stored as the mode's dependencies, which is
11291 // used by (legacy) mechanisms like loadmode.js to automatically
11292 // load a mode. (Preferred mechanism is the require/define calls.)
11293 CodeMirror.defineMode = function(name/*, mode, …*/) {
11294 if (!CodeMirror.defaults.mode && name != "null") { CodeMirror.defaults.mode = name; }
11295 defineMode.apply(this, arguments);
11296 };
11297
11298 CodeMirror.defineMIME = defineMIME;
11299
11300 // Minimal default mode.
11301 CodeMirror.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); });
11302 CodeMirror.defineMIME("text/plain", "null");
11303
11304 // EXTENSIONS
11305
11306 CodeMirror.defineExtension = function (name, func) {
11307 CodeMirror.prototype[name] = func;
11308 };
11309 CodeMirror.defineDocExtension = function (name, func) {
11310 Doc.prototype[name] = func;
11311 };
11312
11313 CodeMirror.fromTextArea = fromTextArea;
11314
11315 addLegacyProps(CodeMirror);
11316
11317 CodeMirror.version = "5.45.0";
11318
11319 return CodeMirror;
11320
11321 })));
11322
11323 },{}],15:[function(require,module,exports){
11324 // CodeMirror, copyright (c) by Marijn Haverbeke and others
11325 // Distributed under an MIT license: https://codemirror.net/LICENSE
11326
11327 (function(mod) {
11328 if (typeof exports == "object" && typeof module == "object") // CommonJS
11329 mod(require("../../lib/codemirror"));
11330 else if (typeof define == "function" && define.amd) // AMD
11331 define(["../../lib/codemirror"], mod);
11332 else // Plain browser env
11333 mod(CodeMirror);
11334 })(function(CodeMirror) {
11335 "use strict";
11336
11337 CodeMirror.defineMode("css", function(config, parserConfig) {
11338 var inline = parserConfig.inline
11339 if (!parserConfig.propertyKeywords) parserConfig = CodeMirror.resolveMode("text/css");
11340
11341 var indentUnit = config.indentUnit,
11342 tokenHooks = parserConfig.tokenHooks,
11343 documentTypes = parserConfig.documentTypes || {},
11344 mediaTypes = parserConfig.mediaTypes || {},
11345 mediaFeatures = parserConfig.mediaFeatures || {},
11346 mediaValueKeywords = parserConfig.mediaValueKeywords || {},
11347 propertyKeywords = parserConfig.propertyKeywords || {},
11348 nonStandardPropertyKeywords = parserConfig.nonStandardPropertyKeywords || {},
11349 fontProperties = parserConfig.fontProperties || {},
11350 counterDescriptors = parserConfig.counterDescriptors || {},
11351 colorKeywords = parserConfig.colorKeywords || {},
11352 valueKeywords = parserConfig.valueKeywords || {},
11353 allowNested = parserConfig.allowNested,
11354 lineComment = parserConfig.lineComment,
11355 supportsAtComponent = parserConfig.supportsAtComponent === true;
11356
11357 var type, override;
11358 function ret(style, tp) { type = tp; return style; }
11359
11360 // Tokenizers
11361
11362 function tokenBase(stream, state) {
11363 var ch = stream.next();
11364 if (tokenHooks[ch]) {
11365 var result = tokenHooks[ch](stream, state);
11366 if (result !== false) return result;
11367 }
11368 if (ch == "@") {
11369 stream.eatWhile(/[\w\\\-]/);
11370 return ret("def", stream.current());
11371 } else if (ch == "=" || (ch == "~" || ch == "|") && stream.eat("=")) {
11372 return ret(null, "compare");
11373 } else if (ch == "\"" || ch == "'") {
11374 state.tokenize = tokenString(ch);
11375 return state.tokenize(stream, state);
11376 } else if (ch == "#") {
11377 stream.eatWhile(/[\w\\\-]/);
11378 return ret("atom", "hash");
11379 } else if (ch == "!") {
11380 stream.match(/^\s*\w*/);
11381 return ret("keyword", "important");
11382 } else if (/\d/.test(ch) || ch == "." && stream.eat(/\d/)) {
11383 stream.eatWhile(/[\w.%]/);
11384 return ret("number", "unit");
11385 } else if (ch === "-") {
11386 if (/[\d.]/.test(stream.peek())) {
11387 stream.eatWhile(/[\w.%]/);
11388 return ret("number", "unit");
11389 } else if (stream.match(/^-[\w\\\-]*/)) {
11390 stream.eatWhile(/[\w\\\-]/);
11391 if (stream.match(/^\s*:/, false))
11392 return ret("variable-2", "variable-definition");
11393 return ret("variable-2", "variable");
11394 } else if (stream.match(/^\w+-/)) {
11395 return ret("meta", "meta");
11396 }
11397 } else if (/[,+>*\/]/.test(ch)) {
11398 return ret(null, "select-op");
11399 } else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {
11400 return ret("qualifier", "qualifier");
11401 } else if (/[:;{}\[\]\(\)]/.test(ch)) {
11402 return ret(null, ch);
11403 } else if (stream.match(/[\w-.]+(?=\()/)) {
11404 if (/^(url(-prefix)?|domain|regexp)$/.test(stream.current().toLowerCase())) {
11405 state.tokenize = tokenParenthesized;
11406 }
11407 return ret("variable callee", "variable");
11408 } else if (/[\w\\\-]/.test(ch)) {
11409 stream.eatWhile(/[\w\\\-]/);
11410 return ret("property", "word");
11411 } else {
11412 return ret(null, null);
11413 }
11414 }
11415
11416 function tokenString(quote) {
11417 return function(stream, state) {
11418 var escaped = false, ch;
11419 while ((ch = stream.next()) != null) {
11420 if (ch == quote && !escaped) {
11421 if (quote == ")") stream.backUp(1);
11422 break;
11423 }
11424 escaped = !escaped && ch == "\\";
11425 }
11426 if (ch == quote || !escaped && quote != ")") state.tokenize = null;
11427 return ret("string", "string");
11428 };
11429 }
11430
11431 function tokenParenthesized(stream, state) {
11432 stream.next(); // Must be '('
11433 if (!stream.match(/\s*[\"\')]/, false))
11434 state.tokenize = tokenString(")");
11435 else
11436 state.tokenize = null;
11437 return ret(null, "(");
11438 }
11439
11440 // Context management
11441
11442 function Context(type, indent, prev) {
11443 this.type = type;
11444 this.indent = indent;
11445 this.prev = prev;
11446 }
11447
11448 function pushContext(state, stream, type, indent) {
11449 state.context = new Context(type, stream.indentation() + (indent === false ? 0 : indentUnit), state.context);
11450 return type;
11451 }
11452
11453 function popContext(state) {
11454 if (state.context.prev)
11455 state.context = state.context.prev;
11456 return state.context.type;
11457 }
11458
11459 function pass(type, stream, state) {
11460 return states[state.context.type](type, stream, state);
11461 }
11462 function popAndPass(type, stream, state, n) {
11463 for (var i = n || 1; i > 0; i--)
11464 state.context = state.context.prev;
11465 return pass(type, stream, state);
11466 }
11467
11468 // Parser
11469
11470 function wordAsValue(stream) {
11471 var word = stream.current().toLowerCase();
11472 if (valueKeywords.hasOwnProperty(word))
11473 override = "atom";
11474 else if (colorKeywords.hasOwnProperty(word))
11475 override = "keyword";
11476 else
11477 override = "variable";
11478 }
11479
11480 var states = {};
11481
11482 states.top = function(type, stream, state) {
11483 if (type == "{") {
11484 return pushContext(state, stream, "block");
11485 } else if (type == "}" && state.context.prev) {
11486 return popContext(state);
11487 } else if (supportsAtComponent && /@component/i.test(type)) {
11488 return pushContext(state, stream, "atComponentBlock");
11489 } else if (/^@(-moz-)?document$/i.test(type)) {
11490 return pushContext(state, stream, "documentTypes");
11491 } else if (/^@(media|supports|(-moz-)?document|import)$/i.test(type)) {
11492 return pushContext(state, stream, "atBlock");
11493 } else if (/^@(font-face|counter-style)/i.test(type)) {
11494 state.stateArg = type;
11495 return "restricted_atBlock_before";
11496 } else if (/^@(-(moz|ms|o|webkit)-)?keyframes$/i.test(type)) {
11497 return "keyframes";
11498 } else if (type && type.charAt(0) == "@") {
11499 return pushContext(state, stream, "at");
11500 } else if (type == "hash") {
11501 override = "builtin";
11502 } else if (type == "word") {
11503 override = "tag";
11504 } else if (type == "variable-definition") {
11505 return "maybeprop";
11506 } else if (type == "interpolation") {
11507 return pushContext(state, stream, "interpolation");
11508 } else if (type == ":") {
11509 return "pseudo";
11510 } else if (allowNested && type == "(") {
11511 return pushContext(state, stream, "parens");
11512 }
11513 return state.context.type;
11514 };
11515
11516 states.block = function(type, stream, state) {
11517 if (type == "word") {
11518 var word = stream.current().toLowerCase();
11519 if (propertyKeywords.hasOwnProperty(word)) {
11520 override = "property";
11521 return "maybeprop";
11522 } else if (nonStandardPropertyKeywords.hasOwnProperty(word)) {
11523 override = "string-2";
11524 return "maybeprop";
11525 } else if (allowNested) {
11526 override = stream.match(/^\s*:(?:\s|$)/, false) ? "property" : "tag";
11527 return "block";
11528 } else {
11529 override += " error";
11530 return "maybeprop";
11531 }
11532 } else if (type == "meta") {
11533 return "block";
11534 } else if (!allowNested && (type == "hash" || type == "qualifier")) {
11535 override = "error";
11536 return "block";
11537 } else {
11538 return states.top(type, stream, state);
11539 }
11540 };
11541
11542 states.maybeprop = function(type, stream, state) {
11543 if (type == ":") return pushContext(state, stream, "prop");
11544 return pass(type, stream, state);
11545 };
11546
11547 states.prop = function(type, stream, state) {
11548 if (type == ";") return popContext(state);
11549 if (type == "{" && allowNested) return pushContext(state, stream, "propBlock");
11550 if (type == "}" || type == "{") return popAndPass(type, stream, state);
11551 if (type == "(") return pushContext(state, stream, "parens");
11552
11553 if (type == "hash" && !/^#([0-9a-fA-f]{3,4}|[0-9a-fA-f]{6}|[0-9a-fA-f]{8})$/.test(stream.current())) {
11554 override += " error";
11555 } else if (type == "word") {
11556 wordAsValue(stream);
11557 } else if (type == "interpolation") {
11558 return pushContext(state, stream, "interpolation");
11559 }
11560 return "prop";
11561 };
11562
11563 states.propBlock = function(type, _stream, state) {
11564 if (type == "}") return popContext(state);
11565 if (type == "word") { override = "property"; return "maybeprop"; }
11566 return state.context.type;
11567 };
11568
11569 states.parens = function(type, stream, state) {
11570 if (type == "{" || type == "}") return popAndPass(type, stream, state);
11571 if (type == ")") return popContext(state);
11572 if (type == "(") return pushContext(state, stream, "parens");
11573 if (type == "interpolation") return pushContext(state, stream, "interpolation");
11574 if (type == "word") wordAsValue(stream);
11575 return "parens";
11576 };
11577
11578 states.pseudo = function(type, stream, state) {
11579 if (type == "meta") return "pseudo";
11580
11581 if (type == "word") {
11582 override = "variable-3";
11583 return state.context.type;
11584 }
11585 return pass(type, stream, state);
11586 };
11587
11588 states.documentTypes = function(type, stream, state) {
11589 if (type == "word" && documentTypes.hasOwnProperty(stream.current())) {
11590 override = "tag";
11591 return state.context.type;
11592 } else {
11593 return states.atBlock(type, stream, state);
11594 }
11595 };
11596
11597 states.atBlock = function(type, stream, state) {
11598 if (type == "(") return pushContext(state, stream, "atBlock_parens");
11599 if (type == "}" || type == ";") return popAndPass(type, stream, state);
11600 if (type == "{") return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top");
11601
11602 if (type == "interpolation") return pushContext(state, stream, "interpolation");
11603
11604 if (type == "word") {
11605 var word = stream.current().toLowerCase();
11606 if (word == "only" || word == "not" || word == "and" || word == "or")
11607 override = "keyword";
11608 else if (mediaTypes.hasOwnProperty(word))
11609 override = "attribute";
11610 else if (mediaFeatures.hasOwnProperty(word))
11611 override = "property";
11612 else if (mediaValueKeywords.hasOwnProperty(word))
11613 override = "keyword";
11614 else if (propertyKeywords.hasOwnProperty(word))
11615 override = "property";
11616 else if (nonStandardPropertyKeywords.hasOwnProperty(word))
11617 override = "string-2";
11618 else if (valueKeywords.hasOwnProperty(word))
11619 override = "atom";
11620 else if (colorKeywords.hasOwnProperty(word))
11621 override = "keyword";
11622 else
11623 override = "error";
11624 }
11625 return state.context.type;
11626 };
11627
11628 states.atComponentBlock = function(type, stream, state) {
11629 if (type == "}")
11630 return popAndPass(type, stream, state);
11631 if (type == "{")
11632 return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top", false);
11633 if (type == "word")
11634 override = "error";
11635 return state.context.type;
11636 };
11637
11638 states.atBlock_parens = function(type, stream, state) {
11639 if (type == ")") return popContext(state);
11640 if (type == "{" || type == "}") return popAndPass(type, stream, state, 2);
11641 return states.atBlock(type, stream, state);
11642 };
11643
11644 states.restricted_atBlock_before = function(type, stream, state) {
11645 if (type == "{")
11646 return pushContext(state, stream, "restricted_atBlock");
11647 if (type == "word" && state.stateArg == "@counter-style") {
11648 override = "variable";
11649 return "restricted_atBlock_before";
11650 }
11651 return pass(type, stream, state);
11652 };
11653
11654 states.restricted_atBlock = function(type, stream, state) {
11655 if (type == "}") {
11656 state.stateArg = null;
11657 return popContext(state);
11658 }
11659 if (type == "word") {
11660 if ((state.stateArg == "@font-face" && !fontProperties.hasOwnProperty(stream.current().toLowerCase())) ||
11661 (state.stateArg == "@counter-style" && !counterDescriptors.hasOwnProperty(stream.current().toLowerCase())))
11662 override = "error";
11663 else
11664 override = "property";
11665 return "maybeprop";
11666 }
11667 return "restricted_atBlock";
11668 };
11669
11670 states.keyframes = function(type, stream, state) {
11671 if (type == "word") { override = "variable"; return "keyframes"; }
11672 if (type == "{") return pushContext(state, stream, "top");
11673 return pass(type, stream, state);
11674 };
11675
11676 states.at = function(type, stream, state) {
11677 if (type == ";") return popContext(state);
11678 if (type == "{" || type == "}") return popAndPass(type, stream, state);
11679 if (type == "word") override = "tag";
11680 else if (type == "hash") override = "builtin";
11681 return "at";
11682 };
11683
11684 states.interpolation = function(type, stream, state) {
11685 if (type == "}") return popContext(state);
11686 if (type == "{" || type == ";") return popAndPass(type, stream, state);
11687 if (type == "word") override = "variable";
11688 else if (type != "variable" && type != "(" && type != ")") override = "error";
11689 return "interpolation";
11690 };
11691
11692 return {
11693 startState: function(base) {
11694 return {tokenize: null,
11695 state: inline ? "block" : "top",
11696 stateArg: null,
11697 context: new Context(inline ? "block" : "top", base || 0, null)};
11698 },
11699
11700 token: function(stream, state) {
11701 if (!state.tokenize && stream.eatSpace()) return null;
11702 var style = (state.tokenize || tokenBase)(stream, state);
11703 if (style && typeof style == "object") {
11704 type = style[1];
11705 style = style[0];
11706 }
11707 override = style;
11708 if (type != "comment")
11709 state.state = states[state.state](type, stream, state);
11710 return override;
11711 },
11712
11713 indent: function(state, textAfter) {
11714 var cx = state.context, ch = textAfter && textAfter.charAt(0);
11715 var indent = cx.indent;
11716 if (cx.type == "prop" && (ch == "}" || ch == ")")) cx = cx.prev;
11717 if (cx.prev) {
11718 if (ch == "}" && (cx.type == "block" || cx.type == "top" ||
11719 cx.type == "interpolation" || cx.type == "restricted_atBlock")) {
11720 // Resume indentation from parent context.
11721 cx = cx.prev;
11722 indent = cx.indent;
11723 } else if (ch == ")" && (cx.type == "parens" || cx.type == "atBlock_parens") ||
11724 ch == "{" && (cx.type == "at" || cx.type == "atBlock")) {
11725 // Dedent relative to current context.
11726 indent = Math.max(0, cx.indent - indentUnit);
11727 }
11728 }
11729 return indent;
11730 },
11731
11732 electricChars: "}",
11733 blockCommentStart: "/*",
11734 blockCommentEnd: "*/",
11735 blockCommentContinue: " * ",
11736 lineComment: lineComment,
11737 fold: "brace"
11738 };
11739 });
11740
11741 function keySet(array) {
11742 var keys = {};
11743 for (var i = 0; i < array.length; ++i) {
11744 keys[array[i].toLowerCase()] = true;
11745 }
11746 return keys;
11747 }
11748
11749 var documentTypes_ = [
11750 "domain", "regexp", "url", "url-prefix"
11751 ], documentTypes = keySet(documentTypes_);
11752
11753 var mediaTypes_ = [
11754 "all", "aural", "braille", "handheld", "print", "projection", "screen",
11755 "tty", "tv", "embossed"
11756 ], mediaTypes = keySet(mediaTypes_);
11757
11758 var mediaFeatures_ = [
11759 "width", "min-width", "max-width", "height", "min-height", "max-height",
11760 "device-width", "min-device-width", "max-device-width", "device-height",
11761 "min-device-height", "max-device-height", "aspect-ratio",
11762 "min-aspect-ratio", "max-aspect-ratio", "device-aspect-ratio",
11763 "min-device-aspect-ratio", "max-device-aspect-ratio", "color", "min-color",
11764 "max-color", "color-index", "min-color-index", "max-color-index",
11765 "monochrome", "min-monochrome", "max-monochrome", "resolution",
11766 "min-resolution", "max-resolution", "scan", "grid", "orientation",
11767 "device-pixel-ratio", "min-device-pixel-ratio", "max-device-pixel-ratio",
11768 "pointer", "any-pointer", "hover", "any-hover"
11769 ], mediaFeatures = keySet(mediaFeatures_);
11770
11771 var mediaValueKeywords_ = [
11772 "landscape", "portrait", "none", "coarse", "fine", "on-demand", "hover",
11773 "interlace", "progressive"
11774 ], mediaValueKeywords = keySet(mediaValueKeywords_);
11775
11776 var propertyKeywords_ = [
11777 "align-content", "align-items", "align-self", "alignment-adjust",
11778 "alignment-baseline", "anchor-point", "animation", "animation-delay",
11779 "animation-direction", "animation-duration", "animation-fill-mode",
11780 "animation-iteration-count", "animation-name", "animation-play-state",
11781 "animation-timing-function", "appearance", "azimuth", "backface-visibility",
11782 "background", "background-attachment", "background-blend-mode", "background-clip",
11783 "background-color", "background-image", "background-origin", "background-position",
11784 "background-repeat", "background-size", "baseline-shift", "binding",
11785 "bleed", "bookmark-label", "bookmark-level", "bookmark-state",
11786 "bookmark-target", "border", "border-bottom", "border-bottom-color",
11787 "border-bottom-left-radius", "border-bottom-right-radius",
11788 "border-bottom-style", "border-bottom-width", "border-collapse",
11789 "border-color", "border-image", "border-image-outset",
11790 "border-image-repeat", "border-image-slice", "border-image-source",
11791 "border-image-width", "border-left", "border-left-color",
11792 "border-left-style", "border-left-width", "border-radius", "border-right",
11793 "border-right-color", "border-right-style", "border-right-width",
11794 "border-spacing", "border-style", "border-top", "border-top-color",
11795 "border-top-left-radius", "border-top-right-radius", "border-top-style",
11796 "border-top-width", "border-width", "bottom", "box-decoration-break",
11797 "box-shadow", "box-sizing", "break-after", "break-before", "break-inside",
11798 "caption-side", "caret-color", "clear", "clip", "color", "color-profile", "column-count",
11799 "column-fill", "column-gap", "column-rule", "column-rule-color",
11800 "column-rule-style", "column-rule-width", "column-span", "column-width",
11801 "columns", "content", "counter-increment", "counter-reset", "crop", "cue",
11802 "cue-after", "cue-before", "cursor", "direction", "display",
11803 "dominant-baseline", "drop-initial-after-adjust",
11804 "drop-initial-after-align", "drop-initial-before-adjust",
11805 "drop-initial-before-align", "drop-initial-size", "drop-initial-value",
11806 "elevation", "empty-cells", "fit", "fit-position", "flex", "flex-basis",
11807 "flex-direction", "flex-flow", "flex-grow", "flex-shrink", "flex-wrap",
11808 "float", "float-offset", "flow-from", "flow-into", "font", "font-feature-settings",
11809 "font-family", "font-kerning", "font-language-override", "font-size", "font-size-adjust",
11810 "font-stretch", "font-style", "font-synthesis", "font-variant",
11811 "font-variant-alternates", "font-variant-caps", "font-variant-east-asian",
11812 "font-variant-ligatures", "font-variant-numeric", "font-variant-position",
11813 "font-weight", "grid", "grid-area", "grid-auto-columns", "grid-auto-flow",
11814 "grid-auto-rows", "grid-column", "grid-column-end", "grid-column-gap",
11815 "grid-column-start", "grid-gap", "grid-row", "grid-row-end", "grid-row-gap",
11816 "grid-row-start", "grid-template", "grid-template-areas", "grid-template-columns",
11817 "grid-template-rows", "hanging-punctuation", "height", "hyphens",
11818 "icon", "image-orientation", "image-rendering", "image-resolution",
11819 "inline-box-align", "justify-content", "justify-items", "justify-self", "left", "letter-spacing",
11820 "line-break", "line-height", "line-stacking", "line-stacking-ruby",
11821 "line-stacking-shift", "line-stacking-strategy", "list-style",
11822 "list-style-image", "list-style-position", "list-style-type", "margin",
11823 "margin-bottom", "margin-left", "margin-right", "margin-top",
11824 "marks", "marquee-direction", "marquee-loop",
11825 "marquee-play-count", "marquee-speed", "marquee-style", "max-height",
11826 "max-width", "min-height", "min-width", "mix-blend-mode", "move-to", "nav-down", "nav-index",
11827 "nav-left", "nav-right", "nav-up", "object-fit", "object-position",
11828 "opacity", "order", "orphans", "outline",
11829 "outline-color", "outline-offset", "outline-style", "outline-width",
11830 "overflow", "overflow-style", "overflow-wrap", "overflow-x", "overflow-y",
11831 "padding", "padding-bottom", "padding-left", "padding-right", "padding-top",
11832 "page", "page-break-after", "page-break-before", "page-break-inside",
11833 "page-policy", "pause", "pause-after", "pause-before", "perspective",
11834 "perspective-origin", "pitch", "pitch-range", "place-content", "place-items", "place-self", "play-during", "position",
11835 "presentation-level", "punctuation-trim", "quotes", "region-break-after",
11836 "region-break-before", "region-break-inside", "region-fragment",
11837 "rendering-intent", "resize", "rest", "rest-after", "rest-before", "richness",
11838 "right", "rotation", "rotation-point", "ruby-align", "ruby-overhang",
11839 "ruby-position", "ruby-span", "shape-image-threshold", "shape-inside", "shape-margin",
11840 "shape-outside", "size", "speak", "speak-as", "speak-header",
11841 "speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set",
11842 "tab-size", "table-layout", "target", "target-name", "target-new",
11843 "target-position", "text-align", "text-align-last", "text-decoration",
11844 "text-decoration-color", "text-decoration-line", "text-decoration-skip",
11845 "text-decoration-style", "text-emphasis", "text-emphasis-color",
11846 "text-emphasis-position", "text-emphasis-style", "text-height",
11847 "text-indent", "text-justify", "text-outline", "text-overflow", "text-shadow",
11848 "text-size-adjust", "text-space-collapse", "text-transform", "text-underline-position",
11849 "text-wrap", "top", "transform", "transform-origin", "transform-style",
11850 "transition", "transition-delay", "transition-duration",
11851 "transition-property", "transition-timing-function", "unicode-bidi",
11852 "user-select", "vertical-align", "visibility", "voice-balance", "voice-duration",
11853 "voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress",
11854 "voice-volume", "volume", "white-space", "widows", "width", "will-change", "word-break",
11855 "word-spacing", "word-wrap", "z-index",
11856 // SVG-specific
11857 "clip-path", "clip-rule", "mask", "enable-background", "filter", "flood-color",
11858 "flood-opacity", "lighting-color", "stop-color", "stop-opacity", "pointer-events",
11859 "color-interpolation", "color-interpolation-filters",
11860 "color-rendering", "fill", "fill-opacity", "fill-rule", "image-rendering",
11861 "marker", "marker-end", "marker-mid", "marker-start", "shape-rendering", "stroke",
11862 "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin",
11863 "stroke-miterlimit", "stroke-opacity", "stroke-width", "text-rendering",
11864 "baseline-shift", "dominant-baseline", "glyph-orientation-horizontal",
11865 "glyph-orientation-vertical", "text-anchor", "writing-mode"
11866 ], propertyKeywords = keySet(propertyKeywords_);
11867
11868 var nonStandardPropertyKeywords_ = [
11869 "scrollbar-arrow-color", "scrollbar-base-color", "scrollbar-dark-shadow-color",
11870 "scrollbar-face-color", "scrollbar-highlight-color", "scrollbar-shadow-color",
11871 "scrollbar-3d-light-color", "scrollbar-track-color", "shape-inside",
11872 "searchfield-cancel-button", "searchfield-decoration", "searchfield-results-button",
11873 "searchfield-results-decoration", "zoom"
11874 ], nonStandardPropertyKeywords = keySet(nonStandardPropertyKeywords_);
11875
11876 var fontProperties_ = [
11877 "font-family", "src", "unicode-range", "font-variant", "font-feature-settings",
11878 "font-stretch", "font-weight", "font-style"
11879 ], fontProperties = keySet(fontProperties_);
11880
11881 var counterDescriptors_ = [
11882 "additive-symbols", "fallback", "negative", "pad", "prefix", "range",
11883 "speak-as", "suffix", "symbols", "system"
11884 ], counterDescriptors = keySet(counterDescriptors_);
11885
11886 var colorKeywords_ = [
11887 "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige",
11888 "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown",
11889 "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue",
11890 "cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod",
11891 "darkgray", "darkgreen", "darkkhaki", "darkmagenta", "darkolivegreen",
11892 "darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen",
11893 "darkslateblue", "darkslategray", "darkturquoise", "darkviolet",
11894 "deeppink", "deepskyblue", "dimgray", "dodgerblue", "firebrick",
11895 "floralwhite", "forestgreen", "fuchsia", "gainsboro", "ghostwhite",
11896 "gold", "goldenrod", "gray", "grey", "green", "greenyellow", "honeydew",
11897 "hotpink", "indianred", "indigo", "ivory", "khaki", "lavender",
11898 "lavenderblush", "lawngreen", "lemonchiffon", "lightblue", "lightcoral",
11899 "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightpink",
11900 "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray",
11901 "lightsteelblue", "lightyellow", "lime", "limegreen", "linen", "magenta",
11902 "maroon", "mediumaquamarine", "mediumblue", "mediumorchid", "mediumpurple",
11903 "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise",
11904 "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin",
11905 "navajowhite", "navy", "oldlace", "olive", "olivedrab", "orange", "orangered",
11906 "orchid", "palegoldenrod", "palegreen", "paleturquoise", "palevioletred",
11907 "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue",
11908 "purple", "rebeccapurple", "red", "rosybrown", "royalblue", "saddlebrown",
11909 "salmon", "sandybrown", "seagreen", "seashell", "sienna", "silver", "skyblue",
11910 "slateblue", "slategray", "snow", "springgreen", "steelblue", "tan",
11911 "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white",
11912 "whitesmoke", "yellow", "yellowgreen"
11913 ], colorKeywords = keySet(colorKeywords_);
11914
11915 var valueKeywords_ = [
11916 "above", "absolute", "activeborder", "additive", "activecaption", "afar",
11917 "after-white-space", "ahead", "alias", "all", "all-scroll", "alphabetic", "alternate",
11918 "always", "amharic", "amharic-abegede", "antialiased", "appworkspace",
11919 "arabic-indic", "armenian", "asterisks", "attr", "auto", "auto-flow", "avoid", "avoid-column", "avoid-page",
11920 "avoid-region", "background", "backwards", "baseline", "below", "bidi-override", "binary",
11921 "bengali", "blink", "block", "block-axis", "bold", "bolder", "border", "border-box",
11922 "both", "bottom", "break", "break-all", "break-word", "bullets", "button", "button-bevel",
11923 "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "calc", "cambodian",
11924 "capitalize", "caps-lock-indicator", "caption", "captiontext", "caret",
11925 "cell", "center", "checkbox", "circle", "cjk-decimal", "cjk-earthly-branch",
11926 "cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote",
11927 "col-resize", "collapse", "color", "color-burn", "color-dodge", "column", "column-reverse",
11928 "compact", "condensed", "contain", "content", "contents",
11929 "content-box", "context-menu", "continuous", "copy", "counter", "counters", "cover", "crop",
11930 "cross", "crosshair", "currentcolor", "cursive", "cyclic", "darken", "dashed", "decimal",
11931 "decimal-leading-zero", "default", "default-button", "dense", "destination-atop",
11932 "destination-in", "destination-out", "destination-over", "devanagari", "difference",
11933 "disc", "discard", "disclosure-closed", "disclosure-open", "document",
11934 "dot-dash", "dot-dot-dash",
11935 "dotted", "double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out",
11936 "element", "ellipse", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede",
11937 "ethiopic-abegede-am-et", "ethiopic-abegede-gez", "ethiopic-abegede-ti-er",
11938 "ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er",
11939 "ethiopic-halehame-aa-et", "ethiopic-halehame-am-et",
11940 "ethiopic-halehame-gez", "ethiopic-halehame-om-et",
11941 "ethiopic-halehame-sid-et", "ethiopic-halehame-so-et",
11942 "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et", "ethiopic-halehame-tig",
11943 "ethiopic-numeric", "ew-resize", "exclusion", "expanded", "extends", "extra-condensed",
11944 "extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "flex", "flex-end", "flex-start", "footnotes",
11945 "forwards", "from", "geometricPrecision", "georgian", "graytext", "grid", "groove",
11946 "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hard-light", "hebrew",
11947 "help", "hidden", "hide", "higher", "highlight", "highlighttext",
11948 "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "hue", "icon", "ignore",
11949 "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite",
11950 "infobackground", "infotext", "inherit", "initial", "inline", "inline-axis",
11951 "inline-block", "inline-flex", "inline-grid", "inline-table", "inset", "inside", "intrinsic", "invert",
11952 "italic", "japanese-formal", "japanese-informal", "justify", "kannada",
11953 "katakana", "katakana-iroha", "keep-all", "khmer",
11954 "korean-hangul-formal", "korean-hanja-formal", "korean-hanja-informal",
11955 "landscape", "lao", "large", "larger", "left", "level", "lighter", "lighten",
11956 "line-through", "linear", "linear-gradient", "lines", "list-item", "listbox", "listitem",
11957 "local", "logical", "loud", "lower", "lower-alpha", "lower-armenian",
11958 "lower-greek", "lower-hexadecimal", "lower-latin", "lower-norwegian",
11959 "lower-roman", "lowercase", "ltr", "luminosity", "malayalam", "match", "matrix", "matrix3d",
11960 "media-controls-background", "media-current-time-display",
11961 "media-fullscreen-button", "media-mute-button", "media-play-button",
11962 "media-return-to-realtime-button", "media-rewind-button",
11963 "media-seek-back-button", "media-seek-forward-button", "media-slider",
11964 "media-sliderthumb", "media-time-remaining-display", "media-volume-slider",
11965 "media-volume-slider-container", "media-volume-sliderthumb", "medium",
11966 "menu", "menulist", "menulist-button", "menulist-text",
11967 "menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic",
11968 "mix", "mongolian", "monospace", "move", "multiple", "multiply", "myanmar", "n-resize",
11969 "narrower", "ne-resize", "nesw-resize", "no-close-quote", "no-drop",
11970 "no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap",
11971 "ns-resize", "numbers", "numeric", "nw-resize", "nwse-resize", "oblique", "octal", "opacity", "open-quote",
11972 "optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset",
11973 "outside", "outside-shape", "overlay", "overline", "padding", "padding-box",
11974 "painted", "page", "paused", "persian", "perspective", "plus-darker", "plus-lighter",
11975 "pointer", "polygon", "portrait", "pre", "pre-line", "pre-wrap", "preserve-3d",
11976 "progress", "push-button", "radial-gradient", "radio", "read-only",
11977 "read-write", "read-write-plaintext-only", "rectangle", "region",
11978 "relative", "repeat", "repeating-linear-gradient",
11979 "repeating-radial-gradient", "repeat-x", "repeat-y", "reset", "reverse",
11980 "rgb", "rgba", "ridge", "right", "rotate", "rotate3d", "rotateX", "rotateY",
11981 "rotateZ", "round", "row", "row-resize", "row-reverse", "rtl", "run-in", "running",
11982 "s-resize", "sans-serif", "saturation", "scale", "scale3d", "scaleX", "scaleY", "scaleZ", "screen",
11983 "scroll", "scrollbar", "scroll-position", "se-resize", "searchfield",
11984 "searchfield-cancel-button", "searchfield-decoration",
11985 "searchfield-results-button", "searchfield-results-decoration", "self-start", "self-end",
11986 "semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama",
11987 "simp-chinese-formal", "simp-chinese-informal", "single",
11988 "skew", "skewX", "skewY", "skip-white-space", "slide", "slider-horizontal",
11989 "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow",
11990 "small", "small-caps", "small-caption", "smaller", "soft-light", "solid", "somali",
11991 "source-atop", "source-in", "source-out", "source-over", "space", "space-around", "space-between", "space-evenly", "spell-out", "square",
11992 "square-button", "start", "static", "status-bar", "stretch", "stroke", "sub",
11993 "subpixel-antialiased", "super", "sw-resize", "symbolic", "symbols", "system-ui", "table",
11994 "table-caption", "table-cell", "table-column", "table-column-group",
11995 "table-footer-group", "table-header-group", "table-row", "table-row-group",
11996 "tamil",
11997 "telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai",
11998 "thick", "thin", "threeddarkshadow", "threedface", "threedhighlight",
11999 "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er",
12000 "tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top",
12001 "trad-chinese-formal", "trad-chinese-informal", "transform",
12002 "translate", "translate3d", "translateX", "translateY", "translateZ",
12003 "transparent", "ultra-condensed", "ultra-expanded", "underline", "unset", "up",
12004 "upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal",
12005 "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url",
12006 "var", "vertical", "vertical-text", "visible", "visibleFill", "visiblePainted",
12007 "visibleStroke", "visual", "w-resize", "wait", "wave", "wider",
12008 "window", "windowframe", "windowtext", "words", "wrap", "wrap-reverse", "x-large", "x-small", "xor",
12009 "xx-large", "xx-small"
12010 ], valueKeywords = keySet(valueKeywords_);
12011
12012 var allWords = documentTypes_.concat(mediaTypes_).concat(mediaFeatures_).concat(mediaValueKeywords_)
12013 .concat(propertyKeywords_).concat(nonStandardPropertyKeywords_).concat(colorKeywords_)
12014 .concat(valueKeywords_);
12015 CodeMirror.registerHelper("hintWords", "css", allWords);
12016
12017 function tokenCComment(stream, state) {
12018 var maybeEnd = false, ch;
12019 while ((ch = stream.next()) != null) {
12020 if (maybeEnd && ch == "/") {
12021 state.tokenize = null;
12022 break;
12023 }
12024 maybeEnd = (ch == "*");
12025 }
12026 return ["comment", "comment"];
12027 }
12028
12029 CodeMirror.defineMIME("text/css", {
12030 documentTypes: documentTypes,
12031 mediaTypes: mediaTypes,
12032 mediaFeatures: mediaFeatures,
12033 mediaValueKeywords: mediaValueKeywords,
12034 propertyKeywords: propertyKeywords,
12035 nonStandardPropertyKeywords: nonStandardPropertyKeywords,
12036 fontProperties: fontProperties,
12037 counterDescriptors: counterDescriptors,
12038 colorKeywords: colorKeywords,
12039 valueKeywords: valueKeywords,
12040 tokenHooks: {
12041 "/": function(stream, state) {
12042 if (!stream.eat("*")) return false;
12043 state.tokenize = tokenCComment;
12044 return tokenCComment(stream, state);
12045 }
12046 },
12047 name: "css"
12048 });
12049
12050 CodeMirror.defineMIME("text/x-scss", {
12051 mediaTypes: mediaTypes,
12052 mediaFeatures: mediaFeatures,
12053 mediaValueKeywords: mediaValueKeywords,
12054 propertyKeywords: propertyKeywords,
12055 nonStandardPropertyKeywords: nonStandardPropertyKeywords,
12056 colorKeywords: colorKeywords,
12057 valueKeywords: valueKeywords,
12058 fontProperties: fontProperties,
12059 allowNested: true,
12060 lineComment: "//",
12061 tokenHooks: {
12062 "/": function(stream, state) {
12063 if (stream.eat("/")) {
12064 stream.skipToEnd();
12065 return ["comment", "comment"];
12066 } else if (stream.eat("*")) {
12067 state.tokenize = tokenCComment;
12068 return tokenCComment(stream, state);
12069 } else {
12070 return ["operator", "operator"];
12071 }
12072 },
12073 ":": function(stream) {
12074 if (stream.match(/\s*\{/, false))
12075 return [null, null]
12076 return false;
12077 },
12078 "$": function(stream) {
12079 stream.match(/^[\w-]+/);
12080 if (stream.match(/^\s*:/, false))
12081 return ["variable-2", "variable-definition"];
12082 return ["variable-2", "variable"];
12083 },
12084 "#": function(stream) {
12085 if (!stream.eat("{")) return false;
12086 return [null, "interpolation"];
12087 }
12088 },
12089 name: "css",
12090 helperType: "scss"
12091 });
12092
12093 CodeMirror.defineMIME("text/x-less", {
12094 mediaTypes: mediaTypes,
12095 mediaFeatures: mediaFeatures,
12096 mediaValueKeywords: mediaValueKeywords,
12097 propertyKeywords: propertyKeywords,
12098 nonStandardPropertyKeywords: nonStandardPropertyKeywords,
12099 colorKeywords: colorKeywords,
12100 valueKeywords: valueKeywords,
12101 fontProperties: fontProperties,
12102 allowNested: true,
12103 lineComment: "//",
12104 tokenHooks: {
12105 "/": function(stream, state) {
12106 if (stream.eat("/")) {
12107 stream.skipToEnd();
12108 return ["comment", "comment"];
12109 } else if (stream.eat("*")) {
12110 state.tokenize = tokenCComment;
12111 return tokenCComment(stream, state);
12112 } else {
12113 return ["operator", "operator"];
12114 }
12115 },
12116 "@": function(stream) {
12117 if (stream.eat("{")) return [null, "interpolation"];
12118 if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/i, false)) return false;
12119 stream.eatWhile(/[\w\\\-]/);
12120 if (stream.match(/^\s*:/, false))
12121 return ["variable-2", "variable-definition"];
12122 return ["variable-2", "variable"];
12123 },
12124 "&": function() {
12125 return ["atom", "atom"];
12126 }
12127 },
12128 name: "css",
12129 helperType: "less"
12130 });
12131
12132 CodeMirror.defineMIME("text/x-gss", {
12133 documentTypes: documentTypes,
12134 mediaTypes: mediaTypes,
12135 mediaFeatures: mediaFeatures,
12136 propertyKeywords: propertyKeywords,
12137 nonStandardPropertyKeywords: nonStandardPropertyKeywords,
12138 fontProperties: fontProperties,
12139 counterDescriptors: counterDescriptors,
12140 colorKeywords: colorKeywords,
12141 valueKeywords: valueKeywords,
12142 supportsAtComponent: true,
12143 tokenHooks: {
12144 "/": function(stream, state) {
12145 if (!stream.eat("*")) return false;
12146 state.tokenize = tokenCComment;
12147 return tokenCComment(stream, state);
12148 }
12149 },
12150 name: "css",
12151 helperType: "gss"
12152 });
12153
12154 });
12155
12156 },{"../../lib/codemirror":14}],16:[function(require,module,exports){
12157 // CodeMirror, copyright (c) by Marijn Haverbeke and others
12158 // Distributed under an MIT license: https://codemirror.net/LICENSE
12159
12160 (function(mod) {
12161 if (typeof exports == "object" && typeof module == "object") // CommonJS
12162 mod(require("../../lib/codemirror"), require("../xml/xml"), require("../javascript/javascript"), require("../css/css"));
12163 else if (typeof define == "function" && define.amd) // AMD
12164 define(["../../lib/codemirror", "../xml/xml", "../javascript/javascript", "../css/css"], mod);
12165 else // Plain browser env
12166 mod(CodeMirror);
12167 })(function(CodeMirror) {
12168 "use strict";
12169
12170 var defaultTags = {
12171 script: [
12172 ["lang", /(javascript|babel)/i, "javascript"],
12173 ["type", /^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^module$|^$/i, "javascript"],
12174 ["type", /./, "text/plain"],
12175 [null, null, "javascript"]
12176 ],
12177 style: [
12178 ["lang", /^css$/i, "css"],
12179 ["type", /^(text\/)?(x-)?(stylesheet|css)$/i, "css"],
12180 ["type", /./, "text/plain"],
12181 [null, null, "css"]
12182 ]
12183 };
12184
12185 function maybeBackup(stream, pat, style) {
12186 var cur = stream.current(), close = cur.search(pat);
12187 if (close > -1) {
12188 stream.backUp(cur.length - close);
12189 } else if (cur.match(/<\/?$/)) {
12190 stream.backUp(cur.length);
12191 if (!stream.match(pat, false)) stream.match(cur);
12192 }
12193 return style;
12194 }
12195
12196 var attrRegexpCache = {};
12197 function getAttrRegexp(attr) {
12198 var regexp = attrRegexpCache[attr];
12199 if (regexp) return regexp;
12200 return attrRegexpCache[attr] = new RegExp("\\s+" + attr + "\\s*=\\s*('|\")?([^'\"]+)('|\")?\\s*");
12201 }
12202
12203 function getAttrValue(text, attr) {
12204 var match = text.match(getAttrRegexp(attr))
12205 return match ? /^\s*(.*?)\s*$/.exec(match[2])[1] : ""
12206 }
12207
12208 function getTagRegexp(tagName, anchored) {
12209 return new RegExp((anchored ? "^" : "") + "<\/\s*" + tagName + "\s*>", "i");
12210 }
12211
12212 function addTags(from, to) {
12213 for (var tag in from) {
12214 var dest = to[tag] || (to[tag] = []);
12215 var source = from[tag];
12216 for (var i = source.length - 1; i >= 0; i--)
12217 dest.unshift(source[i])
12218 }
12219 }
12220
12221 function findMatchingMode(tagInfo, tagText) {
12222 for (var i = 0; i < tagInfo.length; i++) {
12223 var spec = tagInfo[i];
12224 if (!spec[0] || spec[1].test(getAttrValue(tagText, spec[0]))) return spec[2];
12225 }
12226 }
12227
12228 CodeMirror.defineMode("htmlmixed", function (config, parserConfig) {
12229 var htmlMode = CodeMirror.getMode(config, {
12230 name: "xml",
12231 htmlMode: true,
12232 multilineTagIndentFactor: parserConfig.multilineTagIndentFactor,
12233 multilineTagIndentPastTag: parserConfig.multilineTagIndentPastTag
12234 });
12235
12236 var tags = {};
12237 var configTags = parserConfig && parserConfig.tags, configScript = parserConfig && parserConfig.scriptTypes;
12238 addTags(defaultTags, tags);
12239 if (configTags) addTags(configTags, tags);
12240 if (configScript) for (var i = configScript.length - 1; i >= 0; i--)
12241 tags.script.unshift(["type", configScript[i].matches, configScript[i].mode])
12242
12243 function html(stream, state) {
12244 var style = htmlMode.token(stream, state.htmlState), tag = /\btag\b/.test(style), tagName
12245 if (tag && !/[<>\s\/]/.test(stream.current()) &&
12246 (tagName = state.htmlState.tagName && state.htmlState.tagName.toLowerCase()) &&
12247 tags.hasOwnProperty(tagName)) {
12248 state.inTag = tagName + " "
12249 } else if (state.inTag && tag && />$/.test(stream.current())) {
12250 var inTag = /^([\S]+) (.*)/.exec(state.inTag)
12251 state.inTag = null
12252 var modeSpec = stream.current() == ">" && findMatchingMode(tags[inTag[1]], inTag[2])
12253 var mode = CodeMirror.getMode(config, modeSpec)
12254 var endTagA = getTagRegexp(inTag[1], true), endTag = getTagRegexp(inTag[1], false);
12255 state.token = function (stream, state) {
12256 if (stream.match(endTagA, false)) {
12257 state.token = html;
12258 state.localState = state.localMode = null;
12259 return null;
12260 }
12261 return maybeBackup(stream, endTag, state.localMode.token(stream, state.localState));
12262 };
12263 state.localMode = mode;
12264 state.localState = CodeMirror.startState(mode, htmlMode.indent(state.htmlState, "", ""));
12265 } else if (state.inTag) {
12266 state.inTag += stream.current()
12267 if (stream.eol()) state.inTag += " "
12268 }
12269 return style;
12270 };
12271
12272 return {
12273 startState: function () {
12274 var state = CodeMirror.startState(htmlMode);
12275 return {token: html, inTag: null, localMode: null, localState: null, htmlState: state};
12276 },
12277
12278 copyState: function (state) {
12279 var local;
12280 if (state.localState) {
12281 local = CodeMirror.copyState(state.localMode, state.localState);
12282 }
12283 return {token: state.token, inTag: state.inTag,
12284 localMode: state.localMode, localState: local,
12285 htmlState: CodeMirror.copyState(htmlMode, state.htmlState)};
12286 },
12287
12288 token: function (stream, state) {
12289 return state.token(stream, state);
12290 },
12291
12292 indent: function (state, textAfter, line) {
12293 if (!state.localMode || /^\s*<\//.test(textAfter))
12294 return htmlMode.indent(state.htmlState, textAfter, line);
12295 else if (state.localMode.indent)
12296 return state.localMode.indent(state.localState, textAfter, line);
12297 else
12298 return CodeMirror.Pass;
12299 },
12300
12301 innerMode: function (state) {
12302 return {state: state.localState || state.htmlState, mode: state.localMode || htmlMode};
12303 }
12304 };
12305 }, "xml", "javascript", "css");
12306
12307 CodeMirror.defineMIME("text/html", "htmlmixed");
12308 });
12309
12310 },{"../../lib/codemirror":14,"../css/css":15,"../javascript/javascript":17,"../xml/xml":18}],17:[function(require,module,exports){
12311 // CodeMirror, copyright (c) by Marijn Haverbeke and others
12312 // Distributed under an MIT license: https://codemirror.net/LICENSE
12313
12314 (function(mod) {
12315 if (typeof exports == "object" && typeof module == "object") // CommonJS
12316 mod(require("../../lib/codemirror"));
12317 else if (typeof define == "function" && define.amd) // AMD
12318 define(["../../lib/codemirror"], mod);
12319 else // Plain browser env
12320 mod(CodeMirror);
12321 })(function(CodeMirror) {
12322 "use strict";
12323
12324 CodeMirror.defineMode("javascript", function(config, parserConfig) {
12325 var indentUnit = config.indentUnit;
12326 var statementIndent = parserConfig.statementIndent;
12327 var jsonldMode = parserConfig.jsonld;
12328 var jsonMode = parserConfig.json || jsonldMode;
12329 var isTS = parserConfig.typescript;
12330 var wordRE = parserConfig.wordCharacters || /[\w$\xa1-\uffff]/;
12331
12332 // Tokenizer
12333
12334 var keywords = function(){
12335 function kw(type) {return {type: type, style: "keyword"};}
12336 var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"), D = kw("keyword d");
12337 var operator = kw("operator"), atom = {type: "atom", style: "atom"};
12338
12339 return {
12340 "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
12341 "return": D, "break": D, "continue": D, "new": kw("new"), "delete": C, "void": C, "throw": C,
12342 "debugger": kw("debugger"), "var": kw("var"), "const": kw("var"), "let": kw("var"),
12343 "function": kw("function"), "catch": kw("catch"),
12344 "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
12345 "in": operator, "typeof": operator, "instanceof": operator,
12346 "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom,
12347 "this": kw("this"), "class": kw("class"), "super": kw("atom"),
12348 "yield": C, "export": kw("export"), "import": kw("import"), "extends": C,
12349 "await": C
12350 };
12351 }();
12352
12353 var isOperatorChar = /[+\-*&%=<>!?|~^@]/;
12354 var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/;
12355
12356 function readRegexp(stream) {
12357 var escaped = false, next, inSet = false;
12358 while ((next = stream.next()) != null) {
12359 if (!escaped) {
12360 if (next == "/" && !inSet) return;
12361 if (next == "[") inSet = true;
12362 else if (inSet && next == "]") inSet = false;
12363 }
12364 escaped = !escaped && next == "\\";
12365 }
12366 }
12367
12368 // Used as scratch variables to communicate multiple values without
12369 // consing up tons of objects.
12370 var type, content;
12371 function ret(tp, style, cont) {
12372 type = tp; content = cont;
12373 return style;
12374 }
12375 function tokenBase(stream, state) {
12376 var ch = stream.next();
12377 if (ch == '"' || ch == "'") {
12378 state.tokenize = tokenString(ch);
12379 return state.tokenize(stream, state);
12380 } else if (ch == "." && stream.match(/^\d+(?:[eE][+\-]?\d+)?/)) {
12381 return ret("number", "number");
12382 } else if (ch == "." && stream.match("..")) {
12383 return ret("spread", "meta");
12384 } else if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
12385 return ret(ch);
12386 } else if (ch == "=" && stream.eat(">")) {
12387 return ret("=>", "operator");
12388 } else if (ch == "0" && stream.match(/^(?:x[\da-f]+|o[0-7]+|b[01]+)n?/i)) {
12389 return ret("number", "number");
12390 } else if (/\d/.test(ch)) {
12391 stream.match(/^\d*(?:n|(?:\.\d*)?(?:[eE][+\-]?\d+)?)?/);
12392 return ret("number", "number");
12393 } else if (ch == "/") {
12394 if (stream.eat("*")) {
12395 state.tokenize = tokenComment;
12396 return tokenComment(stream, state);
12397 } else if (stream.eat("/")) {
12398 stream.skipToEnd();
12399 return ret("comment", "comment");
12400 } else if (expressionAllowed(stream, state, 1)) {
12401 readRegexp(stream);
12402 stream.match(/^\b(([gimyus])(?![gimyus]*\2))+\b/);
12403 return ret("regexp", "string-2");
12404 } else {
12405 stream.eat("=");
12406 return ret("operator", "operator", stream.current());
12407 }
12408 } else if (ch == "`") {
12409 state.tokenize = tokenQuasi;
12410 return tokenQuasi(stream, state);
12411 } else if (ch == "#") {
12412 stream.skipToEnd();
12413 return ret("error", "error");
12414 } else if (isOperatorChar.test(ch)) {
12415 if (ch != ">" || !state.lexical || state.lexical.type != ">") {
12416 if (stream.eat("=")) {
12417 if (ch == "!" || ch == "=") stream.eat("=")
12418 } else if (/[<>*+\-]/.test(ch)) {
12419 stream.eat(ch)
12420 if (ch == ">") stream.eat(ch)
12421 }
12422 }
12423 return ret("operator", "operator", stream.current());
12424 } else if (wordRE.test(ch)) {
12425 stream.eatWhile(wordRE);
12426 var word = stream.current()
12427 if (state.lastType != ".") {
12428 if (keywords.propertyIsEnumerable(word)) {
12429 var kw = keywords[word]
12430 return ret(kw.type, kw.style, word)
12431 }
12432 if (word == "async" && stream.match(/^(\s|\/\*.*?\*\/)*[\[\(\w]/, false))
12433 return ret("async", "keyword", word)
12434 }
12435 return ret("variable", "variable", word)
12436 }
12437 }
12438
12439 function tokenString(quote) {
12440 return function(stream, state) {
12441 var escaped = false, next;
12442 if (jsonldMode && stream.peek() == "@" && stream.match(isJsonldKeyword)){
12443 state.tokenize = tokenBase;
12444 return ret("jsonld-keyword", "meta");
12445 }
12446 while ((next = stream.next()) != null) {
12447 if (next == quote && !escaped) break;
12448 escaped = !escaped && next == "\\";
12449 }
12450 if (!escaped) state.tokenize = tokenBase;
12451 return ret("string", "string");
12452 };
12453 }
12454
12455 function tokenComment(stream, state) {
12456 var maybeEnd = false, ch;
12457 while (ch = stream.next()) {
12458 if (ch == "/" && maybeEnd) {
12459 state.tokenize = tokenBase;
12460 break;
12461 }
12462 maybeEnd = (ch == "*");
12463 }
12464 return ret("comment", "comment");
12465 }
12466
12467 function tokenQuasi(stream, state) {
12468 var escaped = false, next;
12469 while ((next = stream.next()) != null) {
12470 if (!escaped && (next == "`" || next == "$" && stream.eat("{"))) {
12471 state.tokenize = tokenBase;
12472 break;
12473 }
12474 escaped = !escaped && next == "\\";
12475 }
12476 return ret("quasi", "string-2", stream.current());
12477 }
12478
12479 var brackets = "([{}])";
12480 // This is a crude lookahead trick to try and notice that we're
12481 // parsing the argument patterns for a fat-arrow function before we
12482 // actually hit the arrow token. It only works if the arrow is on
12483 // the same line as the arguments and there's no strange noise
12484 // (comments) in between. Fallback is to only notice when we hit the
12485 // arrow, and not declare the arguments as locals for the arrow
12486 // body.
12487 function findFatArrow(stream, state) {
12488 if (state.fatArrowAt) state.fatArrowAt = null;
12489 var arrow = stream.string.indexOf("=>", stream.start);
12490 if (arrow < 0) return;
12491
12492 if (isTS) { // Try to skip TypeScript return type declarations after the arguments
12493 var m = /:\s*(?:\w+(?:<[^>]*>|\[\])?|\{[^}]*\})\s*$/.exec(stream.string.slice(stream.start, arrow))
12494 if (m) arrow = m.index
12495 }
12496
12497 var depth = 0, sawSomething = false;
12498 for (var pos = arrow - 1; pos >= 0; --pos) {
12499 var ch = stream.string.charAt(pos);
12500 var bracket = brackets.indexOf(ch);
12501 if (bracket >= 0 && bracket < 3) {
12502 if (!depth) { ++pos; break; }
12503 if (--depth == 0) { if (ch == "(") sawSomething = true; break; }
12504 } else if (bracket >= 3 && bracket < 6) {
12505 ++depth;
12506 } else if (wordRE.test(ch)) {
12507 sawSomething = true;
12508 } else if (/["'\/]/.test(ch)) {
12509 return;
12510 } else if (sawSomething && !depth) {
12511 ++pos;
12512 break;
12513 }
12514 }
12515 if (sawSomething && !depth) state.fatArrowAt = pos;
12516 }
12517
12518 // Parser
12519
12520 var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true, "jsonld-keyword": true};
12521
12522 function JSLexical(indented, column, type, align, prev, info) {
12523 this.indented = indented;
12524 this.column = column;
12525 this.type = type;
12526 this.prev = prev;
12527 this.info = info;
12528 if (align != null) this.align = align;
12529 }
12530
12531 function inScope(state, varname) {
12532 for (var v = state.localVars; v; v = v.next)
12533 if (v.name == varname) return true;
12534 for (var cx = state.context; cx; cx = cx.prev) {
12535 for (var v = cx.vars; v; v = v.next)
12536 if (v.name == varname) return true;
12537 }
12538 }
12539
12540 function parseJS(state, style, type, content, stream) {
12541 var cc = state.cc;
12542 // Communicate our context to the combinators.
12543 // (Less wasteful than consing up a hundred closures on every call.)
12544 cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; cx.style = style;
12545
12546 if (!state.lexical.hasOwnProperty("align"))
12547 state.lexical.align = true;
12548
12549 while(true) {
12550 var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;
12551 if (combinator(type, content)) {
12552 while(cc.length && cc[cc.length - 1].lex)
12553 cc.pop()();
12554 if (cx.marked) return cx.marked;
12555 if (type == "variable" && inScope(state, content)) return "variable-2";
12556 return style;
12557 }
12558 }
12559 }
12560
12561 // Combinator utils
12562
12563 var cx = {state: null, column: null, marked: null, cc: null};
12564 function pass() {
12565 for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
12566 }
12567 function cont() {
12568 pass.apply(null, arguments);
12569 return true;
12570 }
12571 function inList(name, list) {
12572 for (var v = list; v; v = v.next) if (v.name == name) return true
12573 return false;
12574 }
12575 function register(varname) {
12576 var state = cx.state;
12577 cx.marked = "def";
12578 if (state.context) {
12579 if (state.lexical.info == "var" && state.context && state.context.block) {
12580 // FIXME function decls are also not block scoped
12581 var newContext = registerVarScoped(varname, state.context)
12582 if (newContext != null) {
12583 state.context = newContext
12584 return
12585 }
12586 } else if (!inList(varname, state.localVars)) {
12587 state.localVars = new Var(varname, state.localVars)
12588 return
12589 }
12590 }
12591 // Fall through means this is global
12592 if (parserConfig.globalVars && !inList(varname, state.globalVars))
12593 state.globalVars = new Var(varname, state.globalVars)
12594 }
12595 function registerVarScoped(varname, context) {
12596 if (!context) {
12597 return null
12598 } else if (context.block) {
12599 var inner = registerVarScoped(varname, context.prev)
12600 if (!inner) return null
12601 if (inner == context.prev) return context
12602 return new Context(inner, context.vars, true)
12603 } else if (inList(varname, context.vars)) {
12604 return context
12605 } else {
12606 return new Context(context.prev, new Var(varname, context.vars), false)
12607 }
12608 }
12609
12610 function isModifier(name) {
12611 return name == "public" || name == "private" || name == "protected" || name == "abstract" || name == "readonly"
12612 }
12613
12614 // Combinators
12615
12616 function Context(prev, vars, block) { this.prev = prev; this.vars = vars; this.block = block }
12617 function Var(name, next) { this.name = name; this.next = next }
12618
12619 var defaultVars = new Var("this", new Var("arguments", null))
12620 function pushcontext() {
12621 cx.state.context = new Context(cx.state.context, cx.state.localVars, false)
12622 cx.state.localVars = defaultVars
12623 }
12624 function pushblockcontext() {
12625 cx.state.context = new Context(cx.state.context, cx.state.localVars, true)
12626 cx.state.localVars = null
12627 }
12628 function popcontext() {
12629 cx.state.localVars = cx.state.context.vars
12630 cx.state.context = cx.state.context.prev
12631 }
12632 popcontext.lex = true
12633 function pushlex(type, info) {
12634 var result = function() {
12635 var state = cx.state, indent = state.indented;
12636 if (state.lexical.type == "stat") indent = state.lexical.indented;
12637 else for (var outer = state.lexical; outer && outer.type == ")" && outer.align; outer = outer.prev)
12638 indent = outer.indented;
12639 state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info);
12640 };
12641 result.lex = true;
12642 return result;
12643 }
12644 function poplex() {
12645 var state = cx.state;
12646 if (state.lexical.prev) {
12647 if (state.lexical.type == ")")
12648 state.indented = state.lexical.indented;
12649 state.lexical = state.lexical.prev;
12650 }
12651 }
12652 poplex.lex = true;
12653
12654 function expect(wanted) {
12655 function exp(type) {
12656 if (type == wanted) return cont();
12657 else if (wanted == ";" || type == "}" || type == ")" || type == "]") return pass();
12658 else return cont(exp);
12659 };
12660 return exp;
12661 }
12662
12663 function statement(type, value) {
12664 if (type == "var") return cont(pushlex("vardef", value), vardef, expect(";"), poplex);
12665 if (type == "keyword a") return cont(pushlex("form"), parenExpr, statement, poplex);
12666 if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
12667 if (type == "keyword d") return cx.stream.match(/^\s*$/, false) ? cont() : cont(pushlex("stat"), maybeexpression, expect(";"), poplex);
12668 if (type == "debugger") return cont(expect(";"));
12669 if (type == "{") return cont(pushlex("}"), pushblockcontext, block, poplex, popcontext);
12670 if (type == ";") return cont();
12671 if (type == "if") {
12672 if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex)
12673 cx.state.cc.pop()();
12674 return cont(pushlex("form"), parenExpr, statement, poplex, maybeelse);
12675 }
12676 if (type == "function") return cont(functiondef);
12677 if (type == "for") return cont(pushlex("form"), forspec, statement, poplex);
12678 if (type == "class" || (isTS && value == "interface")) {
12679 cx.marked = "keyword"
12680 return cont(pushlex("form", type == "class" ? type : value), className, poplex)
12681 }
12682 if (type == "variable") {
12683 if (isTS && value == "declare") {
12684 cx.marked = "keyword"
12685 return cont(statement)
12686 } else if (isTS && (value == "module" || value == "enum" || value == "type") && cx.stream.match(/^\s*\w/, false)) {
12687 cx.marked = "keyword"
12688 if (value == "enum") return cont(enumdef);
12689 else if (value == "type") return cont(typename, expect("operator"), typeexpr, expect(";"));
12690 else return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex)
12691 } else if (isTS && value == "namespace") {
12692 cx.marked = "keyword"
12693 return cont(pushlex("form"), expression, statement, poplex)
12694 } else if (isTS && value == "abstract") {
12695 cx.marked = "keyword"
12696 return cont(statement)
12697 } else {
12698 return cont(pushlex("stat"), maybelabel);
12699 }
12700 }
12701 if (type == "switch") return cont(pushlex("form"), parenExpr, expect("{"), pushlex("}", "switch"), pushblockcontext,
12702 block, poplex, poplex, popcontext);
12703 if (type == "case") return cont(expression, expect(":"));
12704 if (type == "default") return cont(expect(":"));
12705 if (type == "catch") return cont(pushlex("form"), pushcontext, maybeCatchBinding, statement, poplex, popcontext);
12706 if (type == "export") return cont(pushlex("stat"), afterExport, poplex);
12707 if (type == "import") return cont(pushlex("stat"), afterImport, poplex);
12708 if (type == "async") return cont(statement)
12709 if (value == "@") return cont(expression, statement)
12710 return pass(pushlex("stat"), expression, expect(";"), poplex);
12711 }
12712 function maybeCatchBinding(type) {
12713 if (type == "(") return cont(funarg, expect(")"))
12714 }
12715 function expression(type, value) {
12716 return expressionInner(type, value, false);
12717 }
12718 function expressionNoComma(type, value) {
12719 return expressionInner(type, value, true);
12720 }
12721 function parenExpr(type) {
12722 if (type != "(") return pass()
12723 return cont(pushlex(")"), expression, expect(")"), poplex)
12724 }
12725 function expressionInner(type, value, noComma) {
12726 if (cx.state.fatArrowAt == cx.stream.start) {
12727 var body = noComma ? arrowBodyNoComma : arrowBody;
12728 if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, expect("=>"), body, popcontext);
12729 else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext);
12730 }
12731
12732 var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
12733 if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
12734 if (type == "function") return cont(functiondef, maybeop);
12735 if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword"; return cont(pushlex("form"), classExpression, poplex); }
12736 if (type == "keyword c" || type == "async") return cont(noComma ? expressionNoComma : expression);
12737 if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop);
12738 if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression);
12739 if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop);
12740 if (type == "{") return contCommasep(objprop, "}", null, maybeop);
12741 if (type == "quasi") return pass(quasi, maybeop);
12742 if (type == "new") return cont(maybeTarget(noComma));
12743 if (type == "import") return cont(expression);
12744 return cont();
12745 }
12746 function maybeexpression(type) {
12747 if (type.match(/[;\}\)\],]/)) return pass();
12748 return pass(expression);
12749 }
12750
12751 function maybeoperatorComma(type, value) {
12752 if (type == ",") return cont(expression);
12753 return maybeoperatorNoComma(type, value, false);
12754 }
12755 function maybeoperatorNoComma(type, value, noComma) {
12756 var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma;
12757 var expr = noComma == false ? expression : expressionNoComma;
12758 if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext);
12759 if (type == "operator") {
12760 if (/\+\+|--/.test(value) || isTS && value == "!") return cont(me);
12761 if (isTS && value == "<" && cx.stream.match(/^([^>]|<.*?>)*>\s*\(/, false))
12762 return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, me);
12763 if (value == "?") return cont(expression, expect(":"), expr);
12764 return cont(expr);
12765 }
12766 if (type == "quasi") { return pass(quasi, me); }
12767 if (type == ";") return;
12768 if (type == "(") return contCommasep(expressionNoComma, ")", "call", me);
12769 if (type == ".") return cont(property, me);
12770 if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me);
12771 if (isTS && value == "as") { cx.marked = "keyword"; return cont(typeexpr, me) }
12772 if (type == "regexp") {
12773 cx.state.lastType = cx.marked = "operator"
12774 cx.stream.backUp(cx.stream.pos - cx.stream.start - 1)
12775 return cont(expr)
12776 }
12777 }
12778 function quasi(type, value) {
12779 if (type != "quasi") return pass();
12780 if (value.slice(value.length - 2) != "${") return cont(quasi);
12781 return cont(expression, continueQuasi);
12782 }
12783 function continueQuasi(type) {
12784 if (type == "}") {
12785 cx.marked = "string-2";
12786 cx.state.tokenize = tokenQuasi;
12787 return cont(quasi);
12788 }
12789 }
12790 function arrowBody(type) {
12791 findFatArrow(cx.stream, cx.state);
12792 return pass(type == "{" ? statement : expression);
12793 }
12794 function arrowBodyNoComma(type) {
12795 findFatArrow(cx.stream, cx.state);
12796 return pass(type == "{" ? statement : expressionNoComma);
12797 }
12798 function maybeTarget(noComma) {
12799 return function(type) {
12800 if (type == ".") return cont(noComma ? targetNoComma : target);
12801 else if (type == "variable" && isTS) return cont(maybeTypeArgs, noComma ? maybeoperatorNoComma : maybeoperatorComma)
12802 else return pass(noComma ? expressionNoComma : expression);
12803 };
12804 }
12805 function target(_, value) {
12806 if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorComma); }
12807 }
12808 function targetNoComma(_, value) {
12809 if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorNoComma); }
12810 }
12811 function maybelabel(type) {
12812 if (type == ":") return cont(poplex, statement);
12813 return pass(maybeoperatorComma, expect(";"), poplex);
12814 }
12815 function property(type) {
12816 if (type == "variable") {cx.marked = "property"; return cont();}
12817 }
12818 function objprop(type, value) {
12819 if (type == "async") {
12820 cx.marked = "property";
12821 return cont(objprop);
12822 } else if (type == "variable" || cx.style == "keyword") {
12823 cx.marked = "property";
12824 if (value == "get" || value == "set") return cont(getterSetter);
12825 var m // Work around fat-arrow-detection complication for detecting typescript typed arrow params
12826 if (isTS && cx.state.fatArrowAt == cx.stream.start && (m = cx.stream.match(/^\s*:\s*/, false)))
12827 cx.state.fatArrowAt = cx.stream.pos + m[0].length
12828 return cont(afterprop);
12829 } else if (type == "number" || type == "string") {
12830 cx.marked = jsonldMode ? "property" : (cx.style + " property");
12831 return cont(afterprop);
12832 } else if (type == "jsonld-keyword") {
12833 return cont(afterprop);
12834 } else if (isTS && isModifier(value)) {
12835 cx.marked = "keyword"
12836 return cont(objprop)
12837 } else if (type == "[") {
12838 return cont(expression, maybetype, expect("]"), afterprop);
12839 } else if (type == "spread") {
12840 return cont(expressionNoComma, afterprop);
12841 } else if (value == "*") {
12842 cx.marked = "keyword";
12843 return cont(objprop);
12844 } else if (type == ":") {
12845 return pass(afterprop)
12846 }
12847 }
12848 function getterSetter(type) {
12849 if (type != "variable") return pass(afterprop);
12850 cx.marked = "property";
12851 return cont(functiondef);
12852 }
12853 function afterprop(type) {
12854 if (type == ":") return cont(expressionNoComma);
12855 if (type == "(") return pass(functiondef);
12856 }
12857 function commasep(what, end, sep) {
12858 function proceed(type, value) {
12859 if (sep ? sep.indexOf(type) > -1 : type == ",") {
12860 var lex = cx.state.lexical;
12861 if (lex.info == "call") lex.pos = (lex.pos || 0) + 1;
12862 return cont(function(type, value) {
12863 if (type == end || value == end) return pass()
12864 return pass(what)
12865 }, proceed);
12866 }
12867 if (type == end || value == end) return cont();
12868 if (sep && sep.indexOf(";") > -1) return pass(what)
12869 return cont(expect(end));
12870 }
12871 return function(type, value) {
12872 if (type == end || value == end) return cont();
12873 return pass(what, proceed);
12874 };
12875 }
12876 function contCommasep(what, end, info) {
12877 for (var i = 3; i < arguments.length; i++)
12878 cx.cc.push(arguments[i]);
12879 return cont(pushlex(end, info), commasep(what, end), poplex);
12880 }
12881 function block(type) {
12882 if (type == "}") return cont();
12883 return pass(statement, block);
12884 }
12885 function maybetype(type, value) {
12886 if (isTS) {
12887 if (type == ":" || value == "in") return cont(typeexpr);
12888 if (value == "?") return cont(maybetype);
12889 }
12890 }
12891 function mayberettype(type) {
12892 if (isTS && type == ":") {
12893 if (cx.stream.match(/^\s*\w+\s+is\b/, false)) return cont(expression, isKW, typeexpr)
12894 else return cont(typeexpr)
12895 }
12896 }
12897 function isKW(_, value) {
12898 if (value == "is") {
12899 cx.marked = "keyword"
12900 return cont()
12901 }
12902 }
12903 function typeexpr(type, value) {
12904 if (value == "keyof" || value == "typeof" || value == "infer") {
12905 cx.marked = "keyword"
12906 return cont(value == "typeof" ? expressionNoComma : typeexpr)
12907 }
12908 if (type == "variable" || value == "void") {
12909 cx.marked = "type"
12910 return cont(afterType)
12911 }
12912 if (value == "|" || value == "&") return cont(typeexpr)
12913 if (type == "string" || type == "number" || type == "atom") return cont(afterType);
12914 if (type == "[") return cont(pushlex("]"), commasep(typeexpr, "]", ","), poplex, afterType)
12915 if (type == "{") return cont(pushlex("}"), commasep(typeprop, "}", ",;"), poplex, afterType)
12916 if (type == "(") return cont(commasep(typearg, ")"), maybeReturnType, afterType)
12917 if (type == "<") return cont(commasep(typeexpr, ">"), typeexpr)
12918 }
12919 function maybeReturnType(type) {
12920 if (type == "=>") return cont(typeexpr)
12921 }
12922 function typeprop(type, value) {
12923 if (type == "variable" || cx.style == "keyword") {
12924 cx.marked = "property"
12925 return cont(typeprop)
12926 } else if (value == "?" || type == "number" || type == "string") {
12927 return cont(typeprop)
12928 } else if (type == ":") {
12929 return cont(typeexpr)
12930 } else if (type == "[") {
12931 return cont(expect("variable"), maybetype, expect("]"), typeprop)
12932 } else if (type == "(") {
12933 return pass(functiondecl, typeprop)
12934 }
12935 }
12936 function typearg(type, value) {
12937 if (type == "variable" && cx.stream.match(/^\s*[?:]/, false) || value == "?") return cont(typearg)
12938 if (type == ":") return cont(typeexpr)
12939 if (type == "spread") return cont(typearg)
12940 return pass(typeexpr)
12941 }
12942 function afterType(type, value) {
12943 if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
12944 if (value == "|" || type == "." || value == "&") return cont(typeexpr)
12945 if (type == "[") return cont(typeexpr, expect("]"), afterType)
12946 if (value == "extends" || value == "implements") { cx.marked = "keyword"; return cont(typeexpr) }
12947 if (value == "?") return cont(typeexpr, expect(":"), typeexpr)
12948 }
12949 function maybeTypeArgs(_, value) {
12950 if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
12951 }
12952 function typeparam() {
12953 return pass(typeexpr, maybeTypeDefault)
12954 }
12955 function maybeTypeDefault(_, value) {
12956 if (value == "=") return cont(typeexpr)
12957 }
12958 function vardef(_, value) {
12959 if (value == "enum") {cx.marked = "keyword"; return cont(enumdef)}
12960 return pass(pattern, maybetype, maybeAssign, vardefCont);
12961 }
12962 function pattern(type, value) {
12963 if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(pattern) }
12964 if (type == "variable") { register(value); return cont(); }
12965 if (type == "spread") return cont(pattern);
12966 if (type == "[") return contCommasep(eltpattern, "]");
12967 if (type == "{") return contCommasep(proppattern, "}");
12968 }
12969 function proppattern(type, value) {
12970 if (type == "variable" && !cx.stream.match(/^\s*:/, false)) {
12971 register(value);
12972 return cont(maybeAssign);
12973 }
12974 if (type == "variable") cx.marked = "property";
12975 if (type == "spread") return cont(pattern);
12976 if (type == "}") return pass();
12977 if (type == "[") return cont(expression, expect(']'), expect(':'), proppattern);
12978 return cont(expect(":"), pattern, maybeAssign);
12979 }
12980 function eltpattern() {
12981 return pass(pattern, maybeAssign)
12982 }
12983 function maybeAssign(_type, value) {
12984 if (value == "=") return cont(expressionNoComma);
12985 }
12986 function vardefCont(type) {
12987 if (type == ",") return cont(vardef);
12988 }
12989 function maybeelse(type, value) {
12990 if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex);
12991 }
12992 function forspec(type, value) {
12993 if (value == "await") return cont(forspec);
12994 if (type == "(") return cont(pushlex(")"), forspec1, poplex);
12995 }
12996 function forspec1(type) {
12997 if (type == "var") return cont(vardef, forspec2);
12998 if (type == "variable") return cont(forspec2);
12999 return pass(forspec2)
13000 }
13001 function forspec2(type, value) {
13002 if (type == ")") return cont()
13003 if (type == ";") return cont(forspec2)
13004 if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression, forspec2) }
13005 return pass(expression, forspec2)
13006 }
13007 function functiondef(type, value) {
13008 if (value == "*") {cx.marked = "keyword"; return cont(functiondef);}
13009 if (type == "variable") {register(value); return cont(functiondef);}
13010 if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, statement, popcontext);
13011 if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondef)
13012 }
13013 function functiondecl(type, value) {
13014 if (value == "*") {cx.marked = "keyword"; return cont(functiondecl);}
13015 if (type == "variable") {register(value); return cont(functiondecl);}
13016 if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, popcontext);
13017 if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondecl)
13018 }
13019 function typename(type, value) {
13020 if (type == "keyword" || type == "variable") {
13021 cx.marked = "type"
13022 return cont(typename)
13023 } else if (value == "<") {
13024 return cont(pushlex(">"), commasep(typeparam, ">"), poplex)
13025 }
13026 }
13027 function funarg(type, value) {
13028 if (value == "@") cont(expression, funarg)
13029 if (type == "spread") return cont(funarg);
13030 if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(funarg); }
13031 if (isTS && type == "this") return cont(maybetype, maybeAssign)
13032 return pass(pattern, maybetype, maybeAssign);
13033 }
13034 function classExpression(type, value) {
13035 // Class expressions may have an optional name.
13036 if (type == "variable") return className(type, value);
13037 return classNameAfter(type, value);
13038 }
13039 function className(type, value) {
13040 if (type == "variable") {register(value); return cont(classNameAfter);}
13041 }
13042 function classNameAfter(type, value) {
13043 if (value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, classNameAfter)
13044 if (value == "extends" || value == "implements" || (isTS && type == ",")) {
13045 if (value == "implements") cx.marked = "keyword";
13046 return cont(isTS ? typeexpr : expression, classNameAfter);
13047 }
13048 if (type == "{") return cont(pushlex("}"), classBody, poplex);
13049 }
13050 function classBody(type, value) {
13051 if (type == "async" ||
13052 (type == "variable" &&
13053 (value == "static" || value == "get" || value == "set" || (isTS && isModifier(value))) &&
13054 cx.stream.match(/^\s+[\w$\xa1-\uffff]/, false))) {
13055 cx.marked = "keyword";
13056 return cont(classBody);
13057 }
13058 if (type == "variable" || cx.style == "keyword") {
13059 cx.marked = "property";
13060 return cont(isTS ? classfield : functiondef, classBody);
13061 }
13062 if (type == "number" || type == "string") return cont(isTS ? classfield : functiondef, classBody);
13063 if (type == "[")
13064 return cont(expression, maybetype, expect("]"), isTS ? classfield : functiondef, classBody)
13065 if (value == "*") {
13066 cx.marked = "keyword";
13067 return cont(classBody);
13068 }
13069 if (isTS && type == "(") return pass(functiondecl, classBody)
13070 if (type == ";" || type == ",") return cont(classBody);
13071 if (type == "}") return cont();
13072 if (value == "@") return cont(expression, classBody)
13073 }
13074 function classfield(type, value) {
13075 if (value == "?") return cont(classfield)
13076 if (type == ":") return cont(typeexpr, maybeAssign)
13077 if (value == "=") return cont(expressionNoComma)
13078 var context = cx.state.lexical.prev, isInterface = context && context.info == "interface"
13079 return pass(isInterface ? functiondecl : functiondef)
13080 }
13081 function afterExport(type, value) {
13082 if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); }
13083 if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); }
13084 if (type == "{") return cont(commasep(exportField, "}"), maybeFrom, expect(";"));
13085 return pass(statement);
13086 }
13087 function exportField(type, value) {
13088 if (value == "as") { cx.marked = "keyword"; return cont(expect("variable")); }
13089 if (type == "variable") return pass(expressionNoComma, exportField);
13090 }
13091 function afterImport(type) {
13092 if (type == "string") return cont();
13093 if (type == "(") return pass(expression);
13094 return pass(importSpec, maybeMoreImports, maybeFrom);
13095 }
13096 function importSpec(type, value) {
13097 if (type == "{") return contCommasep(importSpec, "}");
13098 if (type == "variable") register(value);
13099 if (value == "*") cx.marked = "keyword";
13100 return cont(maybeAs);
13101 }
13102 function maybeMoreImports(type) {
13103 if (type == ",") return cont(importSpec, maybeMoreImports)
13104 }
13105 function maybeAs(_type, value) {
13106 if (value == "as") { cx.marked = "keyword"; return cont(importSpec); }
13107 }
13108 function maybeFrom(_type, value) {
13109 if (value == "from") { cx.marked = "keyword"; return cont(expression); }
13110 }
13111 function arrayLiteral(type) {
13112 if (type == "]") return cont();
13113 return pass(commasep(expressionNoComma, "]"));
13114 }
13115 function enumdef() {
13116 return pass(pushlex("form"), pattern, expect("{"), pushlex("}"), commasep(enummember, "}"), poplex, poplex)
13117 }
13118 function enummember() {
13119 return pass(pattern, maybeAssign);
13120 }
13121
13122 function isContinuedStatement(state, textAfter) {
13123 return state.lastType == "operator" || state.lastType == "," ||
13124 isOperatorChar.test(textAfter.charAt(0)) ||
13125 /[,.]/.test(textAfter.charAt(0));
13126 }
13127
13128 function expressionAllowed(stream, state, backUp) {
13129 return state.tokenize == tokenBase &&
13130 /^(?:operator|sof|keyword [bcd]|case|new|export|default|spread|[\[{}\(,;:]|=>)$/.test(state.lastType) ||
13131 (state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0))))
13132 }
13133
13134 // Interface
13135
13136 return {
13137 startState: function(basecolumn) {
13138 var state = {
13139 tokenize: tokenBase,
13140 lastType: "sof",
13141 cc: [],
13142 lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
13143 localVars: parserConfig.localVars,
13144 context: parserConfig.localVars && new Context(null, null, false),
13145 indented: basecolumn || 0
13146 };
13147 if (parserConfig.globalVars && typeof parserConfig.globalVars == "object")
13148 state.globalVars = parserConfig.globalVars;
13149 return state;
13150 },
13151
13152 token: function(stream, state) {
13153 if (stream.sol()) {
13154 if (!state.lexical.hasOwnProperty("align"))
13155 state.lexical.align = false;
13156 state.indented = stream.indentation();
13157 findFatArrow(stream, state);
13158 }
13159 if (state.tokenize != tokenComment && stream.eatSpace()) return null;
13160 var style = state.tokenize(stream, state);
13161 if (type == "comment") return style;
13162 state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type;
13163 return parseJS(state, style, type, content, stream);
13164 },
13165
13166 indent: function(state, textAfter) {
13167 if (state.tokenize == tokenComment) return CodeMirror.Pass;
13168 if (state.tokenize != tokenBase) return 0;
13169 var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical, top
13170 // Kludge to prevent 'maybelse' from blocking lexical scope pops
13171 if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) {
13172 var c = state.cc[i];
13173 if (c == poplex) lexical = lexical.prev;
13174 else if (c != maybeelse) break;
13175 }
13176 while ((lexical.type == "stat" || lexical.type == "form") &&
13177 (firstChar == "}" || ((top = state.cc[state.cc.length - 1]) &&
13178 (top == maybeoperatorComma || top == maybeoperatorNoComma) &&
13179 !/^[,\.=+\-*:?[\(]/.test(textAfter))))
13180 lexical = lexical.prev;
13181 if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat")
13182 lexical = lexical.prev;
13183 var type = lexical.type, closing = firstChar == type;
13184
13185 if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info.length + 1 : 0);
13186 else if (type == "form" && firstChar == "{") return lexical.indented;
13187 else if (type == "form") return lexical.indented + indentUnit;
13188 else if (type == "stat")
13189 return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0);
13190 else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false)
13191 return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
13192 else if (lexical.align) return lexical.column + (closing ? 0 : 1);
13193 else return lexical.indented + (closing ? 0 : indentUnit);
13194 },
13195
13196 electricInput: /^\s*(?:case .*?:|default:|\{|\})$/,
13197 blockCommentStart: jsonMode ? null : "/*",
13198 blockCommentEnd: jsonMode ? null : "*/",
13199 blockCommentContinue: jsonMode ? null : " * ",
13200 lineComment: jsonMode ? null : "//",
13201 fold: "brace",
13202 closeBrackets: "()[]{}''\"\"``",
13203
13204 helperType: jsonMode ? "json" : "javascript",
13205 jsonldMode: jsonldMode,
13206 jsonMode: jsonMode,
13207
13208 expressionAllowed: expressionAllowed,
13209
13210 skipExpression: function(state) {
13211 var top = state.cc[state.cc.length - 1]
13212 if (top == expression || top == expressionNoComma) state.cc.pop()
13213 }
13214 };
13215 });
13216
13217 CodeMirror.registerHelper("wordChars", "javascript", /[\w$]/);
13218
13219 CodeMirror.defineMIME("text/javascript", "javascript");
13220 CodeMirror.defineMIME("text/ecmascript", "javascript");
13221 CodeMirror.defineMIME("application/javascript", "javascript");
13222 CodeMirror.defineMIME("application/x-javascript", "javascript");
13223 CodeMirror.defineMIME("application/ecmascript", "javascript");
13224 CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
13225 CodeMirror.defineMIME("application/x-json", {name: "javascript", json: true});
13226 CodeMirror.defineMIME("application/ld+json", {name: "javascript", jsonld: true});
13227 CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true });
13228 CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true });
13229
13230 });
13231
13232 },{"../../lib/codemirror":14}],18:[function(require,module,exports){
13233 // CodeMirror, copyright (c) by Marijn Haverbeke and others
13234 // Distributed under an MIT license: https://codemirror.net/LICENSE
13235
13236 (function(mod) {
13237 if (typeof exports == "object" && typeof module == "object") // CommonJS
13238 mod(require("../../lib/codemirror"));
13239 else if (typeof define == "function" && define.amd) // AMD
13240 define(["../../lib/codemirror"], mod);
13241 else // Plain browser env
13242 mod(CodeMirror);
13243 })(function(CodeMirror) {
13244 "use strict";
13245
13246 var htmlConfig = {
13247 autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true,
13248 'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true,
13249 'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true,
13250 'track': true, 'wbr': true, 'menuitem': true},
13251 implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true,
13252 'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true,
13253 'th': true, 'tr': true},
13254 contextGrabbers: {
13255 'dd': {'dd': true, 'dt': true},
13256 'dt': {'dd': true, 'dt': true},
13257 'li': {'li': true},
13258 'option': {'option': true, 'optgroup': true},
13259 'optgroup': {'optgroup': true},
13260 'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true,
13261 'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true,
13262 'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true,
13263 'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true,
13264 'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true},
13265 'rp': {'rp': true, 'rt': true},
13266 'rt': {'rp': true, 'rt': true},
13267 'tbody': {'tbody': true, 'tfoot': true},
13268 'td': {'td': true, 'th': true},
13269 'tfoot': {'tbody': true},
13270 'th': {'td': true, 'th': true},
13271 'thead': {'tbody': true, 'tfoot': true},
13272 'tr': {'tr': true}
13273 },
13274 doNotIndent: {"pre": true},
13275 allowUnquoted: true,
13276 allowMissing: true,
13277 caseFold: true
13278 }
13279
13280 var xmlConfig = {
13281 autoSelfClosers: {},
13282 implicitlyClosed: {},
13283 contextGrabbers: {},
13284 doNotIndent: {},
13285 allowUnquoted: false,
13286 allowMissing: false,
13287 allowMissingTagName: false,
13288 caseFold: false
13289 }
13290
13291 CodeMirror.defineMode("xml", function(editorConf, config_) {
13292 var indentUnit = editorConf.indentUnit
13293 var config = {}
13294 var defaults = config_.htmlMode ? htmlConfig : xmlConfig
13295 for (var prop in defaults) config[prop] = defaults[prop]
13296 for (var prop in config_) config[prop] = config_[prop]
13297
13298 // Return variables for tokenizers
13299 var type, setStyle;
13300
13301 function inText(stream, state) {
13302 function chain(parser) {
13303 state.tokenize = parser;
13304 return parser(stream, state);
13305 }
13306
13307 var ch = stream.next();
13308 if (ch == "<") {
13309 if (stream.eat("!")) {
13310 if (stream.eat("[")) {
13311 if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));
13312 else return null;
13313 } else if (stream.match("--")) {
13314 return chain(inBlock("comment", "-->"));
13315 } else if (stream.match("DOCTYPE", true, true)) {
13316 stream.eatWhile(/[\w\._\-]/);
13317 return chain(doctype(1));
13318 } else {
13319 return null;
13320 }
13321 } else if (stream.eat("?")) {
13322 stream.eatWhile(/[\w\._\-]/);
13323 state.tokenize = inBlock("meta", "?>");
13324 return "meta";
13325 } else {
13326 type = stream.eat("/") ? "closeTag" : "openTag";
13327 state.tokenize = inTag;
13328 return "tag bracket";
13329 }
13330 } else if (ch == "&") {
13331 var ok;
13332 if (stream.eat("#")) {
13333 if (stream.eat("x")) {
13334 ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";");
13335 } else {
13336 ok = stream.eatWhile(/[\d]/) && stream.eat(";");
13337 }
13338 } else {
13339 ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";");
13340 }
13341 return ok ? "atom" : "error";
13342 } else {
13343 stream.eatWhile(/[^&<]/);
13344 return null;
13345 }
13346 }
13347 inText.isInText = true;
13348
13349 function inTag(stream, state) {
13350 var ch = stream.next();
13351 if (ch == ">" || (ch == "/" && stream.eat(">"))) {
13352 state.tokenize = inText;
13353 type = ch == ">" ? "endTag" : "selfcloseTag";
13354 return "tag bracket";
13355 } else if (ch == "=") {
13356 type = "equals";
13357 return null;
13358 } else if (ch == "<") {
13359 state.tokenize = inText;
13360 state.state = baseState;
13361 state.tagName = state.tagStart = null;
13362 var next = state.tokenize(stream, state);
13363 return next ? next + " tag error" : "tag error";
13364 } else if (/[\'\"]/.test(ch)) {
13365 state.tokenize = inAttribute(ch);
13366 state.stringStartCol = stream.column();
13367 return state.tokenize(stream, state);
13368 } else {
13369 stream.match(/^[^\s\u00a0=<>\"\']*[^\s\u00a0=<>\"\'\/]/);
13370 return "word";
13371 }
13372 }
13373
13374 function inAttribute(quote) {
13375 var closure = function(stream, state) {
13376 while (!stream.eol()) {
13377 if (stream.next() == quote) {
13378 state.tokenize = inTag;
13379 break;
13380 }
13381 }
13382 return "string";
13383 };
13384 closure.isInAttribute = true;
13385 return closure;
13386 }
13387
13388 function inBlock(style, terminator) {
13389 return function(stream, state) {
13390 while (!stream.eol()) {
13391 if (stream.match(terminator)) {
13392 state.tokenize = inText;
13393 break;
13394 }
13395 stream.next();
13396 }
13397 return style;
13398 }
13399 }
13400
13401 function doctype(depth) {
13402 return function(stream, state) {
13403 var ch;
13404 while ((ch = stream.next()) != null) {
13405 if (ch == "<") {
13406 state.tokenize = doctype(depth + 1);
13407 return state.tokenize(stream, state);
13408 } else if (ch == ">") {
13409 if (depth == 1) {
13410 state.tokenize = inText;
13411 break;
13412 } else {
13413 state.tokenize = doctype(depth - 1);
13414 return state.tokenize(stream, state);
13415 }
13416 }
13417 }
13418 return "meta";
13419 };
13420 }
13421
13422 function Context(state, tagName, startOfLine) {
13423 this.prev = state.context;
13424 this.tagName = tagName;
13425 this.indent = state.indented;
13426 this.startOfLine = startOfLine;
13427 if (config.doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.noIndent))
13428 this.noIndent = true;
13429 }
13430 function popContext(state) {
13431 if (state.context) state.context = state.context.prev;
13432 }
13433 function maybePopContext(state, nextTagName) {
13434 var parentTagName;
13435 while (true) {
13436 if (!state.context) {
13437 return;
13438 }
13439 parentTagName = state.context.tagName;
13440 if (!config.contextGrabbers.hasOwnProperty(parentTagName) ||
13441 !config.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
13442 return;
13443 }
13444 popContext(state);
13445 }
13446 }
13447
13448 function baseState(type, stream, state) {
13449 if (type == "openTag") {
13450 state.tagStart = stream.column();
13451 return tagNameState;
13452 } else if (type == "closeTag") {
13453 return closeTagNameState;
13454 } else {
13455 return baseState;
13456 }
13457 }
13458 function tagNameState(type, stream, state) {
13459 if (type == "word") {
13460 state.tagName = stream.current();
13461 setStyle = "tag";
13462 return attrState;
13463 } else if (config.allowMissingTagName && type == "endTag") {
13464 setStyle = "tag bracket";
13465 return attrState(type, stream, state);
13466 } else {
13467 setStyle = "error";
13468 return tagNameState;
13469 }
13470 }
13471 function closeTagNameState(type, stream, state) {
13472 if (type == "word") {
13473 var tagName = stream.current();
13474 if (state.context && state.context.tagName != tagName &&
13475 config.implicitlyClosed.hasOwnProperty(state.context.tagName))
13476 popContext(state);
13477 if ((state.context && state.context.tagName == tagName) || config.matchClosing === false) {
13478 setStyle = "tag";
13479 return closeState;
13480 } else {
13481 setStyle = "tag error";
13482 return closeStateErr;
13483 }
13484 } else if (config.allowMissingTagName && type == "endTag") {
13485 setStyle = "tag bracket";
13486 return closeState(type, stream, state);
13487 } else {
13488 setStyle = "error";
13489 return closeStateErr;
13490 }
13491 }
13492
13493 function closeState(type, _stream, state) {
13494 if (type != "endTag") {
13495 setStyle = "error";
13496 return closeState;
13497 }
13498 popContext(state);
13499 return baseState;
13500 }
13501 function closeStateErr(type, stream, state) {
13502 setStyle = "error";
13503 return closeState(type, stream, state);
13504 }
13505
13506 function attrState(type, _stream, state) {
13507 if (type == "word") {
13508 setStyle = "attribute";
13509 return attrEqState;
13510 } else if (type == "endTag" || type == "selfcloseTag") {
13511 var tagName = state.tagName, tagStart = state.tagStart;
13512 state.tagName = state.tagStart = null;
13513 if (type == "selfcloseTag" ||
13514 config.autoSelfClosers.hasOwnProperty(tagName)) {
13515 maybePopContext(state, tagName);
13516 } else {
13517 maybePopContext(state, tagName);
13518 state.context = new Context(state, tagName, tagStart == state.indented);
13519 }
13520 return baseState;
13521 }
13522 setStyle = "error";
13523 return attrState;
13524 }
13525 function attrEqState(type, stream, state) {
13526 if (type == "equals") return attrValueState;
13527 if (!config.allowMissing) setStyle = "error";
13528 return attrState(type, stream, state);
13529 }
13530 function attrValueState(type, stream, state) {
13531 if (type == "string") return attrContinuedState;
13532 if (type == "word" && config.allowUnquoted) {setStyle = "string"; return attrState;}
13533 setStyle = "error";
13534 return attrState(type, stream, state);
13535 }
13536 function attrContinuedState(type, stream, state) {
13537 if (type == "string") return attrContinuedState;
13538 return attrState(type, stream, state);
13539 }
13540
13541 return {
13542 startState: function(baseIndent) {
13543 var state = {tokenize: inText,
13544 state: baseState,
13545 indented: baseIndent || 0,
13546 tagName: null, tagStart: null,
13547 context: null}
13548 if (baseIndent != null) state.baseIndent = baseIndent
13549 return state
13550 },
13551
13552 token: function(stream, state) {
13553 if (!state.tagName && stream.sol())
13554 state.indented = stream.indentation();
13555
13556 if (stream.eatSpace()) return null;
13557 type = null;
13558 var style = state.tokenize(stream, state);
13559 if ((style || type) && style != "comment") {
13560 setStyle = null;
13561 state.state = state.state(type || style, stream, state);
13562 if (setStyle)
13563 style = setStyle == "error" ? style + " error" : setStyle;
13564 }
13565 return style;
13566 },
13567
13568 indent: function(state, textAfter, fullLine) {
13569 var context = state.context;
13570 // Indent multi-line strings (e.g. css).
13571 if (state.tokenize.isInAttribute) {
13572 if (state.tagStart == state.indented)
13573 return state.stringStartCol + 1;
13574 else
13575 return state.indented + indentUnit;
13576 }
13577 if (context && context.noIndent) return CodeMirror.Pass;
13578 if (state.tokenize != inTag && state.tokenize != inText)
13579 return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
13580 // Indent the starts of attribute names.
13581 if (state.tagName) {
13582 if (config.multilineTagIndentPastTag !== false)
13583 return state.tagStart + state.tagName.length + 2;
13584 else
13585 return state.tagStart + indentUnit * (config.multilineTagIndentFactor || 1);
13586 }
13587 if (config.alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
13588 var tagAfter = textAfter && /^<(\/)?([\w_:\.-]*)/.exec(textAfter);
13589 if (tagAfter && tagAfter[1]) { // Closing tag spotted
13590 while (context) {
13591 if (context.tagName == tagAfter[2]) {
13592 context = context.prev;
13593 break;
13594 } else if (config.implicitlyClosed.hasOwnProperty(context.tagName)) {
13595 context = context.prev;
13596 } else {
13597 break;
13598 }
13599 }
13600 } else if (tagAfter) { // Opening tag spotted
13601 while (context) {
13602 var grabbers = config.contextGrabbers[context.tagName];
13603 if (grabbers && grabbers.hasOwnProperty(tagAfter[2]))
13604 context = context.prev;
13605 else
13606 break;
13607 }
13608 }
13609 while (context && context.prev && !context.startOfLine)
13610 context = context.prev;
13611 if (context) return context.indent + indentUnit;
13612 else return state.baseIndent || 0;
13613 },
13614
13615 electricInput: /<\/[\s\w:]+>$/,
13616 blockCommentStart: "<!--",
13617 blockCommentEnd: "-->",
13618
13619 configuration: config.htmlMode ? "html" : "xml",
13620 helperType: config.htmlMode ? "html" : "xml",
13621
13622 skipAttribute: function(state) {
13623 if (state.state == attrValueState)
13624 state.state = attrState
13625 }
13626 };
13627 });
13628
13629 CodeMirror.defineMIME("text/xml", "xml");
13630 CodeMirror.defineMIME("application/xml", "xml");
13631 if (!CodeMirror.mimeModes.hasOwnProperty("text/html"))
13632 CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
13633
13634 });
13635
13636 },{"../../lib/codemirror":14}],19:[function(require,module,exports){
13637 (function(global,factory){if(typeof define === 'function' && define.amd){define(['exports'],factory);}else if(typeof exports !== 'undefined'){factory(exports);}else {var mod={exports:{}};factory(mod.exports);global.decko = mod.exports;}})(this,function(exports){'use strict';exports.__esModule = true;var EMPTY={};var HOP=Object.prototype.hasOwnProperty;var fns={memoize:function memoize(fn){var opt=arguments.length <= 1 || arguments[1] === undefined?EMPTY:arguments[1];var cache=opt.cache || {};return function(){for(var _len=arguments.length,a=Array(_len),_key=0;_key < _len;_key++) {a[_key] = arguments[_key];}var k=String(a[0]);if(opt.caseSensitive === false)k = k.toLowerCase();return HOP.call(cache,k)?cache[k]:cache[k] = fn.apply(this,a);};},debounce:function debounce(fn,opts){if(typeof opts === 'function'){var p=fn;fn = opts;opts = p;}var delay=opts && opts.delay || opts || 0,args=undefined,context=undefined,timer=undefined;return function(){for(var _len2=arguments.length,a=Array(_len2),_key2=0;_key2 < _len2;_key2++) {a[_key2] = arguments[_key2];}args = a;context = this;if(!timer)timer = setTimeout(function(){fn.apply(context,args);args = context = timer = null;},delay);};},bind:function bind(target,key,_ref){var fn=_ref.value;return {configurable:true,get:function get(){var value=fn.bind(this);Object.defineProperty(this,key,{value:value,configurable:true,writable:true});return value;}};}};var memoize=multiMethod(fns.memoize),debounce=multiMethod(fns.debounce),bind=multiMethod(function(f,c){return f.bind(c);},function(){return fns.bind;});exports.memoize = memoize;exports.debounce = debounce;exports.bind = bind;exports['default'] = {memoize:memoize,debounce:debounce,bind:bind};function multiMethod(inner,deco){deco = deco || inner.decorate || decorator(inner);var d=deco();return function(){for(var _len3=arguments.length,args=Array(_len3),_key3=0;_key3 < _len3;_key3++) {args[_key3] = arguments[_key3];}var l=args.length;return (l < 2?deco:l > 2?d:inner).apply(undefined,args);};}function decorator(fn){return function(opt){return typeof opt === 'function'?fn(opt):function(target,key,desc){desc.value = fn(desc.value,opt,target,key,desc);};};}});
13638
13639
13640 },{}],20:[function(require,module,exports){
13641 function dlv(t,e,n,l){for(l=0,e=e.split?e.split("."):e;t&&l<e.length;)t=t[e[l++]];return void 0===t?n:t}function linkState(t,e,n){var l=e.split("."),r=t.__lsc||(t.__lsc={});return r[e+n]||(r[e+n]=function(e){for(var r=e&&e.target||this,a={},i=a,o="string"==typeof n?dlv(e,n):r.nodeName?r.type.match(/^che|rad/)?r.checked:r.value:e,s=0;s<l.length-1;s++)i=i[l[s]]||(i[l[s]]=!s&&t.state[l[s]]||{});i[l[s]]=o,t.setState(a)})}module.exports=linkState;
13642
13643
13644 },{}],21:[function(require,module,exports){
13645 !function() {
13646 'use strict';
13647 function h(nodeName, attributes) {
13648 var lastSimple, child, simple, i, children = EMPTY_CHILDREN;
13649 for (i = arguments.length; i-- > 2; ) stack.push(arguments[i]);
13650 if (attributes && null != attributes.children) {
13651 if (!stack.length) stack.push(attributes.children);
13652 delete attributes.children;
13653 }
13654 while (stack.length) if ((child = stack.pop()) && void 0 !== child.pop) for (i = child.length; i--; ) stack.push(child[i]); else {
13655 if ('boolean' == typeof child) child = null;
13656 if (simple = 'function' != typeof nodeName) if (null == child) child = ''; else if ('number' == typeof child) child = String(child); else if ('string' != typeof child) simple = !1;
13657 if (simple && lastSimple) children[children.length - 1] += child; else if (children === EMPTY_CHILDREN) children = [ child ]; else children.push(child);
13658 lastSimple = simple;
13659 }
13660 var p = new VNode();
13661 p.nodeName = nodeName;
13662 p.children = children;
13663 p.attributes = null == attributes ? void 0 : attributes;
13664 p.key = null == attributes ? void 0 : attributes.key;
13665 if (void 0 !== options.vnode) options.vnode(p);
13666 return p;
13667 }
13668 function extend(obj, props) {
13669 for (var i in props) obj[i] = props[i];
13670 return obj;
13671 }
13672 function applyRef(ref, value) {
13673 if (null != ref) if ('function' == typeof ref) ref(value); else ref.current = value;
13674 }
13675 function cloneElement(vnode, props) {
13676 return h(vnode.nodeName, extend(extend({}, vnode.attributes), props), arguments.length > 2 ? [].slice.call(arguments, 2) : vnode.children);
13677 }
13678 function enqueueRender(component) {
13679 if (!component.__d && (component.__d = !0) && 1 == items.push(component)) (options.debounceRendering || defer)(rerender);
13680 }
13681 function rerender() {
13682 var p;
13683 while (p = items.pop()) if (p.__d) renderComponent(p);
13684 }
13685 function isSameNodeType(node, vnode, hydrating) {
13686 if ('string' == typeof vnode || 'number' == typeof vnode) return void 0 !== node.splitText;
13687 if ('string' == typeof vnode.nodeName) return !node._componentConstructor && isNamedNode(node, vnode.nodeName); else return hydrating || node._componentConstructor === vnode.nodeName;
13688 }
13689 function isNamedNode(node, nodeName) {
13690 return node.__n === nodeName || node.nodeName.toLowerCase() === nodeName.toLowerCase();
13691 }
13692 function getNodeProps(vnode) {
13693 var props = extend({}, vnode.attributes);
13694 props.children = vnode.children;
13695 var defaultProps = vnode.nodeName.defaultProps;
13696 if (void 0 !== defaultProps) for (var i in defaultProps) if (void 0 === props[i]) props[i] = defaultProps[i];
13697 return props;
13698 }
13699 function createNode(nodeName, isSvg) {
13700 var node = isSvg ? document.createElementNS('http://www.w3.org/2000/svg', nodeName) : document.createElement(nodeName);
13701 node.__n = nodeName;
13702 return node;
13703 }
13704 function removeNode(node) {
13705 var parentNode = node.parentNode;
13706 if (parentNode) parentNode.removeChild(node);
13707 }
13708 function setAccessor(node, name, old, value, isSvg) {
13709 if ('className' === name) name = 'class';
13710 if ('key' === name) ; else if ('ref' === name) {
13711 applyRef(old, null);
13712 applyRef(value, node);
13713 } else if ('class' === name && !isSvg) node.className = value || ''; else if ('style' === name) {
13714 if (!value || 'string' == typeof value || 'string' == typeof old) node.style.cssText = value || '';
13715 if (value && 'object' == typeof value) {
13716 if ('string' != typeof old) for (var i in old) if (!(i in value)) node.style[i] = '';
13717 for (var i in value) node.style[i] = 'number' == typeof value[i] && !1 === IS_NON_DIMENSIONAL.test(i) ? value[i] + 'px' : value[i];
13718 }
13719 } else if ('dangerouslySetInnerHTML' === name) {
13720 if (value) node.innerHTML = value.__html || '';
13721 } else if ('o' == name[0] && 'n' == name[1]) {
13722 var useCapture = name !== (name = name.replace(/Capture$/, ''));
13723 name = name.toLowerCase().substring(2);
13724 if (value) {
13725 if (!old) node.addEventListener(name, eventProxy, useCapture);
13726 } else node.removeEventListener(name, eventProxy, useCapture);
13727 (node.__l || (node.__l = {}))[name] = value;
13728 } else if ('list' !== name && 'type' !== name && !isSvg && name in node) {
13729 try {
13730 node[name] = null == value ? '' : value;
13731 } catch (e) {}
13732 if ((null == value || !1 === value) && 'spellcheck' != name) node.removeAttribute(name);
13733 } else {
13734 var ns = isSvg && name !== (name = name.replace(/^xlink:?/, ''));
13735 if (null == value || !1 === value) if (ns) node.removeAttributeNS('http://www.w3.org/1999/xlink', name.toLowerCase()); else node.removeAttribute(name); else if ('function' != typeof value) if (ns) node.setAttributeNS('http://www.w3.org/1999/xlink', name.toLowerCase(), value); else node.setAttribute(name, value);
13736 }
13737 }
13738 function eventProxy(e) {
13739 return this.__l[e.type](options.event && options.event(e) || e);
13740 }
13741 function flushMounts() {
13742 var c;
13743 while (c = mounts.shift()) {
13744 if (options.afterMount) options.afterMount(c);
13745 if (c.componentDidMount) c.componentDidMount();
13746 }
13747 }
13748 function diff(dom, vnode, context, mountAll, parent, componentRoot) {
13749 if (!diffLevel++) {
13750 isSvgMode = null != parent && void 0 !== parent.ownerSVGElement;
13751 hydrating = null != dom && !('__preactattr_' in dom);
13752 }
13753 var ret = idiff(dom, vnode, context, mountAll, componentRoot);
13754 if (parent && ret.parentNode !== parent) parent.appendChild(ret);
13755 if (!--diffLevel) {
13756 hydrating = !1;
13757 if (!componentRoot) flushMounts();
13758 }
13759 return ret;
13760 }
13761 function idiff(dom, vnode, context, mountAll, componentRoot) {
13762 var out = dom, prevSvgMode = isSvgMode;
13763 if (null == vnode || 'boolean' == typeof vnode) vnode = '';
13764 if ('string' == typeof vnode || 'number' == typeof vnode) {
13765 if (dom && void 0 !== dom.splitText && dom.parentNode && (!dom._component || componentRoot)) {
13766 if (dom.nodeValue != vnode) dom.nodeValue = vnode;
13767 } else {
13768 out = document.createTextNode(vnode);
13769 if (dom) {
13770 if (dom.parentNode) dom.parentNode.replaceChild(out, dom);
13771 recollectNodeTree(dom, !0);
13772 }
13773 }
13774 out.__preactattr_ = !0;
13775 return out;
13776 }
13777 var vnodeName = vnode.nodeName;
13778 if ('function' == typeof vnodeName) return buildComponentFromVNode(dom, vnode, context, mountAll);
13779 isSvgMode = 'svg' === vnodeName ? !0 : 'foreignObject' === vnodeName ? !1 : isSvgMode;
13780 vnodeName = String(vnodeName);
13781 if (!dom || !isNamedNode(dom, vnodeName)) {
13782 out = createNode(vnodeName, isSvgMode);
13783 if (dom) {
13784 while (dom.firstChild) out.appendChild(dom.firstChild);
13785 if (dom.parentNode) dom.parentNode.replaceChild(out, dom);
13786 recollectNodeTree(dom, !0);
13787 }
13788 }
13789 var fc = out.firstChild, props = out.__preactattr_, vchildren = vnode.children;
13790 if (null == props) {
13791 props = out.__preactattr_ = {};
13792 for (var a = out.attributes, i = a.length; i--; ) props[a[i].name] = a[i].value;
13793 }
13794 if (!hydrating && vchildren && 1 === vchildren.length && 'string' == typeof vchildren[0] && null != fc && void 0 !== fc.splitText && null == fc.nextSibling) {
13795 if (fc.nodeValue != vchildren[0]) fc.nodeValue = vchildren[0];
13796 } else if (vchildren && vchildren.length || null != fc) innerDiffNode(out, vchildren, context, mountAll, hydrating || null != props.dangerouslySetInnerHTML);
13797 diffAttributes(out, vnode.attributes, props);
13798 isSvgMode = prevSvgMode;
13799 return out;
13800 }
13801 function innerDiffNode(dom, vchildren, context, mountAll, isHydrating) {
13802 var j, c, f, vchild, child, originalChildren = dom.childNodes, children = [], keyed = {}, keyedLen = 0, min = 0, len = originalChildren.length, childrenLen = 0, vlen = vchildren ? vchildren.length : 0;
13803 if (0 !== len) for (var i = 0; i < len; i++) {
13804 var _child = originalChildren[i], props = _child.__preactattr_, key = vlen && props ? _child._component ? _child._component.__k : props.key : null;
13805 if (null != key) {
13806 keyedLen++;
13807 keyed[key] = _child;
13808 } else if (props || (void 0 !== _child.splitText ? isHydrating ? _child.nodeValue.trim() : !0 : isHydrating)) children[childrenLen++] = _child;
13809 }
13810 if (0 !== vlen) for (var i = 0; i < vlen; i++) {
13811 vchild = vchildren[i];
13812 child = null;
13813 var key = vchild.key;
13814 if (null != key) {
13815 if (keyedLen && void 0 !== keyed[key]) {
13816 child = keyed[key];
13817 keyed[key] = void 0;
13818 keyedLen--;
13819 }
13820 } else if (min < childrenLen) for (j = min; j < childrenLen; j++) if (void 0 !== children[j] && isSameNodeType(c = children[j], vchild, isHydrating)) {
13821 child = c;
13822 children[j] = void 0;
13823 if (j === childrenLen - 1) childrenLen--;
13824 if (j === min) min++;
13825 break;
13826 }
13827 child = idiff(child, vchild, context, mountAll);
13828 f = originalChildren[i];
13829 if (child && child !== dom && child !== f) if (null == f) dom.appendChild(child); else if (child === f.nextSibling) removeNode(f); else dom.insertBefore(child, f);
13830 }
13831 if (keyedLen) for (var i in keyed) if (void 0 !== keyed[i]) recollectNodeTree(keyed[i], !1);
13832 while (min <= childrenLen) if (void 0 !== (child = children[childrenLen--])) recollectNodeTree(child, !1);
13833 }
13834 function recollectNodeTree(node, unmountOnly) {
13835 var component = node._component;
13836 if (component) unmountComponent(component); else {
13837 if (null != node.__preactattr_) applyRef(node.__preactattr_.ref, null);
13838 if (!1 === unmountOnly || null == node.__preactattr_) removeNode(node);
13839 removeChildren(node);
13840 }
13841 }
13842 function removeChildren(node) {
13843 node = node.lastChild;
13844 while (node) {
13845 var next = node.previousSibling;
13846 recollectNodeTree(node, !0);
13847 node = next;
13848 }
13849 }
13850 function diffAttributes(dom, attrs, old) {
13851 var name;
13852 for (name in old) if ((!attrs || null == attrs[name]) && null != old[name]) setAccessor(dom, name, old[name], old[name] = void 0, isSvgMode);
13853 for (name in attrs) if (!('children' === name || 'innerHTML' === name || name in old && attrs[name] === ('value' === name || 'checked' === name ? dom[name] : old[name]))) setAccessor(dom, name, old[name], old[name] = attrs[name], isSvgMode);
13854 }
13855 function createComponent(Ctor, props, context) {
13856 var inst, i = recyclerComponents.length;
13857 if (Ctor.prototype && Ctor.prototype.render) {
13858 inst = new Ctor(props, context);
13859 Component.call(inst, props, context);
13860 } else {
13861 inst = new Component(props, context);
13862 inst.constructor = Ctor;
13863 inst.render = doRender;
13864 }
13865 while (i--) if (recyclerComponents[i].constructor === Ctor) {
13866 inst.__b = recyclerComponents[i].__b;
13867 recyclerComponents.splice(i, 1);
13868 return inst;
13869 }
13870 return inst;
13871 }
13872 function doRender(props, state, context) {
13873 return this.constructor(props, context);
13874 }
13875 function setComponentProps(component, props, renderMode, context, mountAll) {
13876 if (!component.__x) {
13877 component.__x = !0;
13878 component.__r = props.ref;
13879 component.__k = props.key;
13880 delete props.ref;
13881 delete props.key;
13882 if (void 0 === component.constructor.getDerivedStateFromProps) if (!component.base || mountAll) {
13883 if (component.componentWillMount) component.componentWillMount();
13884 } else if (component.componentWillReceiveProps) component.componentWillReceiveProps(props, context);
13885 if (context && context !== component.context) {
13886 if (!component.__c) component.__c = component.context;
13887 component.context = context;
13888 }
13889 if (!component.__p) component.__p = component.props;
13890 component.props = props;
13891 component.__x = !1;
13892 if (0 !== renderMode) if (1 === renderMode || !1 !== options.syncComponentUpdates || !component.base) renderComponent(component, 1, mountAll); else enqueueRender(component);
13893 applyRef(component.__r, component);
13894 }
13895 }
13896 function renderComponent(component, renderMode, mountAll, isChild) {
13897 if (!component.__x) {
13898 var rendered, inst, cbase, props = component.props, state = component.state, context = component.context, previousProps = component.__p || props, previousState = component.__s || state, previousContext = component.__c || context, isUpdate = component.base, nextBase = component.__b, initialBase = isUpdate || nextBase, initialChildComponent = component._component, skip = !1, snapshot = previousContext;
13899 if (component.constructor.getDerivedStateFromProps) {
13900 state = extend(extend({}, state), component.constructor.getDerivedStateFromProps(props, state));
13901 component.state = state;
13902 }
13903 if (isUpdate) {
13904 component.props = previousProps;
13905 component.state = previousState;
13906 component.context = previousContext;
13907 if (2 !== renderMode && component.shouldComponentUpdate && !1 === component.shouldComponentUpdate(props, state, context)) skip = !0; else if (component.componentWillUpdate) component.componentWillUpdate(props, state, context);
13908 component.props = props;
13909 component.state = state;
13910 component.context = context;
13911 }
13912 component.__p = component.__s = component.__c = component.__b = null;
13913 component.__d = !1;
13914 if (!skip) {
13915 rendered = component.render(props, state, context);
13916 if (component.getChildContext) context = extend(extend({}, context), component.getChildContext());
13917 if (isUpdate && component.getSnapshotBeforeUpdate) snapshot = component.getSnapshotBeforeUpdate(previousProps, previousState);
13918 var toUnmount, base, childComponent = rendered && rendered.nodeName;
13919 if ('function' == typeof childComponent) {
13920 var childProps = getNodeProps(rendered);
13921 inst = initialChildComponent;
13922 if (inst && inst.constructor === childComponent && childProps.key == inst.__k) setComponentProps(inst, childProps, 1, context, !1); else {
13923 toUnmount = inst;
13924 component._component = inst = createComponent(childComponent, childProps, context);
13925 inst.__b = inst.__b || nextBase;
13926 inst.__u = component;
13927 setComponentProps(inst, childProps, 0, context, !1);
13928 renderComponent(inst, 1, mountAll, !0);
13929 }
13930 base = inst.base;
13931 } else {
13932 cbase = initialBase;
13933 toUnmount = initialChildComponent;
13934 if (toUnmount) cbase = component._component = null;
13935 if (initialBase || 1 === renderMode) {
13936 if (cbase) cbase._component = null;
13937 base = diff(cbase, rendered, context, mountAll || !isUpdate, initialBase && initialBase.parentNode, !0);
13938 }
13939 }
13940 if (initialBase && base !== initialBase && inst !== initialChildComponent) {
13941 var baseParent = initialBase.parentNode;
13942 if (baseParent && base !== baseParent) {
13943 baseParent.replaceChild(base, initialBase);
13944 if (!toUnmount) {
13945 initialBase._component = null;
13946 recollectNodeTree(initialBase, !1);
13947 }
13948 }
13949 }
13950 if (toUnmount) unmountComponent(toUnmount);
13951 component.base = base;
13952 if (base && !isChild) {
13953 var componentRef = component, t = component;
13954 while (t = t.__u) (componentRef = t).base = base;
13955 base._component = componentRef;
13956 base._componentConstructor = componentRef.constructor;
13957 }
13958 }
13959 if (!isUpdate || mountAll) mounts.push(component); else if (!skip) {
13960 if (component.componentDidUpdate) component.componentDidUpdate(previousProps, previousState, snapshot);
13961 if (options.afterUpdate) options.afterUpdate(component);
13962 }
13963 while (component.__h.length) component.__h.pop().call(component);
13964 if (!diffLevel && !isChild) flushMounts();
13965 }
13966 }
13967 function buildComponentFromVNode(dom, vnode, context, mountAll) {
13968 var c = dom && dom._component, originalComponent = c, oldDom = dom, isDirectOwner = c && dom._componentConstructor === vnode.nodeName, isOwner = isDirectOwner, props = getNodeProps(vnode);
13969 while (c && !isOwner && (c = c.__u)) isOwner = c.constructor === vnode.nodeName;
13970 if (c && isOwner && (!mountAll || c._component)) {
13971 setComponentProps(c, props, 3, context, mountAll);
13972 dom = c.base;
13973 } else {
13974 if (originalComponent && !isDirectOwner) {
13975 unmountComponent(originalComponent);
13976 dom = oldDom = null;
13977 }
13978 c = createComponent(vnode.nodeName, props, context);
13979 if (dom && !c.__b) {
13980 c.__b = dom;
13981 oldDom = null;
13982 }
13983 setComponentProps(c, props, 1, context, mountAll);
13984 dom = c.base;
13985 if (oldDom && dom !== oldDom) {
13986 oldDom._component = null;
13987 recollectNodeTree(oldDom, !1);
13988 }
13989 }
13990 return dom;
13991 }
13992 function unmountComponent(component) {
13993 if (options.beforeUnmount) options.beforeUnmount(component);
13994 var base = component.base;
13995 component.__x = !0;
13996 if (component.componentWillUnmount) component.componentWillUnmount();
13997 component.base = null;
13998 var inner = component._component;
13999 if (inner) unmountComponent(inner); else if (base) {
14000 if (null != base.__preactattr_) applyRef(base.__preactattr_.ref, null);
14001 component.__b = base;
14002 removeNode(base);
14003 recyclerComponents.push(component);
14004 removeChildren(base);
14005 }
14006 applyRef(component.__r, null);
14007 }
14008 function Component(props, context) {
14009 this.__d = !0;
14010 this.context = context;
14011 this.props = props;
14012 this.state = this.state || {};
14013 this.__h = [];
14014 }
14015 function render(vnode, parent, merge) {
14016 return diff(merge, vnode, {}, !1, parent, !1);
14017 }
14018 function createRef() {
14019 return {};
14020 }
14021 var VNode = function() {};
14022 var options = {};
14023 var stack = [];
14024 var EMPTY_CHILDREN = [];
14025 var defer = 'function' == typeof Promise ? Promise.resolve().then.bind(Promise.resolve()) : setTimeout;
14026 var IS_NON_DIMENSIONAL = /acit|ex(?:s|g|n|p|$)|rph|ows|mnc|ntw|ine[ch]|zoo|^ord/i;
14027 var items = [];
14028 var mounts = [];
14029 var diffLevel = 0;
14030 var isSvgMode = !1;
14031 var hydrating = !1;
14032 var recyclerComponents = [];
14033 extend(Component.prototype, {
14034 setState: function(state, callback) {
14035 if (!this.__s) this.__s = this.state;
14036 this.state = extend(extend({}, this.state), 'function' == typeof state ? state(this.state, this.props) : state);
14037 if (callback) this.__h.push(callback);
14038 enqueueRender(this);
14039 },
14040 forceUpdate: function(callback) {
14041 if (callback) this.__h.push(callback);
14042 renderComponent(this, 2);
14043 },
14044 render: function() {}
14045 });
14046 var preact = {
14047 h: h,
14048 createElement: h,
14049 cloneElement: cloneElement,
14050 createRef: createRef,
14051 Component: Component,
14052 render: render,
14053 rerender: rerender,
14054 options: options
14055 };
14056 if ('undefined' != typeof module) module.exports = preact; else self.preact = preact;
14057 }();
14058
14059 },{}],22:[function(require,module,exports){
14060 function tlite(getTooltipOpts) {
14061 document.addEventListener('mouseover', function (e) {
14062 var el = e.target;
14063 var opts = getTooltipOpts(el);
14064
14065 if (!opts) {
14066 el = el.parentElement;
14067 opts = el && getTooltipOpts(el);
14068 }
14069
14070 opts && tlite.show(el, opts, true);
14071 });
14072 }
14073
14074 tlite.show = function (el, opts, isAuto) {
14075 var fallbackAttrib = 'data-tlite';
14076 opts = opts || {};
14077
14078 (el.tooltip || Tooltip(el, opts)).show();
14079
14080 function Tooltip(el, opts) {
14081 var tooltipEl;
14082 var showTimer;
14083 var text;
14084
14085 el.addEventListener('mousedown', autoHide);
14086 el.addEventListener('mouseleave', autoHide);
14087
14088 function show() {
14089 text = el.title || el.getAttribute(fallbackAttrib) || text;
14090 el.title = '';
14091 el.setAttribute(fallbackAttrib, '');
14092 text && !showTimer && (showTimer = setTimeout(fadeIn, isAuto ? 150 : 1))
14093 }
14094
14095 function autoHide() {
14096 tlite.hide(el, true);
14097 }
14098
14099 function hide(isAutoHiding) {
14100 if (isAuto === isAutoHiding) {
14101 showTimer = clearTimeout(showTimer);
14102 var parent = tooltipEl && tooltipEl.parentNode;
14103 parent && parent.removeChild(tooltipEl);
14104 tooltipEl = undefined;
14105 }
14106 }
14107
14108 function fadeIn() {
14109 if (!tooltipEl) {
14110 tooltipEl = createTooltip(el, text, opts);
14111 }
14112 }
14113
14114 return el.tooltip = {
14115 show: show,
14116 hide: hide
14117 };
14118 }
14119
14120 function createTooltip(el, text, opts) {
14121 var tooltipEl = document.createElement('span');
14122 var grav = opts.grav || el.getAttribute('data-tlite') || 'n';
14123
14124 tooltipEl.innerHTML = text;
14125
14126 el.appendChild(tooltipEl);
14127
14128 var vertGrav = grav[0] || '';
14129 var horzGrav = grav[1] || '';
14130
14131 function positionTooltip() {
14132 tooltipEl.className = 'tlite ' + 'tlite-' + vertGrav + horzGrav;
14133
14134 var arrowSize = 10;
14135 var top = el.offsetTop;
14136 var left = el.offsetLeft;
14137
14138 if (tooltipEl.offsetParent === el) {
14139 top = left = 0;
14140 }
14141
14142 var width = el.offsetWidth;
14143 var height = el.offsetHeight;
14144 var tooltipHeight = tooltipEl.offsetHeight;
14145 var tooltipWidth = tooltipEl.offsetWidth;
14146 var centerEl = left + (width / 2);
14147
14148 tooltipEl.style.top = (
14149 vertGrav === 's' ? (top - tooltipHeight - arrowSize) :
14150 vertGrav === 'n' ? (top + height + arrowSize) :
14151 (top + (height / 2) - (tooltipHeight / 2))
14152 ) + 'px';
14153
14154 tooltipEl.style.left = (
14155 horzGrav === 'w' ? left :
14156 horzGrav === 'e' ? left + width - tooltipWidth :
14157 vertGrav === 'w' ? (left + width + arrowSize) :
14158 vertGrav === 'e' ? (left - tooltipWidth - arrowSize) :
14159 (centerEl - tooltipWidth / 2)
14160 ) + 'px';
14161 }
14162
14163 positionTooltip();
14164
14165 var rect = tooltipEl.getBoundingClientRect();
14166
14167 if (vertGrav === 's' && rect.top < 0) {
14168 vertGrav = 'n';
14169 positionTooltip();
14170 } else if (vertGrav === 'n' && rect.bottom > window.innerHeight) {
14171 vertGrav = 's';
14172 positionTooltip();
14173 } else if (vertGrav === 'e' && rect.left < 0) {
14174 vertGrav = 'w';
14175 positionTooltip();
14176 } else if (vertGrav === 'w' && rect.right > window.innerWidth) {
14177 vertGrav = 'e';
14178 positionTooltip();
14179 }
14180
14181 tooltipEl.className += ' tlite-visible';
14182
14183 return tooltipEl;
14184 }
14185 };
14186
14187 tlite.hide = function (el, isAuto) {
14188 el.tooltip && el.tooltip.hide(isAuto);
14189 };
14190
14191 if (typeof module !== 'undefined' && module.exports) {
14192 module.exports = tlite;
14193 }
14194
14195 },{}]},{},[2]);
14196 ; })();