PluginProbe
HTML Forms – Simple WordPress Forms Plugin / 1.3.7
HTML Forms – Simple WordPress Forms Plugin v1.3.7
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.7, at assets/js/admin.js

14,205 lines 567.5 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":24}],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":23}],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":23}],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":23}],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":23}],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 _jsx = _interopRequireDefault(require("preact-render-to-string/jsx"));
683
684 var _preact = require("preact");
685
686 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
687
688 var renderOpts = {
689 pretty: true,
690 jsx: false,
691 xml: true,
692 attributeHook: null
693 };
694
695 function htmlgenerate(conf) {
696 var fieldName = namify(conf.fieldLabel);
697 var fieldId = conf.formSlug + '-' + fieldName;
698 var label = conf.fieldLabel.length && conf.fieldType !== 'submit' ? (0, _preact.h)("label", {
699 "for": fieldId
700 }, conf.fieldLabel) : "";
701 var fieldAttr, field;
702
703 switch (conf.fieldType) {
704 case "text":
705 default:
706 fieldAttr = {
707 type: conf.fieldType,
708 name: fieldName,
709 value: conf.value,
710 placeholder: conf.placeholder,
711 required: conf.required,
712 id: fieldId
713 };
714 field = html("input", fieldAttr);
715 break;
716
717 case "textarea":
718 fieldAttr = {
719 name: fieldName,
720 placeholder: conf.placeholder,
721 required: conf.required,
722 id: fieldId
723 };
724 field = html("textarea", fieldAttr, conf.value);
725 break;
726
727 case "dropdown":
728 fieldAttr = {
729 name: fieldName + (conf.multiple ? '[]' : ''),
730 required: conf.required,
731 multiple: conf.multiple,
732 id: fieldId
733 };
734 var opts = conf.choices.map(function (choice) {
735 return html("option", {
736 selected: choice.checked
737 }, choice.label);
738 });
739 field = html("select", fieldAttr, opts);
740 break;
741
742 case "radio":
743 field = conf.choices.map(function (choice) {
744 return html("label", {}, [html("input", {
745 type: "radio",
746 name: fieldName,
747 value: choice.label,
748 selected: choice.checked
749 }), " ", html("span", {}, choice.label)]);
750 });
751 break;
752
753 case "checkbox":
754 field = conf.choices.map(function (choice) {
755 return html("label", {}, [html("input", {
756 type: "checkbox",
757 name: fieldName + "[]",
758 value: choice.label,
759 checked: choice.checked
760 }), " ", html("span", {}, choice.label)]);
761 });
762 break;
763
764 case "file":
765 fieldAttr = {
766 type: "file",
767 name: fieldName,
768 required: conf.required,
769 id: fieldId
770 };
771
772 if (conf['accept']) {
773 fieldAttr['accept'] = conf['accept'];
774 }
775
776 field = html("input", fieldAttr);
777 break;
778
779 case "submit":
780 fieldAttr = {
781 type: "submit",
782 value: conf.value
783 };
784 field = html("input", fieldAttr);
785 break;
786 }
787
788 var str = "";
789
790 if (conf.wrap) {
791 var tmpl = (0, _preact.h)("p", {}, [label, field]);
792 str = (0, _jsx["default"])(tmpl, null, renderOpts);
793 } else {
794 str += (0, _jsx["default"])(label, null, renderOpts);
795 str += "\n";
796 str += (0, _jsx["default"])(field, null, renderOpts);
797 }
798
799 return str;
800 }
801
802 function html(tag, attr, children) {
803 attr = filterEmptyObjectValues(attr);
804 return (0, _preact.h)(tag, attr, children);
805 }
806
807 function namify(str) {
808 return str.replace(/ /g, '_').replace(/[^\w\[\]_]*/g, "").toUpperCase();
809 }
810
811 function filterEmptyObjectValues(obj) {
812 var newObj = {};
813
814 for (var propName in obj) {
815 if (obj[propName] !== false && obj[propName] !== "") {
816 newObj[propName] = obj[propName];
817 }
818 }
819
820 return newObj;
821 }
822
823 },{"preact":23,"preact-render-to-string/jsx":22}],8:[function(require,module,exports){
824 'use strict';
825
826 Object.defineProperty(exports, "__esModule", {
827 value: true
828 });
829 exports["default"] = void 0;
830 var availableActions, actionTemplates, actions;
831
832 function init() {
833 actions = document.getElementById('hf-form-actions');
834 availableActions = document.getElementById('hf-available-form-actions');
835 actionTemplates = document.getElementById('hf-form-action-templates'); // turn settings into accordions
836
837 [].forEach.call(actions.querySelectorAll('.hf-action-settings'), function (el) {
838 el.parentNode.removeChild(el);
839 var heading = el.getAttribute('data-title');
840 var summary = el.querySelector('.hf-action-summary');
841
842 if (summary) {
843 heading += ' &mdash; <span class="hf-muted">' + summary.innerHTML + '</span>';
844 }
845
846 var wrap = createAccordion(heading, el.innerHTML);
847 actions.appendChild(wrap);
848 actions.querySelector('#hf-form-actions-empty').style.display = 'none';
849 });
850 availableActions.addEventListener('click', addAction, true);
851 }
852
853 function createAccordion(headingHTML, contentHTML) {
854 var wrap = document.createElement('div');
855 wrap.className = "hf-accordion expanded ";
856 var heading = document.createElement('h4');
857 heading.className = "hf-accordion-heading";
858 heading.innerHTML = headingHTML;
859 wrap.appendChild(heading);
860 var content = document.createElement('div');
861 content.className = "hf-accordion-content";
862 content.innerHTML = contentHTML;
863 wrap.appendChild(content);
864 var deleteWrap = document.createElement('p');
865 deleteWrap.style.textAlign = 'right';
866 var deleteLink = document.createElement('a');
867 deleteLink.href = 'javascript:void(0);';
868 deleteLink.className = "danger";
869 deleteLink.innerText = 'Delete this action';
870 deleteWrap.appendChild(deleteLink);
871 content.appendChild(deleteWrap); // bind handlers
872
873 heading.addEventListener('click', createToggleActionHandler(wrap, content));
874 deleteLink.addEventListener('click', createDeleteActionHandler(wrap));
875 return wrap;
876 }
877
878 function addAction(e) {
879 var el = e.target || e.srcElement;
880
881 if (el.tagName !== 'INPUT') {
882 return;
883 }
884
885 var actionType = el.getAttribute('data-action-type');
886 var actionTemplate = actionTemplates.querySelector("#hf-action-type-".concat(actionType, "-template")); // append HTML to actions wrapper
887
888 var index = actions.querySelectorAll('div').length - 1;
889 var wrap = createAccordion(el.value, actionTemplate.innerHTML.replace(/\$index/g, index));
890 actions.appendChild(wrap); // hide "no form actions" message
891
892 actions.querySelector('#hf-form-actions-empty').style.display = 'none';
893 window.html_forms.Editor.updateFieldVariables();
894 }
895
896 function createDeleteActionHandler(wrap) {
897 return function () {
898 actions.removeChild(wrap);
899
900 if (actions.childElementCount === 1) {
901 actions.querySelector('#hf-form-actions-empty').style.display = '';
902 }
903 };
904 }
905
906 function createToggleActionHandler(wrap, content) {
907 return function () {
908 var show = content.offsetParent === null;
909 wrap.className = wrap.className.replace('expanded', '') + (show ? ' expanded' : '');
910 content.style.display = show ? 'block' : 'none';
911 };
912 }
913
914 var _default = {
915 init: init
916 };
917 exports["default"] = _default;
918
919 },{}],9:[function(require,module,exports){
920 'use strict'; // load CodeMirror & plugins
921
922 Object.defineProperty(exports, "__esModule", {
923 value: true
924 });
925 exports["default"] = void 0;
926
927 var CodeMirror = require('codemirror');
928
929 require('codemirror/mode/xml/xml');
930
931 require('codemirror/mode/javascript/javascript');
932
933 require('codemirror/mode/css/css');
934
935 require('codemirror/mode/htmlmixed/htmlmixed');
936
937 require('codemirror/addon/fold/xml-fold');
938
939 require('codemirror/addon/edit/matchtags');
940
941 require('codemirror/addon/edit/closetag.js');
942
943 var editor, element, dom, requiredFieldsInput, emailFieldsInput, previewFrame, previewDom;
944 var templateRegex = /\{\{ *(\w+)(?:\.([\w\.]+))? *(?:\|\| *(\w+))? *\}\}/g;
945
946 function init() {
947 previewFrame = document.getElementById('hf-form-preview');
948 element = document.getElementById('hf-form-editor');
949 dom = document.createElement('form');
950 requiredFieldsInput = document.getElementById('hf-required-fields');
951 emailFieldsInput = document.getElementById('hf-email-fields');
952 dom.innerHTML = element.value;
953 editor = CodeMirror.fromTextArea(element, {
954 selectionPointer: true,
955 matchTags: {
956 bothTags: true
957 },
958 mode: "htmlmixed",
959 htmlMode: true,
960 autoCloseTags: true,
961 autoRefresh: true,
962 styleActiveLine: true,
963 matchBrackets: true
964 });
965 editor.on('changes', debounce(updatePreview, 500));
966 editor.on('changes', debounce(updateShadowDOM, 100));
967 editor.on('changes', debounce(updateFieldVariables, 500));
968 editor.on('blur', updatePreview);
969 editor.on('blur', updateShadowDOM);
970 editor.on('blur', updateFieldVariables);
971 editor.on('blur', updateRequiredFields);
972 editor.on('blur', updateEmailFields);
973 previewFrame.addEventListener('load', setPreviewDom);
974 setPreviewDom();
975 updateFieldVariables();
976 }
977
978 function setPreviewDom() {
979 var frameContent = previewFrame.contentDocument || previewFrame.contentWindow.document;
980 previewDom = frameContent.querySelector('.hf-fields-wrap');
981
982 if (previewDom) {
983 updatePreview();
984 }
985 }
986
987 function getFieldVariableName(f) {
988 return f.name.replace('[]', '').replace(/\[(\w+)\]/g, '.$1');
989 }
990
991 function updateFieldVariables() {
992 var fields = dom.querySelectorAll('input[name], select[name], textarea[name], button[name]');
993 var fieldVariables = uniq([].map.call(fields, function (f) {
994 return '[' + getFieldVariableName(f) + ']';
995 }));
996 var wpbody = document.getElementById('wpbody-content');
997 [].forEach.call(document.querySelectorAll('.hf-field-names'), function (el) {
998 // remove existing variables
999 while (el.firstChild) {
1000 el.removeChild(el.firstChild);
1001 }
1002
1003 var variableElements = fieldVariables.map(function (n) {
1004 // measure width of actual font size for prettiness
1005 var sizeEl = document.createElement('span');
1006 sizeEl.style.visibility = 'hidden';
1007 sizeEl.innerText = n;
1008 wpbody.appendChild(sizeEl);
1009 var width = sizeEl.offsetWidth;
1010 wpbody.removeChild(sizeEl); // add input el
1011
1012 var el = document.createElement('input');
1013 el.setAttribute('type', 'text');
1014 el.style.maxWidth = width * 1.1 + 14 + 'px';
1015 el.setAttribute('value', n);
1016 el.setAttribute('readonly', true);
1017 el.setAttribute('onfocus', 'this.select()');
1018 return el;
1019 });
1020 variableElements.forEach(function (vel, i, arr) {
1021 el.appendChild(vel);
1022 });
1023 });
1024 }
1025
1026 function updatePreview() {
1027 var markup = editor.getValue(); // replace template tags
1028
1029 markup = markup.replace(templateRegex, function (s, m) {
1030 // if a default value was provided, use that
1031 if (arguments[3]) {
1032 return arguments[3];
1033 }
1034
1035 return '';
1036 }); // update dom
1037
1038 previewDom.innerHTML = markup;
1039 previewDom.dispatchEvent(new Event('hf-refresh'));
1040 }
1041
1042 function updateShadowDOM() {
1043 dom.innerHTML = editor.getValue();
1044 }
1045
1046 function updateRequiredFields() {
1047 var fields = dom.querySelectorAll('[required]');
1048 var fieldNames = [].map.call(fields, getFieldVariableName);
1049 requiredFieldsInput.value = fieldNames.join(',');
1050 }
1051
1052 function updateEmailFields() {
1053 var fields = dom.querySelectorAll('input[type="email"]');
1054 var fieldNames = [].map.call(fields, getFieldVariableName);
1055 emailFieldsInput.value = fieldNames.join(',');
1056 }
1057
1058 function replaceSelection(str) {
1059 editor.replaceSelection(str);
1060 editor.focus();
1061 }
1062
1063 function debounce(func, wait, immediate) {
1064 var timeout;
1065 return function () {
1066 var context = this,
1067 args = arguments;
1068
1069 var later = function later() {
1070 timeout = null;
1071 if (!immediate) func.apply(context, args);
1072 };
1073
1074 var callNow = immediate && !timeout;
1075 clearTimeout(timeout);
1076 timeout = setTimeout(later, wait);
1077 if (callNow) func.apply(context, args);
1078 };
1079 }
1080
1081 ;
1082
1083 function uniq(a) {
1084 var seen = {};
1085 return a.filter(function (item) {
1086 return seen.hasOwnProperty(item) ? false : seen[item] = true;
1087 });
1088 }
1089
1090 var _default = {
1091 init: init,
1092 replaceSelection: replaceSelection,
1093 updateFieldVariables: updateFieldVariables
1094 };
1095 exports["default"] = _default;
1096
1097 },{"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){
1098 'use strict';
1099
1100 Object.defineProperty(exports, "__esModule", {
1101 value: true
1102 });
1103 exports["default"] = void 0;
1104 var tabs, tabNavs;
1105
1106 function init() {
1107 tabs = document.querySelectorAll('.hf-tab');
1108 tabNavs = document.querySelectorAll('#hf-tabs-nav a');
1109
1110 for (var i = 0; i < tabNavs.length; i++) {
1111 tabNavs[i].addEventListener('click', open);
1112 }
1113 }
1114
1115 ;
1116
1117 function open(e) {
1118 var tabTarget = this.getAttribute('data-tab-target');
1119
1120 for (var i = 0; i < tabNavs.length; i++) {
1121 tabNavs[i].classList.toggle('nav-tab-active', tabNavs[i] === this);
1122 }
1123
1124 this.blur();
1125
1126 for (var _i = 0; _i < tabs.length; _i++) {
1127 var tab = tabs[_i];
1128 tab.classList.toggle('hf-tab-active', tab.getAttribute('data-tab') === tabTarget);
1129 }
1130
1131 document.title = document.title.replace(document.title.split(' - ').shift(), this.innerText + " ");
1132
1133 if (window.history) {
1134 var newUrl = window.location.href;
1135 newUrl = newUrl.replace(/\&tab\=\w+/, "");
1136 newUrl += "&tab=" + tabTarget;
1137 window.history.replaceState({
1138 tab: tabTarget
1139 }, document.title, newUrl);
1140 }
1141
1142 e.preventDefault();
1143 }
1144
1145 ;
1146 var _default = {
1147 init: init,
1148 open: open
1149 };
1150 exports["default"] = _default;
1151
1152 },{}],11:[function(require,module,exports){
1153 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1154 // Distributed under an MIT license: https://codemirror.net/LICENSE
1155
1156 /**
1157 * Tag-closer extension for CodeMirror.
1158 *
1159 * This extension adds an "autoCloseTags" option that can be set to
1160 * either true to get the default behavior, or an object to further
1161 * configure its behavior.
1162 *
1163 * These are supported options:
1164 *
1165 * `whenClosing` (default true)
1166 * Whether to autoclose when the '/' of a closing tag is typed.
1167 * `whenOpening` (default true)
1168 * Whether to autoclose the tag when the final '>' of an opening
1169 * tag is typed.
1170 * `dontCloseTags` (default is empty tags for HTML, none for XML)
1171 * An array of tag names that should not be autoclosed.
1172 * `indentTags` (default is block tags for HTML, none for XML)
1173 * An array of tag names that should, when opened, cause a
1174 * blank line to be added inside the tag, and the blank line and
1175 * closing line to be indented.
1176 *
1177 * See demos/closetag.html for a usage example.
1178 */
1179
1180 (function(mod) {
1181 if (typeof exports == "object" && typeof module == "object") // CommonJS
1182 mod(require("../../lib/codemirror"), require("../fold/xml-fold"));
1183 else if (typeof define == "function" && define.amd) // AMD
1184 define(["../../lib/codemirror", "../fold/xml-fold"], mod);
1185 else // Plain browser env
1186 mod(CodeMirror);
1187 })(function(CodeMirror) {
1188 CodeMirror.defineOption("autoCloseTags", false, function(cm, val, old) {
1189 if (old != CodeMirror.Init && old)
1190 cm.removeKeyMap("autoCloseTags");
1191 if (!val) return;
1192 var map = {name: "autoCloseTags"};
1193 if (typeof val != "object" || val.whenClosing)
1194 map["'/'"] = function(cm) { return autoCloseSlash(cm); };
1195 if (typeof val != "object" || val.whenOpening)
1196 map["'>'"] = function(cm) { return autoCloseGT(cm); };
1197 cm.addKeyMap(map);
1198 });
1199
1200 var htmlDontClose = ["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param",
1201 "source", "track", "wbr"];
1202 var htmlIndent = ["applet", "blockquote", "body", "button", "div", "dl", "fieldset", "form", "frameset", "h1", "h2", "h3", "h4",
1203 "h5", "h6", "head", "html", "iframe", "layer", "legend", "object", "ol", "p", "select", "table", "ul"];
1204
1205 function autoCloseGT(cm) {
1206 if (cm.getOption("disableInput")) return CodeMirror.Pass;
1207 var ranges = cm.listSelections(), replacements = [];
1208 var opt = cm.getOption("autoCloseTags");
1209 for (var i = 0; i < ranges.length; i++) {
1210 if (!ranges[i].empty()) return CodeMirror.Pass;
1211 var pos = ranges[i].head, tok = cm.getTokenAt(pos);
1212 var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state;
1213 if (inner.mode.name != "xml" || !state.tagName) return CodeMirror.Pass;
1214
1215 var html = inner.mode.configuration == "html";
1216 var dontCloseTags = (typeof opt == "object" && opt.dontCloseTags) || (html && htmlDontClose);
1217 var indentTags = (typeof opt == "object" && opt.indentTags) || (html && htmlIndent);
1218
1219 var tagName = state.tagName;
1220 if (tok.end > pos.ch) tagName = tagName.slice(0, tagName.length - tok.end + pos.ch);
1221 var lowerTagName = tagName.toLowerCase();
1222 // Don't process the '>' at the end of an end-tag or self-closing tag
1223 if (!tagName ||
1224 tok.type == "string" && (tok.end != pos.ch || !/[\"\']/.test(tok.string.charAt(tok.string.length - 1)) || tok.string.length == 1) ||
1225 tok.type == "tag" && state.type == "closeTag" ||
1226 tok.string.indexOf("/") == (tok.string.length - 1) || // match something like <someTagName />
1227 dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1 ||
1228 closingTagExists(cm, tagName, pos, state, true))
1229 return CodeMirror.Pass;
1230
1231 var indent = indentTags && indexOf(indentTags, lowerTagName) > -1;
1232 replacements[i] = {indent: indent,
1233 text: ">" + (indent ? "\n\n" : "") + "</" + tagName + ">",
1234 newPos: indent ? CodeMirror.Pos(pos.line + 1, 0) : CodeMirror.Pos(pos.line, pos.ch + 1)};
1235 }
1236
1237 var dontIndentOnAutoClose = (typeof opt == "object" && opt.dontIndentOnAutoClose);
1238 for (var i = ranges.length - 1; i >= 0; i--) {
1239 var info = replacements[i];
1240 cm.replaceRange(info.text, ranges[i].head, ranges[i].anchor, "+insert");
1241 var sel = cm.listSelections().slice(0);
1242 sel[i] = {head: info.newPos, anchor: info.newPos};
1243 cm.setSelections(sel);
1244 if (!dontIndentOnAutoClose && info.indent) {
1245 cm.indentLine(info.newPos.line, null, true);
1246 cm.indentLine(info.newPos.line + 1, null, true);
1247 }
1248 }
1249 }
1250
1251 function autoCloseCurrent(cm, typingSlash) {
1252 var ranges = cm.listSelections(), replacements = [];
1253 var head = typingSlash ? "/" : "</";
1254 var opt = cm.getOption("autoCloseTags");
1255 var dontIndentOnAutoClose = (typeof opt == "object" && opt.dontIndentOnSlash);
1256 for (var i = 0; i < ranges.length; i++) {
1257 if (!ranges[i].empty()) return CodeMirror.Pass;
1258 var pos = ranges[i].head, tok = cm.getTokenAt(pos);
1259 var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state;
1260 if (typingSlash && (tok.type == "string" || tok.string.charAt(0) != "<" ||
1261 tok.start != pos.ch - 1))
1262 return CodeMirror.Pass;
1263 // Kludge to get around the fact that we are not in XML mode
1264 // when completing in JS/CSS snippet in htmlmixed mode. Does not
1265 // work for other XML embedded languages (there is no general
1266 // way to go from a mixed mode to its current XML state).
1267 var replacement;
1268 if (inner.mode.name != "xml") {
1269 if (cm.getMode().name == "htmlmixed" && inner.mode.name == "javascript")
1270 replacement = head + "script";
1271 else if (cm.getMode().name == "htmlmixed" && inner.mode.name == "css")
1272 replacement = head + "style";
1273 else
1274 return CodeMirror.Pass;
1275 } else {
1276 if (!state.context || !state.context.tagName ||
1277 closingTagExists(cm, state.context.tagName, pos, state))
1278 return CodeMirror.Pass;
1279 replacement = head + state.context.tagName;
1280 }
1281 if (cm.getLine(pos.line).charAt(tok.end) != ">") replacement += ">";
1282 replacements[i] = replacement;
1283 }
1284 cm.replaceSelections(replacements);
1285 ranges = cm.listSelections();
1286 if (!dontIndentOnAutoClose) {
1287 for (var i = 0; i < ranges.length; i++)
1288 if (i == ranges.length - 1 || ranges[i].head.line < ranges[i + 1].head.line)
1289 cm.indentLine(ranges[i].head.line);
1290 }
1291 }
1292
1293 function autoCloseSlash(cm) {
1294 if (cm.getOption("disableInput")) return CodeMirror.Pass;
1295 return autoCloseCurrent(cm, true);
1296 }
1297
1298 CodeMirror.commands.closeTag = function(cm) { return autoCloseCurrent(cm); };
1299
1300 function indexOf(collection, elt) {
1301 if (collection.indexOf) return collection.indexOf(elt);
1302 for (var i = 0, e = collection.length; i < e; ++i)
1303 if (collection[i] == elt) return i;
1304 return -1;
1305 }
1306
1307 // If xml-fold is loaded, we use its functionality to try and verify
1308 // whether a given tag is actually unclosed.
1309 function closingTagExists(cm, tagName, pos, state, newTag) {
1310 if (!CodeMirror.scanForClosingTag) return false;
1311 var end = Math.min(cm.lastLine() + 1, pos.line + 500);
1312 var nextClose = CodeMirror.scanForClosingTag(cm, pos, null, end);
1313 if (!nextClose || nextClose.tag != tagName) return false;
1314 var cx = state.context;
1315 // If the immediate wrapping context contains onCx instances of
1316 // the same tag, a closing tag only exists if there are at least
1317 // that many closing tags of that type following.
1318 for (var onCx = newTag ? 1 : 0; cx && cx.tagName == tagName; cx = cx.prev) ++onCx;
1319 pos = nextClose.to;
1320 for (var i = 1; i < onCx; i++) {
1321 var next = CodeMirror.scanForClosingTag(cm, pos, null, end);
1322 if (!next || next.tag != tagName) return false;
1323 pos = next.to;
1324 }
1325 return true;
1326 }
1327 });
1328
1329 },{"../../lib/codemirror":14,"../fold/xml-fold":13}],12:[function(require,module,exports){
1330 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1331 // Distributed under an MIT license: https://codemirror.net/LICENSE
1332
1333 (function(mod) {
1334 if (typeof exports == "object" && typeof module == "object") // CommonJS
1335 mod(require("../../lib/codemirror"), require("../fold/xml-fold"));
1336 else if (typeof define == "function" && define.amd) // AMD
1337 define(["../../lib/codemirror", "../fold/xml-fold"], mod);
1338 else // Plain browser env
1339 mod(CodeMirror);
1340 })(function(CodeMirror) {
1341 "use strict";
1342
1343 CodeMirror.defineOption("matchTags", false, function(cm, val, old) {
1344 if (old && old != CodeMirror.Init) {
1345 cm.off("cursorActivity", doMatchTags);
1346 cm.off("viewportChange", maybeUpdateMatch);
1347 clear(cm);
1348 }
1349 if (val) {
1350 cm.state.matchBothTags = typeof val == "object" && val.bothTags;
1351 cm.on("cursorActivity", doMatchTags);
1352 cm.on("viewportChange", maybeUpdateMatch);
1353 doMatchTags(cm);
1354 }
1355 });
1356
1357 function clear(cm) {
1358 if (cm.state.tagHit) cm.state.tagHit.clear();
1359 if (cm.state.tagOther) cm.state.tagOther.clear();
1360 cm.state.tagHit = cm.state.tagOther = null;
1361 }
1362
1363 function doMatchTags(cm) {
1364 cm.state.failedTagMatch = false;
1365 cm.operation(function() {
1366 clear(cm);
1367 if (cm.somethingSelected()) return;
1368 var cur = cm.getCursor(), range = cm.getViewport();
1369 range.from = Math.min(range.from, cur.line); range.to = Math.max(cur.line + 1, range.to);
1370 var match = CodeMirror.findMatchingTag(cm, cur, range);
1371 if (!match) return;
1372 if (cm.state.matchBothTags) {
1373 var hit = match.at == "open" ? match.open : match.close;
1374 if (hit) cm.state.tagHit = cm.markText(hit.from, hit.to, {className: "CodeMirror-matchingtag"});
1375 }
1376 var other = match.at == "close" ? match.open : match.close;
1377 if (other)
1378 cm.state.tagOther = cm.markText(other.from, other.to, {className: "CodeMirror-matchingtag"});
1379 else
1380 cm.state.failedTagMatch = true;
1381 });
1382 }
1383
1384 function maybeUpdateMatch(cm) {
1385 if (cm.state.failedTagMatch) doMatchTags(cm);
1386 }
1387
1388 CodeMirror.commands.toMatchingTag = function(cm) {
1389 var found = CodeMirror.findMatchingTag(cm, cm.getCursor());
1390 if (found) {
1391 var other = found.at == "close" ? found.open : found.close;
1392 if (other) cm.extendSelection(other.to, other.from);
1393 }
1394 };
1395 });
1396
1397 },{"../../lib/codemirror":14,"../fold/xml-fold":13}],13:[function(require,module,exports){
1398 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1399 // Distributed under an MIT license: https://codemirror.net/LICENSE
1400
1401 (function(mod) {
1402 if (typeof exports == "object" && typeof module == "object") // CommonJS
1403 mod(require("../../lib/codemirror"));
1404 else if (typeof define == "function" && define.amd) // AMD
1405 define(["../../lib/codemirror"], mod);
1406 else // Plain browser env
1407 mod(CodeMirror);
1408 })(function(CodeMirror) {
1409 "use strict";
1410
1411 var Pos = CodeMirror.Pos;
1412 function cmp(a, b) { return a.line - b.line || a.ch - b.ch; }
1413
1414 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";
1415 var nameChar = nameStartChar + "\-\:\.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040";
1416 var xmlTagStart = new RegExp("<(/?)([" + nameStartChar + "][" + nameChar + "]*)", "g");
1417
1418 function Iter(cm, line, ch, range) {
1419 this.line = line; this.ch = ch;
1420 this.cm = cm; this.text = cm.getLine(line);
1421 this.min = range ? Math.max(range.from, cm.firstLine()) : cm.firstLine();
1422 this.max = range ? Math.min(range.to - 1, cm.lastLine()) : cm.lastLine();
1423 }
1424
1425 function tagAt(iter, ch) {
1426 var type = iter.cm.getTokenTypeAt(Pos(iter.line, ch));
1427 return type && /\btag\b/.test(type);
1428 }
1429
1430 function nextLine(iter) {
1431 if (iter.line >= iter.max) return;
1432 iter.ch = 0;
1433 iter.text = iter.cm.getLine(++iter.line);
1434 return true;
1435 }
1436 function prevLine(iter) {
1437 if (iter.line <= iter.min) return;
1438 iter.text = iter.cm.getLine(--iter.line);
1439 iter.ch = iter.text.length;
1440 return true;
1441 }
1442
1443 function toTagEnd(iter) {
1444 for (;;) {
1445 var gt = iter.text.indexOf(">", iter.ch);
1446 if (gt == -1) { if (nextLine(iter)) continue; else return; }
1447 if (!tagAt(iter, gt + 1)) { iter.ch = gt + 1; continue; }
1448 var lastSlash = iter.text.lastIndexOf("/", gt);
1449 var selfClose = lastSlash > -1 && !/\S/.test(iter.text.slice(lastSlash + 1, gt));
1450 iter.ch = gt + 1;
1451 return selfClose ? "selfClose" : "regular";
1452 }
1453 }
1454 function toTagStart(iter) {
1455 for (;;) {
1456 var lt = iter.ch ? iter.text.lastIndexOf("<", iter.ch - 1) : -1;
1457 if (lt == -1) { if (prevLine(iter)) continue; else return; }
1458 if (!tagAt(iter, lt + 1)) { iter.ch = lt; continue; }
1459 xmlTagStart.lastIndex = lt;
1460 iter.ch = lt;
1461 var match = xmlTagStart.exec(iter.text);
1462 if (match && match.index == lt) return match;
1463 }
1464 }
1465
1466 function toNextTag(iter) {
1467 for (;;) {
1468 xmlTagStart.lastIndex = iter.ch;
1469 var found = xmlTagStart.exec(iter.text);
1470 if (!found) { if (nextLine(iter)) continue; else return; }
1471 if (!tagAt(iter, found.index + 1)) { iter.ch = found.index + 1; continue; }
1472 iter.ch = found.index + found[0].length;
1473 return found;
1474 }
1475 }
1476 function toPrevTag(iter) {
1477 for (;;) {
1478 var gt = iter.ch ? iter.text.lastIndexOf(">", iter.ch - 1) : -1;
1479 if (gt == -1) { if (prevLine(iter)) continue; else return; }
1480 if (!tagAt(iter, gt + 1)) { iter.ch = gt; continue; }
1481 var lastSlash = iter.text.lastIndexOf("/", gt);
1482 var selfClose = lastSlash > -1 && !/\S/.test(iter.text.slice(lastSlash + 1, gt));
1483 iter.ch = gt + 1;
1484 return selfClose ? "selfClose" : "regular";
1485 }
1486 }
1487
1488 function findMatchingClose(iter, tag) {
1489 var stack = [];
1490 for (;;) {
1491 var next = toNextTag(iter), end, startLine = iter.line, startCh = iter.ch - (next ? next[0].length : 0);
1492 if (!next || !(end = toTagEnd(iter))) return;
1493 if (end == "selfClose") continue;
1494 if (next[1]) { // closing tag
1495 for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == next[2]) {
1496 stack.length = i;
1497 break;
1498 }
1499 if (i < 0 && (!tag || tag == next[2])) return {
1500 tag: next[2],
1501 from: Pos(startLine, startCh),
1502 to: Pos(iter.line, iter.ch)
1503 };
1504 } else { // opening tag
1505 stack.push(next[2]);
1506 }
1507 }
1508 }
1509 function findMatchingOpen(iter, tag) {
1510 var stack = [];
1511 for (;;) {
1512 var prev = toPrevTag(iter);
1513 if (!prev) return;
1514 if (prev == "selfClose") { toTagStart(iter); continue; }
1515 var endLine = iter.line, endCh = iter.ch;
1516 var start = toTagStart(iter);
1517 if (!start) return;
1518 if (start[1]) { // closing tag
1519 stack.push(start[2]);
1520 } else { // opening tag
1521 for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == start[2]) {
1522 stack.length = i;
1523 break;
1524 }
1525 if (i < 0 && (!tag || tag == start[2])) return {
1526 tag: start[2],
1527 from: Pos(iter.line, iter.ch),
1528 to: Pos(endLine, endCh)
1529 };
1530 }
1531 }
1532 }
1533
1534 CodeMirror.registerHelper("fold", "xml", function(cm, start) {
1535 var iter = new Iter(cm, start.line, 0);
1536 for (;;) {
1537 var openTag = toNextTag(iter)
1538 if (!openTag || iter.line != start.line) return
1539 var end = toTagEnd(iter)
1540 if (!end) return
1541 if (!openTag[1] && end != "selfClose") {
1542 var startPos = Pos(iter.line, iter.ch);
1543 var endPos = findMatchingClose(iter, openTag[2]);
1544 return endPos && cmp(endPos.from, startPos) > 0 ? {from: startPos, to: endPos.from} : null
1545 }
1546 }
1547 });
1548 CodeMirror.findMatchingTag = function(cm, pos, range) {
1549 var iter = new Iter(cm, pos.line, pos.ch, range);
1550 if (iter.text.indexOf(">") == -1 && iter.text.indexOf("<") == -1) return;
1551 var end = toTagEnd(iter), to = end && Pos(iter.line, iter.ch);
1552 var start = end && toTagStart(iter);
1553 if (!end || !start || cmp(iter, pos) > 0) return;
1554 var here = {from: Pos(iter.line, iter.ch), to: to, tag: start[2]};
1555 if (end == "selfClose") return {open: here, close: null, at: "open"};
1556
1557 if (start[1]) { // closing tag
1558 return {open: findMatchingOpen(iter, start[2]), close: here, at: "close"};
1559 } else { // opening tag
1560 iter = new Iter(cm, to.line, to.ch, range);
1561 return {open: here, close: findMatchingClose(iter, start[2]), at: "open"};
1562 }
1563 };
1564
1565 CodeMirror.findEnclosingTag = function(cm, pos, range, tag) {
1566 var iter = new Iter(cm, pos.line, pos.ch, range);
1567 for (;;) {
1568 var open = findMatchingOpen(iter, tag);
1569 if (!open) break;
1570 var forward = new Iter(cm, pos.line, pos.ch, range);
1571 var close = findMatchingClose(forward, open.tag);
1572 if (close) return {open: open, close: close};
1573 }
1574 };
1575
1576 // Used by addon/edit/closetag.js
1577 CodeMirror.scanForClosingTag = function(cm, pos, name, end) {
1578 var iter = new Iter(cm, pos.line, pos.ch, end ? {from: 0, to: end} : null);
1579 return findMatchingClose(iter, name);
1580 };
1581 });
1582
1583 },{"../../lib/codemirror":14}],14:[function(require,module,exports){
1584 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1585 // Distributed under an MIT license: https://codemirror.net/LICENSE
1586
1587 // This is CodeMirror (https://codemirror.net), a code editor
1588 // implemented in JavaScript on top of the browser's DOM.
1589 //
1590 // You can find some technical background for some of the code below
1591 // at http://marijnhaverbeke.nl/blog/#cm-internals .
1592
1593 (function (global, factory) {
1594 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
1595 typeof define === 'function' && define.amd ? define(factory) :
1596 (global.CodeMirror = factory());
1597 }(this, (function () { 'use strict';
1598
1599 // Kludges for bugs and behavior differences that can't be feature
1600 // detected are enabled based on userAgent etc sniffing.
1601 var userAgent = navigator.userAgent;
1602 var platform = navigator.platform;
1603
1604 var gecko = /gecko\/\d/i.test(userAgent);
1605 var ie_upto10 = /MSIE \d/.test(userAgent);
1606 var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent);
1607 var edge = /Edge\/(\d+)/.exec(userAgent);
1608 var ie = ie_upto10 || ie_11up || edge;
1609 var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1]);
1610 var webkit = !edge && /WebKit\//.test(userAgent);
1611 var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent);
1612 var chrome = !edge && /Chrome\//.test(userAgent);
1613 var presto = /Opera\//.test(userAgent);
1614 var safari = /Apple Computer/.test(navigator.vendor);
1615 var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent);
1616 var phantom = /PhantomJS/.test(userAgent);
1617
1618 var ios = !edge && /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent);
1619 var android = /Android/.test(userAgent);
1620 // This is woefully incomplete. Suggestions for alternative methods welcome.
1621 var mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent);
1622 var mac = ios || /Mac/.test(platform);
1623 var chromeOS = /\bCrOS\b/.test(userAgent);
1624 var windows = /win/i.test(platform);
1625
1626 var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/);
1627 if (presto_version) { presto_version = Number(presto_version[1]); }
1628 if (presto_version && presto_version >= 15) { presto = false; webkit = true; }
1629 // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
1630 var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11));
1631 var captureRightClick = gecko || (ie && ie_version >= 9);
1632
1633 function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") }
1634
1635 var rmClass = function(node, cls) {
1636 var current = node.className;
1637 var match = classTest(cls).exec(current);
1638 if (match) {
1639 var after = current.slice(match.index + match[0].length);
1640 node.className = current.slice(0, match.index) + (after ? match[1] + after : "");
1641 }
1642 };
1643
1644 function removeChildren(e) {
1645 for (var count = e.childNodes.length; count > 0; --count)
1646 { e.removeChild(e.firstChild); }
1647 return e
1648 }
1649
1650 function removeChildrenAndAdd(parent, e) {
1651 return removeChildren(parent).appendChild(e)
1652 }
1653
1654 function elt(tag, content, className, style) {
1655 var e = document.createElement(tag);
1656 if (className) { e.className = className; }
1657 if (style) { e.style.cssText = style; }
1658 if (typeof content == "string") { e.appendChild(document.createTextNode(content)); }
1659 else if (content) { for (var i = 0; i < content.length; ++i) { e.appendChild(content[i]); } }
1660 return e
1661 }
1662 // wrapper for elt, which removes the elt from the accessibility tree
1663 function eltP(tag, content, className, style) {
1664 var e = elt(tag, content, className, style);
1665 e.setAttribute("role", "presentation");
1666 return e
1667 }
1668
1669 var range;
1670 if (document.createRange) { range = function(node, start, end, endNode) {
1671 var r = document.createRange();
1672 r.setEnd(endNode || node, end);
1673 r.setStart(node, start);
1674 return r
1675 }; }
1676 else { range = function(node, start, end) {
1677 var r = document.body.createTextRange();
1678 try { r.moveToElementText(node.parentNode); }
1679 catch(e) { return r }
1680 r.collapse(true);
1681 r.moveEnd("character", end);
1682 r.moveStart("character", start);
1683 return r
1684 }; }
1685
1686 function contains(parent, child) {
1687 if (child.nodeType == 3) // Android browser always returns false when child is a textnode
1688 { child = child.parentNode; }
1689 if (parent.contains)
1690 { return parent.contains(child) }
1691 do {
1692 if (child.nodeType == 11) { child = child.host; }
1693 if (child == parent) { return true }
1694 } while (child = child.parentNode)
1695 }
1696
1697 function activeElt() {
1698 // IE and Edge may throw an "Unspecified Error" when accessing document.activeElement.
1699 // IE < 10 will throw when accessed while the page is loading or in an iframe.
1700 // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable.
1701 var activeElement;
1702 try {
1703 activeElement = document.activeElement;
1704 } catch(e) {
1705 activeElement = document.body || null;
1706 }
1707 while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement)
1708 { activeElement = activeElement.shadowRoot.activeElement; }
1709 return activeElement
1710 }
1711
1712 function addClass(node, cls) {
1713 var current = node.className;
1714 if (!classTest(cls).test(current)) { node.className += (current ? " " : "") + cls; }
1715 }
1716 function joinClasses(a, b) {
1717 var as = a.split(" ");
1718 for (var i = 0; i < as.length; i++)
1719 { if (as[i] && !classTest(as[i]).test(b)) { b += " " + as[i]; } }
1720 return b
1721 }
1722
1723 var selectInput = function(node) { node.select(); };
1724 if (ios) // Mobile Safari apparently has a bug where select() is broken.
1725 { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; }; }
1726 else if (ie) // Suppress mysterious IE10 errors
1727 { selectInput = function(node) { try { node.select(); } catch(_e) {} }; }
1728
1729 function bind(f) {
1730 var args = Array.prototype.slice.call(arguments, 1);
1731 return function(){return f.apply(null, args)}
1732 }
1733
1734 function copyObj(obj, target, overwrite) {
1735 if (!target) { target = {}; }
1736 for (var prop in obj)
1737 { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
1738 { target[prop] = obj[prop]; } }
1739 return target
1740 }
1741
1742 // Counts the column offset in a string, taking tabs into account.
1743 // Used mostly to find indentation.
1744 function countColumn(string, end, tabSize, startIndex, startValue) {
1745 if (end == null) {
1746 end = string.search(/[^\s\u00a0]/);
1747 if (end == -1) { end = string.length; }
1748 }
1749 for (var i = startIndex || 0, n = startValue || 0;;) {
1750 var nextTab = string.indexOf("\t", i);
1751 if (nextTab < 0 || nextTab >= end)
1752 { return n + (end - i) }
1753 n += nextTab - i;
1754 n += tabSize - (n % tabSize);
1755 i = nextTab + 1;
1756 }
1757 }
1758
1759 var Delayed = function() {this.id = null;};
1760 Delayed.prototype.set = function (ms, f) {
1761 clearTimeout(this.id);
1762 this.id = setTimeout(f, ms);
1763 };
1764
1765 function indexOf(array, elt) {
1766 for (var i = 0; i < array.length; ++i)
1767 { if (array[i] == elt) { return i } }
1768 return -1
1769 }
1770
1771 // Number of pixels added to scroller and sizer to hide scrollbar
1772 var scrollerGap = 30;
1773
1774 // Returned or thrown by various protocols to signal 'I'm not
1775 // handling this'.
1776 var Pass = {toString: function(){return "CodeMirror.Pass"}};
1777
1778 // Reused option objects for setSelection & friends
1779 var sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"};
1780
1781 // The inverse of countColumn -- find the offset that corresponds to
1782 // a particular column.
1783 function findColumn(string, goal, tabSize) {
1784 for (var pos = 0, col = 0;;) {
1785 var nextTab = string.indexOf("\t", pos);
1786 if (nextTab == -1) { nextTab = string.length; }
1787 var skipped = nextTab - pos;
1788 if (nextTab == string.length || col + skipped >= goal)
1789 { return pos + Math.min(skipped, goal - col) }
1790 col += nextTab - pos;
1791 col += tabSize - (col % tabSize);
1792 pos = nextTab + 1;
1793 if (col >= goal) { return pos }
1794 }
1795 }
1796
1797 var spaceStrs = [""];
1798 function spaceStr(n) {
1799 while (spaceStrs.length <= n)
1800 { spaceStrs.push(lst(spaceStrs) + " "); }
1801 return spaceStrs[n]
1802 }
1803
1804 function lst(arr) { return arr[arr.length-1] }
1805
1806 function map(array, f) {
1807 var out = [];
1808 for (var i = 0; i < array.length; i++) { out[i] = f(array[i], i); }
1809 return out
1810 }
1811
1812 function insertSorted(array, value, score) {
1813 var pos = 0, priority = score(value);
1814 while (pos < array.length && score(array[pos]) <= priority) { pos++; }
1815 array.splice(pos, 0, value);
1816 }
1817
1818 function nothing() {}
1819
1820 function createObj(base, props) {
1821 var inst;
1822 if (Object.create) {
1823 inst = Object.create(base);
1824 } else {
1825 nothing.prototype = base;
1826 inst = new nothing();
1827 }
1828 if (props) { copyObj(props, inst); }
1829 return inst
1830 }
1831
1832 var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
1833 function isWordCharBasic(ch) {
1834 return /\w/.test(ch) || ch > "\x80" &&
1835 (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch))
1836 }
1837 function isWordChar(ch, helper) {
1838 if (!helper) { return isWordCharBasic(ch) }
1839 if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) { return true }
1840 return helper.test(ch)
1841 }
1842
1843 function isEmpty(obj) {
1844 for (var n in obj) { if (obj.hasOwnProperty(n) && obj[n]) { return false } }
1845 return true
1846 }
1847
1848 // Extending unicode characters. A series of a non-extending char +
1849 // any number of extending chars is treated as a single unit as far
1850 // as editing and measuring is concerned. This is not fully correct,
1851 // since some scripts/fonts/browsers also treat other configurations
1852 // of code points as a group.
1853 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]/;
1854 function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) }
1855
1856 // Returns a number from the range [`0`; `str.length`] unless `pos` is outside that range.
1857 function skipExtendingChars(str, pos, dir) {
1858 while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(pos))) { pos += dir; }
1859 return pos
1860 }
1861
1862 // Returns the value from the range [`from`; `to`] that satisfies
1863 // `pred` and is closest to `from`. Assumes that at least `to`
1864 // satisfies `pred`. Supports `from` being greater than `to`.
1865 function findFirst(pred, from, to) {
1866 // At any point we are certain `to` satisfies `pred`, don't know
1867 // whether `from` does.
1868 var dir = from > to ? -1 : 1;
1869 for (;;) {
1870 if (from == to) { return from }
1871 var midF = (from + to) / 2, mid = dir < 0 ? Math.ceil(midF) : Math.floor(midF);
1872 if (mid == from) { return pred(mid) ? from : to }
1873 if (pred(mid)) { to = mid; }
1874 else { from = mid + dir; }
1875 }
1876 }
1877
1878 // The display handles the DOM integration, both for input reading
1879 // and content drawing. It holds references to DOM nodes and
1880 // display-related state.
1881
1882 function Display(place, doc, input) {
1883 var d = this;
1884 this.input = input;
1885
1886 // Covers bottom-right square when both scrollbars are present.
1887 d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
1888 d.scrollbarFiller.setAttribute("cm-not-content", "true");
1889 // Covers bottom of gutter when coverGutterNextToScrollbar is on
1890 // and h scrollbar is present.
1891 d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler");
1892 d.gutterFiller.setAttribute("cm-not-content", "true");
1893 // Will contain the actual code, positioned to cover the viewport.
1894 d.lineDiv = eltP("div", null, "CodeMirror-code");
1895 // Elements are added to these to represent selection and cursors.
1896 d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
1897 d.cursorDiv = elt("div", null, "CodeMirror-cursors");
1898 // A visibility: hidden element used to find the size of things.
1899 d.measure = elt("div", null, "CodeMirror-measure");
1900 // When lines outside of the viewport are measured, they are drawn in this.
1901 d.lineMeasure = elt("div", null, "CodeMirror-measure");
1902 // Wraps everything that needs to exist inside the vertically-padded coordinate system
1903 d.lineSpace = eltP("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],
1904 null, "position: relative; outline: none");
1905 var lines = eltP("div", [d.lineSpace], "CodeMirror-lines");
1906 // Moved around its parent to cover visible view.
1907 d.mover = elt("div", [lines], null, "position: relative");
1908 // Set to the height of the document, allowing scrolling.
1909 d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
1910 d.sizerWidth = null;
1911 // Behavior of elts with overflow: auto and padding is
1912 // inconsistent across browsers. This is used to ensure the
1913 // scrollable area is big enough.
1914 d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;");
1915 // Will contain the gutters, if any.
1916 d.gutters = elt("div", null, "CodeMirror-gutters");
1917 d.lineGutter = null;
1918 // Actual scrollable element.
1919 d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll");
1920 d.scroller.setAttribute("tabIndex", "-1");
1921 // The element in which the editor lives.
1922 d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror");
1923
1924 // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)
1925 if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
1926 if (!webkit && !(gecko && mobile)) { d.scroller.draggable = true; }
1927
1928 if (place) {
1929 if (place.appendChild) { place.appendChild(d.wrapper); }
1930 else { place(d.wrapper); }
1931 }
1932
1933 // Current rendered range (may be bigger than the view window).
1934 d.viewFrom = d.viewTo = doc.first;
1935 d.reportedViewFrom = d.reportedViewTo = doc.first;
1936 // Information about the rendered lines.
1937 d.view = [];
1938 d.renderedView = null;
1939 // Holds info about a single rendered line when it was rendered
1940 // for measurement, while not in view.
1941 d.externalMeasured = null;
1942 // Empty space (in pixels) above the view
1943 d.viewOffset = 0;
1944 d.lastWrapHeight = d.lastWrapWidth = 0;
1945 d.updateLineNumbers = null;
1946
1947 d.nativeBarWidth = d.barHeight = d.barWidth = 0;
1948 d.scrollbarsClipped = false;
1949
1950 // Used to only resize the line number gutter when necessary (when
1951 // the amount of lines crosses a boundary that makes its width change)
1952 d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
1953 // Set to true when a non-horizontal-scrolling line widget is
1954 // added. As an optimization, line widget aligning is skipped when
1955 // this is false.
1956 d.alignWidgets = false;
1957
1958 d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
1959
1960 // Tracks the maximum line length so that the horizontal scrollbar
1961 // can be kept static when scrolling.
1962 d.maxLine = null;
1963 d.maxLineLength = 0;
1964 d.maxLineChanged = false;
1965
1966 // Used for measuring wheel scrolling granularity
1967 d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
1968
1969 // True when shift is held down.
1970 d.shift = false;
1971
1972 // Used to track whether anything happened since the context menu
1973 // was opened.
1974 d.selForContextMenu = null;
1975
1976 d.activeTouch = null;
1977
1978 input.init(d);
1979 }
1980
1981 // Find the line object corresponding to the given line number.
1982 function getLine(doc, n) {
1983 n -= doc.first;
1984 if (n < 0 || n >= doc.size) { throw new Error("There is no line " + (n + doc.first) + " in the document.") }
1985 var chunk = doc;
1986 while (!chunk.lines) {
1987 for (var i = 0;; ++i) {
1988 var child = chunk.children[i], sz = child.chunkSize();
1989 if (n < sz) { chunk = child; break }
1990 n -= sz;
1991 }
1992 }
1993 return chunk.lines[n]
1994 }
1995
1996 // Get the part of a document between two positions, as an array of
1997 // strings.
1998 function getBetween(doc, start, end) {
1999 var out = [], n = start.line;
2000 doc.iter(start.line, end.line + 1, function (line) {
2001 var text = line.text;
2002 if (n == end.line) { text = text.slice(0, end.ch); }
2003 if (n == start.line) { text = text.slice(start.ch); }
2004 out.push(text);
2005 ++n;
2006 });
2007 return out
2008 }
2009 // Get the lines between from and to, as array of strings.
2010 function getLines(doc, from, to) {
2011 var out = [];
2012 doc.iter(from, to, function (line) { out.push(line.text); }); // iter aborts when callback returns truthy value
2013 return out
2014 }
2015
2016 // Update the height of a line, propagating the height change
2017 // upwards to parent nodes.
2018 function updateLineHeight(line, height) {
2019 var diff = height - line.height;
2020 if (diff) { for (var n = line; n; n = n.parent) { n.height += diff; } }
2021 }
2022
2023 // Given a line object, find its line number by walking up through
2024 // its parent links.
2025 function lineNo(line) {
2026 if (line.parent == null) { return null }
2027 var cur = line.parent, no = indexOf(cur.lines, line);
2028 for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
2029 for (var i = 0;; ++i) {
2030 if (chunk.children[i] == cur) { break }
2031 no += chunk.children[i].chunkSize();
2032 }
2033 }
2034 return no + cur.first
2035 }
2036
2037 // Find the line at the given vertical position, using the height
2038 // information in the document tree.
2039 function lineAtHeight(chunk, h) {
2040 var n = chunk.first;
2041 outer: do {
2042 for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) {
2043 var child = chunk.children[i$1], ch = child.height;
2044 if (h < ch) { chunk = child; continue outer }
2045 h -= ch;
2046 n += child.chunkSize();
2047 }
2048 return n
2049 } while (!chunk.lines)
2050 var i = 0;
2051 for (; i < chunk.lines.length; ++i) {
2052 var line = chunk.lines[i], lh = line.height;
2053 if (h < lh) { break }
2054 h -= lh;
2055 }
2056 return n + i
2057 }
2058
2059 function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size}
2060
2061 function lineNumberFor(options, i) {
2062 return String(options.lineNumberFormatter(i + options.firstLineNumber))
2063 }
2064
2065 // A Pos instance represents a position within the text.
2066 function Pos(line, ch, sticky) {
2067 if ( sticky === void 0 ) sticky = null;
2068
2069 if (!(this instanceof Pos)) { return new Pos(line, ch, sticky) }
2070 this.line = line;
2071 this.ch = ch;
2072 this.sticky = sticky;
2073 }
2074
2075 // Compare two positions, return 0 if they are the same, a negative
2076 // number when a is less, and a positive number otherwise.
2077 function cmp(a, b) { return a.line - b.line || a.ch - b.ch }
2078
2079 function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 }
2080
2081 function copyPos(x) {return Pos(x.line, x.ch)}
2082 function maxPos(a, b) { return cmp(a, b) < 0 ? b : a }
2083 function minPos(a, b) { return cmp(a, b) < 0 ? a : b }
2084
2085 // Most of the external API clips given positions to make sure they
2086 // actually exist within the document.
2087 function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))}
2088 function clipPos(doc, pos) {
2089 if (pos.line < doc.first) { return Pos(doc.first, 0) }
2090 var last = doc.first + doc.size - 1;
2091 if (pos.line > last) { return Pos(last, getLine(doc, last).text.length) }
2092 return clipToLen(pos, getLine(doc, pos.line).text.length)
2093 }
2094 function clipToLen(pos, linelen) {
2095 var ch = pos.ch;
2096 if (ch == null || ch > linelen) { return Pos(pos.line, linelen) }
2097 else if (ch < 0) { return Pos(pos.line, 0) }
2098 else { return pos }
2099 }
2100 function clipPosArray(doc, array) {
2101 var out = [];
2102 for (var i = 0; i < array.length; i++) { out[i] = clipPos(doc, array[i]); }
2103 return out
2104 }
2105
2106 // Optimize some code when these features are not used.
2107 var sawReadOnlySpans = false, sawCollapsedSpans = false;
2108
2109 function seeReadOnlySpans() {
2110 sawReadOnlySpans = true;
2111 }
2112
2113 function seeCollapsedSpans() {
2114 sawCollapsedSpans = true;
2115 }
2116
2117 // TEXTMARKER SPANS
2118
2119 function MarkedSpan(marker, from, to) {
2120 this.marker = marker;
2121 this.from = from; this.to = to;
2122 }
2123
2124 // Search an array of spans for a span matching the given marker.
2125 function getMarkedSpanFor(spans, marker) {
2126 if (spans) { for (var i = 0; i < spans.length; ++i) {
2127 var span = spans[i];
2128 if (span.marker == marker) { return span }
2129 } }
2130 }
2131 // Remove a span from an array, returning undefined if no spans are
2132 // left (we don't store arrays for lines without spans).
2133 function removeMarkedSpan(spans, span) {
2134 var r;
2135 for (var i = 0; i < spans.length; ++i)
2136 { if (spans[i] != span) { (r || (r = [])).push(spans[i]); } }
2137 return r
2138 }
2139 // Add a span to a line.
2140 function addMarkedSpan(line, span) {
2141 line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
2142 span.marker.attachLine(line);
2143 }
2144
2145 // Used for the algorithm that adjusts markers for a change in the
2146 // document. These functions cut an array of spans at a given
2147 // character position, returning an array of remaining chunks (or
2148 // undefined if nothing remains).
2149 function markedSpansBefore(old, startCh, isInsert) {
2150 var nw;
2151 if (old) { for (var i = 0; i < old.length; ++i) {
2152 var span = old[i], marker = span.marker;
2153 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
2154 if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) {
2155 var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh)
2156 ;(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to));
2157 }
2158 } }
2159 return nw
2160 }
2161 function markedSpansAfter(old, endCh, isInsert) {
2162 var nw;
2163 if (old) { for (var i = 0; i < old.length; ++i) {
2164 var span = old[i], marker = span.marker;
2165 var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
2166 if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) {
2167 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh)
2168 ;(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh,
2169 span.to == null ? null : span.to - endCh));
2170 }
2171 } }
2172 return nw
2173 }
2174
2175 // Given a change object, compute the new set of marker spans that
2176 // cover the line in which the change took place. Removes spans
2177 // entirely within the change, reconnects spans belonging to the
2178 // same marker that appear on both sides of the change, and cuts off
2179 // spans partially within the change. Returns an array of span
2180 // arrays with one element for each line in (after) the change.
2181 function stretchSpansOverChange(doc, change) {
2182 if (change.full) { return null }
2183 var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans;
2184 var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans;
2185 if (!oldFirst && !oldLast) { return null }
2186
2187 var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0;
2188 // Get the spans that 'stick out' on both sides
2189 var first = markedSpansBefore(oldFirst, startCh, isInsert);
2190 var last = markedSpansAfter(oldLast, endCh, isInsert);
2191
2192 // Next, merge those two ends
2193 var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0);
2194 if (first) {
2195 // Fix up .to properties of first
2196 for (var i = 0; i < first.length; ++i) {
2197 var span = first[i];
2198 if (span.to == null) {
2199 var found = getMarkedSpanFor(last, span.marker);
2200 if (!found) { span.to = startCh; }
2201 else if (sameLine) { span.to = found.to == null ? null : found.to + offset; }
2202 }
2203 }
2204 }
2205 if (last) {
2206 // Fix up .from in last (or move them into first in case of sameLine)
2207 for (var i$1 = 0; i$1 < last.length; ++i$1) {
2208 var span$1 = last[i$1];
2209 if (span$1.to != null) { span$1.to += offset; }
2210 if (span$1.from == null) {
2211 var found$1 = getMarkedSpanFor(first, span$1.marker);
2212 if (!found$1) {
2213 span$1.from = offset;
2214 if (sameLine) { (first || (first = [])).push(span$1); }
2215 }
2216 } else {
2217 span$1.from += offset;
2218 if (sameLine) { (first || (first = [])).push(span$1); }
2219 }
2220 }
2221 }
2222 // Make sure we didn't create any zero-length spans
2223 if (first) { first = clearEmptySpans(first); }
2224 if (last && last != first) { last = clearEmptySpans(last); }
2225
2226 var newMarkers = [first];
2227 if (!sameLine) {
2228 // Fill gap with whole-line-spans
2229 var gap = change.text.length - 2, gapMarkers;
2230 if (gap > 0 && first)
2231 { for (var i$2 = 0; i$2 < first.length; ++i$2)
2232 { if (first[i$2].to == null)
2233 { (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i$2].marker, null, null)); } } }
2234 for (var i$3 = 0; i$3 < gap; ++i$3)
2235 { newMarkers.push(gapMarkers); }
2236 newMarkers.push(last);
2237 }
2238 return newMarkers
2239 }
2240
2241 // Remove spans that are empty and don't have a clearWhenEmpty
2242 // option of false.
2243 function clearEmptySpans(spans) {
2244 for (var i = 0; i < spans.length; ++i) {
2245 var span = spans[i];
2246 if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)
2247 { spans.splice(i--, 1); }
2248 }
2249 if (!spans.length) { return null }
2250 return spans
2251 }
2252
2253 // Used to 'clip' out readOnly ranges when making a change.
2254 function removeReadOnlyRanges(doc, from, to) {
2255 var markers = null;
2256 doc.iter(from.line, to.line + 1, function (line) {
2257 if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
2258 var mark = line.markedSpans[i].marker;
2259 if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
2260 { (markers || (markers = [])).push(mark); }
2261 } }
2262 });
2263 if (!markers) { return null }
2264 var parts = [{from: from, to: to}];
2265 for (var i = 0; i < markers.length; ++i) {
2266 var mk = markers[i], m = mk.find(0);
2267 for (var j = 0; j < parts.length; ++j) {
2268 var p = parts[j];
2269 if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) { continue }
2270 var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to);
2271 if (dfrom < 0 || !mk.inclusiveLeft && !dfrom)
2272 { newParts.push({from: p.from, to: m.from}); }
2273 if (dto > 0 || !mk.inclusiveRight && !dto)
2274 { newParts.push({from: m.to, to: p.to}); }
2275 parts.splice.apply(parts, newParts);
2276 j += newParts.length - 3;
2277 }
2278 }
2279 return parts
2280 }
2281
2282 // Connect or disconnect spans from a line.
2283 function detachMarkedSpans(line) {
2284 var spans = line.markedSpans;
2285 if (!spans) { return }
2286 for (var i = 0; i < spans.length; ++i)
2287 { spans[i].marker.detachLine(line); }
2288 line.markedSpans = null;
2289 }
2290 function attachMarkedSpans(line, spans) {
2291 if (!spans) { return }
2292 for (var i = 0; i < spans.length; ++i)
2293 { spans[i].marker.attachLine(line); }
2294 line.markedSpans = spans;
2295 }
2296
2297 // Helpers used when computing which overlapping collapsed span
2298 // counts as the larger one.
2299 function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 }
2300 function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 }
2301
2302 // Returns a number indicating which of two overlapping collapsed
2303 // spans is larger (and thus includes the other). Falls back to
2304 // comparing ids when the spans cover exactly the same range.
2305 function compareCollapsedMarkers(a, b) {
2306 var lenDiff = a.lines.length - b.lines.length;
2307 if (lenDiff != 0) { return lenDiff }
2308 var aPos = a.find(), bPos = b.find();
2309 var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b);
2310 if (fromCmp) { return -fromCmp }
2311 var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b);
2312 if (toCmp) { return toCmp }
2313 return b.id - a.id
2314 }
2315
2316 // Find out whether a line ends or starts in a collapsed span. If
2317 // so, return the marker for that span.
2318 function collapsedSpanAtSide(line, start) {
2319 var sps = sawCollapsedSpans && line.markedSpans, found;
2320 if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
2321 sp = sps[i];
2322 if (sp.marker.collapsed && (start ? sp.from : sp.to) == null &&
2323 (!found || compareCollapsedMarkers(found, sp.marker) < 0))
2324 { found = sp.marker; }
2325 } }
2326 return found
2327 }
2328 function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) }
2329 function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) }
2330
2331 function collapsedSpanAround(line, ch) {
2332 var sps = sawCollapsedSpans && line.markedSpans, found;
2333 if (sps) { for (var i = 0; i < sps.length; ++i) {
2334 var sp = sps[i];
2335 if (sp.marker.collapsed && (sp.from == null || sp.from < ch) && (sp.to == null || sp.to > ch) &&
2336 (!found || compareCollapsedMarkers(found, sp.marker) < 0)) { found = sp.marker; }
2337 } }
2338 return found
2339 }
2340
2341 // Test whether there exists a collapsed span that partially
2342 // overlaps (covers the start or end, but not both) of a new span.
2343 // Such overlap is not allowed.
2344 function conflictingCollapsedRange(doc, lineNo$$1, from, to, marker) {
2345 var line = getLine(doc, lineNo$$1);
2346 var sps = sawCollapsedSpans && line.markedSpans;
2347 if (sps) { for (var i = 0; i < sps.length; ++i) {
2348 var sp = sps[i];
2349 if (!sp.marker.collapsed) { continue }
2350 var found = sp.marker.find(0);
2351 var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker);
2352 var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker);
2353 if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue }
2354 if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) ||
2355 fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0))
2356 { return true }
2357 } }
2358 }
2359
2360 // A visual line is a line as drawn on the screen. Folding, for
2361 // example, can cause multiple logical lines to appear on the same
2362 // visual line. This finds the start of the visual line that the
2363 // given line is part of (usually that is the line itself).
2364 function visualLine(line) {
2365 var merged;
2366 while (merged = collapsedSpanAtStart(line))
2367 { line = merged.find(-1, true).line; }
2368 return line
2369 }
2370
2371 function visualLineEnd(line) {
2372 var merged;
2373 while (merged = collapsedSpanAtEnd(line))
2374 { line = merged.find(1, true).line; }
2375 return line
2376 }
2377
2378 // Returns an array of logical lines that continue the visual line
2379 // started by the argument, or undefined if there are no such lines.
2380 function visualLineContinued(line) {
2381 var merged, lines;
2382 while (merged = collapsedSpanAtEnd(line)) {
2383 line = merged.find(1, true).line
2384 ;(lines || (lines = [])).push(line);
2385 }
2386 return lines
2387 }
2388
2389 // Get the line number of the start of the visual line that the
2390 // given line number is part of.
2391 function visualLineNo(doc, lineN) {
2392 var line = getLine(doc, lineN), vis = visualLine(line);
2393 if (line == vis) { return lineN }
2394 return lineNo(vis)
2395 }
2396
2397 // Get the line number of the start of the next visual line after
2398 // the given line.
2399 function visualLineEndNo(doc, lineN) {
2400 if (lineN > doc.lastLine()) { return lineN }
2401 var line = getLine(doc, lineN), merged;
2402 if (!lineIsHidden(doc, line)) { return lineN }
2403 while (merged = collapsedSpanAtEnd(line))
2404 { line = merged.find(1, true).line; }
2405 return lineNo(line) + 1
2406 }
2407
2408 // Compute whether a line is hidden. Lines count as hidden when they
2409 // are part of a visual line that starts with another line, or when
2410 // they are entirely covered by collapsed, non-widget span.
2411 function lineIsHidden(doc, line) {
2412 var sps = sawCollapsedSpans && line.markedSpans;
2413 if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
2414 sp = sps[i];
2415 if (!sp.marker.collapsed) { continue }
2416 if (sp.from == null) { return true }
2417 if (sp.marker.widgetNode) { continue }
2418 if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
2419 { return true }
2420 } }
2421 }
2422 function lineIsHiddenInner(doc, line, span) {
2423 if (span.to == null) {
2424 var end = span.marker.find(1, true);
2425 return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker))
2426 }
2427 if (span.marker.inclusiveRight && span.to == line.text.length)
2428 { return true }
2429 for (var sp = (void 0), i = 0; i < line.markedSpans.length; ++i) {
2430 sp = line.markedSpans[i];
2431 if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to &&
2432 (sp.to == null || sp.to != span.from) &&
2433 (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
2434 lineIsHiddenInner(doc, line, sp)) { return true }
2435 }
2436 }
2437
2438 // Find the height above the given line.
2439 function heightAtLine(lineObj) {
2440 lineObj = visualLine(lineObj);
2441
2442 var h = 0, chunk = lineObj.parent;
2443 for (var i = 0; i < chunk.lines.length; ++i) {
2444 var line = chunk.lines[i];
2445 if (line == lineObj) { break }
2446 else { h += line.height; }
2447 }
2448 for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
2449 for (var i$1 = 0; i$1 < p.children.length; ++i$1) {
2450 var cur = p.children[i$1];
2451 if (cur == chunk) { break }
2452 else { h += cur.height; }
2453 }
2454 }
2455 return h
2456 }
2457
2458 // Compute the character length of a line, taking into account
2459 // collapsed ranges (see markText) that might hide parts, and join
2460 // other lines onto it.
2461 function lineLength(line) {
2462 if (line.height == 0) { return 0 }
2463 var len = line.text.length, merged, cur = line;
2464 while (merged = collapsedSpanAtStart(cur)) {
2465 var found = merged.find(0, true);
2466 cur = found.from.line;
2467 len += found.from.ch - found.to.ch;
2468 }
2469 cur = line;
2470 while (merged = collapsedSpanAtEnd(cur)) {
2471 var found$1 = merged.find(0, true);
2472 len -= cur.text.length - found$1.from.ch;
2473 cur = found$1.to.line;
2474 len += cur.text.length - found$1.to.ch;
2475 }
2476 return len
2477 }
2478
2479 // Find the longest line in the document.
2480 function findMaxLine(cm) {
2481 var d = cm.display, doc = cm.doc;
2482 d.maxLine = getLine(doc, doc.first);
2483 d.maxLineLength = lineLength(d.maxLine);
2484 d.maxLineChanged = true;
2485 doc.iter(function (line) {
2486 var len = lineLength(line);
2487 if (len > d.maxLineLength) {
2488 d.maxLineLength = len;
2489 d.maxLine = line;
2490 }
2491 });
2492 }
2493
2494 // BIDI HELPERS
2495
2496 function iterateBidiSections(order, from, to, f) {
2497 if (!order) { return f(from, to, "ltr", 0) }
2498 var found = false;
2499 for (var i = 0; i < order.length; ++i) {
2500 var part = order[i];
2501 if (part.from < to && part.to > from || from == to && part.to == from) {
2502 f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr", i);
2503 found = true;
2504 }
2505 }
2506 if (!found) { f(from, to, "ltr"); }
2507 }
2508
2509 var bidiOther = null;
2510 function getBidiPartAt(order, ch, sticky) {
2511 var found;
2512 bidiOther = null;
2513 for (var i = 0; i < order.length; ++i) {
2514 var cur = order[i];
2515 if (cur.from < ch && cur.to > ch) { return i }
2516 if (cur.to == ch) {
2517 if (cur.from != cur.to && sticky == "before") { found = i; }
2518 else { bidiOther = i; }
2519 }
2520 if (cur.from == ch) {
2521 if (cur.from != cur.to && sticky != "before") { found = i; }
2522 else { bidiOther = i; }
2523 }
2524 }
2525 return found != null ? found : bidiOther
2526 }
2527
2528 // Bidirectional ordering algorithm
2529 // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
2530 // that this (partially) implements.
2531
2532 // One-char codes used for character types:
2533 // L (L): Left-to-Right
2534 // R (R): Right-to-Left
2535 // r (AL): Right-to-Left Arabic
2536 // 1 (EN): European Number
2537 // + (ES): European Number Separator
2538 // % (ET): European Number Terminator
2539 // n (AN): Arabic Number
2540 // , (CS): Common Number Separator
2541 // m (NSM): Non-Spacing Mark
2542 // b (BN): Boundary Neutral
2543 // s (B): Paragraph Separator
2544 // t (S): Segment Separator
2545 // w (WS): Whitespace
2546 // N (ON): Other Neutrals
2547
2548 // Returns null if characters are ordered as they appear
2549 // (left-to-right), or an array of sections ({from, to, level}
2550 // objects) in the order in which they occur visually.
2551 var bidiOrdering = (function() {
2552 // Character types for codepoints 0 to 0xff
2553 var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN";
2554 // Character types for codepoints 0x600 to 0x6f9
2555 var arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111";
2556 function charType(code) {
2557 if (code <= 0xf7) { return lowTypes.charAt(code) }
2558 else if (0x590 <= code && code <= 0x5f4) { return "R" }
2559 else if (0x600 <= code && code <= 0x6f9) { return arabicTypes.charAt(code - 0x600) }
2560 else if (0x6ee <= code && code <= 0x8ac) { return "r" }
2561 else if (0x2000 <= code && code <= 0x200b) { return "w" }
2562 else if (code == 0x200c) { return "b" }
2563 else { return "L" }
2564 }
2565
2566 var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
2567 var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
2568
2569 function BidiSpan(level, from, to) {
2570 this.level = level;
2571 this.from = from; this.to = to;
2572 }
2573
2574 return function(str, direction) {
2575 var outerType = direction == "ltr" ? "L" : "R";
2576
2577 if (str.length == 0 || direction == "ltr" && !bidiRE.test(str)) { return false }
2578 var len = str.length, types = [];
2579 for (var i = 0; i < len; ++i)
2580 { types.push(charType(str.charCodeAt(i))); }
2581
2582 // W1. Examine each non-spacing mark (NSM) in the level run, and
2583 // change the type of the NSM to the type of the previous
2584 // character. If the NSM is at the start of the level run, it will
2585 // get the type of sor.
2586 for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) {
2587 var type = types[i$1];
2588 if (type == "m") { types[i$1] = prev; }
2589 else { prev = type; }
2590 }
2591
2592 // W2. Search backwards from each instance of a European number
2593 // until the first strong type (R, L, AL, or sor) is found. If an
2594 // AL is found, change the type of the European number to Arabic
2595 // number.
2596 // W3. Change all ALs to R.
2597 for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) {
2598 var type$1 = types[i$2];
2599 if (type$1 == "1" && cur == "r") { types[i$2] = "n"; }
2600 else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == "r") { types[i$2] = "R"; } }
2601 }
2602
2603 // W4. A single European separator between two European numbers
2604 // changes to a European number. A single common separator between
2605 // two numbers of the same type changes to that type.
2606 for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) {
2607 var type$2 = types[i$3];
2608 if (type$2 == "+" && prev$1 == "1" && types[i$3+1] == "1") { types[i$3] = "1"; }
2609 else if (type$2 == "," && prev$1 == types[i$3+1] &&
2610 (prev$1 == "1" || prev$1 == "n")) { types[i$3] = prev$1; }
2611 prev$1 = type$2;
2612 }
2613
2614 // W5. A sequence of European terminators adjacent to European
2615 // numbers changes to all European numbers.
2616 // W6. Otherwise, separators and terminators change to Other
2617 // Neutral.
2618 for (var i$4 = 0; i$4 < len; ++i$4) {
2619 var type$3 = types[i$4];
2620 if (type$3 == ",") { types[i$4] = "N"; }
2621 else if (type$3 == "%") {
2622 var end = (void 0);
2623 for (end = i$4 + 1; end < len && types[end] == "%"; ++end) {}
2624 var replace = (i$4 && types[i$4-1] == "!") || (end < len && types[end] == "1") ? "1" : "N";
2625 for (var j = i$4; j < end; ++j) { types[j] = replace; }
2626 i$4 = end - 1;
2627 }
2628 }
2629
2630 // W7. Search backwards from each instance of a European number
2631 // until the first strong type (R, L, or sor) is found. If an L is
2632 // found, then change the type of the European number to L.
2633 for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) {
2634 var type$4 = types[i$5];
2635 if (cur$1 == "L" && type$4 == "1") { types[i$5] = "L"; }
2636 else if (isStrong.test(type$4)) { cur$1 = type$4; }
2637 }
2638
2639 // N1. A sequence of neutrals takes the direction of the
2640 // surrounding strong text if the text on both sides has the same
2641 // direction. European and Arabic numbers act as if they were R in
2642 // terms of their influence on neutrals. Start-of-level-run (sor)
2643 // and end-of-level-run (eor) are used at level run boundaries.
2644 // N2. Any remaining neutrals take the embedding direction.
2645 for (var i$6 = 0; i$6 < len; ++i$6) {
2646 if (isNeutral.test(types[i$6])) {
2647 var end$1 = (void 0);
2648 for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {}
2649 var before = (i$6 ? types[i$6-1] : outerType) == "L";
2650 var after = (end$1 < len ? types[end$1] : outerType) == "L";
2651 var replace$1 = before == after ? (before ? "L" : "R") : outerType;
2652 for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1; }
2653 i$6 = end$1 - 1;
2654 }
2655 }
2656
2657 // Here we depart from the documented algorithm, in order to avoid
2658 // building up an actual levels array. Since there are only three
2659 // levels (0, 1, 2) in an implementation that doesn't take
2660 // explicit embedding into account, we can build up the order on
2661 // the fly, without following the level-based algorithm.
2662 var order = [], m;
2663 for (var i$7 = 0; i$7 < len;) {
2664 if (countsAsLeft.test(types[i$7])) {
2665 var start = i$7;
2666 for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {}
2667 order.push(new BidiSpan(0, start, i$7));
2668 } else {
2669 var pos = i$7, at = order.length;
2670 for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {}
2671 for (var j$2 = pos; j$2 < i$7;) {
2672 if (countsAsNum.test(types[j$2])) {
2673 if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)); }
2674 var nstart = j$2;
2675 for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {}
2676 order.splice(at, 0, new BidiSpan(2, nstart, j$2));
2677 pos = j$2;
2678 } else { ++j$2; }
2679 }
2680 if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)); }
2681 }
2682 }
2683 if (direction == "ltr") {
2684 if (order[0].level == 1 && (m = str.match(/^\s+/))) {
2685 order[0].from = m[0].length;
2686 order.unshift(new BidiSpan(0, 0, m[0].length));
2687 }
2688 if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
2689 lst(order).to -= m[0].length;
2690 order.push(new BidiSpan(0, len - m[0].length, len));
2691 }
2692 }
2693
2694 return direction == "rtl" ? order.reverse() : order
2695 }
2696 })();
2697
2698 // Get the bidi ordering for the given line (and cache it). Returns
2699 // false for lines that are fully left-to-right, and an array of
2700 // BidiSpan objects otherwise.
2701 function getOrder(line, direction) {
2702 var order = line.order;
2703 if (order == null) { order = line.order = bidiOrdering(line.text, direction); }
2704 return order
2705 }
2706
2707 // EVENT HANDLING
2708
2709 // Lightweight event framework. on/off also work on DOM nodes,
2710 // registering native DOM handlers.
2711
2712 var noHandlers = [];
2713
2714 var on = function(emitter, type, f) {
2715 if (emitter.addEventListener) {
2716 emitter.addEventListener(type, f, false);
2717 } else if (emitter.attachEvent) {
2718 emitter.attachEvent("on" + type, f);
2719 } else {
2720 var map$$1 = emitter._handlers || (emitter._handlers = {});
2721 map$$1[type] = (map$$1[type] || noHandlers).concat(f);
2722 }
2723 };
2724
2725 function getHandlers(emitter, type) {
2726 return emitter._handlers && emitter._handlers[type] || noHandlers
2727 }
2728
2729 function off(emitter, type, f) {
2730 if (emitter.removeEventListener) {
2731 emitter.removeEventListener(type, f, false);
2732 } else if (emitter.detachEvent) {
2733 emitter.detachEvent("on" + type, f);
2734 } else {
2735 var map$$1 = emitter._handlers, arr = map$$1 && map$$1[type];
2736 if (arr) {
2737 var index = indexOf(arr, f);
2738 if (index > -1)
2739 { map$$1[type] = arr.slice(0, index).concat(arr.slice(index + 1)); }
2740 }
2741 }
2742 }
2743
2744 function signal(emitter, type /*, values...*/) {
2745 var handlers = getHandlers(emitter, type);
2746 if (!handlers.length) { return }
2747 var args = Array.prototype.slice.call(arguments, 2);
2748 for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args); }
2749 }
2750
2751 // The DOM events that CodeMirror handles can be overridden by
2752 // registering a (non-DOM) handler on the editor for the event name,
2753 // and preventDefault-ing the event in that handler.
2754 function signalDOMEvent(cm, e, override) {
2755 if (typeof e == "string")
2756 { e = {type: e, preventDefault: function() { this.defaultPrevented = true; }}; }
2757 signal(cm, override || e.type, cm, e);
2758 return e_defaultPrevented(e) || e.codemirrorIgnore
2759 }
2760
2761 function signalCursorActivity(cm) {
2762 var arr = cm._handlers && cm._handlers.cursorActivity;
2763 if (!arr) { return }
2764 var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []);
2765 for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1)
2766 { set.push(arr[i]); } }
2767 }
2768
2769 function hasHandler(emitter, type) {
2770 return getHandlers(emitter, type).length > 0
2771 }
2772
2773 // Add on and off methods to a constructor's prototype, to make
2774 // registering events on such objects more convenient.
2775 function eventMixin(ctor) {
2776 ctor.prototype.on = function(type, f) {on(this, type, f);};
2777 ctor.prototype.off = function(type, f) {off(this, type, f);};
2778 }
2779
2780 // Due to the fact that we still support jurassic IE versions, some
2781 // compatibility wrappers are needed.
2782
2783 function e_preventDefault(e) {
2784 if (e.preventDefault) { e.preventDefault(); }
2785 else { e.returnValue = false; }
2786 }
2787 function e_stopPropagation(e) {
2788 if (e.stopPropagation) { e.stopPropagation(); }
2789 else { e.cancelBubble = true; }
2790 }
2791 function e_defaultPrevented(e) {
2792 return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false
2793 }
2794 function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
2795
2796 function e_target(e) {return e.target || e.srcElement}
2797 function e_button(e) {
2798 var b = e.which;
2799 if (b == null) {
2800 if (e.button & 1) { b = 1; }
2801 else if (e.button & 2) { b = 3; }
2802 else if (e.button & 4) { b = 2; }
2803 }
2804 if (mac && e.ctrlKey && b == 1) { b = 3; }
2805 return b
2806 }
2807
2808 // Detect drag-and-drop
2809 var dragAndDrop = function() {
2810 // There is *some* kind of drag-and-drop support in IE6-8, but I
2811 // couldn't get it to work yet.
2812 if (ie && ie_version < 9) { return false }
2813 var div = elt('div');
2814 return "draggable" in div || "dragDrop" in div
2815 }();
2816
2817 var zwspSupported;
2818 function zeroWidthElement(measure) {
2819 if (zwspSupported == null) {
2820 var test = elt("span", "\u200b");
2821 removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]));
2822 if (measure.firstChild.offsetHeight != 0)
2823 { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8); }
2824 }
2825 var node = zwspSupported ? elt("span", "\u200b") :
2826 elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
2827 node.setAttribute("cm-text", "");
2828 return node
2829 }
2830
2831 // Feature-detect IE's crummy client rect reporting for bidi text
2832 var badBidiRects;
2833 function hasBadBidiRects(measure) {
2834 if (badBidiRects != null) { return badBidiRects }
2835 var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA"));
2836 var r0 = range(txt, 0, 1).getBoundingClientRect();
2837 var r1 = range(txt, 1, 2).getBoundingClientRect();
2838 removeChildren(measure);
2839 if (!r0 || r0.left == r0.right) { return false } // Safari returns null in some cases (#2780)
2840 return badBidiRects = (r1.right - r0.right < 3)
2841 }
2842
2843 // See if "".split is the broken IE version, if so, provide an
2844 // alternative way to split lines.
2845 var splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function (string) {
2846 var pos = 0, result = [], l = string.length;
2847 while (pos <= l) {
2848 var nl = string.indexOf("\n", pos);
2849 if (nl == -1) { nl = string.length; }
2850 var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
2851 var rt = line.indexOf("\r");
2852 if (rt != -1) {
2853 result.push(line.slice(0, rt));
2854 pos += rt + 1;
2855 } else {
2856 result.push(line);
2857 pos = nl + 1;
2858 }
2859 }
2860 return result
2861 } : function (string) { return string.split(/\r\n?|\n/); };
2862
2863 var hasSelection = window.getSelection ? function (te) {
2864 try { return te.selectionStart != te.selectionEnd }
2865 catch(e) { return false }
2866 } : function (te) {
2867 var range$$1;
2868 try {range$$1 = te.ownerDocument.selection.createRange();}
2869 catch(e) {}
2870 if (!range$$1 || range$$1.parentElement() != te) { return false }
2871 return range$$1.compareEndPoints("StartToEnd", range$$1) != 0
2872 };
2873
2874 var hasCopyEvent = (function () {
2875 var e = elt("div");
2876 if ("oncopy" in e) { return true }
2877 e.setAttribute("oncopy", "return;");
2878 return typeof e.oncopy == "function"
2879 })();
2880
2881 var badZoomedRects = null;
2882 function hasBadZoomedRects(measure) {
2883 if (badZoomedRects != null) { return badZoomedRects }
2884 var node = removeChildrenAndAdd(measure, elt("span", "x"));
2885 var normal = node.getBoundingClientRect();
2886 var fromRange = range(node, 0, 1).getBoundingClientRect();
2887 return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1
2888 }
2889
2890 // Known modes, by name and by MIME
2891 var modes = {}, mimeModes = {};
2892
2893 // Extra arguments are stored as the mode's dependencies, which is
2894 // used by (legacy) mechanisms like loadmode.js to automatically
2895 // load a mode. (Preferred mechanism is the require/define calls.)
2896 function defineMode(name, mode) {
2897 if (arguments.length > 2)
2898 { mode.dependencies = Array.prototype.slice.call(arguments, 2); }
2899 modes[name] = mode;
2900 }
2901
2902 function defineMIME(mime, spec) {
2903 mimeModes[mime] = spec;
2904 }
2905
2906 // Given a MIME type, a {name, ...options} config object, or a name
2907 // string, return a mode config object.
2908 function resolveMode(spec) {
2909 if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
2910 spec = mimeModes[spec];
2911 } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
2912 var found = mimeModes[spec.name];
2913 if (typeof found == "string") { found = {name: found}; }
2914 spec = createObj(found, spec);
2915 spec.name = found.name;
2916 } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
2917 return resolveMode("application/xml")
2918 } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) {
2919 return resolveMode("application/json")
2920 }
2921 if (typeof spec == "string") { return {name: spec} }
2922 else { return spec || {name: "null"} }
2923 }
2924
2925 // Given a mode spec (anything that resolveMode accepts), find and
2926 // initialize an actual mode object.
2927 function getMode(options, spec) {
2928 spec = resolveMode(spec);
2929 var mfactory = modes[spec.name];
2930 if (!mfactory) { return getMode(options, "text/plain") }
2931 var modeObj = mfactory(options, spec);
2932 if (modeExtensions.hasOwnProperty(spec.name)) {
2933 var exts = modeExtensions[spec.name];
2934 for (var prop in exts) {
2935 if (!exts.hasOwnProperty(prop)) { continue }
2936 if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop]; }
2937 modeObj[prop] = exts[prop];
2938 }
2939 }
2940 modeObj.name = spec.name;
2941 if (spec.helperType) { modeObj.helperType = spec.helperType; }
2942 if (spec.modeProps) { for (var prop$1 in spec.modeProps)
2943 { modeObj[prop$1] = spec.modeProps[prop$1]; } }
2944
2945 return modeObj
2946 }
2947
2948 // This can be used to attach properties to mode objects from
2949 // outside the actual mode definition.
2950 var modeExtensions = {};
2951 function extendMode(mode, properties) {
2952 var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
2953 copyObj(properties, exts);
2954 }
2955
2956 function copyState(mode, state) {
2957 if (state === true) { return state }
2958 if (mode.copyState) { return mode.copyState(state) }
2959 var nstate = {};
2960 for (var n in state) {
2961 var val = state[n];
2962 if (val instanceof Array) { val = val.concat([]); }
2963 nstate[n] = val;
2964 }
2965 return nstate
2966 }
2967
2968 // Given a mode and a state (for that mode), find the inner mode and
2969 // state at the position that the state refers to.
2970 function innerMode(mode, state) {
2971 var info;
2972 while (mode.innerMode) {
2973 info = mode.innerMode(state);
2974 if (!info || info.mode == mode) { break }
2975 state = info.state;
2976 mode = info.mode;
2977 }
2978 return info || {mode: mode, state: state}
2979 }
2980
2981 function startState(mode, a1, a2) {
2982 return mode.startState ? mode.startState(a1, a2) : true
2983 }
2984
2985 // STRING STREAM
2986
2987 // Fed to the mode parsers, provides helper functions to make
2988 // parsers more succinct.
2989
2990 var StringStream = function(string, tabSize, lineOracle) {
2991 this.pos = this.start = 0;
2992 this.string = string;
2993 this.tabSize = tabSize || 8;
2994 this.lastColumnPos = this.lastColumnValue = 0;
2995 this.lineStart = 0;
2996 this.lineOracle = lineOracle;
2997 };
2998
2999 StringStream.prototype.eol = function () {return this.pos >= this.string.length};
3000 StringStream.prototype.sol = function () {return this.pos == this.lineStart};
3001 StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined};
3002 StringStream.prototype.next = function () {
3003 if (this.pos < this.string.length)
3004 { return this.string.charAt(this.pos++) }
3005 };
3006 StringStream.prototype.eat = function (match) {
3007 var ch = this.string.charAt(this.pos);
3008 var ok;
3009 if (typeof match == "string") { ok = ch == match; }
3010 else { ok = ch && (match.test ? match.test(ch) : match(ch)); }
3011 if (ok) {++this.pos; return ch}
3012 };
3013 StringStream.prototype.eatWhile = function (match) {
3014 var start = this.pos;
3015 while (this.eat(match)){}
3016 return this.pos > start
3017 };
3018 StringStream.prototype.eatSpace = function () {
3019 var this$1 = this;
3020
3021 var start = this.pos;
3022 while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this$1.pos; }
3023 return this.pos > start
3024 };
3025 StringStream.prototype.skipToEnd = function () {this.pos = this.string.length;};
3026 StringStream.prototype.skipTo = function (ch) {
3027 var found = this.string.indexOf(ch, this.pos);
3028 if (found > -1) {this.pos = found; return true}
3029 };
3030 StringStream.prototype.backUp = function (n) {this.pos -= n;};
3031 StringStream.prototype.column = function () {
3032 if (this.lastColumnPos < this.start) {
3033 this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
3034 this.lastColumnPos = this.start;
3035 }
3036 return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
3037 };
3038 StringStream.prototype.indentation = function () {
3039 return countColumn(this.string, null, this.tabSize) -
3040 (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
3041 };
3042 StringStream.prototype.match = function (pattern, consume, caseInsensitive) {
3043 if (typeof pattern == "string") {
3044 var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; };
3045 var substr = this.string.substr(this.pos, pattern.length);
3046 if (cased(substr) == cased(pattern)) {
3047 if (consume !== false) { this.pos += pattern.length; }
3048 return true
3049 }
3050 } else {
3051 var match = this.string.slice(this.pos).match(pattern);
3052 if (match && match.index > 0) { return null }
3053 if (match && consume !== false) { this.pos += match[0].length; }
3054 return match
3055 }
3056 };
3057 StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)};
3058 StringStream.prototype.hideFirstChars = function (n, inner) {
3059 this.lineStart += n;
3060 try { return inner() }
3061 finally { this.lineStart -= n; }
3062 };
3063 StringStream.prototype.lookAhead = function (n) {
3064 var oracle = this.lineOracle;
3065 return oracle && oracle.lookAhead(n)
3066 };
3067 StringStream.prototype.baseToken = function () {
3068 var oracle = this.lineOracle;
3069 return oracle && oracle.baseToken(this.pos)
3070 };
3071
3072 var SavedContext = function(state, lookAhead) {
3073 this.state = state;
3074 this.lookAhead = lookAhead;
3075 };
3076
3077 var Context = function(doc, state, line, lookAhead) {
3078 this.state = state;
3079 this.doc = doc;
3080 this.line = line;
3081 this.maxLookAhead = lookAhead || 0;
3082 this.baseTokens = null;
3083 this.baseTokenPos = 1;
3084 };
3085
3086 Context.prototype.lookAhead = function (n) {
3087 var line = this.doc.getLine(this.line + n);
3088 if (line != null && n > this.maxLookAhead) { this.maxLookAhead = n; }
3089 return line
3090 };
3091
3092 Context.prototype.baseToken = function (n) {
3093 var this$1 = this;
3094
3095 if (!this.baseTokens) { return null }
3096 while (this.baseTokens[this.baseTokenPos] <= n)
3097 { this$1.baseTokenPos += 2; }
3098 var type = this.baseTokens[this.baseTokenPos + 1];
3099 return {type: type && type.replace(/( |^)overlay .*/, ""),
3100 size: this.baseTokens[this.baseTokenPos] - n}
3101 };
3102
3103 Context.prototype.nextLine = function () {
3104 this.line++;
3105 if (this.maxLookAhead > 0) { this.maxLookAhead--; }
3106 };
3107
3108 Context.fromSaved = function (doc, saved, line) {
3109 if (saved instanceof SavedContext)
3110 { return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead) }
3111 else
3112 { return new Context(doc, copyState(doc.mode, saved), line) }
3113 };
3114
3115 Context.prototype.save = function (copy) {
3116 var state = copy !== false ? copyState(this.doc.mode, this.state) : this.state;
3117 return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state
3118 };
3119
3120
3121 // Compute a style array (an array starting with a mode generation
3122 // -- for invalidation -- followed by pairs of end positions and
3123 // style strings), which is used to highlight the tokens on the
3124 // line.
3125 function highlightLine(cm, line, context, forceToEnd) {
3126 // A styles array always starts with a number identifying the
3127 // mode/overlays that it is based on (for easy invalidation).
3128 var st = [cm.state.modeGen], lineClasses = {};
3129 // Compute the base array of styles
3130 runMode(cm, line.text, cm.doc.mode, context, function (end, style) { return st.push(end, style); },
3131 lineClasses, forceToEnd);
3132 var state = context.state;
3133
3134 // Run overlays, adjust style array.
3135 var loop = function ( o ) {
3136 context.baseTokens = st;
3137 var overlay = cm.state.overlays[o], i = 1, at = 0;
3138 context.state = true;
3139 runMode(cm, line.text, overlay.mode, context, function (end, style) {
3140 var start = i;
3141 // Ensure there's a token end at the current position, and that i points at it
3142 while (at < end) {
3143 var i_end = st[i];
3144 if (i_end > end)
3145 { st.splice(i, 1, end, st[i+1], i_end); }
3146 i += 2;
3147 at = Math.min(end, i_end);
3148 }
3149 if (!style) { return }
3150 if (overlay.opaque) {
3151 st.splice(start, i - start, end, "overlay " + style);
3152 i = start + 2;
3153 } else {
3154 for (; start < i; start += 2) {
3155 var cur = st[start+1];
3156 st[start+1] = (cur ? cur + " " : "") + "overlay " + style;
3157 }
3158 }
3159 }, lineClasses);
3160 context.state = state;
3161 context.baseTokens = null;
3162 context.baseTokenPos = 1;
3163 };
3164
3165 for (var o = 0; o < cm.state.overlays.length; ++o) loop( o );
3166
3167 return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null}
3168 }
3169
3170 function getLineStyles(cm, line, updateFrontier) {
3171 if (!line.styles || line.styles[0] != cm.state.modeGen) {
3172 var context = getContextBefore(cm, lineNo(line));
3173 var resetState = line.text.length > cm.options.maxHighlightLength && copyState(cm.doc.mode, context.state);
3174 var result = highlightLine(cm, line, context);
3175 if (resetState) { context.state = resetState; }
3176 line.stateAfter = context.save(!resetState);
3177 line.styles = result.styles;
3178 if (result.classes) { line.styleClasses = result.classes; }
3179 else if (line.styleClasses) { line.styleClasses = null; }
3180 if (updateFrontier === cm.doc.highlightFrontier)
3181 { cm.doc.modeFrontier = Math.max(cm.doc.modeFrontier, ++cm.doc.highlightFrontier); }
3182 }
3183 return line.styles
3184 }
3185
3186 function getContextBefore(cm, n, precise) {
3187 var doc = cm.doc, display = cm.display;
3188 if (!doc.mode.startState) { return new Context(doc, true, n) }
3189 var start = findStartLine(cm, n, precise);
3190 var saved = start > doc.first && getLine(doc, start - 1).stateAfter;
3191 var context = saved ? Context.fromSaved(doc, saved, start) : new Context(doc, startState(doc.mode), start);
3192
3193 doc.iter(start, n, function (line) {
3194 processLine(cm, line.text, context);
3195 var pos = context.line;
3196 line.stateAfter = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo ? context.save() : null;
3197 context.nextLine();
3198 });
3199 if (precise) { doc.modeFrontier = context.line; }
3200 return context
3201 }
3202
3203 // Lightweight form of highlight -- proceed over this line and
3204 // update state, but don't save a style array. Used for lines that
3205 // aren't currently visible.
3206 function processLine(cm, text, context, startAt) {
3207 var mode = cm.doc.mode;
3208 var stream = new StringStream(text, cm.options.tabSize, context);
3209 stream.start = stream.pos = startAt || 0;
3210 if (text == "") { callBlankLine(mode, context.state); }
3211 while (!stream.eol()) {
3212 readToken(mode, stream, context.state);
3213 stream.start = stream.pos;
3214 }
3215 }
3216
3217 function callBlankLine(mode, state) {
3218 if (mode.blankLine) { return mode.blankLine(state) }
3219 if (!mode.innerMode) { return }
3220 var inner = innerMode(mode, state);
3221 if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) }
3222 }
3223
3224 function readToken(mode, stream, state, inner) {
3225 for (var i = 0; i < 10; i++) {
3226 if (inner) { inner[0] = innerMode(mode, state).mode; }
3227 var style = mode.token(stream, state);
3228 if (stream.pos > stream.start) { return style }
3229 }
3230 throw new Error("Mode " + mode.name + " failed to advance stream.")
3231 }
3232
3233 var Token = function(stream, type, state) {
3234 this.start = stream.start; this.end = stream.pos;
3235 this.string = stream.current();
3236 this.type = type || null;
3237 this.state = state;
3238 };
3239
3240 // Utility for getTokenAt and getLineTokens
3241 function takeToken(cm, pos, precise, asArray) {
3242 var doc = cm.doc, mode = doc.mode, style;
3243 pos = clipPos(doc, pos);
3244 var line = getLine(doc, pos.line), context = getContextBefore(cm, pos.line, precise);
3245 var stream = new StringStream(line.text, cm.options.tabSize, context), tokens;
3246 if (asArray) { tokens = []; }
3247 while ((asArray || stream.pos < pos.ch) && !stream.eol()) {
3248 stream.start = stream.pos;
3249 style = readToken(mode, stream, context.state);
3250 if (asArray) { tokens.push(new Token(stream, style, copyState(doc.mode, context.state))); }
3251 }
3252 return asArray ? tokens : new Token(stream, style, context.state)
3253 }
3254
3255 function extractLineClasses(type, output) {
3256 if (type) { for (;;) {
3257 var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/);
3258 if (!lineClass) { break }
3259 type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length);
3260 var prop = lineClass[1] ? "bgClass" : "textClass";
3261 if (output[prop] == null)
3262 { output[prop] = lineClass[2]; }
3263 else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop]))
3264 { output[prop] += " " + lineClass[2]; }
3265 } }
3266 return type
3267 }
3268
3269 // Run the given mode's parser over a line, calling f for each token.
3270 function runMode(cm, text, mode, context, f, lineClasses, forceToEnd) {
3271 var flattenSpans = mode.flattenSpans;
3272 if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans; }
3273 var curStart = 0, curStyle = null;
3274 var stream = new StringStream(text, cm.options.tabSize, context), style;
3275 var inner = cm.options.addModeClass && [null];
3276 if (text == "") { extractLineClasses(callBlankLine(mode, context.state), lineClasses); }
3277 while (!stream.eol()) {
3278 if (stream.pos > cm.options.maxHighlightLength) {
3279 flattenSpans = false;
3280 if (forceToEnd) { processLine(cm, text, context, stream.pos); }
3281 stream.pos = text.length;
3282 style = null;
3283 } else {
3284 style = extractLineClasses(readToken(mode, stream, context.state, inner), lineClasses);
3285 }
3286 if (inner) {
3287 var mName = inner[0].name;
3288 if (mName) { style = "m-" + (style ? mName + " " + style : mName); }
3289 }
3290 if (!flattenSpans || curStyle != style) {
3291 while (curStart < stream.start) {
3292 curStart = Math.min(stream.start, curStart + 5000);
3293 f(curStart, curStyle);
3294 }
3295 curStyle = style;
3296 }
3297 stream.start = stream.pos;
3298 }
3299 while (curStart < stream.pos) {
3300 // Webkit seems to refuse to render text nodes longer than 57444
3301 // characters, and returns inaccurate measurements in nodes
3302 // starting around 5000 chars.
3303 var pos = Math.min(stream.pos, curStart + 5000);
3304 f(pos, curStyle);
3305 curStart = pos;
3306 }
3307 }
3308
3309 // Finds the line to start with when starting a parse. Tries to
3310 // find a line with a stateAfter, so that it can start with a
3311 // valid state. If that fails, it returns the line with the
3312 // smallest indentation, which tends to need the least context to
3313 // parse correctly.
3314 function findStartLine(cm, n, precise) {
3315 var minindent, minline, doc = cm.doc;
3316 var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100);
3317 for (var search = n; search > lim; --search) {
3318 if (search <= doc.first) { return doc.first }
3319 var line = getLine(doc, search - 1), after = line.stateAfter;
3320 if (after && (!precise || search + (after instanceof SavedContext ? after.lookAhead : 0) <= doc.modeFrontier))
3321 { return search }
3322 var indented = countColumn(line.text, null, cm.options.tabSize);
3323 if (minline == null || minindent > indented) {
3324 minline = search - 1;
3325 minindent = indented;
3326 }
3327 }
3328 return minline
3329 }
3330
3331 function retreatFrontier(doc, n) {
3332 doc.modeFrontier = Math.min(doc.modeFrontier, n);
3333 if (doc.highlightFrontier < n - 10) { return }
3334 var start = doc.first;
3335 for (var line = n - 1; line > start; line--) {
3336 var saved = getLine(doc, line).stateAfter;
3337 // change is on 3
3338 // state on line 1 looked ahead 2 -- so saw 3
3339 // test 1 + 2 < 3 should cover this
3340 if (saved && (!(saved instanceof SavedContext) || line + saved.lookAhead < n)) {
3341 start = line + 1;
3342 break
3343 }
3344 }
3345 doc.highlightFrontier = Math.min(doc.highlightFrontier, start);
3346 }
3347
3348 // LINE DATA STRUCTURE
3349
3350 // Line objects. These hold state related to a line, including
3351 // highlighting info (the styles array).
3352 var Line = function(text, markedSpans, estimateHeight) {
3353 this.text = text;
3354 attachMarkedSpans(this, markedSpans);
3355 this.height = estimateHeight ? estimateHeight(this) : 1;
3356 };
3357
3358 Line.prototype.lineNo = function () { return lineNo(this) };
3359 eventMixin(Line);
3360
3361 // Change the content (text, markers) of a line. Automatically
3362 // invalidates cached information and tries to re-estimate the
3363 // line's height.
3364 function updateLine(line, text, markedSpans, estimateHeight) {
3365 line.text = text;
3366 if (line.stateAfter) { line.stateAfter = null; }
3367 if (line.styles) { line.styles = null; }
3368 if (line.order != null) { line.order = null; }
3369 detachMarkedSpans(line);
3370 attachMarkedSpans(line, markedSpans);
3371 var estHeight = estimateHeight ? estimateHeight(line) : 1;
3372 if (estHeight != line.height) { updateLineHeight(line, estHeight); }
3373 }
3374
3375 // Detach a line from the document tree and its markers.
3376 function cleanUpLine(line) {
3377 line.parent = null;
3378 detachMarkedSpans(line);
3379 }
3380
3381 // Convert a style as returned by a mode (either null, or a string
3382 // containing one or more styles) to a CSS style. This is cached,
3383 // and also looks for line-wide styles.
3384 var styleToClassCache = {}, styleToClassCacheWithMode = {};
3385 function interpretTokenStyle(style, options) {
3386 if (!style || /^\s*$/.test(style)) { return null }
3387 var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache;
3388 return cache[style] ||
3389 (cache[style] = style.replace(/\S+/g, "cm-$&"))
3390 }
3391
3392 // Render the DOM representation of the text of a line. Also builds
3393 // up a 'line map', which points at the DOM nodes that represent
3394 // specific stretches of text, and is used by the measuring code.
3395 // The returned object contains the DOM node, this map, and
3396 // information about line-wide styles that were set by the mode.
3397 function buildLineContent(cm, lineView) {
3398 // The padding-right forces the element to have a 'border', which
3399 // is needed on Webkit to be able to get line-level bounding
3400 // rectangles for it (in measureChar).
3401 var content = eltP("span", null, null, webkit ? "padding-right: .1px" : null);
3402 var builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: content,
3403 col: 0, pos: 0, cm: cm,
3404 trailingSpace: false,
3405 splitSpaces: cm.getOption("lineWrapping")};
3406 lineView.measure = {};
3407
3408 // Iterate over the logical lines that make up this visual line.
3409 for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {
3410 var line = i ? lineView.rest[i - 1] : lineView.line, order = (void 0);
3411 builder.pos = 0;
3412 builder.addToken = buildToken;
3413 // Optionally wire in some hacks into the token-rendering
3414 // algorithm, to deal with browser quirks.
3415 if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction)))
3416 { builder.addToken = buildTokenBadBidi(builder.addToken, order); }
3417 builder.map = [];
3418 var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line);
3419 insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate));
3420 if (line.styleClasses) {
3421 if (line.styleClasses.bgClass)
3422 { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || ""); }
3423 if (line.styleClasses.textClass)
3424 { builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || ""); }
3425 }
3426
3427 // Ensure at least a single node is present, for measuring.
3428 if (builder.map.length == 0)
3429 { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))); }
3430
3431 // Store the map and a cache object for the current logical line
3432 if (i == 0) {
3433 lineView.measure.map = builder.map;
3434 lineView.measure.cache = {};
3435 } else {
3436 (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map)
3437 ;(lineView.measure.caches || (lineView.measure.caches = [])).push({});
3438 }
3439 }
3440
3441 // See issue #2901
3442 if (webkit) {
3443 var last = builder.content.lastChild;
3444 if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab")))
3445 { builder.content.className = "cm-tab-wrap-hack"; }
3446 }
3447
3448 signal(cm, "renderLine", cm, lineView.line, builder.pre);
3449 if (builder.pre.className)
3450 { builder.textClass = joinClasses(builder.pre.className, builder.textClass || ""); }
3451
3452 return builder
3453 }
3454
3455 function defaultSpecialCharPlaceholder(ch) {
3456 var token = elt("span", "\u2022", "cm-invalidchar");
3457 token.title = "\\u" + ch.charCodeAt(0).toString(16);
3458 token.setAttribute("aria-label", token.title);
3459 return token
3460 }
3461
3462 // Build up the DOM representation for a single token, and add it to
3463 // the line map. Takes care to render special characters separately.
3464 function buildToken(builder, text, style, startStyle, endStyle, css, attributes) {
3465 if (!text) { return }
3466 var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text;
3467 var special = builder.cm.state.specialChars, mustWrap = false;
3468 var content;
3469 if (!special.test(text)) {
3470 builder.col += text.length;
3471 content = document.createTextNode(displayText);
3472 builder.map.push(builder.pos, builder.pos + text.length, content);
3473 if (ie && ie_version < 9) { mustWrap = true; }
3474 builder.pos += text.length;
3475 } else {
3476 content = document.createDocumentFragment();
3477 var pos = 0;
3478 while (true) {
3479 special.lastIndex = pos;
3480 var m = special.exec(text);
3481 var skipped = m ? m.index - pos : text.length - pos;
3482 if (skipped) {
3483 var txt = document.createTextNode(displayText.slice(pos, pos + skipped));
3484 if (ie && ie_version < 9) { content.appendChild(elt("span", [txt])); }
3485 else { content.appendChild(txt); }
3486 builder.map.push(builder.pos, builder.pos + skipped, txt);
3487 builder.col += skipped;
3488 builder.pos += skipped;
3489 }
3490 if (!m) { break }
3491 pos += skipped + 1;
3492 var txt$1 = (void 0);
3493 if (m[0] == "\t") {
3494 var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
3495 txt$1 = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
3496 txt$1.setAttribute("role", "presentation");
3497 txt$1.setAttribute("cm-text", "\t");
3498 builder.col += tabWidth;
3499 } else if (m[0] == "\r" || m[0] == "\n") {
3500 txt$1 = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar"));
3501 txt$1.setAttribute("cm-text", m[0]);
3502 builder.col += 1;
3503 } else {
3504 txt$1 = builder.cm.options.specialCharPlaceholder(m[0]);
3505 txt$1.setAttribute("cm-text", m[0]);
3506 if (ie && ie_version < 9) { content.appendChild(elt("span", [txt$1])); }
3507 else { content.appendChild(txt$1); }
3508 builder.col += 1;
3509 }
3510 builder.map.push(builder.pos, builder.pos + 1, txt$1);
3511 builder.pos++;
3512 }
3513 }
3514 builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32;
3515 if (style || startStyle || endStyle || mustWrap || css) {
3516 var fullStyle = style || "";
3517 if (startStyle) { fullStyle += startStyle; }
3518 if (endStyle) { fullStyle += endStyle; }
3519 var token = elt("span", [content], fullStyle, css);
3520 if (attributes) {
3521 for (var attr in attributes) { if (attributes.hasOwnProperty(attr) && attr != "style" && attr != "class")
3522 { token.setAttribute(attr, attributes[attr]); } }
3523 }
3524 return builder.content.appendChild(token)
3525 }
3526 builder.content.appendChild(content);
3527 }
3528
3529 // Change some spaces to NBSP to prevent the browser from collapsing
3530 // trailing spaces at the end of a line when rendering text (issue #1362).
3531 function splitSpaces(text, trailingBefore) {
3532 if (text.length > 1 && !/ /.test(text)) { return text }
3533 var spaceBefore = trailingBefore, result = "";
3534 for (var i = 0; i < text.length; i++) {
3535 var ch = text.charAt(i);
3536 if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32))
3537 { ch = "\u00a0"; }
3538 result += ch;
3539 spaceBefore = ch == " ";
3540 }
3541 return result
3542 }
3543
3544 // Work around nonsense dimensions being reported for stretches of
3545 // right-to-left text.
3546 function buildTokenBadBidi(inner, order) {
3547 return function (builder, text, style, startStyle, endStyle, css, attributes) {
3548 style = style ? style + " cm-force-border" : "cm-force-border";
3549 var start = builder.pos, end = start + text.length;
3550 for (;;) {
3551 // Find the part that overlaps with the start of this text
3552 var part = (void 0);
3553 for (var i = 0; i < order.length; i++) {
3554 part = order[i];
3555 if (part.to > start && part.from <= start) { break }
3556 }
3557 if (part.to >= end) { return inner(builder, text, style, startStyle, endStyle, css, attributes) }
3558 inner(builder, text.slice(0, part.to - start), style, startStyle, null, css, attributes);
3559 startStyle = null;
3560 text = text.slice(part.to - start);
3561 start = part.to;
3562 }
3563 }
3564 }
3565
3566 function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
3567 var widget = !ignoreWidget && marker.widgetNode;
3568 if (widget) { builder.map.push(builder.pos, builder.pos + size, widget); }
3569 if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {
3570 if (!widget)
3571 { widget = builder.content.appendChild(document.createElement("span")); }
3572 widget.setAttribute("cm-marker", marker.id);
3573 }
3574 if (widget) {
3575 builder.cm.display.input.setUneditable(widget);
3576 builder.content.appendChild(widget);
3577 }
3578 builder.pos += size;
3579 builder.trailingSpace = false;
3580 }
3581
3582 // Outputs a number of spans to make up a line, taking highlighting
3583 // and marked text into account.
3584 function insertLineContent(line, builder, styles) {
3585 var spans = line.markedSpans, allText = line.text, at = 0;
3586 if (!spans) {
3587 for (var i$1 = 1; i$1 < styles.length; i$1+=2)
3588 { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpretTokenStyle(styles[i$1+1], builder.cm.options)); }
3589 return
3590 }
3591
3592 var len = allText.length, pos = 0, i = 1, text = "", style, css;
3593 var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed, attributes;
3594 for (;;) {
3595 if (nextChange == pos) { // Update current marker set
3596 spanStyle = spanEndStyle = spanStartStyle = css = "";
3597 attributes = null;
3598 collapsed = null; nextChange = Infinity;
3599 var foundBookmarks = [], endStyles = (void 0);
3600 for (var j = 0; j < spans.length; ++j) {
3601 var sp = spans[j], m = sp.marker;
3602 if (m.type == "bookmark" && sp.from == pos && m.widgetNode) {
3603 foundBookmarks.push(m);
3604 } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) {
3605 if (sp.to != null && sp.to != pos && nextChange > sp.to) {
3606 nextChange = sp.to;
3607 spanEndStyle = "";
3608 }
3609 if (m.className) { spanStyle += " " + m.className; }
3610 if (m.css) { css = (css ? css + ";" : "") + m.css; }
3611 if (m.startStyle && sp.from == pos) { spanStartStyle += " " + m.startStyle; }
3612 if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [])).push(m.endStyle, sp.to); }
3613 // support for the old title property
3614 // https://github.com/codemirror/CodeMirror/pull/5673
3615 if (m.title) { (attributes || (attributes = {})).title = m.title; }
3616 if (m.attributes) {
3617 for (var attr in m.attributes)
3618 { (attributes || (attributes = {}))[attr] = m.attributes[attr]; }
3619 }
3620 if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))
3621 { collapsed = sp; }
3622 } else if (sp.from > pos && nextChange > sp.from) {
3623 nextChange = sp.from;
3624 }
3625 }
3626 if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2)
3627 { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += " " + endStyles[j$1]; } } }
3628
3629 if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2)
3630 { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]); } }
3631 if (collapsed && (collapsed.from || 0) == pos) {
3632 buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos,
3633 collapsed.marker, collapsed.from == null);
3634 if (collapsed.to == null) { return }
3635 if (collapsed.to == pos) { collapsed = false; }
3636 }
3637 }
3638 if (pos >= len) { break }
3639
3640 var upto = Math.min(len, nextChange);
3641 while (true) {
3642 if (text) {
3643 var end = pos + text.length;
3644 if (!collapsed) {
3645 var tokenText = end > upto ? text.slice(0, upto - pos) : text;
3646 builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
3647 spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", css, attributes);
3648 }
3649 if (end >= upto) {text = text.slice(upto - pos); pos = upto; break}
3650 pos = end;
3651 spanStartStyle = "";
3652 }
3653 text = allText.slice(at, at = styles[i++]);
3654 style = interpretTokenStyle(styles[i++], builder.cm.options);
3655 }
3656 }
3657 }
3658
3659
3660 // These objects are used to represent the visible (currently drawn)
3661 // part of the document. A LineView may correspond to multiple
3662 // logical lines, if those are connected by collapsed ranges.
3663 function LineView(doc, line, lineN) {
3664 // The starting line
3665 this.line = line;
3666 // Continuing lines, if any
3667 this.rest = visualLineContinued(line);
3668 // Number of logical lines in this visual line
3669 this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1;
3670 this.node = this.text = null;
3671 this.hidden = lineIsHidden(doc, line);
3672 }
3673
3674 // Create a range of LineView objects for the given lines.
3675 function buildViewArray(cm, from, to) {
3676 var array = [], nextPos;
3677 for (var pos = from; pos < to; pos = nextPos) {
3678 var view = new LineView(cm.doc, getLine(cm.doc, pos), pos);
3679 nextPos = pos + view.size;
3680 array.push(view);
3681 }
3682 return array
3683 }
3684
3685 var operationGroup = null;
3686
3687 function pushOperation(op) {
3688 if (operationGroup) {
3689 operationGroup.ops.push(op);
3690 } else {
3691 op.ownsGroup = operationGroup = {
3692 ops: [op],
3693 delayedCallbacks: []
3694 };
3695 }
3696 }
3697
3698 function fireCallbacksForOps(group) {
3699 // Calls delayed callbacks and cursorActivity handlers until no
3700 // new ones appear
3701 var callbacks = group.delayedCallbacks, i = 0;
3702 do {
3703 for (; i < callbacks.length; i++)
3704 { callbacks[i].call(null); }
3705 for (var j = 0; j < group.ops.length; j++) {
3706 var op = group.ops[j];
3707 if (op.cursorActivityHandlers)
3708 { while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
3709 { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm); } }
3710 }
3711 } while (i < callbacks.length)
3712 }
3713
3714 function finishOperation(op, endCb) {
3715 var group = op.ownsGroup;
3716 if (!group) { return }
3717
3718 try { fireCallbacksForOps(group); }
3719 finally {
3720 operationGroup = null;
3721 endCb(group);
3722 }
3723 }
3724
3725 var orphanDelayedCallbacks = null;
3726
3727 // Often, we want to signal events at a point where we are in the
3728 // middle of some work, but don't want the handler to start calling
3729 // other methods on the editor, which might be in an inconsistent
3730 // state or simply not expect any other events to happen.
3731 // signalLater looks whether there are any handlers, and schedules
3732 // them to be executed when the last operation ends, or, if no
3733 // operation is active, when a timeout fires.
3734 function signalLater(emitter, type /*, values...*/) {
3735 var arr = getHandlers(emitter, type);
3736 if (!arr.length) { return }
3737 var args = Array.prototype.slice.call(arguments, 2), list;
3738 if (operationGroup) {
3739 list = operationGroup.delayedCallbacks;
3740 } else if (orphanDelayedCallbacks) {
3741 list = orphanDelayedCallbacks;
3742 } else {
3743 list = orphanDelayedCallbacks = [];
3744 setTimeout(fireOrphanDelayed, 0);
3745 }
3746 var loop = function ( i ) {
3747 list.push(function () { return arr[i].apply(null, args); });
3748 };
3749
3750 for (var i = 0; i < arr.length; ++i)
3751 loop( i );
3752 }
3753
3754 function fireOrphanDelayed() {
3755 var delayed = orphanDelayedCallbacks;
3756 orphanDelayedCallbacks = null;
3757 for (var i = 0; i < delayed.length; ++i) { delayed[i](); }
3758 }
3759
3760 // When an aspect of a line changes, a string is added to
3761 // lineView.changes. This updates the relevant part of the line's
3762 // DOM structure.
3763 function updateLineForChanges(cm, lineView, lineN, dims) {
3764 for (var j = 0; j < lineView.changes.length; j++) {
3765 var type = lineView.changes[j];
3766 if (type == "text") { updateLineText(cm, lineView); }
3767 else if (type == "gutter") { updateLineGutter(cm, lineView, lineN, dims); }
3768 else if (type == "class") { updateLineClasses(cm, lineView); }
3769 else if (type == "widget") { updateLineWidgets(cm, lineView, dims); }
3770 }
3771 lineView.changes = null;
3772 }
3773
3774 // Lines with gutter elements, widgets or a background class need to
3775 // be wrapped, and have the extra elements added to the wrapper div
3776 function ensureLineWrapped(lineView) {
3777 if (lineView.node == lineView.text) {
3778 lineView.node = elt("div", null, null, "position: relative");
3779 if (lineView.text.parentNode)
3780 { lineView.text.parentNode.replaceChild(lineView.node, lineView.text); }
3781 lineView.node.appendChild(lineView.text);
3782 if (ie && ie_version < 8) { lineView.node.style.zIndex = 2; }
3783 }
3784 return lineView.node
3785 }
3786
3787 function updateLineBackground(cm, lineView) {
3788 var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass;
3789 if (cls) { cls += " CodeMirror-linebackground"; }
3790 if (lineView.background) {
3791 if (cls) { lineView.background.className = cls; }
3792 else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; }
3793 } else if (cls) {
3794 var wrap = ensureLineWrapped(lineView);
3795 lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild);
3796 cm.display.input.setUneditable(lineView.background);
3797 }
3798 }
3799
3800 // Wrapper around buildLineContent which will reuse the structure
3801 // in display.externalMeasured when possible.
3802 function getLineContent(cm, lineView) {
3803 var ext = cm.display.externalMeasured;
3804 if (ext && ext.line == lineView.line) {
3805 cm.display.externalMeasured = null;
3806 lineView.measure = ext.measure;
3807 return ext.built
3808 }
3809 return buildLineContent(cm, lineView)
3810 }
3811
3812 // Redraw the line's text. Interacts with the background and text
3813 // classes because the mode may output tokens that influence these
3814 // classes.
3815 function updateLineText(cm, lineView) {
3816 var cls = lineView.text.className;
3817 var built = getLineContent(cm, lineView);
3818 if (lineView.text == lineView.node) { lineView.node = built.pre; }
3819 lineView.text.parentNode.replaceChild(built.pre, lineView.text);
3820 lineView.text = built.pre;
3821 if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {
3822 lineView.bgClass = built.bgClass;
3823 lineView.textClass = built.textClass;
3824 updateLineClasses(cm, lineView);
3825 } else if (cls) {
3826 lineView.text.className = cls;
3827 }
3828 }
3829
3830 function updateLineClasses(cm, lineView) {
3831 updateLineBackground(cm, lineView);
3832 if (lineView.line.wrapClass)
3833 { ensureLineWrapped(lineView).className = lineView.line.wrapClass; }
3834 else if (lineView.node != lineView.text)
3835 { lineView.node.className = ""; }
3836 var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass;
3837 lineView.text.className = textClass || "";
3838 }
3839
3840 function updateLineGutter(cm, lineView, lineN, dims) {
3841 if (lineView.gutter) {
3842 lineView.node.removeChild(lineView.gutter);
3843 lineView.gutter = null;
3844 }
3845 if (lineView.gutterBackground) {
3846 lineView.node.removeChild(lineView.gutterBackground);
3847 lineView.gutterBackground = null;
3848 }
3849 if (lineView.line.gutterClass) {
3850 var wrap = ensureLineWrapped(lineView);
3851 lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass,
3852 ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px; width: " + (dims.gutterTotalWidth) + "px"));
3853 cm.display.input.setUneditable(lineView.gutterBackground);
3854 wrap.insertBefore(lineView.gutterBackground, lineView.text);
3855 }
3856 var markers = lineView.line.gutterMarkers;
3857 if (cm.options.lineNumbers || markers) {
3858 var wrap$1 = ensureLineWrapped(lineView);
3859 var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"));
3860 cm.display.input.setUneditable(gutterWrap);
3861 wrap$1.insertBefore(gutterWrap, lineView.text);
3862 if (lineView.line.gutterClass)
3863 { gutterWrap.className += " " + lineView.line.gutterClass; }
3864 if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
3865 { lineView.lineNumber = gutterWrap.appendChild(
3866 elt("div", lineNumberFor(cm.options, lineN),
3867 "CodeMirror-linenumber CodeMirror-gutter-elt",
3868 ("left: " + (dims.gutterLeft["CodeMirror-linenumbers"]) + "px; width: " + (cm.display.lineNumInnerWidth) + "px"))); }
3869 if (markers) { for (var k = 0; k < cm.options.gutters.length; ++k) {
3870 var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id];
3871 if (found)
3872 { gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt",
3873 ("left: " + (dims.gutterLeft[id]) + "px; width: " + (dims.gutterWidth[id]) + "px"))); }
3874 } }
3875 }
3876 }
3877
3878 function updateLineWidgets(cm, lineView, dims) {
3879 if (lineView.alignable) { lineView.alignable = null; }
3880 for (var node = lineView.node.firstChild, next = (void 0); node; node = next) {
3881 next = node.nextSibling;
3882 if (node.className == "CodeMirror-linewidget")
3883 { lineView.node.removeChild(node); }
3884 }
3885 insertLineWidgets(cm, lineView, dims);
3886 }
3887
3888 // Build a line's DOM representation from scratch
3889 function buildLineElement(cm, lineView, lineN, dims) {
3890 var built = getLineContent(cm, lineView);
3891 lineView.text = lineView.node = built.pre;
3892 if (built.bgClass) { lineView.bgClass = built.bgClass; }
3893 if (built.textClass) { lineView.textClass = built.textClass; }
3894
3895 updateLineClasses(cm, lineView);
3896 updateLineGutter(cm, lineView, lineN, dims);
3897 insertLineWidgets(cm, lineView, dims);
3898 return lineView.node
3899 }
3900
3901 // A lineView may contain multiple logical lines (when merged by
3902 // collapsed spans). The widgets for all of them need to be drawn.
3903 function insertLineWidgets(cm, lineView, dims) {
3904 insertLineWidgetsFor(cm, lineView.line, lineView, dims, true);
3905 if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
3906 { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false); } }
3907 }
3908
3909 function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {
3910 if (!line.widgets) { return }
3911 var wrap = ensureLineWrapped(lineView);
3912 for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
3913 var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
3914 if (!widget.handleMouseEvents) { node.setAttribute("cm-ignore-events", "true"); }
3915 positionLineWidget(widget, node, lineView, dims);
3916 cm.display.input.setUneditable(node);
3917 if (allowAbove && widget.above)
3918 { wrap.insertBefore(node, lineView.gutter || lineView.text); }
3919 else
3920 { wrap.appendChild(node); }
3921 signalLater(widget, "redraw");
3922 }
3923 }
3924
3925 function positionLineWidget(widget, node, lineView, dims) {
3926 if (widget.noHScroll) {
3927 (lineView.alignable || (lineView.alignable = [])).push(node);
3928 var width = dims.wrapperWidth;
3929 node.style.left = dims.fixedPos + "px";
3930 if (!widget.coverGutter) {
3931 width -= dims.gutterTotalWidth;
3932 node.style.paddingLeft = dims.gutterTotalWidth + "px";
3933 }
3934 node.style.width = width + "px";
3935 }
3936 if (widget.coverGutter) {
3937 node.style.zIndex = 5;
3938 node.style.position = "relative";
3939 if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + "px"; }
3940 }
3941 }
3942
3943 function widgetHeight(widget) {
3944 if (widget.height != null) { return widget.height }
3945 var cm = widget.doc.cm;
3946 if (!cm) { return 0 }
3947 if (!contains(document.body, widget.node)) {
3948 var parentStyle = "position: relative;";
3949 if (widget.coverGutter)
3950 { parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;"; }
3951 if (widget.noHScroll)
3952 { parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;"; }
3953 removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle));
3954 }
3955 return widget.height = widget.node.parentNode.offsetHeight
3956 }
3957
3958 // Return true when the given mouse event happened in a widget
3959 function eventInWidget(display, e) {
3960 for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
3961 if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") ||
3962 (n.parentNode == display.sizer && n != display.mover))
3963 { return true }
3964 }
3965 }
3966
3967 // POSITION MEASUREMENT
3968
3969 function paddingTop(display) {return display.lineSpace.offsetTop}
3970 function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight}
3971 function paddingH(display) {
3972 if (display.cachedPaddingH) { return display.cachedPaddingH }
3973 var e = removeChildrenAndAdd(display.measure, elt("pre", "x"));
3974 var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle;
3975 var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)};
3976 if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data; }
3977 return data
3978 }
3979
3980 function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth }
3981 function displayWidth(cm) {
3982 return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth
3983 }
3984 function displayHeight(cm) {
3985 return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight
3986 }
3987
3988 // Ensure the lineView.wrapping.heights array is populated. This is
3989 // an array of bottom offsets for the lines that make up a drawn
3990 // line. When lineWrapping is on, there might be more than one
3991 // height.
3992 function ensureLineHeights(cm, lineView, rect) {
3993 var wrapping = cm.options.lineWrapping;
3994 var curWidth = wrapping && displayWidth(cm);
3995 if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {
3996 var heights = lineView.measure.heights = [];
3997 if (wrapping) {
3998 lineView.measure.width = curWidth;
3999 var rects = lineView.text.firstChild.getClientRects();
4000 for (var i = 0; i < rects.length - 1; i++) {
4001 var cur = rects[i], next = rects[i + 1];
4002 if (Math.abs(cur.bottom - next.bottom) > 2)
4003 { heights.push((cur.bottom + next.top) / 2 - rect.top); }
4004 }
4005 }
4006 heights.push(rect.bottom - rect.top);
4007 }
4008 }
4009
4010 // Find a line map (mapping character offsets to text nodes) and a
4011 // measurement cache for the given line number. (A line view might
4012 // contain multiple lines when collapsed ranges are present.)
4013 function mapFromLineView(lineView, line, lineN) {
4014 if (lineView.line == line)
4015 { return {map: lineView.measure.map, cache: lineView.measure.cache} }
4016 for (var i = 0; i < lineView.rest.length; i++)
4017 { if (lineView.rest[i] == line)
4018 { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } }
4019 for (var i$1 = 0; i$1 < lineView.rest.length; i$1++)
4020 { if (lineNo(lineView.rest[i$1]) > lineN)
4021 { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } }
4022 }
4023
4024 // Render a line into the hidden node display.externalMeasured. Used
4025 // when measurement is needed for a line that's not in the viewport.
4026 function updateExternalMeasurement(cm, line) {
4027 line = visualLine(line);
4028 var lineN = lineNo(line);
4029 var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN);
4030 view.lineN = lineN;
4031 var built = view.built = buildLineContent(cm, view);
4032 view.text = built.pre;
4033 removeChildrenAndAdd(cm.display.lineMeasure, built.pre);
4034 return view
4035 }
4036
4037 // Get a {top, bottom, left, right} box (in line-local coordinates)
4038 // for a given character.
4039 function measureChar(cm, line, ch, bias) {
4040 return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias)
4041 }
4042
4043 // Find a line view that corresponds to the given line number.
4044 function findViewForLine(cm, lineN) {
4045 if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo)
4046 { return cm.display.view[findViewIndex(cm, lineN)] }
4047 var ext = cm.display.externalMeasured;
4048 if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size)
4049 { return ext }
4050 }
4051
4052 // Measurement can be split in two steps, the set-up work that
4053 // applies to the whole line, and the measurement of the actual
4054 // character. Functions like coordsChar, that need to do a lot of
4055 // measurements in a row, can thus ensure that the set-up work is
4056 // only done once.
4057 function prepareMeasureForLine(cm, line) {
4058 var lineN = lineNo(line);
4059 var view = findViewForLine(cm, lineN);
4060 if (view && !view.text) {
4061 view = null;
4062 } else if (view && view.changes) {
4063 updateLineForChanges(cm, view, lineN, getDimensions(cm));
4064 cm.curOp.forceUpdate = true;
4065 }
4066 if (!view)
4067 { view = updateExternalMeasurement(cm, line); }
4068
4069 var info = mapFromLineView(view, line, lineN);
4070 return {
4071 line: line, view: view, rect: null,
4072 map: info.map, cache: info.cache, before: info.before,
4073 hasHeights: false
4074 }
4075 }
4076
4077 // Given a prepared measurement object, measures the position of an
4078 // actual character (or fetches it from the cache).
4079 function measureCharPrepared(cm, prepared, ch, bias, varHeight) {
4080 if (prepared.before) { ch = -1; }
4081 var key = ch + (bias || ""), found;
4082 if (prepared.cache.hasOwnProperty(key)) {
4083 found = prepared.cache[key];
4084 } else {
4085 if (!prepared.rect)
4086 { prepared.rect = prepared.view.text.getBoundingClientRect(); }
4087 if (!prepared.hasHeights) {
4088 ensureLineHeights(cm, prepared.view, prepared.rect);
4089 prepared.hasHeights = true;
4090 }
4091 found = measureCharInner(cm, prepared, ch, bias);
4092 if (!found.bogus) { prepared.cache[key] = found; }
4093 }
4094 return {left: found.left, right: found.right,
4095 top: varHeight ? found.rtop : found.top,
4096 bottom: varHeight ? found.rbottom : found.bottom}
4097 }
4098
4099 var nullRect = {left: 0, right: 0, top: 0, bottom: 0};
4100
4101 function nodeAndOffsetInLineMap(map$$1, ch, bias) {
4102 var node, start, end, collapse, mStart, mEnd;
4103 // First, search the line map for the text node corresponding to,
4104 // or closest to, the target character.
4105 for (var i = 0; i < map$$1.length; i += 3) {
4106 mStart = map$$1[i];
4107 mEnd = map$$1[i + 1];
4108 if (ch < mStart) {
4109 start = 0; end = 1;
4110 collapse = "left";
4111 } else if (ch < mEnd) {
4112 start = ch - mStart;
4113 end = start + 1;
4114 } else if (i == map$$1.length - 3 || ch == mEnd && map$$1[i + 3] > ch) {
4115 end = mEnd - mStart;
4116 start = end - 1;
4117 if (ch >= mEnd) { collapse = "right"; }
4118 }
4119 if (start != null) {
4120 node = map$$1[i + 2];
4121 if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right"))
4122 { collapse = bias; }
4123 if (bias == "left" && start == 0)
4124 { while (i && map$$1[i - 2] == map$$1[i - 3] && map$$1[i - 1].insertLeft) {
4125 node = map$$1[(i -= 3) + 2];
4126 collapse = "left";
4127 } }
4128 if (bias == "right" && start == mEnd - mStart)
4129 { while (i < map$$1.length - 3 && map$$1[i + 3] == map$$1[i + 4] && !map$$1[i + 5].insertLeft) {
4130 node = map$$1[(i += 3) + 2];
4131 collapse = "right";
4132 } }
4133 break
4134 }
4135 }
4136 return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd}
4137 }
4138
4139 function getUsefulRect(rects, bias) {
4140 var rect = nullRect;
4141 if (bias == "left") { for (var i = 0; i < rects.length; i++) {
4142 if ((rect = rects[i]).left != rect.right) { break }
4143 } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) {
4144 if ((rect = rects[i$1]).left != rect.right) { break }
4145 } }
4146 return rect
4147 }
4148
4149 function measureCharInner(cm, prepared, ch, bias) {
4150 var place = nodeAndOffsetInLineMap(prepared.map, ch, bias);
4151 var node = place.node, start = place.start, end = place.end, collapse = place.collapse;
4152
4153 var rect;
4154 if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.
4155 for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonsense rectangles are returned
4156 while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start; }
4157 while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) { ++end; }
4158 if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart)
4159 { rect = node.parentNode.getBoundingClientRect(); }
4160 else
4161 { rect = getUsefulRect(range(node, start, end).getClientRects(), bias); }
4162 if (rect.left || rect.right || start == 0) { break }
4163 end = start;
4164 start = start - 1;
4165 collapse = "right";
4166 }
4167 if (ie && ie_version < 11) { rect = maybeUpdateRectForZooming(cm.display.measure, rect); }
4168 } else { // If it is a widget, simply get the box for the whole widget.
4169 if (start > 0) { collapse = bias = "right"; }
4170 var rects;
4171 if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)
4172 { rect = rects[bias == "right" ? rects.length - 1 : 0]; }
4173 else
4174 { rect = node.getBoundingClientRect(); }
4175 }
4176 if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {
4177 var rSpan = node.parentNode.getClientRects()[0];
4178 if (rSpan)
4179 { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom}; }
4180 else
4181 { rect = nullRect; }
4182 }
4183
4184 var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top;
4185 var mid = (rtop + rbot) / 2;
4186 var heights = prepared.view.measure.heights;
4187 var i = 0;
4188 for (; i < heights.length - 1; i++)
4189 { if (mid < heights[i]) { break } }
4190 var top = i ? heights[i - 1] : 0, bot = heights[i];
4191 var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left,
4192 right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left,
4193 top: top, bottom: bot};
4194 if (!rect.left && !rect.right) { result.bogus = true; }
4195 if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; }
4196
4197 return result
4198 }
4199
4200 // Work around problem with bounding client rects on ranges being
4201 // returned incorrectly when zoomed on IE10 and below.
4202 function maybeUpdateRectForZooming(measure, rect) {
4203 if (!window.screen || screen.logicalXDPI == null ||
4204 screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))
4205 { return rect }
4206 var scaleX = screen.logicalXDPI / screen.deviceXDPI;
4207 var scaleY = screen.logicalYDPI / screen.deviceYDPI;
4208 return {left: rect.left * scaleX, right: rect.right * scaleX,
4209 top: rect.top * scaleY, bottom: rect.bottom * scaleY}
4210 }
4211
4212 function clearLineMeasurementCacheFor(lineView) {
4213 if (lineView.measure) {
4214 lineView.measure.cache = {};
4215 lineView.measure.heights = null;
4216 if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
4217 { lineView.measure.caches[i] = {}; } }
4218 }
4219 }
4220
4221 function clearLineMeasurementCache(cm) {
4222 cm.display.externalMeasure = null;
4223 removeChildren(cm.display.lineMeasure);
4224 for (var i = 0; i < cm.display.view.length; i++)
4225 { clearLineMeasurementCacheFor(cm.display.view[i]); }
4226 }
4227
4228 function clearCaches(cm) {
4229 clearLineMeasurementCache(cm);
4230 cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null;
4231 if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true; }
4232 cm.display.lineNumChars = null;
4233 }
4234
4235 function pageScrollX() {
4236 // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=489206
4237 // which causes page_Offset and bounding client rects to use
4238 // different reference viewports and invalidate our calculations.
4239 if (chrome && android) { return -(document.body.getBoundingClientRect().left - parseInt(getComputedStyle(document.body).marginLeft)) }
4240 return window.pageXOffset || (document.documentElement || document.body).scrollLeft
4241 }
4242 function pageScrollY() {
4243 if (chrome && android) { return -(document.body.getBoundingClientRect().top - parseInt(getComputedStyle(document.body).marginTop)) }
4244 return window.pageYOffset || (document.documentElement || document.body).scrollTop
4245 }
4246
4247 function widgetTopHeight(lineObj) {
4248 var height = 0;
4249 if (lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above)
4250 { height += widgetHeight(lineObj.widgets[i]); } } }
4251 return height
4252 }
4253
4254 // Converts a {top, bottom, left, right} box from line-local
4255 // coordinates into another coordinate system. Context may be one of
4256 // "line", "div" (display.lineDiv), "local"./null (editor), "window",
4257 // or "page".
4258 function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) {
4259 if (!includeWidgets) {
4260 var height = widgetTopHeight(lineObj);
4261 rect.top += height; rect.bottom += height;
4262 }
4263 if (context == "line") { return rect }
4264 if (!context) { context = "local"; }
4265 var yOff = heightAtLine(lineObj);
4266 if (context == "local") { yOff += paddingTop(cm.display); }
4267 else { yOff -= cm.display.viewOffset; }
4268 if (context == "page" || context == "window") {
4269 var lOff = cm.display.lineSpace.getBoundingClientRect();
4270 yOff += lOff.top + (context == "window" ? 0 : pageScrollY());
4271 var xOff = lOff.left + (context == "window" ? 0 : pageScrollX());
4272 rect.left += xOff; rect.right += xOff;
4273 }
4274 rect.top += yOff; rect.bottom += yOff;
4275 return rect
4276 }
4277
4278 // Coverts a box from "div" coords to another coordinate system.
4279 // Context may be "window", "page", "div", or "local"./null.
4280 function fromCoordSystem(cm, coords, context) {
4281 if (context == "div") { return coords }
4282 var left = coords.left, top = coords.top;
4283 // First move into "page" coordinate system
4284 if (context == "page") {
4285 left -= pageScrollX();
4286 top -= pageScrollY();
4287 } else if (context == "local" || !context) {
4288 var localBox = cm.display.sizer.getBoundingClientRect();
4289 left += localBox.left;
4290 top += localBox.top;
4291 }
4292
4293 var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect();
4294 return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top}
4295 }
4296
4297 function charCoords(cm, pos, context, lineObj, bias) {
4298 if (!lineObj) { lineObj = getLine(cm.doc, pos.line); }
4299 return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context)
4300 }
4301
4302 // Returns a box for a given cursor position, which may have an
4303 // 'other' property containing the position of the secondary cursor
4304 // on a bidi boundary.
4305 // A cursor Pos(line, char, "before") is on the same visual line as `char - 1`
4306 // and after `char - 1` in writing order of `char - 1`
4307 // A cursor Pos(line, char, "after") is on the same visual line as `char`
4308 // and before `char` in writing order of `char`
4309 // Examples (upper-case letters are RTL, lower-case are LTR):
4310 // Pos(0, 1, ...)
4311 // before after
4312 // ab a|b a|b
4313 // aB a|B aB|
4314 // Ab |Ab A|b
4315 // AB B|A B|A
4316 // Every position after the last character on a line is considered to stick
4317 // to the last character on the line.
4318 function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
4319 lineObj = lineObj || getLine(cm.doc, pos.line);
4320 if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); }
4321 function get(ch, right) {
4322 var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight);
4323 if (right) { m.left = m.right; } else { m.right = m.left; }
4324 return intoCoordSystem(cm, lineObj, m, context)
4325 }
4326 var order = getOrder(lineObj, cm.doc.direction), ch = pos.ch, sticky = pos.sticky;
4327 if (ch >= lineObj.text.length) {
4328 ch = lineObj.text.length;
4329 sticky = "before";
4330 } else if (ch <= 0) {
4331 ch = 0;
4332 sticky = "after";
4333 }
4334 if (!order) { return get(sticky == "before" ? ch - 1 : ch, sticky == "before") }
4335
4336 function getBidi(ch, partPos, invert) {
4337 var part = order[partPos], right = part.level == 1;
4338 return get(invert ? ch - 1 : ch, right != invert)
4339 }
4340 var partPos = getBidiPartAt(order, ch, sticky);
4341 var other = bidiOther;
4342 var val = getBidi(ch, partPos, sticky == "before");
4343 if (other != null) { val.other = getBidi(ch, other, sticky != "before"); }
4344 return val
4345 }
4346
4347 // Used to cheaply estimate the coordinates for a position. Used for
4348 // intermediate scroll updates.
4349 function estimateCoords(cm, pos) {
4350 var left = 0;
4351 pos = clipPos(cm.doc, pos);
4352 if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch; }
4353 var lineObj = getLine(cm.doc, pos.line);
4354 var top = heightAtLine(lineObj) + paddingTop(cm.display);
4355 return {left: left, right: left, top: top, bottom: top + lineObj.height}
4356 }
4357
4358 // Positions returned by coordsChar contain some extra information.
4359 // xRel is the relative x position of the input coordinates compared
4360 // to the found position (so xRel > 0 means the coordinates are to
4361 // the right of the character position, for example). When outside
4362 // is true, that means the coordinates lie outside the line's
4363 // vertical range.
4364 function PosWithInfo(line, ch, sticky, outside, xRel) {
4365 var pos = Pos(line, ch, sticky);
4366 pos.xRel = xRel;
4367 if (outside) { pos.outside = true; }
4368 return pos
4369 }
4370
4371 // Compute the character position closest to the given coordinates.
4372 // Input must be lineSpace-local ("div" coordinate system).
4373 function coordsChar(cm, x, y) {
4374 var doc = cm.doc;
4375 y += cm.display.viewOffset;
4376 if (y < 0) { return PosWithInfo(doc.first, 0, null, true, -1) }
4377 var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
4378 if (lineN > last)
4379 { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, true, 1) }
4380 if (x < 0) { x = 0; }
4381
4382 var lineObj = getLine(doc, lineN);
4383 for (;;) {
4384 var found = coordsCharInner(cm, lineObj, lineN, x, y);
4385 var collapsed = collapsedSpanAround(lineObj, found.ch + (found.xRel > 0 ? 1 : 0));
4386 if (!collapsed) { return found }
4387 var rangeEnd = collapsed.find(1);
4388 if (rangeEnd.line == lineN) { return rangeEnd }
4389 lineObj = getLine(doc, lineN = rangeEnd.line);
4390 }
4391 }
4392
4393 function wrappedLineExtent(cm, lineObj, preparedMeasure, y) {
4394 y -= widgetTopHeight(lineObj);
4395 var end = lineObj.text.length;
4396 var begin = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch - 1).bottom <= y; }, end, 0);
4397 end = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch).top > y; }, begin, end);
4398 return {begin: begin, end: end}
4399 }
4400
4401 function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) {
4402 if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); }
4403 var targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), "line").top;
4404 return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop)
4405 }
4406
4407 // Returns true if the given side of a box is after the given
4408 // coordinates, in top-to-bottom, left-to-right order.
4409 function boxIsAfter(box, x, y, left) {
4410 return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x
4411 }
4412
4413 function coordsCharInner(cm, lineObj, lineNo$$1, x, y) {
4414 // Move y into line-local coordinate space
4415 y -= heightAtLine(lineObj);
4416 var preparedMeasure = prepareMeasureForLine(cm, lineObj);
4417 // When directly calling `measureCharPrepared`, we have to adjust
4418 // for the widgets at this line.
4419 var widgetHeight$$1 = widgetTopHeight(lineObj);
4420 var begin = 0, end = lineObj.text.length, ltr = true;
4421
4422 var order = getOrder(lineObj, cm.doc.direction);
4423 // If the line isn't plain left-to-right text, first figure out
4424 // which bidi section the coordinates fall into.
4425 if (order) {
4426 var part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart)
4427 (cm, lineObj, lineNo$$1, preparedMeasure, order, x, y);
4428 ltr = part.level != 1;
4429 // The awkward -1 offsets are needed because findFirst (called
4430 // on these below) will treat its first bound as inclusive,
4431 // second as exclusive, but we want to actually address the
4432 // characters in the part's range
4433 begin = ltr ? part.from : part.to - 1;
4434 end = ltr ? part.to : part.from - 1;
4435 }
4436
4437 // A binary search to find the first character whose bounding box
4438 // starts after the coordinates. If we run across any whose box wrap
4439 // the coordinates, store that.
4440 var chAround = null, boxAround = null;
4441 var ch = findFirst(function (ch) {
4442 var box = measureCharPrepared(cm, preparedMeasure, ch);
4443 box.top += widgetHeight$$1; box.bottom += widgetHeight$$1;
4444 if (!boxIsAfter(box, x, y, false)) { return false }
4445 if (box.top <= y && box.left <= x) {
4446 chAround = ch;
4447 boxAround = box;
4448 }
4449 return true
4450 }, begin, end);
4451
4452 var baseX, sticky, outside = false;
4453 // If a box around the coordinates was found, use that
4454 if (boxAround) {
4455 // Distinguish coordinates nearer to the left or right side of the box
4456 var atLeft = x - boxAround.left < boxAround.right - x, atStart = atLeft == ltr;
4457 ch = chAround + (atStart ? 0 : 1);
4458 sticky = atStart ? "after" : "before";
4459 baseX = atLeft ? boxAround.left : boxAround.right;
4460 } else {
4461 // (Adjust for extended bound, if necessary.)
4462 if (!ltr && (ch == end || ch == begin)) { ch++; }
4463 // To determine which side to associate with, get the box to the
4464 // left of the character and compare it's vertical position to the
4465 // coordinates
4466 sticky = ch == 0 ? "after" : ch == lineObj.text.length ? "before" :
4467 (measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight$$1 <= y) == ltr ?
4468 "after" : "before";
4469 // Now get accurate coordinates for this place, in order to get a
4470 // base X position
4471 var coords = cursorCoords(cm, Pos(lineNo$$1, ch, sticky), "line", lineObj, preparedMeasure);
4472 baseX = coords.left;
4473 outside = y < coords.top || y >= coords.bottom;
4474 }
4475
4476 ch = skipExtendingChars(lineObj.text, ch, 1);
4477 return PosWithInfo(lineNo$$1, ch, sticky, outside, x - baseX)
4478 }
4479
4480 function coordsBidiPart(cm, lineObj, lineNo$$1, preparedMeasure, order, x, y) {
4481 // Bidi parts are sorted left-to-right, and in a non-line-wrapping
4482 // situation, we can take this ordering to correspond to the visual
4483 // ordering. This finds the first part whose end is after the given
4484 // coordinates.
4485 var index = findFirst(function (i) {
4486 var part = order[i], ltr = part.level != 1;
4487 return boxIsAfter(cursorCoords(cm, Pos(lineNo$$1, ltr ? part.to : part.from, ltr ? "before" : "after"),
4488 "line", lineObj, preparedMeasure), x, y, true)
4489 }, 0, order.length - 1);
4490 var part = order[index];
4491 // If this isn't the first part, the part's start is also after
4492 // the coordinates, and the coordinates aren't on the same line as
4493 // that start, move one part back.
4494 if (index > 0) {
4495 var ltr = part.level != 1;
4496 var start = cursorCoords(cm, Pos(lineNo$$1, ltr ? part.from : part.to, ltr ? "after" : "before"),
4497 "line", lineObj, preparedMeasure);
4498 if (boxIsAfter(start, x, y, true) && start.top > y)
4499 { part = order[index - 1]; }
4500 }
4501 return part
4502 }
4503
4504 function coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x, y) {
4505 // In a wrapped line, rtl text on wrapping boundaries can do things
4506 // that don't correspond to the ordering in our `order` array at
4507 // all, so a binary search doesn't work, and we want to return a
4508 // part that only spans one line so that the binary search in
4509 // coordsCharInner is safe. As such, we first find the extent of the
4510 // wrapped line, and then do a flat search in which we discard any
4511 // spans that aren't on the line.
4512 var ref = wrappedLineExtent(cm, lineObj, preparedMeasure, y);
4513 var begin = ref.begin;
4514 var end = ref.end;
4515 if (/\s/.test(lineObj.text.charAt(end - 1))) { end--; }
4516 var part = null, closestDist = null;
4517 for (var i = 0; i < order.length; i++) {
4518 var p = order[i];
4519 if (p.from >= end || p.to <= begin) { continue }
4520 var ltr = p.level != 1;
4521 var endX = measureCharPrepared(cm, preparedMeasure, ltr ? Math.min(end, p.to) - 1 : Math.max(begin, p.from)).right;
4522 // Weigh against spans ending before this, so that they are only
4523 // picked if nothing ends after
4524 var dist = endX < x ? x - endX + 1e9 : endX - x;
4525 if (!part || closestDist > dist) {
4526 part = p;
4527 closestDist = dist;
4528 }
4529 }
4530 if (!part) { part = order[order.length - 1]; }
4531 // Clip the part to the wrapped line.
4532 if (part.from < begin) { part = {from: begin, to: part.to, level: part.level}; }
4533 if (part.to > end) { part = {from: part.from, to: end, level: part.level}; }
4534 return part
4535 }
4536
4537 var measureText;
4538 // Compute the default text height.
4539 function textHeight(display) {
4540 if (display.cachedTextHeight != null) { return display.cachedTextHeight }
4541 if (measureText == null) {
4542 measureText = elt("pre");
4543 // Measure a bunch of lines, for browsers that compute
4544 // fractional heights.
4545 for (var i = 0; i < 49; ++i) {
4546 measureText.appendChild(document.createTextNode("x"));
4547 measureText.appendChild(elt("br"));
4548 }
4549 measureText.appendChild(document.createTextNode("x"));
4550 }
4551 removeChildrenAndAdd(display.measure, measureText);
4552 var height = measureText.offsetHeight / 50;
4553 if (height > 3) { display.cachedTextHeight = height; }
4554 removeChildren(display.measure);
4555 return height || 1
4556 }
4557
4558 // Compute the default character width.
4559 function charWidth(display) {
4560 if (display.cachedCharWidth != null) { return display.cachedCharWidth }
4561 var anchor = elt("span", "xxxxxxxxxx");
4562 var pre = elt("pre", [anchor]);
4563 removeChildrenAndAdd(display.measure, pre);
4564 var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10;
4565 if (width > 2) { display.cachedCharWidth = width; }
4566 return width || 10
4567 }
4568
4569 // Do a bulk-read of the DOM positions and sizes needed to draw the
4570 // view, so that we don't interleave reading and writing to the DOM.
4571 function getDimensions(cm) {
4572 var d = cm.display, left = {}, width = {};
4573 var gutterLeft = d.gutters.clientLeft;
4574 for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
4575 left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft;
4576 width[cm.options.gutters[i]] = n.clientWidth;
4577 }
4578 return {fixedPos: compensateForHScroll(d),
4579 gutterTotalWidth: d.gutters.offsetWidth,
4580 gutterLeft: left,
4581 gutterWidth: width,
4582 wrapperWidth: d.wrapper.clientWidth}
4583 }
4584
4585 // Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
4586 // but using getBoundingClientRect to get a sub-pixel-accurate
4587 // result.
4588 function compensateForHScroll(display) {
4589 return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left
4590 }
4591
4592 // Returns a function that estimates the height of a line, to use as
4593 // first approximation until the line becomes visible (and is thus
4594 // properly measurable).
4595 function estimateHeight(cm) {
4596 var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
4597 var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
4598 return function (line) {
4599 if (lineIsHidden(cm.doc, line)) { return 0 }
4600
4601 var widgetsHeight = 0;
4602 if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) {
4603 if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height; }
4604 } }
4605
4606 if (wrapping)
4607 { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th }
4608 else
4609 { return widgetsHeight + th }
4610 }
4611 }
4612
4613 function estimateLineHeights(cm) {
4614 var doc = cm.doc, est = estimateHeight(cm);
4615 doc.iter(function (line) {
4616 var estHeight = est(line);
4617 if (estHeight != line.height) { updateLineHeight(line, estHeight); }
4618 });
4619 }
4620
4621 // Given a mouse event, find the corresponding position. If liberal
4622 // is false, it checks whether a gutter or scrollbar was clicked,
4623 // and returns null if it was. forRect is used by rectangular
4624 // selections, and tries to estimate a character position even for
4625 // coordinates beyond the right of the text.
4626 function posFromMouse(cm, e, liberal, forRect) {
4627 var display = cm.display;
4628 if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") { return null }
4629
4630 var x, y, space = display.lineSpace.getBoundingClientRect();
4631 // Fails unpredictably on IE[67] when mouse is dragged around quickly.
4632 try { x = e.clientX - space.left; y = e.clientY - space.top; }
4633 catch (e) { return null }
4634 var coords = coordsChar(cm, x, y), line;
4635 if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) {
4636 var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length;
4637 coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff));
4638 }
4639 return coords
4640 }
4641
4642 // Find the view element corresponding to a given line. Return null
4643 // when the line isn't visible.
4644 function findViewIndex(cm, n) {
4645 if (n >= cm.display.viewTo) { return null }
4646 n -= cm.display.viewFrom;
4647 if (n < 0) { return null }
4648 var view = cm.display.view;
4649 for (var i = 0; i < view.length; i++) {
4650 n -= view[i].size;
4651 if (n < 0) { return i }
4652 }
4653 }
4654
4655 function updateSelection(cm) {
4656 cm.display.input.showSelection(cm.display.input.prepareSelection());
4657 }
4658
4659 function prepareSelection(cm, primary) {
4660 if ( primary === void 0 ) primary = true;
4661
4662 var doc = cm.doc, result = {};
4663 var curFragment = result.cursors = document.createDocumentFragment();
4664 var selFragment = result.selection = document.createDocumentFragment();
4665
4666 for (var i = 0; i < doc.sel.ranges.length; i++) {
4667 if (!primary && i == doc.sel.primIndex) { continue }
4668 var range$$1 = doc.sel.ranges[i];
4669 if (range$$1.from().line >= cm.display.viewTo || range$$1.to().line < cm.display.viewFrom) { continue }
4670 var collapsed = range$$1.empty();
4671 if (collapsed || cm.options.showCursorWhenSelecting)
4672 { drawSelectionCursor(cm, range$$1.head, curFragment); }
4673 if (!collapsed)
4674 { drawSelectionRange(cm, range$$1, selFragment); }
4675 }
4676 return result
4677 }
4678
4679 // Draws a cursor for the given range
4680 function drawSelectionCursor(cm, head, output) {
4681 var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine);
4682
4683 var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor"));
4684 cursor.style.left = pos.left + "px";
4685 cursor.style.top = pos.top + "px";
4686 cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
4687
4688 if (pos.other) {
4689 // Secondary cursor, shown when on a 'jump' in bi-directional text
4690 var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"));
4691 otherCursor.style.display = "";
4692 otherCursor.style.left = pos.other.left + "px";
4693 otherCursor.style.top = pos.other.top + "px";
4694 otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
4695 }
4696 }
4697
4698 function cmpCoords(a, b) { return a.top - b.top || a.left - b.left }
4699
4700 // Draws the given range as a highlighted selection
4701 function drawSelectionRange(cm, range$$1, output) {
4702 var display = cm.display, doc = cm.doc;
4703 var fragment = document.createDocumentFragment();
4704 var padding = paddingH(cm.display), leftSide = padding.left;
4705 var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right;
4706 var docLTR = doc.direction == "ltr";
4707
4708 function add(left, top, width, bottom) {
4709 if (top < 0) { top = 0; }
4710 top = Math.round(top);
4711 bottom = Math.round(bottom);
4712 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")));
4713 }
4714
4715 function drawForLine(line, fromArg, toArg) {
4716 var lineObj = getLine(doc, line);
4717 var lineLen = lineObj.text.length;
4718 var start, end;
4719 function coords(ch, bias) {
4720 return charCoords(cm, Pos(line, ch), "div", lineObj, bias)
4721 }
4722
4723 function wrapX(pos, dir, side) {
4724 var extent = wrappedLineExtentChar(cm, lineObj, null, pos);
4725 var prop = (dir == "ltr") == (side == "after") ? "left" : "right";
4726 var ch = side == "after" ? extent.begin : extent.end - (/\s/.test(lineObj.text.charAt(extent.end - 1)) ? 2 : 1);
4727 return coords(ch, prop)[prop]
4728 }
4729
4730 var order = getOrder(lineObj, doc.direction);
4731 iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir, i) {
4732 var ltr = dir == "ltr";
4733 var fromPos = coords(from, ltr ? "left" : "right");
4734 var toPos = coords(to - 1, ltr ? "right" : "left");
4735
4736 var openStart = fromArg == null && from == 0, openEnd = toArg == null && to == lineLen;
4737 var first = i == 0, last = !order || i == order.length - 1;
4738 if (toPos.top - fromPos.top <= 3) { // Single line
4739 var openLeft = (docLTR ? openStart : openEnd) && first;
4740 var openRight = (docLTR ? openEnd : openStart) && last;
4741 var left = openLeft ? leftSide : (ltr ? fromPos : toPos).left;
4742 var right = openRight ? rightSide : (ltr ? toPos : fromPos).right;
4743 add(left, fromPos.top, right - left, fromPos.bottom);
4744 } else { // Multiple lines
4745 var topLeft, topRight, botLeft, botRight;
4746 if (ltr) {
4747 topLeft = docLTR && openStart && first ? leftSide : fromPos.left;
4748 topRight = docLTR ? rightSide : wrapX(from, dir, "before");
4749 botLeft = docLTR ? leftSide : wrapX(to, dir, "after");
4750 botRight = docLTR && openEnd && last ? rightSide : toPos.right;
4751 } else {
4752 topLeft = !docLTR ? leftSide : wrapX(from, dir, "before");
4753 topRight = !docLTR && openStart && first ? rightSide : fromPos.right;
4754 botLeft = !docLTR && openEnd && last ? leftSide : toPos.left;
4755 botRight = !docLTR ? rightSide : wrapX(to, dir, "after");
4756 }
4757 add(topLeft, fromPos.top, topRight - topLeft, fromPos.bottom);
4758 if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top); }
4759 add(botLeft, toPos.top, botRight - botLeft, toPos.bottom);
4760 }
4761
4762 if (!start || cmpCoords(fromPos, start) < 0) { start = fromPos; }
4763 if (cmpCoords(toPos, start) < 0) { start = toPos; }
4764 if (!end || cmpCoords(fromPos, end) < 0) { end = fromPos; }
4765 if (cmpCoords(toPos, end) < 0) { end = toPos; }
4766 });
4767 return {start: start, end: end}
4768 }
4769
4770 var sFrom = range$$1.from(), sTo = range$$1.to();
4771 if (sFrom.line == sTo.line) {
4772 drawForLine(sFrom.line, sFrom.ch, sTo.ch);
4773 } else {
4774 var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line);
4775 var singleVLine = visualLine(fromLine) == visualLine(toLine);
4776 var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end;
4777 var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start;
4778 if (singleVLine) {
4779 if (leftEnd.top < rightStart.top - 2) {
4780 add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);
4781 add(leftSide, rightStart.top, rightStart.left, rightStart.bottom);
4782 } else {
4783 add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);
4784 }
4785 }
4786 if (leftEnd.bottom < rightStart.top)
4787 { add(leftSide, leftEnd.bottom, null, rightStart.top); }
4788 }
4789
4790 output.appendChild(fragment);
4791 }
4792
4793 // Cursor-blinking
4794 function restartBlink(cm) {
4795 if (!cm.state.focused) { return }
4796 var display = cm.display;
4797 clearInterval(display.blinker);
4798 var on = true;
4799 display.cursorDiv.style.visibility = "";
4800 if (cm.options.cursorBlinkRate > 0)
4801 { display.blinker = setInterval(function () { return display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; },
4802 cm.options.cursorBlinkRate); }
4803 else if (cm.options.cursorBlinkRate < 0)
4804 { display.cursorDiv.style.visibility = "hidden"; }
4805 }
4806
4807 function ensureFocus(cm) {
4808 if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); }
4809 }
4810
4811 function delayBlurEvent(cm) {
4812 cm.state.delayingBlurEvent = true;
4813 setTimeout(function () { if (cm.state.delayingBlurEvent) {
4814 cm.state.delayingBlurEvent = false;
4815 onBlur(cm);
4816 } }, 100);
4817 }
4818
4819 function onFocus(cm, e) {
4820 if (cm.state.delayingBlurEvent) { cm.state.delayingBlurEvent = false; }
4821
4822 if (cm.options.readOnly == "nocursor") { return }
4823 if (!cm.state.focused) {
4824 signal(cm, "focus", cm, e);
4825 cm.state.focused = true;
4826 addClass(cm.display.wrapper, "CodeMirror-focused");
4827 // This test prevents this from firing when a context
4828 // menu is closed (since the input reset would kill the
4829 // select-all detection hack)
4830 if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {
4831 cm.display.input.reset();
4832 if (webkit) { setTimeout(function () { return cm.display.input.reset(true); }, 20); } // Issue #1730
4833 }
4834 cm.display.input.receivedFocus();
4835 }
4836 restartBlink(cm);
4837 }
4838 function onBlur(cm, e) {
4839 if (cm.state.delayingBlurEvent) { return }
4840
4841 if (cm.state.focused) {
4842 signal(cm, "blur", cm, e);
4843 cm.state.focused = false;
4844 rmClass(cm.display.wrapper, "CodeMirror-focused");
4845 }
4846 clearInterval(cm.display.blinker);
4847 setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false; } }, 150);
4848 }
4849
4850 // Read the actual heights of the rendered lines, and update their
4851 // stored heights to match.
4852 function updateHeightsInViewport(cm) {
4853 var display = cm.display;
4854 var prevBottom = display.lineDiv.offsetTop;
4855 for (var i = 0; i < display.view.length; i++) {
4856 var cur = display.view[i], wrapping = cm.options.lineWrapping;
4857 var height = (void 0), width = 0;
4858 if (cur.hidden) { continue }
4859 if (ie && ie_version < 8) {
4860 var bot = cur.node.offsetTop + cur.node.offsetHeight;
4861 height = bot - prevBottom;
4862 prevBottom = bot;
4863 } else {
4864 var box = cur.node.getBoundingClientRect();
4865 height = box.bottom - box.top;
4866 // Check that lines don't extend past the right of the current
4867 // editor width
4868 if (!wrapping && cur.text.firstChild)
4869 { width = cur.text.firstChild.getBoundingClientRect().right - box.left - 1; }
4870 }
4871 var diff = cur.line.height - height;
4872 if (diff > .005 || diff < -.005) {
4873 updateLineHeight(cur.line, height);
4874 updateWidgetHeight(cur.line);
4875 if (cur.rest) { for (var j = 0; j < cur.rest.length; j++)
4876 { updateWidgetHeight(cur.rest[j]); } }
4877 }
4878 if (width > cm.display.sizerWidth) {
4879 var chWidth = Math.ceil(width / charWidth(cm.display));
4880 if (chWidth > cm.display.maxLineLength) {
4881 cm.display.maxLineLength = chWidth;
4882 cm.display.maxLine = cur.line;
4883 cm.display.maxLineChanged = true;
4884 }
4885 }
4886 }
4887 }
4888
4889 // Read and store the height of line widgets associated with the
4890 // given line.
4891 function updateWidgetHeight(line) {
4892 if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i) {
4893 var w = line.widgets[i], parent = w.node.parentNode;
4894 if (parent) { w.height = parent.offsetHeight; }
4895 } }
4896 }
4897
4898 // Compute the lines that are visible in a given viewport (defaults
4899 // the the current scroll position). viewport may contain top,
4900 // height, and ensure (see op.scrollToPos) properties.
4901 function visibleLines(display, doc, viewport) {
4902 var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop;
4903 top = Math.floor(top - paddingTop(display));
4904 var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight;
4905
4906 var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom);
4907 // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
4908 // forces those lines into the viewport (if possible).
4909 if (viewport && viewport.ensure) {
4910 var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line;
4911 if (ensureFrom < from) {
4912 from = ensureFrom;
4913 to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight);
4914 } else if (Math.min(ensureTo, doc.lastLine()) >= to) {
4915 from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight);
4916 to = ensureTo;
4917 }
4918 }
4919 return {from: from, to: Math.max(to, from + 1)}
4920 }
4921
4922 // Re-align line numbers and gutter marks to compensate for
4923 // horizontal scrolling.
4924 function alignHorizontally(cm) {
4925 var display = cm.display, view = display.view;
4926 if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) { return }
4927 var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;
4928 var gutterW = display.gutters.offsetWidth, left = comp + "px";
4929 for (var i = 0; i < view.length; i++) { if (!view[i].hidden) {
4930 if (cm.options.fixedGutter) {
4931 if (view[i].gutter)
4932 { view[i].gutter.style.left = left; }
4933 if (view[i].gutterBackground)
4934 { view[i].gutterBackground.style.left = left; }
4935 }
4936 var align = view[i].alignable;
4937 if (align) { for (var j = 0; j < align.length; j++)
4938 { align[j].style.left = left; } }
4939 } }
4940 if (cm.options.fixedGutter)
4941 { display.gutters.style.left = (comp + gutterW) + "px"; }
4942 }
4943
4944 // Used to ensure that the line number gutter is still the right
4945 // size for the current document size. Returns true when an update
4946 // is needed.
4947 function maybeUpdateLineNumberWidth(cm) {
4948 if (!cm.options.lineNumbers) { return false }
4949 var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;
4950 if (last.length != display.lineNumChars) {
4951 var test = display.measure.appendChild(elt("div", [elt("div", last)],
4952 "CodeMirror-linenumber CodeMirror-gutter-elt"));
4953 var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
4954 display.lineGutter.style.width = "";
4955 display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1;
4956 display.lineNumWidth = display.lineNumInnerWidth + padding;
4957 display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
4958 display.lineGutter.style.width = display.lineNumWidth + "px";
4959 updateGutterSpace(cm);
4960 return true
4961 }
4962 return false
4963 }
4964
4965 // SCROLLING THINGS INTO VIEW
4966
4967 // If an editor sits on the top or bottom of the window, partially
4968 // scrolled out of view, this ensures that the cursor is visible.
4969 function maybeScrollWindow(cm, rect) {
4970 if (signalDOMEvent(cm, "scrollCursorIntoView")) { return }
4971
4972 var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;
4973 if (rect.top + box.top < 0) { doScroll = true; }
4974 else if (rect.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) { doScroll = false; }
4975 if (doScroll != null && !phantom) {
4976 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;"));
4977 cm.display.lineSpace.appendChild(scrollNode);
4978 scrollNode.scrollIntoView(doScroll);
4979 cm.display.lineSpace.removeChild(scrollNode);
4980 }
4981 }
4982
4983 // Scroll a given position into view (immediately), verifying that
4984 // it actually became visible (as line heights are accurately
4985 // measured, the position of something may 'drift' during drawing).
4986 function scrollPosIntoView(cm, pos, end, margin) {
4987 if (margin == null) { margin = 0; }
4988 var rect;
4989 if (!cm.options.lineWrapping && pos == end) {
4990 // Set pos and end to the cursor positions around the character pos sticks to
4991 // If pos.sticky == "before", that is around pos.ch - 1, otherwise around pos.ch
4992 // If pos == Pos(_, 0, "before"), pos and end are unchanged
4993 pos = pos.ch ? Pos(pos.line, pos.sticky == "before" ? pos.ch - 1 : pos.ch, "after") : pos;
4994 end = pos.sticky == "before" ? Pos(pos.line, pos.ch + 1, "before") : pos;
4995 }
4996 for (var limit = 0; limit < 5; limit++) {
4997 var changed = false;
4998 var coords = cursorCoords(cm, pos);
4999 var endCoords = !end || end == pos ? coords : cursorCoords(cm, end);
5000 rect = {left: Math.min(coords.left, endCoords.left),
5001 top: Math.min(coords.top, endCoords.top) - margin,
5002 right: Math.max(coords.left, endCoords.left),
5003 bottom: Math.max(coords.bottom, endCoords.bottom) + margin};
5004 var scrollPos = calculateScrollPos(cm, rect);
5005 var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
5006 if (scrollPos.scrollTop != null) {
5007 updateScrollTop(cm, scrollPos.scrollTop);
5008 if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true; }
5009 }
5010 if (scrollPos.scrollLeft != null) {
5011 setScrollLeft(cm, scrollPos.scrollLeft);
5012 if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true; }
5013 }
5014 if (!changed) { break }
5015 }
5016 return rect
5017 }
5018
5019 // Scroll a given set of coordinates into view (immediately).
5020 function scrollIntoView(cm, rect) {
5021 var scrollPos = calculateScrollPos(cm, rect);
5022 if (scrollPos.scrollTop != null) { updateScrollTop(cm, scrollPos.scrollTop); }
5023 if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft); }
5024 }
5025
5026 // Calculate a new scroll position needed to scroll the given
5027 // rectangle into view. Returns an object with scrollTop and
5028 // scrollLeft properties. When these are undefined, the
5029 // vertical/horizontal position does not need to be adjusted.
5030 function calculateScrollPos(cm, rect) {
5031 var display = cm.display, snapMargin = textHeight(cm.display);
5032 if (rect.top < 0) { rect.top = 0; }
5033 var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop;
5034 var screen = displayHeight(cm), result = {};
5035 if (rect.bottom - rect.top > screen) { rect.bottom = rect.top + screen; }
5036 var docBottom = cm.doc.height + paddingVert(display);
5037 var atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMargin;
5038 if (rect.top < screentop) {
5039 result.scrollTop = atTop ? 0 : rect.top;
5040 } else if (rect.bottom > screentop + screen) {
5041 var newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen);
5042 if (newTop != screentop) { result.scrollTop = newTop; }
5043 }
5044
5045 var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft;
5046 var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0);
5047 var tooWide = rect.right - rect.left > screenw;
5048 if (tooWide) { rect.right = rect.left + screenw; }
5049 if (rect.left < 10)
5050 { result.scrollLeft = 0; }
5051 else if (rect.left < screenleft)
5052 { result.scrollLeft = Math.max(0, rect.left - (tooWide ? 0 : 10)); }
5053 else if (rect.right > screenw + screenleft - 3)
5054 { result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw; }
5055 return result
5056 }
5057
5058 // Store a relative adjustment to the scroll position in the current
5059 // operation (to be applied when the operation finishes).
5060 function addToScrollTop(cm, top) {
5061 if (top == null) { return }
5062 resolveScrollToPos(cm);
5063 cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top;
5064 }
5065
5066 // Make sure that at the end of the operation the current cursor is
5067 // shown.
5068 function ensureCursorVisible(cm) {
5069 resolveScrollToPos(cm);
5070 var cur = cm.getCursor();
5071 cm.curOp.scrollToPos = {from: cur, to: cur, margin: cm.options.cursorScrollMargin};
5072 }
5073
5074 function scrollToCoords(cm, x, y) {
5075 if (x != null || y != null) { resolveScrollToPos(cm); }
5076 if (x != null) { cm.curOp.scrollLeft = x; }
5077 if (y != null) { cm.curOp.scrollTop = y; }
5078 }
5079
5080 function scrollToRange(cm, range$$1) {
5081 resolveScrollToPos(cm);
5082 cm.curOp.scrollToPos = range$$1;
5083 }
5084
5085 // When an operation has its scrollToPos property set, and another
5086 // scroll action is applied before the end of the operation, this
5087 // 'simulates' scrolling that position into view in a cheap way, so
5088 // that the effect of intermediate scroll commands is not ignored.
5089 function resolveScrollToPos(cm) {
5090 var range$$1 = cm.curOp.scrollToPos;
5091 if (range$$1) {
5092 cm.curOp.scrollToPos = null;
5093 var from = estimateCoords(cm, range$$1.from), to = estimateCoords(cm, range$$1.to);
5094 scrollToCoordsRange(cm, from, to, range$$1.margin);
5095 }
5096 }
5097
5098 function scrollToCoordsRange(cm, from, to, margin) {
5099 var sPos = calculateScrollPos(cm, {
5100 left: Math.min(from.left, to.left),
5101 top: Math.min(from.top, to.top) - margin,
5102 right: Math.max(from.right, to.right),
5103 bottom: Math.max(from.bottom, to.bottom) + margin
5104 });
5105 scrollToCoords(cm, sPos.scrollLeft, sPos.scrollTop);
5106 }
5107
5108 // Sync the scrollable area and scrollbars, ensure the viewport
5109 // covers the visible area.
5110 function updateScrollTop(cm, val) {
5111 if (Math.abs(cm.doc.scrollTop - val) < 2) { return }
5112 if (!gecko) { updateDisplaySimple(cm, {top: val}); }
5113 setScrollTop(cm, val, true);
5114 if (gecko) { updateDisplaySimple(cm); }
5115 startWorker(cm, 100);
5116 }
5117
5118 function setScrollTop(cm, val, forceScroll) {
5119 val = Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val);
5120 if (cm.display.scroller.scrollTop == val && !forceScroll) { return }
5121 cm.doc.scrollTop = val;
5122 cm.display.scrollbars.setScrollTop(val);
5123 if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = val; }
5124 }
5125
5126 // Sync scroller and scrollbar, ensure the gutter elements are
5127 // aligned.
5128 function setScrollLeft(cm, val, isScroller, forceScroll) {
5129 val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth);
5130 if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) { return }
5131 cm.doc.scrollLeft = val;
5132 alignHorizontally(cm);
5133 if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val; }
5134 cm.display.scrollbars.setScrollLeft(val);
5135 }
5136
5137 // SCROLLBARS
5138
5139 // Prepare DOM reads needed to update the scrollbars. Done in one
5140 // shot to minimize update/measure roundtrips.
5141 function measureForScrollbars(cm) {
5142 var d = cm.display, gutterW = d.gutters.offsetWidth;
5143 var docH = Math.round(cm.doc.height + paddingVert(cm.display));
5144 return {
5145 clientHeight: d.scroller.clientHeight,
5146 viewHeight: d.wrapper.clientHeight,
5147 scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,
5148 viewWidth: d.wrapper.clientWidth,
5149 barLeft: cm.options.fixedGutter ? gutterW : 0,
5150 docHeight: docH,
5151 scrollHeight: docH + scrollGap(cm) + d.barHeight,
5152 nativeBarWidth: d.nativeBarWidth,
5153 gutterWidth: gutterW
5154 }
5155 }
5156
5157 var NativeScrollbars = function(place, scroll, cm) {
5158 this.cm = cm;
5159 var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar");
5160 var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar");
5161 vert.tabIndex = horiz.tabIndex = -1;
5162 place(vert); place(horiz);
5163
5164 on(vert, "scroll", function () {
5165 if (vert.clientHeight) { scroll(vert.scrollTop, "vertical"); }
5166 });
5167 on(horiz, "scroll", function () {
5168 if (horiz.clientWidth) { scroll(horiz.scrollLeft, "horizontal"); }
5169 });
5170
5171 this.checkedZeroWidth = false;
5172 // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
5173 if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = "18px"; }
5174 };
5175
5176 NativeScrollbars.prototype.update = function (measure) {
5177 var needsH = measure.scrollWidth > measure.clientWidth + 1;
5178 var needsV = measure.scrollHeight > measure.clientHeight + 1;
5179 var sWidth = measure.nativeBarWidth;
5180
5181 if (needsV) {
5182 this.vert.style.display = "block";
5183 this.vert.style.bottom = needsH ? sWidth + "px" : "0";
5184 var totalHeight = measure.viewHeight - (needsH ? sWidth : 0);
5185 // A bug in IE8 can cause this value to be negative, so guard it.
5186 this.vert.firstChild.style.height =
5187 Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px";
5188 } else {
5189 this.vert.style.display = "";
5190 this.vert.firstChild.style.height = "0";
5191 }
5192
5193 if (needsH) {
5194 this.horiz.style.display = "block";
5195 this.horiz.style.right = needsV ? sWidth + "px" : "0";
5196 this.horiz.style.left = measure.barLeft + "px";
5197 var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0);
5198 this.horiz.firstChild.style.width =
5199 Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + "px";
5200 } else {
5201 this.horiz.style.display = "";
5202 this.horiz.firstChild.style.width = "0";
5203 }
5204
5205 if (!this.checkedZeroWidth && measure.clientHeight > 0) {
5206 if (sWidth == 0) { this.zeroWidthHack(); }
5207 this.checkedZeroWidth = true;
5208 }
5209
5210 return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0}
5211 };
5212
5213 NativeScrollbars.prototype.setScrollLeft = function (pos) {
5214 if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos; }
5215 if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz, "horiz"); }
5216 };
5217
5218 NativeScrollbars.prototype.setScrollTop = function (pos) {
5219 if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos; }
5220 if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert, "vert"); }
5221 };
5222
5223 NativeScrollbars.prototype.zeroWidthHack = function () {
5224 var w = mac && !mac_geMountainLion ? "12px" : "18px";
5225 this.horiz.style.height = this.vert.style.width = w;
5226 this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none";
5227 this.disableHoriz = new Delayed;
5228 this.disableVert = new Delayed;
5229 };
5230
5231 NativeScrollbars.prototype.enableZeroWidthBar = function (bar, delay, type) {
5232 bar.style.pointerEvents = "auto";
5233 function maybeDisable() {
5234 // To find out whether the scrollbar is still visible, we
5235 // check whether the element under the pixel in the bottom
5236 // right corner of the scrollbar box is the scrollbar box
5237 // itself (when the bar is still visible) or its filler child
5238 // (when the bar is hidden). If it is still visible, we keep
5239 // it enabled, if it's hidden, we disable pointer events.
5240 var box = bar.getBoundingClientRect();
5241 var elt$$1 = type == "vert" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2)
5242 : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1);
5243 if (elt$$1 != bar) { bar.style.pointerEvents = "none"; }
5244 else { delay.set(1000, maybeDisable); }
5245 }
5246 delay.set(1000, maybeDisable);
5247 };
5248
5249 NativeScrollbars.prototype.clear = function () {
5250 var parent = this.horiz.parentNode;
5251 parent.removeChild(this.horiz);
5252 parent.removeChild(this.vert);
5253 };
5254
5255 var NullScrollbars = function () {};
5256
5257 NullScrollbars.prototype.update = function () { return {bottom: 0, right: 0} };
5258 NullScrollbars.prototype.setScrollLeft = function () {};
5259 NullScrollbars.prototype.setScrollTop = function () {};
5260 NullScrollbars.prototype.clear = function () {};
5261
5262 function updateScrollbars(cm, measure) {
5263 if (!measure) { measure = measureForScrollbars(cm); }
5264 var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight;
5265 updateScrollbarsInner(cm, measure);
5266 for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) {
5267 if (startWidth != cm.display.barWidth && cm.options.lineWrapping)
5268 { updateHeightsInViewport(cm); }
5269 updateScrollbarsInner(cm, measureForScrollbars(cm));
5270 startWidth = cm.display.barWidth; startHeight = cm.display.barHeight;
5271 }
5272 }
5273
5274 // Re-synchronize the fake scrollbars with the actual size of the
5275 // content.
5276 function updateScrollbarsInner(cm, measure) {
5277 var d = cm.display;
5278 var sizes = d.scrollbars.update(measure);
5279
5280 d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px";
5281 d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px";
5282 d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent";
5283
5284 if (sizes.right && sizes.bottom) {
5285 d.scrollbarFiller.style.display = "block";
5286 d.scrollbarFiller.style.height = sizes.bottom + "px";
5287 d.scrollbarFiller.style.width = sizes.right + "px";
5288 } else { d.scrollbarFiller.style.display = ""; }
5289 if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
5290 d.gutterFiller.style.display = "block";
5291 d.gutterFiller.style.height = sizes.bottom + "px";
5292 d.gutterFiller.style.width = measure.gutterWidth + "px";
5293 } else { d.gutterFiller.style.display = ""; }
5294 }
5295
5296 var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars};
5297
5298 function initScrollbars(cm) {
5299 if (cm.display.scrollbars) {
5300 cm.display.scrollbars.clear();
5301 if (cm.display.scrollbars.addClass)
5302 { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass); }
5303 }
5304
5305 cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) {
5306 cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller);
5307 // Prevent clicks in the scrollbars from killing focus
5308 on(node, "mousedown", function () {
5309 if (cm.state.focused) { setTimeout(function () { return cm.display.input.focus(); }, 0); }
5310 });
5311 node.setAttribute("cm-not-content", "true");
5312 }, function (pos, axis) {
5313 if (axis == "horizontal") { setScrollLeft(cm, pos); }
5314 else { updateScrollTop(cm, pos); }
5315 }, cm);
5316 if (cm.display.scrollbars.addClass)
5317 { addClass(cm.display.wrapper, cm.display.scrollbars.addClass); }
5318 }
5319
5320 // Operations are used to wrap a series of changes to the editor
5321 // state in such a way that each change won't have to update the
5322 // cursor and display (which would be awkward, slow, and
5323 // error-prone). Instead, display updates are batched and then all
5324 // combined and executed at once.
5325
5326 var nextOpId = 0;
5327 // Start a new operation.
5328 function startOperation(cm) {
5329 cm.curOp = {
5330 cm: cm,
5331 viewChanged: false, // Flag that indicates that lines might need to be redrawn
5332 startHeight: cm.doc.height, // Used to detect need to update scrollbar
5333 forceUpdate: false, // Used to force a redraw
5334 updateInput: 0, // Whether to reset the input textarea
5335 typing: false, // Whether this reset should be careful to leave existing text (for compositing)
5336 changeObjs: null, // Accumulated changes, for firing change events
5337 cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on
5338 cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
5339 selectionChanged: false, // Whether the selection needs to be redrawn
5340 updateMaxLine: false, // Set when the widest line needs to be determined anew
5341 scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
5342 scrollToPos: null, // Used to scroll to a specific position
5343 focus: false,
5344 id: ++nextOpId // Unique ID
5345 };
5346 pushOperation(cm.curOp);
5347 }
5348
5349 // Finish an operation, updating the display and signalling delayed events
5350 function endOperation(cm) {
5351 var op = cm.curOp;
5352 if (op) { finishOperation(op, function (group) {
5353 for (var i = 0; i < group.ops.length; i++)
5354 { group.ops[i].cm.curOp = null; }
5355 endOperations(group);
5356 }); }
5357 }
5358
5359 // The DOM updates done when an operation finishes are batched so
5360 // that the minimum number of relayouts are required.
5361 function endOperations(group) {
5362 var ops = group.ops;
5363 for (var i = 0; i < ops.length; i++) // Read DOM
5364 { endOperation_R1(ops[i]); }
5365 for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe)
5366 { endOperation_W1(ops[i$1]); }
5367 for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM
5368 { endOperation_R2(ops[i$2]); }
5369 for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe)
5370 { endOperation_W2(ops[i$3]); }
5371 for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM
5372 { endOperation_finish(ops[i$4]); }
5373 }
5374
5375 function endOperation_R1(op) {
5376 var cm = op.cm, display = cm.display;
5377 maybeClipScrollbars(cm);
5378 if (op.updateMaxLine) { findMaxLine(cm); }
5379
5380 op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||
5381 op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
5382 op.scrollToPos.to.line >= display.viewTo) ||
5383 display.maxLineChanged && cm.options.lineWrapping;
5384 op.update = op.mustUpdate &&
5385 new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate);
5386 }
5387
5388 function endOperation_W1(op) {
5389 op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update);
5390 }
5391
5392 function endOperation_R2(op) {
5393 var cm = op.cm, display = cm.display;
5394 if (op.updatedDisplay) { updateHeightsInViewport(cm); }
5395
5396 op.barMeasure = measureForScrollbars(cm);
5397
5398 // If the max line changed since it was last measured, measure it,
5399 // and ensure the document's width matches it.
5400 // updateDisplay_W2 will use these properties to do the actual resizing
5401 if (display.maxLineChanged && !cm.options.lineWrapping) {
5402 op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3;
5403 cm.display.sizerWidth = op.adjustWidthTo;
5404 op.barMeasure.scrollWidth =
5405 Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth);
5406 op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm));
5407 }
5408
5409 if (op.updatedDisplay || op.selectionChanged)
5410 { op.preparedSelection = display.input.prepareSelection(); }
5411 }
5412
5413 function endOperation_W2(op) {
5414 var cm = op.cm;
5415
5416 if (op.adjustWidthTo != null) {
5417 cm.display.sizer.style.minWidth = op.adjustWidthTo + "px";
5418 if (op.maxScrollLeft < cm.doc.scrollLeft)
5419 { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true); }
5420 cm.display.maxLineChanged = false;
5421 }
5422
5423 var takeFocus = op.focus && op.focus == activeElt();
5424 if (op.preparedSelection)
5425 { cm.display.input.showSelection(op.preparedSelection, takeFocus); }
5426 if (op.updatedDisplay || op.startHeight != cm.doc.height)
5427 { updateScrollbars(cm, op.barMeasure); }
5428 if (op.updatedDisplay)
5429 { setDocumentHeight(cm, op.barMeasure); }
5430
5431 if (op.selectionChanged) { restartBlink(cm); }
5432
5433 if (cm.state.focused && op.updateInput)
5434 { cm.display.input.reset(op.typing); }
5435 if (takeFocus) { ensureFocus(op.cm); }
5436 }
5437
5438 function endOperation_finish(op) {
5439 var cm = op.cm, display = cm.display, doc = cm.doc;
5440
5441 if (op.updatedDisplay) { postUpdateDisplay(cm, op.update); }
5442
5443 // Abort mouse wheel delta measurement, when scrolling explicitly
5444 if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))
5445 { display.wheelStartX = display.wheelStartY = null; }
5446
5447 // Propagate the scroll position to the actual DOM scroller
5448 if (op.scrollTop != null) { setScrollTop(cm, op.scrollTop, op.forceScroll); }
5449
5450 if (op.scrollLeft != null) { setScrollLeft(cm, op.scrollLeft, true, true); }
5451 // If we need to scroll a specific position into view, do so.
5452 if (op.scrollToPos) {
5453 var rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
5454 clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin);
5455 maybeScrollWindow(cm, rect);
5456 }
5457
5458 // Fire events for markers that are hidden/unidden by editing or
5459 // undoing
5460 var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
5461 if (hidden) { for (var i = 0; i < hidden.length; ++i)
5462 { if (!hidden[i].lines.length) { signal(hidden[i], "hide"); } } }
5463 if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1)
5464 { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], "unhide"); } } }
5465
5466 if (display.wrapper.offsetHeight)
5467 { doc.scrollTop = cm.display.scroller.scrollTop; }
5468
5469 // Fire change events, and delayed event handlers
5470 if (op.changeObjs)
5471 { signal(cm, "changes", cm, op.changeObjs); }
5472 if (op.update)
5473 { op.update.finish(); }
5474 }
5475
5476 // Run the given function in an operation
5477 function runInOp(cm, f) {
5478 if (cm.curOp) { return f() }
5479 startOperation(cm);
5480 try { return f() }
5481 finally { endOperation(cm); }
5482 }
5483 // Wraps a function in an operation. Returns the wrapped function.
5484 function operation(cm, f) {
5485 return function() {
5486 if (cm.curOp) { return f.apply(cm, arguments) }
5487 startOperation(cm);
5488 try { return f.apply(cm, arguments) }
5489 finally { endOperation(cm); }
5490 }
5491 }
5492 // Used to add methods to editor and doc instances, wrapping them in
5493 // operations.
5494 function methodOp(f) {
5495 return function() {
5496 if (this.curOp) { return f.apply(this, arguments) }
5497 startOperation(this);
5498 try { return f.apply(this, arguments) }
5499 finally { endOperation(this); }
5500 }
5501 }
5502 function docMethodOp(f) {
5503 return function() {
5504 var cm = this.cm;
5505 if (!cm || cm.curOp) { return f.apply(this, arguments) }
5506 startOperation(cm);
5507 try { return f.apply(this, arguments) }
5508 finally { endOperation(cm); }
5509 }
5510 }
5511
5512 // Updates the display.view data structure for a given change to the
5513 // document. From and to are in pre-change coordinates. Lendiff is
5514 // the amount of lines added or subtracted by the change. This is
5515 // used for changes that span multiple lines, or change the way
5516 // lines are divided into visual lines. regLineChange (below)
5517 // registers single-line changes.
5518 function regChange(cm, from, to, lendiff) {
5519 if (from == null) { from = cm.doc.first; }
5520 if (to == null) { to = cm.doc.first + cm.doc.size; }
5521 if (!lendiff) { lendiff = 0; }
5522
5523 var display = cm.display;
5524 if (lendiff && to < display.viewTo &&
5525 (display.updateLineNumbers == null || display.updateLineNumbers > from))
5526 { display.updateLineNumbers = from; }
5527
5528 cm.curOp.viewChanged = true;
5529
5530 if (from >= display.viewTo) { // Change after
5531 if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)
5532 { resetView(cm); }
5533 } else if (to <= display.viewFrom) { // Change before
5534 if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {
5535 resetView(cm);
5536 } else {
5537 display.viewFrom += lendiff;
5538 display.viewTo += lendiff;
5539 }
5540 } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap
5541 resetView(cm);
5542 } else if (from <= display.viewFrom) { // Top overlap
5543 var cut = viewCuttingPoint(cm, to, to + lendiff, 1);
5544 if (cut) {
5545 display.view = display.view.slice(cut.index);
5546 display.viewFrom = cut.lineN;
5547 display.viewTo += lendiff;
5548 } else {
5549 resetView(cm);
5550 }
5551 } else if (to >= display.viewTo) { // Bottom overlap
5552 var cut$1 = viewCuttingPoint(cm, from, from, -1);
5553 if (cut$1) {
5554 display.view = display.view.slice(0, cut$1.index);
5555 display.viewTo = cut$1.lineN;
5556 } else {
5557 resetView(cm);
5558 }
5559 } else { // Gap in the middle
5560 var cutTop = viewCuttingPoint(cm, from, from, -1);
5561 var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1);
5562 if (cutTop && cutBot) {
5563 display.view = display.view.slice(0, cutTop.index)
5564 .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))
5565 .concat(display.view.slice(cutBot.index));
5566 display.viewTo += lendiff;
5567 } else {
5568 resetView(cm);
5569 }
5570 }
5571
5572 var ext = display.externalMeasured;
5573 if (ext) {
5574 if (to < ext.lineN)
5575 { ext.lineN += lendiff; }
5576 else if (from < ext.lineN + ext.size)
5577 { display.externalMeasured = null; }
5578 }
5579 }
5580
5581 // Register a change to a single line. Type must be one of "text",
5582 // "gutter", "class", "widget"
5583 function regLineChange(cm, line, type) {
5584 cm.curOp.viewChanged = true;
5585 var display = cm.display, ext = cm.display.externalMeasured;
5586 if (ext && line >= ext.lineN && line < ext.lineN + ext.size)
5587 { display.externalMeasured = null; }
5588
5589 if (line < display.viewFrom || line >= display.viewTo) { return }
5590 var lineView = display.view[findViewIndex(cm, line)];
5591 if (lineView.node == null) { return }
5592 var arr = lineView.changes || (lineView.changes = []);
5593 if (indexOf(arr, type) == -1) { arr.push(type); }
5594 }
5595
5596 // Clear the view.
5597 function resetView(cm) {
5598 cm.display.viewFrom = cm.display.viewTo = cm.doc.first;
5599 cm.display.view = [];
5600 cm.display.viewOffset = 0;
5601 }
5602
5603 function viewCuttingPoint(cm, oldN, newN, dir) {
5604 var index = findViewIndex(cm, oldN), diff, view = cm.display.view;
5605 if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)
5606 { return {index: index, lineN: newN} }
5607 var n = cm.display.viewFrom;
5608 for (var i = 0; i < index; i++)
5609 { n += view[i].size; }
5610 if (n != oldN) {
5611 if (dir > 0) {
5612 if (index == view.length - 1) { return null }
5613 diff = (n + view[index].size) - oldN;
5614 index++;
5615 } else {
5616 diff = n - oldN;
5617 }
5618 oldN += diff; newN += diff;
5619 }
5620 while (visualLineNo(cm.doc, newN) != newN) {
5621 if (index == (dir < 0 ? 0 : view.length - 1)) { return null }
5622 newN += dir * view[index - (dir < 0 ? 1 : 0)].size;
5623 index += dir;
5624 }
5625 return {index: index, lineN: newN}
5626 }
5627
5628 // Force the view to cover a given range, adding empty view element
5629 // or clipping off existing ones as needed.
5630 function adjustView(cm, from, to) {
5631 var display = cm.display, view = display.view;
5632 if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {
5633 display.view = buildViewArray(cm, from, to);
5634 display.viewFrom = from;
5635 } else {
5636 if (display.viewFrom > from)
5637 { display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view); }
5638 else if (display.viewFrom < from)
5639 { display.view = display.view.slice(findViewIndex(cm, from)); }
5640 display.viewFrom = from;
5641 if (display.viewTo < to)
5642 { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)); }
5643 else if (display.viewTo > to)
5644 { display.view = display.view.slice(0, findViewIndex(cm, to)); }
5645 }
5646 display.viewTo = to;
5647 }
5648
5649 // Count the number of lines in the view whose DOM representation is
5650 // out of date (or nonexistent).
5651 function countDirtyView(cm) {
5652 var view = cm.display.view, dirty = 0;
5653 for (var i = 0; i < view.length; i++) {
5654 var lineView = view[i];
5655 if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty; }
5656 }
5657 return dirty
5658 }
5659
5660 // HIGHLIGHT WORKER
5661
5662 function startWorker(cm, time) {
5663 if (cm.doc.highlightFrontier < cm.display.viewTo)
5664 { cm.state.highlight.set(time, bind(highlightWorker, cm)); }
5665 }
5666
5667 function highlightWorker(cm) {
5668 var doc = cm.doc;
5669 if (doc.highlightFrontier >= cm.display.viewTo) { return }
5670 var end = +new Date + cm.options.workTime;
5671 var context = getContextBefore(cm, doc.highlightFrontier);
5672 var changedLines = [];
5673
5674 doc.iter(context.line, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function (line) {
5675 if (context.line >= cm.display.viewFrom) { // Visible
5676 var oldStyles = line.styles;
5677 var resetState = line.text.length > cm.options.maxHighlightLength ? copyState(doc.mode, context.state) : null;
5678 var highlighted = highlightLine(cm, line, context, true);
5679 if (resetState) { context.state = resetState; }
5680 line.styles = highlighted.styles;
5681 var oldCls = line.styleClasses, newCls = highlighted.classes;
5682 if (newCls) { line.styleClasses = newCls; }
5683 else if (oldCls) { line.styleClasses = null; }
5684 var ischange = !oldStyles || oldStyles.length != line.styles.length ||
5685 oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass);
5686 for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldStyles[i] != line.styles[i]; }
5687 if (ischange) { changedLines.push(context.line); }
5688 line.stateAfter = context.save();
5689 context.nextLine();
5690 } else {
5691 if (line.text.length <= cm.options.maxHighlightLength)
5692 { processLine(cm, line.text, context); }
5693 line.stateAfter = context.line % 5 == 0 ? context.save() : null;
5694 context.nextLine();
5695 }
5696 if (+new Date > end) {
5697 startWorker(cm, cm.options.workDelay);
5698 return true
5699 }
5700 });
5701 doc.highlightFrontier = context.line;
5702 doc.modeFrontier = Math.max(doc.modeFrontier, context.line);
5703 if (changedLines.length) { runInOp(cm, function () {
5704 for (var i = 0; i < changedLines.length; i++)
5705 { regLineChange(cm, changedLines[i], "text"); }
5706 }); }
5707 }
5708
5709 // DISPLAY DRAWING
5710
5711 var DisplayUpdate = function(cm, viewport, force) {
5712 var display = cm.display;
5713
5714 this.viewport = viewport;
5715 // Store some values that we'll need later (but don't want to force a relayout for)
5716 this.visible = visibleLines(display, cm.doc, viewport);
5717 this.editorIsHidden = !display.wrapper.offsetWidth;
5718 this.wrapperHeight = display.wrapper.clientHeight;
5719 this.wrapperWidth = display.wrapper.clientWidth;
5720 this.oldDisplayWidth = displayWidth(cm);
5721 this.force = force;
5722 this.dims = getDimensions(cm);
5723 this.events = [];
5724 };
5725
5726 DisplayUpdate.prototype.signal = function (emitter, type) {
5727 if (hasHandler(emitter, type))
5728 { this.events.push(arguments); }
5729 };
5730 DisplayUpdate.prototype.finish = function () {
5731 var this$1 = this;
5732
5733 for (var i = 0; i < this.events.length; i++)
5734 { signal.apply(null, this$1.events[i]); }
5735 };
5736
5737 function maybeClipScrollbars(cm) {
5738 var display = cm.display;
5739 if (!display.scrollbarsClipped && display.scroller.offsetWidth) {
5740 display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth;
5741 display.heightForcer.style.height = scrollGap(cm) + "px";
5742 display.sizer.style.marginBottom = -display.nativeBarWidth + "px";
5743 display.sizer.style.borderRightWidth = scrollGap(cm) + "px";
5744 display.scrollbarsClipped = true;
5745 }
5746 }
5747
5748 function selectionSnapshot(cm) {
5749 if (cm.hasFocus()) { return null }
5750 var active = activeElt();
5751 if (!active || !contains(cm.display.lineDiv, active)) { return null }
5752 var result = {activeElt: active};
5753 if (window.getSelection) {
5754 var sel = window.getSelection();
5755 if (sel.anchorNode && sel.extend && contains(cm.display.lineDiv, sel.anchorNode)) {
5756 result.anchorNode = sel.anchorNode;
5757 result.anchorOffset = sel.anchorOffset;
5758 result.focusNode = sel.focusNode;
5759 result.focusOffset = sel.focusOffset;
5760 }
5761 }
5762 return result
5763 }
5764
5765 function restoreSelection(snapshot) {
5766 if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt()) { return }
5767 snapshot.activeElt.focus();
5768 if (snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) {
5769 var sel = window.getSelection(), range$$1 = document.createRange();
5770 range$$1.setEnd(snapshot.anchorNode, snapshot.anchorOffset);
5771 range$$1.collapse(false);
5772 sel.removeAllRanges();
5773 sel.addRange(range$$1);
5774 sel.extend(snapshot.focusNode, snapshot.focusOffset);
5775 }
5776 }
5777
5778 // Does the actual updating of the line display. Bails out
5779 // (returning false) when there is nothing to be done and forced is
5780 // false.
5781 function updateDisplayIfNeeded(cm, update) {
5782 var display = cm.display, doc = cm.doc;
5783
5784 if (update.editorIsHidden) {
5785 resetView(cm);
5786 return false
5787 }
5788
5789 // Bail out if the visible area is already rendered and nothing changed.
5790 if (!update.force &&
5791 update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&
5792 (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&
5793 display.renderedView == display.view && countDirtyView(cm) == 0)
5794 { return false }
5795
5796 if (maybeUpdateLineNumberWidth(cm)) {
5797 resetView(cm);
5798 update.dims = getDimensions(cm);
5799 }
5800
5801 // Compute a suitable new viewport (from & to)
5802 var end = doc.first + doc.size;
5803 var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first);
5804 var to = Math.min(end, update.visible.to + cm.options.viewportMargin);
5805 if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max(doc.first, display.viewFrom); }
5806 if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, display.viewTo); }
5807 if (sawCollapsedSpans) {
5808 from = visualLineNo(cm.doc, from);
5809 to = visualLineEndNo(cm.doc, to);
5810 }
5811
5812 var different = from != display.viewFrom || to != display.viewTo ||
5813 display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth;
5814 adjustView(cm, from, to);
5815
5816 display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom));
5817 // Position the mover div to align with the current scroll position
5818 cm.display.mover.style.top = display.viewOffset + "px";
5819
5820 var toUpdate = countDirtyView(cm);
5821 if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view &&
5822 (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))
5823 { return false }
5824
5825 // For big changes, we hide the enclosing element during the
5826 // update, since that speeds up the operations on most browsers.
5827 var selSnapshot = selectionSnapshot(cm);
5828 if (toUpdate > 4) { display.lineDiv.style.display = "none"; }
5829 patchDisplay(cm, display.updateLineNumbers, update.dims);
5830 if (toUpdate > 4) { display.lineDiv.style.display = ""; }
5831 display.renderedView = display.view;
5832 // There might have been a widget with a focused element that got
5833 // hidden or updated, if so re-focus it.
5834 restoreSelection(selSnapshot);
5835
5836 // Prevent selection and cursors from interfering with the scroll
5837 // width and height.
5838 removeChildren(display.cursorDiv);
5839 removeChildren(display.selectionDiv);
5840 display.gutters.style.height = display.sizer.style.minHeight = 0;
5841
5842 if (different) {
5843 display.lastWrapHeight = update.wrapperHeight;
5844 display.lastWrapWidth = update.wrapperWidth;
5845 startWorker(cm, 400);
5846 }
5847
5848 display.updateLineNumbers = null;
5849
5850 return true
5851 }
5852
5853 function postUpdateDisplay(cm, update) {
5854 var viewport = update.viewport;
5855
5856 for (var first = true;; first = false) {
5857 if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) {
5858 // Clip forced viewport to actual scrollable area.
5859 if (viewport && viewport.top != null)
5860 { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)}; }
5861 // Updated line heights might result in the drawn area not
5862 // actually covering the viewport. Keep looping until it does.
5863 update.visible = visibleLines(cm.display, cm.doc, viewport);
5864 if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)
5865 { break }
5866 }
5867 if (!updateDisplayIfNeeded(cm, update)) { break }
5868 updateHeightsInViewport(cm);
5869 var barMeasure = measureForScrollbars(cm);
5870 updateSelection(cm);
5871 updateScrollbars(cm, barMeasure);
5872 setDocumentHeight(cm, barMeasure);
5873 update.force = false;
5874 }
5875
5876 update.signal(cm, "update", cm);
5877 if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {
5878 update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo);
5879 cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo;
5880 }
5881 }
5882
5883 function updateDisplaySimple(cm, viewport) {
5884 var update = new DisplayUpdate(cm, viewport);
5885 if (updateDisplayIfNeeded(cm, update)) {
5886 updateHeightsInViewport(cm);
5887 postUpdateDisplay(cm, update);
5888 var barMeasure = measureForScrollbars(cm);
5889 updateSelection(cm);
5890 updateScrollbars(cm, barMeasure);
5891 setDocumentHeight(cm, barMeasure);
5892 update.finish();
5893 }
5894 }
5895
5896 // Sync the actual display DOM structure with display.view, removing
5897 // nodes for lines that are no longer in view, and creating the ones
5898 // that are not there yet, and updating the ones that are out of
5899 // date.
5900 function patchDisplay(cm, updateNumbersFrom, dims) {
5901 var display = cm.display, lineNumbers = cm.options.lineNumbers;
5902 var container = display.lineDiv, cur = container.firstChild;
5903
5904 function rm(node) {
5905 var next = node.nextSibling;
5906 // Works around a throw-scroll bug in OS X Webkit
5907 if (webkit && mac && cm.display.currentWheelTarget == node)
5908 { node.style.display = "none"; }
5909 else
5910 { node.parentNode.removeChild(node); }
5911 return next
5912 }
5913
5914 var view = display.view, lineN = display.viewFrom;
5915 // Loop over the elements in the view, syncing cur (the DOM nodes
5916 // in display.lineDiv) with the view as we go.
5917 for (var i = 0; i < view.length; i++) {
5918 var lineView = view[i];
5919 if (lineView.hidden) ; else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet
5920 var node = buildLineElement(cm, lineView, lineN, dims);
5921 container.insertBefore(node, cur);
5922 } else { // Already drawn
5923 while (cur != lineView.node) { cur = rm(cur); }
5924 var updateNumber = lineNumbers && updateNumbersFrom != null &&
5925 updateNumbersFrom <= lineN && lineView.lineNumber;
5926 if (lineView.changes) {
5927 if (indexOf(lineView.changes, "gutter") > -1) { updateNumber = false; }
5928 updateLineForChanges(cm, lineView, lineN, dims);
5929 }
5930 if (updateNumber) {
5931 removeChildren(lineView.lineNumber);
5932 lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN)));
5933 }
5934 cur = lineView.node.nextSibling;
5935 }
5936 lineN += lineView.size;
5937 }
5938 while (cur) { cur = rm(cur); }
5939 }
5940
5941 function updateGutterSpace(cm) {
5942 var width = cm.display.gutters.offsetWidth;
5943 cm.display.sizer.style.marginLeft = width + "px";
5944 }
5945
5946 function setDocumentHeight(cm, measure) {
5947 cm.display.sizer.style.minHeight = measure.docHeight + "px";
5948 cm.display.heightForcer.style.top = measure.docHeight + "px";
5949 cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px";
5950 }
5951
5952 // Rebuild the gutter elements, ensure the margin to the left of the
5953 // code matches their width.
5954 function updateGutters(cm) {
5955 var gutters = cm.display.gutters, specs = cm.options.gutters;
5956 removeChildren(gutters);
5957 var i = 0;
5958 for (; i < specs.length; ++i) {
5959 var gutterClass = specs[i];
5960 var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass));
5961 if (gutterClass == "CodeMirror-linenumbers") {
5962 cm.display.lineGutter = gElt;
5963 gElt.style.width = (cm.display.lineNumWidth || 1) + "px";
5964 }
5965 }
5966 gutters.style.display = i ? "" : "none";
5967 updateGutterSpace(cm);
5968 }
5969
5970 // Make sure the gutters options contains the element
5971 // "CodeMirror-linenumbers" when the lineNumbers option is true.
5972 function setGuttersForLineNumbers(options) {
5973 var found = indexOf(options.gutters, "CodeMirror-linenumbers");
5974 if (found == -1 && options.lineNumbers) {
5975 options.gutters = options.gutters.concat(["CodeMirror-linenumbers"]);
5976 } else if (found > -1 && !options.lineNumbers) {
5977 options.gutters = options.gutters.slice(0);
5978 options.gutters.splice(found, 1);
5979 }
5980 }
5981
5982 // Since the delta values reported on mouse wheel events are
5983 // unstandardized between browsers and even browser versions, and
5984 // generally horribly unpredictable, this code starts by measuring
5985 // the scroll effect that the first few mouse wheel events have,
5986 // and, from that, detects the way it can convert deltas to pixel
5987 // offsets afterwards.
5988 //
5989 // The reason we want to know the amount a wheel event will scroll
5990 // is that it gives us a chance to update the display before the
5991 // actual scrolling happens, reducing flickering.
5992
5993 var wheelSamples = 0, wheelPixelsPerUnit = null;
5994 // Fill in a browser-detected starting value on browsers where we
5995 // know one. These don't have to be accurate -- the result of them
5996 // being wrong would just be a slight flicker on the first wheel
5997 // scroll (if it is large enough).
5998 if (ie) { wheelPixelsPerUnit = -.53; }
5999 else if (gecko) { wheelPixelsPerUnit = 15; }
6000 else if (chrome) { wheelPixelsPerUnit = -.7; }
6001 else if (safari) { wheelPixelsPerUnit = -1/3; }
6002
6003 function wheelEventDelta(e) {
6004 var dx = e.wheelDeltaX, dy = e.wheelDeltaY;
6005 if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail; }
6006 if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail; }
6007 else if (dy == null) { dy = e.wheelDelta; }
6008 return {x: dx, y: dy}
6009 }
6010 function wheelEventPixels(e) {
6011 var delta = wheelEventDelta(e);
6012 delta.x *= wheelPixelsPerUnit;
6013 delta.y *= wheelPixelsPerUnit;
6014 return delta
6015 }
6016
6017 function onScrollWheel(cm, e) {
6018 var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y;
6019
6020 var display = cm.display, scroll = display.scroller;
6021 // Quit if there's nothing to scroll here
6022 var canScrollX = scroll.scrollWidth > scroll.clientWidth;
6023 var canScrollY = scroll.scrollHeight > scroll.clientHeight;
6024 if (!(dx && canScrollX || dy && canScrollY)) { return }
6025
6026 // Webkit browsers on OS X abort momentum scrolls when the target
6027 // of the scroll event is removed from the scrollable element.
6028 // This hack (see related code in patchDisplay) makes sure the
6029 // element is kept around.
6030 if (dy && mac && webkit) {
6031 outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {
6032 for (var i = 0; i < view.length; i++) {
6033 if (view[i].node == cur) {
6034 cm.display.currentWheelTarget = cur;
6035 break outer
6036 }
6037 }
6038 }
6039 }
6040
6041 // On some browsers, horizontal scrolling will cause redraws to
6042 // happen before the gutter has been realigned, causing it to
6043 // wriggle around in a most unseemly way. When we have an
6044 // estimated pixels/delta value, we just handle horizontal
6045 // scrolling entirely here. It'll be slightly off from native, but
6046 // better than glitching out.
6047 if (dx && !gecko && !presto && wheelPixelsPerUnit != null) {
6048 if (dy && canScrollY)
6049 { updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * wheelPixelsPerUnit)); }
6050 setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * wheelPixelsPerUnit));
6051 // Only prevent default scrolling if vertical scrolling is
6052 // actually possible. Otherwise, it causes vertical scroll
6053 // jitter on OSX trackpads when deltaX is small and deltaY
6054 // is large (issue #3579)
6055 if (!dy || (dy && canScrollY))
6056 { e_preventDefault(e); }
6057 display.wheelStartX = null; // Abort measurement, if in progress
6058 return
6059 }
6060
6061 // 'Project' the visible viewport to cover the area that is being
6062 // scrolled into view (if we know enough to estimate it).
6063 if (dy && wheelPixelsPerUnit != null) {
6064 var pixels = dy * wheelPixelsPerUnit;
6065 var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
6066 if (pixels < 0) { top = Math.max(0, top + pixels - 50); }
6067 else { bot = Math.min(cm.doc.height, bot + pixels + 50); }
6068 updateDisplaySimple(cm, {top: top, bottom: bot});
6069 }
6070
6071 if (wheelSamples < 20) {
6072 if (display.wheelStartX == null) {
6073 display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;
6074 display.wheelDX = dx; display.wheelDY = dy;
6075 setTimeout(function () {
6076 if (display.wheelStartX == null) { return }
6077 var movedX = scroll.scrollLeft - display.wheelStartX;
6078 var movedY = scroll.scrollTop - display.wheelStartY;
6079 var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
6080 (movedX && display.wheelDX && movedX / display.wheelDX);
6081 display.wheelStartX = display.wheelStartY = null;
6082 if (!sample) { return }
6083 wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
6084 ++wheelSamples;
6085 }, 200);
6086 } else {
6087 display.wheelDX += dx; display.wheelDY += dy;
6088 }
6089 }
6090 }
6091
6092 // Selection objects are immutable. A new one is created every time
6093 // the selection changes. A selection is one or more non-overlapping
6094 // (and non-touching) ranges, sorted, and an integer that indicates
6095 // which one is the primary selection (the one that's scrolled into
6096 // view, that getCursor returns, etc).
6097 var Selection = function(ranges, primIndex) {
6098 this.ranges = ranges;
6099 this.primIndex = primIndex;
6100 };
6101
6102 Selection.prototype.primary = function () { return this.ranges[this.primIndex] };
6103
6104 Selection.prototype.equals = function (other) {
6105 var this$1 = this;
6106
6107 if (other == this) { return true }
6108 if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false }
6109 for (var i = 0; i < this.ranges.length; i++) {
6110 var here = this$1.ranges[i], there = other.ranges[i];
6111 if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) { return false }
6112 }
6113 return true
6114 };
6115
6116 Selection.prototype.deepCopy = function () {
6117 var this$1 = this;
6118
6119 var out = [];
6120 for (var i = 0; i < this.ranges.length; i++)
6121 { out[i] = new Range(copyPos(this$1.ranges[i].anchor), copyPos(this$1.ranges[i].head)); }
6122 return new Selection(out, this.primIndex)
6123 };
6124
6125 Selection.prototype.somethingSelected = function () {
6126 var this$1 = this;
6127
6128 for (var i = 0; i < this.ranges.length; i++)
6129 { if (!this$1.ranges[i].empty()) { return true } }
6130 return false
6131 };
6132
6133 Selection.prototype.contains = function (pos, end) {
6134 var this$1 = this;
6135
6136 if (!end) { end = pos; }
6137 for (var i = 0; i < this.ranges.length; i++) {
6138 var range = this$1.ranges[i];
6139 if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)
6140 { return i }
6141 }
6142 return -1
6143 };
6144
6145 var Range = function(anchor, head) {
6146 this.anchor = anchor; this.head = head;
6147 };
6148
6149 Range.prototype.from = function () { return minPos(this.anchor, this.head) };
6150 Range.prototype.to = function () { return maxPos(this.anchor, this.head) };
6151 Range.prototype.empty = function () { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch };
6152
6153 // Take an unsorted, potentially overlapping set of ranges, and
6154 // build a selection out of it. 'Consumes' ranges array (modifying
6155 // it).
6156 function normalizeSelection(cm, ranges, primIndex) {
6157 var mayTouch = cm && cm.options.selectionsMayTouch;
6158 var prim = ranges[primIndex];
6159 ranges.sort(function (a, b) { return cmp(a.from(), b.from()); });
6160 primIndex = indexOf(ranges, prim);
6161 for (var i = 1; i < ranges.length; i++) {
6162 var cur = ranges[i], prev = ranges[i - 1];
6163 var diff = cmp(prev.to(), cur.from());
6164 if (mayTouch && !cur.empty() ? diff > 0 : diff >= 0) {
6165 var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to());
6166 var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head;
6167 if (i <= primIndex) { --primIndex; }
6168 ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to));
6169 }
6170 }
6171 return new Selection(ranges, primIndex)
6172 }
6173
6174 function simpleSelection(anchor, head) {
6175 return new Selection([new Range(anchor, head || anchor)], 0)
6176 }
6177
6178 // Compute the position of the end of a change (its 'to' property
6179 // refers to the pre-change end).
6180 function changeEnd(change) {
6181 if (!change.text) { return change.to }
6182 return Pos(change.from.line + change.text.length - 1,
6183 lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0))
6184 }
6185
6186 // Adjust a position to refer to the post-change position of the
6187 // same text, or the end of the change if the change covers it.
6188 function adjustForChange(pos, change) {
6189 if (cmp(pos, change.from) < 0) { return pos }
6190 if (cmp(pos, change.to) <= 0) { return changeEnd(change) }
6191
6192 var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;
6193 if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch; }
6194 return Pos(line, ch)
6195 }
6196
6197 function computeSelAfterChange(doc, change) {
6198 var out = [];
6199 for (var i = 0; i < doc.sel.ranges.length; i++) {
6200 var range = doc.sel.ranges[i];
6201 out.push(new Range(adjustForChange(range.anchor, change),
6202 adjustForChange(range.head, change)));
6203 }
6204 return normalizeSelection(doc.cm, out, doc.sel.primIndex)
6205 }
6206
6207 function offsetPos(pos, old, nw) {
6208 if (pos.line == old.line)
6209 { return Pos(nw.line, pos.ch - old.ch + nw.ch) }
6210 else
6211 { return Pos(nw.line + (pos.line - old.line), pos.ch) }
6212 }
6213
6214 // Used by replaceSelections to allow moving the selection to the
6215 // start or around the replaced test. Hint may be "start" or "around".
6216 function computeReplacedSel(doc, changes, hint) {
6217 var out = [];
6218 var oldPrev = Pos(doc.first, 0), newPrev = oldPrev;
6219 for (var i = 0; i < changes.length; i++) {
6220 var change = changes[i];
6221 var from = offsetPos(change.from, oldPrev, newPrev);
6222 var to = offsetPos(changeEnd(change), oldPrev, newPrev);
6223 oldPrev = change.to;
6224 newPrev = to;
6225 if (hint == "around") {
6226 var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0;
6227 out[i] = new Range(inv ? to : from, inv ? from : to);
6228 } else {
6229 out[i] = new Range(from, from);
6230 }
6231 }
6232 return new Selection(out, doc.sel.primIndex)
6233 }
6234
6235 // Used to get the editor into a consistent state again when options change.
6236
6237 function loadMode(cm) {
6238 cm.doc.mode = getMode(cm.options, cm.doc.modeOption);
6239 resetModeState(cm);
6240 }
6241
6242 function resetModeState(cm) {
6243 cm.doc.iter(function (line) {
6244 if (line.stateAfter) { line.stateAfter = null; }
6245 if (line.styles) { line.styles = null; }
6246 });
6247 cm.doc.modeFrontier = cm.doc.highlightFrontier = cm.doc.first;
6248 startWorker(cm, 100);
6249 cm.state.modeGen++;
6250 if (cm.curOp) { regChange(cm); }
6251 }
6252
6253 // DOCUMENT DATA STRUCTURE
6254
6255 // By default, updates that start and end at the beginning of a line
6256 // are treated specially, in order to make the association of line
6257 // widgets and marker elements with the text behave more intuitive.
6258 function isWholeLineUpdate(doc, change) {
6259 return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" &&
6260 (!doc.cm || doc.cm.options.wholeLineUpdateBefore)
6261 }
6262
6263 // Perform a change on the document data structure.
6264 function updateDoc(doc, change, markedSpans, estimateHeight$$1) {
6265 function spansFor(n) {return markedSpans ? markedSpans[n] : null}
6266 function update(line, text, spans) {
6267 updateLine(line, text, spans, estimateHeight$$1);
6268 signalLater(line, "change", line, change);
6269 }
6270 function linesFor(start, end) {
6271 var result = [];
6272 for (var i = start; i < end; ++i)
6273 { result.push(new Line(text[i], spansFor(i), estimateHeight$$1)); }
6274 return result
6275 }
6276
6277 var from = change.from, to = change.to, text = change.text;
6278 var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
6279 var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;
6280
6281 // Adjust the line structure
6282 if (change.full) {
6283 doc.insert(0, linesFor(0, text.length));
6284 doc.remove(text.length, doc.size - text.length);
6285 } else if (isWholeLineUpdate(doc, change)) {
6286 // This is a whole-line replace. Treated specially to make
6287 // sure line objects move the way they are supposed to.
6288 var added = linesFor(0, text.length - 1);
6289 update(lastLine, lastLine.text, lastSpans);
6290 if (nlines) { doc.remove(from.line, nlines); }
6291 if (added.length) { doc.insert(from.line, added); }
6292 } else if (firstLine == lastLine) {
6293 if (text.length == 1) {
6294 update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
6295 } else {
6296 var added$1 = linesFor(1, text.length - 1);
6297 added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight$$1));
6298 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
6299 doc.insert(from.line + 1, added$1);
6300 }
6301 } else if (text.length == 1) {
6302 update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0));
6303 doc.remove(from.line + 1, nlines);
6304 } else {
6305 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
6306 update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
6307 var added$2 = linesFor(1, text.length - 1);
6308 if (nlines > 1) { doc.remove(from.line + 1, nlines - 1); }
6309 doc.insert(from.line + 1, added$2);
6310 }
6311
6312 signalLater(doc, "change", doc, change);
6313 }
6314
6315 // Call f for all linked documents.
6316 function linkedDocs(doc, f, sharedHistOnly) {
6317 function propagate(doc, skip, sharedHist) {
6318 if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) {
6319 var rel = doc.linked[i];
6320 if (rel.doc == skip) { continue }
6321 var shared = sharedHist && rel.sharedHist;
6322 if (sharedHistOnly && !shared) { continue }
6323 f(rel.doc, shared);
6324 propagate(rel.doc, doc, shared);
6325 } }
6326 }
6327 propagate(doc, null, true);
6328 }
6329
6330 // Attach a document to an editor.
6331 function attachDoc(cm, doc) {
6332 if (doc.cm) { throw new Error("This document is already in use.") }
6333 cm.doc = doc;
6334 doc.cm = cm;
6335 estimateLineHeights(cm);
6336 loadMode(cm);
6337 setDirectionClass(cm);
6338 if (!cm.options.lineWrapping) { findMaxLine(cm); }
6339 cm.options.mode = doc.modeOption;
6340 regChange(cm);
6341 }
6342
6343 function setDirectionClass(cm) {
6344 (cm.doc.direction == "rtl" ? addClass : rmClass)(cm.display.lineDiv, "CodeMirror-rtl");
6345 }
6346
6347 function directionChanged(cm) {
6348 runInOp(cm, function () {
6349 setDirectionClass(cm);
6350 regChange(cm);
6351 });
6352 }
6353
6354 function History(startGen) {
6355 // Arrays of change events and selections. Doing something adds an
6356 // event to done and clears undo. Undoing moves events from done
6357 // to undone, redoing moves them in the other direction.
6358 this.done = []; this.undone = [];
6359 this.undoDepth = Infinity;
6360 // Used to track when changes can be merged into a single undo
6361 // event
6362 this.lastModTime = this.lastSelTime = 0;
6363 this.lastOp = this.lastSelOp = null;
6364 this.lastOrigin = this.lastSelOrigin = null;
6365 // Used by the isClean() method
6366 this.generation = this.maxGeneration = startGen || 1;
6367 }
6368
6369 // Create a history change event from an updateDoc-style change
6370 // object.
6371 function historyChangeFromChange(doc, change) {
6372 var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
6373 attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
6374 linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); }, true);
6375 return histChange
6376 }
6377
6378 // Pop all selection events off the end of a history array. Stop at
6379 // a change event.
6380 function clearSelectionEvents(array) {
6381 while (array.length) {
6382 var last = lst(array);
6383 if (last.ranges) { array.pop(); }
6384 else { break }
6385 }
6386 }
6387
6388 // Find the top change event in the history. Pop off selection
6389 // events that are in the way.
6390 function lastChangeEvent(hist, force) {
6391 if (force) {
6392 clearSelectionEvents(hist.done);
6393 return lst(hist.done)
6394 } else if (hist.done.length && !lst(hist.done).ranges) {
6395 return lst(hist.done)
6396 } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) {
6397 hist.done.pop();
6398 return lst(hist.done)
6399 }
6400 }
6401
6402 // Register a change in the history. Merges changes that are within
6403 // a single operation, or are close together with an origin that
6404 // allows merging (starting with "+") into a single event.
6405 function addChangeToHistory(doc, change, selAfter, opId) {
6406 var hist = doc.history;
6407 hist.undone.length = 0;
6408 var time = +new Date, cur;
6409 var last;
6410
6411 if ((hist.lastOp == opId ||
6412 hist.lastOrigin == change.origin && change.origin &&
6413 ((change.origin.charAt(0) == "+" && hist.lastModTime > time - (doc.cm ? doc.cm.options.historyEventDelay : 500)) ||
6414 change.origin.charAt(0) == "*")) &&
6415 (cur = lastChangeEvent(hist, hist.lastOp == opId))) {
6416 // Merge this change into the last event
6417 last = lst(cur.changes);
6418 if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) {
6419 // Optimized case for simple insertion -- don't want to add
6420 // new changesets for every character typed
6421 last.to = changeEnd(change);
6422 } else {
6423 // Add new sub-event
6424 cur.changes.push(historyChangeFromChange(doc, change));
6425 }
6426 } else {
6427 // Can not be merged, start a new event.
6428 var before = lst(hist.done);
6429 if (!before || !before.ranges)
6430 { pushSelectionToHistory(doc.sel, hist.done); }
6431 cur = {changes: [historyChangeFromChange(doc, change)],
6432 generation: hist.generation};
6433 hist.done.push(cur);
6434 while (hist.done.length > hist.undoDepth) {
6435 hist.done.shift();
6436 if (!hist.done[0].ranges) { hist.done.shift(); }
6437 }
6438 }
6439 hist.done.push(selAfter);
6440 hist.generation = ++hist.maxGeneration;
6441 hist.lastModTime = hist.lastSelTime = time;
6442 hist.lastOp = hist.lastSelOp = opId;
6443 hist.lastOrigin = hist.lastSelOrigin = change.origin;
6444
6445 if (!last) { signal(doc, "historyAdded"); }
6446 }
6447
6448 function selectionEventCanBeMerged(doc, origin, prev, sel) {
6449 var ch = origin.charAt(0);
6450 return ch == "*" ||
6451 ch == "+" &&
6452 prev.ranges.length == sel.ranges.length &&
6453 prev.somethingSelected() == sel.somethingSelected() &&
6454 new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500)
6455 }
6456
6457 // Called whenever the selection changes, sets the new selection as
6458 // the pending selection in the history, and pushes the old pending
6459 // selection into the 'done' array when it was significantly
6460 // different (in number of selected ranges, emptiness, or time).
6461 function addSelectionToHistory(doc, sel, opId, options) {
6462 var hist = doc.history, origin = options && options.origin;
6463
6464 // A new event is started when the previous origin does not match
6465 // the current, or the origins don't allow matching. Origins
6466 // starting with * are always merged, those starting with + are
6467 // merged when similar and close together in time.
6468 if (opId == hist.lastSelOp ||
6469 (origin && hist.lastSelOrigin == origin &&
6470 (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||
6471 selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))
6472 { hist.done[hist.done.length - 1] = sel; }
6473 else
6474 { pushSelectionToHistory(sel, hist.done); }
6475
6476 hist.lastSelTime = +new Date;
6477 hist.lastSelOrigin = origin;
6478 hist.lastSelOp = opId;
6479 if (options && options.clearRedo !== false)
6480 { clearSelectionEvents(hist.undone); }
6481 }
6482
6483 function pushSelectionToHistory(sel, dest) {
6484 var top = lst(dest);
6485 if (!(top && top.ranges && top.equals(sel)))
6486 { dest.push(sel); }
6487 }
6488
6489 // Used to store marked span information in the history.
6490 function attachLocalSpans(doc, change, from, to) {
6491 var existing = change["spans_" + doc.id], n = 0;
6492 doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function (line) {
6493 if (line.markedSpans)
6494 { (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans; }
6495 ++n;
6496 });
6497 }
6498
6499 // When un/re-doing restores text containing marked spans, those
6500 // that have been explicitly cleared should not be restored.
6501 function removeClearedSpans(spans) {
6502 if (!spans) { return null }
6503 var out;
6504 for (var i = 0; i < spans.length; ++i) {
6505 if (spans[i].marker.explicitlyCleared) { if (!out) { out = spans.slice(0, i); } }
6506 else if (out) { out.push(spans[i]); }
6507 }
6508 return !out ? spans : out.length ? out : null
6509 }
6510
6511 // Retrieve and filter the old marked spans stored in a change event.
6512 function getOldSpans(doc, change) {
6513 var found = change["spans_" + doc.id];
6514 if (!found) { return null }
6515 var nw = [];
6516 for (var i = 0; i < change.text.length; ++i)
6517 { nw.push(removeClearedSpans(found[i])); }
6518 return nw
6519 }
6520
6521 // Used for un/re-doing changes from the history. Combines the
6522 // result of computing the existing spans with the set of spans that
6523 // existed in the history (so that deleting around a span and then
6524 // undoing brings back the span).
6525 function mergeOldSpans(doc, change) {
6526 var old = getOldSpans(doc, change);
6527 var stretched = stretchSpansOverChange(doc, change);
6528 if (!old) { return stretched }
6529 if (!stretched) { return old }
6530
6531 for (var i = 0; i < old.length; ++i) {
6532 var oldCur = old[i], stretchCur = stretched[i];
6533 if (oldCur && stretchCur) {
6534 spans: for (var j = 0; j < stretchCur.length; ++j) {
6535 var span = stretchCur[j];
6536 for (var k = 0; k < oldCur.length; ++k)
6537 { if (oldCur[k].marker == span.marker) { continue spans } }
6538 oldCur.push(span);
6539 }
6540 } else if (stretchCur) {
6541 old[i] = stretchCur;
6542 }
6543 }
6544 return old
6545 }
6546
6547 // Used both to provide a JSON-safe object in .getHistory, and, when
6548 // detaching a document, to split the history in two
6549 function copyHistoryArray(events, newGroup, instantiateSel) {
6550 var copy = [];
6551 for (var i = 0; i < events.length; ++i) {
6552 var event = events[i];
6553 if (event.ranges) {
6554 copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event);
6555 continue
6556 }
6557 var changes = event.changes, newChanges = [];
6558 copy.push({changes: newChanges});
6559 for (var j = 0; j < changes.length; ++j) {
6560 var change = changes[j], m = (void 0);
6561 newChanges.push({from: change.from, to: change.to, text: change.text});
6562 if (newGroup) { for (var prop in change) { if (m = prop.match(/^spans_(\d+)$/)) {
6563 if (indexOf(newGroup, Number(m[1])) > -1) {
6564 lst(newChanges)[prop] = change[prop];
6565 delete change[prop];
6566 }
6567 } } }
6568 }
6569 }
6570 return copy
6571 }
6572
6573 // The 'scroll' parameter given to many of these indicated whether
6574 // the new cursor position should be scrolled into view after
6575 // modifying the selection.
6576
6577 // If shift is held or the extend flag is set, extends a range to
6578 // include a given position (and optionally a second position).
6579 // Otherwise, simply returns the range between the given positions.
6580 // Used for cursor motion and such.
6581 function extendRange(range, head, other, extend) {
6582 if (extend) {
6583 var anchor = range.anchor;
6584 if (other) {
6585 var posBefore = cmp(head, anchor) < 0;
6586 if (posBefore != (cmp(other, anchor) < 0)) {
6587 anchor = head;
6588 head = other;
6589 } else if (posBefore != (cmp(head, other) < 0)) {
6590 head = other;
6591 }
6592 }
6593 return new Range(anchor, head)
6594 } else {
6595 return new Range(other || head, head)
6596 }
6597 }
6598
6599 // Extend the primary selection range, discard the rest.
6600 function extendSelection(doc, head, other, options, extend) {
6601 if (extend == null) { extend = doc.cm && (doc.cm.display.shift || doc.extend); }
6602 setSelection(doc, new Selection([extendRange(doc.sel.primary(), head, other, extend)], 0), options);
6603 }
6604
6605 // Extend all selections (pos is an array of selections with length
6606 // equal the number of selections)
6607 function extendSelections(doc, heads, options) {
6608 var out = [];
6609 var extend = doc.cm && (doc.cm.display.shift || doc.extend);
6610 for (var i = 0; i < doc.sel.ranges.length; i++)
6611 { out[i] = extendRange(doc.sel.ranges[i], heads[i], null, extend); }
6612 var newSel = normalizeSelection(doc.cm, out, doc.sel.primIndex);
6613 setSelection(doc, newSel, options);
6614 }
6615
6616 // Updates a single range in the selection.
6617 function replaceOneSelection(doc, i, range, options) {
6618 var ranges = doc.sel.ranges.slice(0);
6619 ranges[i] = range;
6620 setSelection(doc, normalizeSelection(doc.cm, ranges, doc.sel.primIndex), options);
6621 }
6622
6623 // Reset the selection to a single range.
6624 function setSimpleSelection(doc, anchor, head, options) {
6625 setSelection(doc, simpleSelection(anchor, head), options);
6626 }
6627
6628 // Give beforeSelectionChange handlers a change to influence a
6629 // selection update.
6630 function filterSelectionChange(doc, sel, options) {
6631 var obj = {
6632 ranges: sel.ranges,
6633 update: function(ranges) {
6634 var this$1 = this;
6635
6636 this.ranges = [];
6637 for (var i = 0; i < ranges.length; i++)
6638 { this$1.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),
6639 clipPos(doc, ranges[i].head)); }
6640 },
6641 origin: options && options.origin
6642 };
6643 signal(doc, "beforeSelectionChange", doc, obj);
6644 if (doc.cm) { signal(doc.cm, "beforeSelectionChange", doc.cm, obj); }
6645 if (obj.ranges != sel.ranges) { return normalizeSelection(doc.cm, obj.ranges, obj.ranges.length - 1) }
6646 else { return sel }
6647 }
6648
6649 function setSelectionReplaceHistory(doc, sel, options) {
6650 var done = doc.history.done, last = lst(done);
6651 if (last && last.ranges) {
6652 done[done.length - 1] = sel;
6653 setSelectionNoUndo(doc, sel, options);
6654 } else {
6655 setSelection(doc, sel, options);
6656 }
6657 }
6658
6659 // Set a new selection.
6660 function setSelection(doc, sel, options) {
6661 setSelectionNoUndo(doc, sel, options);
6662 addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options);
6663 }
6664
6665 function setSelectionNoUndo(doc, sel, options) {
6666 if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange"))
6667 { sel = filterSelectionChange(doc, sel, options); }
6668
6669 var bias = options && options.bias ||
6670 (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1);
6671 setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true));
6672
6673 if (!(options && options.scroll === false) && doc.cm)
6674 { ensureCursorVisible(doc.cm); }
6675 }
6676
6677 function setSelectionInner(doc, sel) {
6678 if (sel.equals(doc.sel)) { return }
6679
6680 doc.sel = sel;
6681
6682 if (doc.cm) {
6683 doc.cm.curOp.updateInput = 1;
6684 doc.cm.curOp.selectionChanged = true;
6685 signalCursorActivity(doc.cm);
6686 }
6687 signalLater(doc, "cursorActivity", doc);
6688 }
6689
6690 // Verify that the selection does not partially select any atomic
6691 // marked ranges.
6692 function reCheckSelection(doc) {
6693 setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false));
6694 }
6695
6696 // Return a selection that does not partially select any atomic
6697 // ranges.
6698 function skipAtomicInSelection(doc, sel, bias, mayClear) {
6699 var out;
6700 for (var i = 0; i < sel.ranges.length; i++) {
6701 var range = sel.ranges[i];
6702 var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i];
6703 var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear);
6704 var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear);
6705 if (out || newAnchor != range.anchor || newHead != range.head) {
6706 if (!out) { out = sel.ranges.slice(0, i); }
6707 out[i] = new Range(newAnchor, newHead);
6708 }
6709 }
6710 return out ? normalizeSelection(doc.cm, out, sel.primIndex) : sel
6711 }
6712
6713 function skipAtomicInner(doc, pos, oldPos, dir, mayClear) {
6714 var line = getLine(doc, pos.line);
6715 if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
6716 var sp = line.markedSpans[i], m = sp.marker;
6717 if ((sp.from == null || (m.inclusiveLeft ? sp.from <= pos.ch : sp.from < pos.ch)) &&
6718 (sp.to == null || (m.inclusiveRight ? sp.to >= pos.ch : sp.to > pos.ch))) {
6719 if (mayClear) {
6720 signal(m, "beforeCursorEnter");
6721 if (m.explicitlyCleared) {
6722 if (!line.markedSpans) { break }
6723 else {--i; continue}
6724 }
6725 }
6726 if (!m.atomic) { continue }
6727
6728 if (oldPos) {
6729 var near = m.find(dir < 0 ? 1 : -1), diff = (void 0);
6730 if (dir < 0 ? m.inclusiveRight : m.inclusiveLeft)
6731 { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null); }
6732 if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0))
6733 { return skipAtomicInner(doc, near, pos, dir, mayClear) }
6734 }
6735
6736 var far = m.find(dir < 0 ? -1 : 1);
6737 if (dir < 0 ? m.inclusiveLeft : m.inclusiveRight)
6738 { far = movePos(doc, far, dir, far.line == pos.line ? line : null); }
6739 return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null
6740 }
6741 } }
6742 return pos
6743 }
6744
6745 // Ensure a given position is not inside an atomic range.
6746 function skipAtomic(doc, pos, oldPos, bias, mayClear) {
6747 var dir = bias || 1;
6748 var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) ||
6749 (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) ||
6750 skipAtomicInner(doc, pos, oldPos, -dir, mayClear) ||
6751 (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true));
6752 if (!found) {
6753 doc.cantEdit = true;
6754 return Pos(doc.first, 0)
6755 }
6756 return found
6757 }
6758
6759 function movePos(doc, pos, dir, line) {
6760 if (dir < 0 && pos.ch == 0) {
6761 if (pos.line > doc.first) { return clipPos(doc, Pos(pos.line - 1)) }
6762 else { return null }
6763 } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) {
6764 if (pos.line < doc.first + doc.size - 1) { return Pos(pos.line + 1, 0) }
6765 else { return null }
6766 } else {
6767 return new Pos(pos.line, pos.ch + dir)
6768 }
6769 }
6770
6771 function selectAll(cm) {
6772 cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll);
6773 }
6774
6775 // UPDATING
6776
6777 // Allow "beforeChange" event handlers to influence a change
6778 function filterChange(doc, change, update) {
6779 var obj = {
6780 canceled: false,
6781 from: change.from,
6782 to: change.to,
6783 text: change.text,
6784 origin: change.origin,
6785 cancel: function () { return obj.canceled = true; }
6786 };
6787 if (update) { obj.update = function (from, to, text, origin) {
6788 if (from) { obj.from = clipPos(doc, from); }
6789 if (to) { obj.to = clipPos(doc, to); }
6790 if (text) { obj.text = text; }
6791 if (origin !== undefined) { obj.origin = origin; }
6792 }; }
6793 signal(doc, "beforeChange", doc, obj);
6794 if (doc.cm) { signal(doc.cm, "beforeChange", doc.cm, obj); }
6795
6796 if (obj.canceled) {
6797 if (doc.cm) { doc.cm.curOp.updateInput = 2; }
6798 return null
6799 }
6800 return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin}
6801 }
6802
6803 // Apply a change to a document, and add it to the document's
6804 // history, and propagating it to all linked documents.
6805 function makeChange(doc, change, ignoreReadOnly) {
6806 if (doc.cm) {
6807 if (!doc.cm.curOp) { return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) }
6808 if (doc.cm.state.suppressEdits) { return }
6809 }
6810
6811 if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
6812 change = filterChange(doc, change, true);
6813 if (!change) { return }
6814 }
6815
6816 // Possibly split or suppress the update based on the presence
6817 // of read-only spans in its range.
6818 var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);
6819 if (split) {
6820 for (var i = split.length - 1; i >= 0; --i)
6821 { makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text, origin: change.origin}); }
6822 } else {
6823 makeChangeInner(doc, change);
6824 }
6825 }
6826
6827 function makeChangeInner(doc, change) {
6828 if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) { return }
6829 var selAfter = computeSelAfterChange(doc, change);
6830 addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);
6831
6832 makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));
6833 var rebased = [];
6834
6835 linkedDocs(doc, function (doc, sharedHist) {
6836 if (!sharedHist && indexOf(rebased, doc.history) == -1) {
6837 rebaseHist(doc.history, change);
6838 rebased.push(doc.history);
6839 }
6840 makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change));
6841 });
6842 }
6843
6844 // Revert a change stored in a document's history.
6845 function makeChangeFromHistory(doc, type, allowSelectionOnly) {
6846 var suppress = doc.cm && doc.cm.state.suppressEdits;
6847 if (suppress && !allowSelectionOnly) { return }
6848
6849 var hist = doc.history, event, selAfter = doc.sel;
6850 var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done;
6851
6852 // Verify that there is a useable event (so that ctrl-z won't
6853 // needlessly clear selection events)
6854 var i = 0;
6855 for (; i < source.length; i++) {
6856 event = source[i];
6857 if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges)
6858 { break }
6859 }
6860 if (i == source.length) { return }
6861 hist.lastOrigin = hist.lastSelOrigin = null;
6862
6863 for (;;) {
6864 event = source.pop();
6865 if (event.ranges) {
6866 pushSelectionToHistory(event, dest);
6867 if (allowSelectionOnly && !event.equals(doc.sel)) {
6868 setSelection(doc, event, {clearRedo: false});
6869 return
6870 }
6871 selAfter = event;
6872 } else if (suppress) {
6873 source.push(event);
6874 return
6875 } else { break }
6876 }
6877
6878 // Build up a reverse change object to add to the opposite history
6879 // stack (redo when undoing, and vice versa).
6880 var antiChanges = [];
6881 pushSelectionToHistory(selAfter, dest);
6882 dest.push({changes: antiChanges, generation: hist.generation});
6883 hist.generation = event.generation || ++hist.maxGeneration;
6884
6885 var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange");
6886
6887 var loop = function ( i ) {
6888 var change = event.changes[i];
6889 change.origin = type;
6890 if (filter && !filterChange(doc, change, false)) {
6891 source.length = 0;
6892 return {}
6893 }
6894
6895 antiChanges.push(historyChangeFromChange(doc, change));
6896
6897 var after = i ? computeSelAfterChange(doc, change) : lst(source);
6898 makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
6899 if (!i && doc.cm) { doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}); }
6900 var rebased = [];
6901
6902 // Propagate to the linked documents
6903 linkedDocs(doc, function (doc, sharedHist) {
6904 if (!sharedHist && indexOf(rebased, doc.history) == -1) {
6905 rebaseHist(doc.history, change);
6906 rebased.push(doc.history);
6907 }
6908 makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change));
6909 });
6910 };
6911
6912 for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) {
6913 var returned = loop( i$1 );
6914
6915 if ( returned ) return returned.v;
6916 }
6917 }
6918
6919 // Sub-views need their line numbers shifted when text is added
6920 // above or below them in the parent document.
6921 function shiftDoc(doc, distance) {
6922 if (distance == 0) { return }
6923 doc.first += distance;
6924 doc.sel = new Selection(map(doc.sel.ranges, function (range) { return new Range(
6925 Pos(range.anchor.line + distance, range.anchor.ch),
6926 Pos(range.head.line + distance, range.head.ch)
6927 ); }), doc.sel.primIndex);
6928 if (doc.cm) {
6929 regChange(doc.cm, doc.first, doc.first - distance, distance);
6930 for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++)
6931 { regLineChange(doc.cm, l, "gutter"); }
6932 }
6933 }
6934
6935 // More lower-level change function, handling only a single document
6936 // (not linked ones).
6937 function makeChangeSingleDoc(doc, change, selAfter, spans) {
6938 if (doc.cm && !doc.cm.curOp)
6939 { return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) }
6940
6941 if (change.to.line < doc.first) {
6942 shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line));
6943 return
6944 }
6945 if (change.from.line > doc.lastLine()) { return }
6946
6947 // Clip the change to the size of this doc
6948 if (change.from.line < doc.first) {
6949 var shift = change.text.length - 1 - (doc.first - change.from.line);
6950 shiftDoc(doc, shift);
6951 change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
6952 text: [lst(change.text)], origin: change.origin};
6953 }
6954 var last = doc.lastLine();
6955 if (change.to.line > last) {
6956 change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
6957 text: [change.text[0]], origin: change.origin};
6958 }
6959
6960 change.removed = getBetween(doc, change.from, change.to);
6961
6962 if (!selAfter) { selAfter = computeSelAfterChange(doc, change); }
6963 if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans); }
6964 else { updateDoc(doc, change, spans); }
6965 setSelectionNoUndo(doc, selAfter, sel_dontScroll);
6966 }
6967
6968 // Handle the interaction of a change to a document with the editor
6969 // that this document is part of.
6970 function makeChangeSingleDocInEditor(cm, change, spans) {
6971 var doc = cm.doc, display = cm.display, from = change.from, to = change.to;
6972
6973 var recomputeMaxLength = false, checkWidthStart = from.line;
6974 if (!cm.options.lineWrapping) {
6975 checkWidthStart = lineNo(visualLine(getLine(doc, from.line)));
6976 doc.iter(checkWidthStart, to.line + 1, function (line) {
6977 if (line == display.maxLine) {
6978 recomputeMaxLength = true;
6979 return true
6980 }
6981 });
6982 }
6983
6984 if (doc.sel.contains(change.from, change.to) > -1)
6985 { signalCursorActivity(cm); }
6986
6987 updateDoc(doc, change, spans, estimateHeight(cm));
6988
6989 if (!cm.options.lineWrapping) {
6990 doc.iter(checkWidthStart, from.line + change.text.length, function (line) {
6991 var len = lineLength(line);
6992 if (len > display.maxLineLength) {
6993 display.maxLine = line;
6994 display.maxLineLength = len;
6995 display.maxLineChanged = true;
6996 recomputeMaxLength = false;
6997 }
6998 });
6999 if (recomputeMaxLength) { cm.curOp.updateMaxLine = true; }
7000 }
7001
7002 retreatFrontier(doc, from.line);
7003 startWorker(cm, 400);
7004
7005 var lendiff = change.text.length - (to.line - from.line) - 1;
7006 // Remember that these lines changed, for updating the display
7007 if (change.full)
7008 { regChange(cm); }
7009 else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change))
7010 { regLineChange(cm, from.line, "text"); }
7011 else
7012 { regChange(cm, from.line, to.line + 1, lendiff); }
7013
7014 var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change");
7015 if (changeHandler || changesHandler) {
7016 var obj = {
7017 from: from, to: to,
7018 text: change.text,
7019 removed: change.removed,
7020 origin: change.origin
7021 };
7022 if (changeHandler) { signalLater(cm, "change", cm, obj); }
7023 if (changesHandler) { (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj); }
7024 }
7025 cm.display.selForContextMenu = null;
7026 }
7027
7028 function replaceRange(doc, code, from, to, origin) {
7029 var assign;
7030
7031 if (!to) { to = from; }
7032 if (cmp(to, from) < 0) { (assign = [to, from], from = assign[0], to = assign[1]); }
7033 if (typeof code == "string") { code = doc.splitLines(code); }
7034 makeChange(doc, {from: from, to: to, text: code, origin: origin});
7035 }
7036
7037 // Rebasing/resetting history to deal with externally-sourced changes
7038
7039 function rebaseHistSelSingle(pos, from, to, diff) {
7040 if (to < pos.line) {
7041 pos.line += diff;
7042 } else if (from < pos.line) {
7043 pos.line = from;
7044 pos.ch = 0;
7045 }
7046 }
7047
7048 // Tries to rebase an array of history events given a change in the
7049 // document. If the change touches the same lines as the event, the
7050 // event, and everything 'behind' it, is discarded. If the change is
7051 // before the event, the event's positions are updated. Uses a
7052 // copy-on-write scheme for the positions, to avoid having to
7053 // reallocate them all on every rebase, but also avoid problems with
7054 // shared position objects being unsafely updated.
7055 function rebaseHistArray(array, from, to, diff) {
7056 for (var i = 0; i < array.length; ++i) {
7057 var sub = array[i], ok = true;
7058 if (sub.ranges) {
7059 if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; }
7060 for (var j = 0; j < sub.ranges.length; j++) {
7061 rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff);
7062 rebaseHistSelSingle(sub.ranges[j].head, from, to, diff);
7063 }
7064 continue
7065 }
7066 for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) {
7067 var cur = sub.changes[j$1];
7068 if (to < cur.from.line) {
7069 cur.from = Pos(cur.from.line + diff, cur.from.ch);
7070 cur.to = Pos(cur.to.line + diff, cur.to.ch);
7071 } else if (from <= cur.to.line) {
7072 ok = false;
7073 break
7074 }
7075 }
7076 if (!ok) {
7077 array.splice(0, i + 1);
7078 i = 0;
7079 }
7080 }
7081 }
7082
7083 function rebaseHist(hist, change) {
7084 var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1;
7085 rebaseHistArray(hist.done, from, to, diff);
7086 rebaseHistArray(hist.undone, from, to, diff);
7087 }
7088
7089 // Utility for applying a change to a line by handle or number,
7090 // returning the number and optionally registering the line as
7091 // changed.
7092 function changeLine(doc, handle, changeType, op) {
7093 var no = handle, line = handle;
7094 if (typeof handle == "number") { line = getLine(doc, clipLine(doc, handle)); }
7095 else { no = lineNo(handle); }
7096 if (no == null) { return null }
7097 if (op(line, no) && doc.cm) { regLineChange(doc.cm, no, changeType); }
7098 return line
7099 }
7100
7101 // The document is represented as a BTree consisting of leaves, with
7102 // chunk of lines in them, and branches, with up to ten leaves or
7103 // other branch nodes below them. The top node is always a branch
7104 // node, and is the document object itself (meaning it has
7105 // additional methods and properties).
7106 //
7107 // All nodes have parent links. The tree is used both to go from
7108 // line numbers to line objects, and to go from objects to numbers.
7109 // It also indexes by height, and is used to convert between height
7110 // and line object, and to find the total height of the document.
7111 //
7112 // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html
7113
7114 function LeafChunk(lines) {
7115 var this$1 = this;
7116
7117 this.lines = lines;
7118 this.parent = null;
7119 var height = 0;
7120 for (var i = 0; i < lines.length; ++i) {
7121 lines[i].parent = this$1;
7122 height += lines[i].height;
7123 }
7124 this.height = height;
7125 }
7126
7127 LeafChunk.prototype = {
7128 chunkSize: function() { return this.lines.length },
7129
7130 // Remove the n lines at offset 'at'.
7131 removeInner: function(at, n) {
7132 var this$1 = this;
7133
7134 for (var i = at, e = at + n; i < e; ++i) {
7135 var line = this$1.lines[i];
7136 this$1.height -= line.height;
7137 cleanUpLine(line);
7138 signalLater(line, "delete");
7139 }
7140 this.lines.splice(at, n);
7141 },
7142
7143 // Helper used to collapse a small branch into a single leaf.
7144 collapse: function(lines) {
7145 lines.push.apply(lines, this.lines);
7146 },
7147
7148 // Insert the given array of lines at offset 'at', count them as
7149 // having the given height.
7150 insertInner: function(at, lines, height) {
7151 var this$1 = this;
7152
7153 this.height += height;
7154 this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
7155 for (var i = 0; i < lines.length; ++i) { lines[i].parent = this$1; }
7156 },
7157
7158 // Used to iterate over a part of the tree.
7159 iterN: function(at, n, op) {
7160 var this$1 = this;
7161
7162 for (var e = at + n; at < e; ++at)
7163 { if (op(this$1.lines[at])) { return true } }
7164 }
7165 };
7166
7167 function BranchChunk(children) {
7168 var this$1 = this;
7169
7170 this.children = children;
7171 var size = 0, height = 0;
7172 for (var i = 0; i < children.length; ++i) {
7173 var ch = children[i];
7174 size += ch.chunkSize(); height += ch.height;
7175 ch.parent = this$1;
7176 }
7177 this.size = size;
7178 this.height = height;
7179 this.parent = null;
7180 }
7181
7182 BranchChunk.prototype = {
7183 chunkSize: function() { return this.size },
7184
7185 removeInner: function(at, n) {
7186 var this$1 = this;
7187
7188 this.size -= n;
7189 for (var i = 0; i < this.children.length; ++i) {
7190 var child = this$1.children[i], sz = child.chunkSize();
7191 if (at < sz) {
7192 var rm = Math.min(n, sz - at), oldHeight = child.height;
7193 child.removeInner(at, rm);
7194 this$1.height -= oldHeight - child.height;
7195 if (sz == rm) { this$1.children.splice(i--, 1); child.parent = null; }
7196 if ((n -= rm) == 0) { break }
7197 at = 0;
7198 } else { at -= sz; }
7199 }
7200 // If the result is smaller than 25 lines, ensure that it is a
7201 // single leaf node.
7202 if (this.size - n < 25 &&
7203 (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) {
7204 var lines = [];
7205 this.collapse(lines);
7206 this.children = [new LeafChunk(lines)];
7207 this.children[0].parent = this;
7208 }
7209 },
7210
7211 collapse: function(lines) {
7212 var this$1 = this;
7213
7214 for (var i = 0; i < this.children.length; ++i) { this$1.children[i].collapse(lines); }
7215 },
7216
7217 insertInner: function(at, lines, height) {
7218 var this$1 = this;
7219
7220 this.size += lines.length;
7221 this.height += height;
7222 for (var i = 0; i < this.children.length; ++i) {
7223 var child = this$1.children[i], sz = child.chunkSize();
7224 if (at <= sz) {
7225 child.insertInner(at, lines, height);
7226 if (child.lines && child.lines.length > 50) {
7227 // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced.
7228 // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest.
7229 var remaining = child.lines.length % 25 + 25;
7230 for (var pos = remaining; pos < child.lines.length;) {
7231 var leaf = new LeafChunk(child.lines.slice(pos, pos += 25));
7232 child.height -= leaf.height;
7233 this$1.children.splice(++i, 0, leaf);
7234 leaf.parent = this$1;
7235 }
7236 child.lines = child.lines.slice(0, remaining);
7237 this$1.maybeSpill();
7238 }
7239 break
7240 }
7241 at -= sz;
7242 }
7243 },
7244
7245 // When a node has grown, check whether it should be split.
7246 maybeSpill: function() {
7247 if (this.children.length <= 10) { return }
7248 var me = this;
7249 do {
7250 var spilled = me.children.splice(me.children.length - 5, 5);
7251 var sibling = new BranchChunk(spilled);
7252 if (!me.parent) { // Become the parent node
7253 var copy = new BranchChunk(me.children);
7254 copy.parent = me;
7255 me.children = [copy, sibling];
7256 me = copy;
7257 } else {
7258 me.size -= sibling.size;
7259 me.height -= sibling.height;
7260 var myIndex = indexOf(me.parent.children, me);
7261 me.parent.children.splice(myIndex + 1, 0, sibling);
7262 }
7263 sibling.parent = me.parent;
7264 } while (me.children.length > 10)
7265 me.parent.maybeSpill();
7266 },
7267
7268 iterN: function(at, n, op) {
7269 var this$1 = this;
7270
7271 for (var i = 0; i < this.children.length; ++i) {
7272 var child = this$1.children[i], sz = child.chunkSize();
7273 if (at < sz) {
7274 var used = Math.min(n, sz - at);
7275 if (child.iterN(at, used, op)) { return true }
7276 if ((n -= used) == 0) { break }
7277 at = 0;
7278 } else { at -= sz; }
7279 }
7280 }
7281 };
7282
7283 // Line widgets are block elements displayed above or below a line.
7284
7285 var LineWidget = function(doc, node, options) {
7286 var this$1 = this;
7287
7288 if (options) { for (var opt in options) { if (options.hasOwnProperty(opt))
7289 { this$1[opt] = options[opt]; } } }
7290 this.doc = doc;
7291 this.node = node;
7292 };
7293
7294 LineWidget.prototype.clear = function () {
7295 var this$1 = this;
7296
7297 var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line);
7298 if (no == null || !ws) { return }
7299 for (var i = 0; i < ws.length; ++i) { if (ws[i] == this$1) { ws.splice(i--, 1); } }
7300 if (!ws.length) { line.widgets = null; }
7301 var height = widgetHeight(this);
7302 updateLineHeight(line, Math.max(0, line.height - height));
7303 if (cm) {
7304 runInOp(cm, function () {
7305 adjustScrollWhenAboveVisible(cm, line, -height);
7306 regLineChange(cm, no, "widget");
7307 });
7308 signalLater(cm, "lineWidgetCleared", cm, this, no);
7309 }
7310 };
7311
7312 LineWidget.prototype.changed = function () {
7313 var this$1 = this;
7314
7315 var oldH = this.height, cm = this.doc.cm, line = this.line;
7316 this.height = null;
7317 var diff = widgetHeight(this) - oldH;
7318 if (!diff) { return }
7319 if (!lineIsHidden(this.doc, line)) { updateLineHeight(line, line.height + diff); }
7320 if (cm) {
7321 runInOp(cm, function () {
7322 cm.curOp.forceUpdate = true;
7323 adjustScrollWhenAboveVisible(cm, line, diff);
7324 signalLater(cm, "lineWidgetChanged", cm, this$1, lineNo(line));
7325 });
7326 }
7327 };
7328 eventMixin(LineWidget);
7329
7330 function adjustScrollWhenAboveVisible(cm, line, diff) {
7331 if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))
7332 { addToScrollTop(cm, diff); }
7333 }
7334
7335 function addLineWidget(doc, handle, node, options) {
7336 var widget = new LineWidget(doc, node, options);
7337 var cm = doc.cm;
7338 if (cm && widget.noHScroll) { cm.display.alignWidgets = true; }
7339 changeLine(doc, handle, "widget", function (line) {
7340 var widgets = line.widgets || (line.widgets = []);
7341 if (widget.insertAt == null) { widgets.push(widget); }
7342 else { widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget); }
7343 widget.line = line;
7344 if (cm && !lineIsHidden(doc, line)) {
7345 var aboveVisible = heightAtLine(line) < doc.scrollTop;
7346 updateLineHeight(line, line.height + widgetHeight(widget));
7347 if (aboveVisible) { addToScrollTop(cm, widget.height); }
7348 cm.curOp.forceUpdate = true;
7349 }
7350 return true
7351 });
7352 if (cm) { signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle)); }
7353 return widget
7354 }
7355
7356 // TEXTMARKERS
7357
7358 // Created with markText and setBookmark methods. A TextMarker is a
7359 // handle that can be used to clear or find a marked position in the
7360 // document. Line objects hold arrays (markedSpans) containing
7361 // {from, to, marker} object pointing to such marker objects, and
7362 // indicating that such a marker is present on that line. Multiple
7363 // lines may point to the same marker when it spans across lines.
7364 // The spans will have null for their from/to properties when the
7365 // marker continues beyond the start/end of the line. Markers have
7366 // links back to the lines they currently touch.
7367
7368 // Collapsed markers have unique ids, in order to be able to order
7369 // them, which is needed for uniquely determining an outer marker
7370 // when they overlap (they may nest, but not partially overlap).
7371 var nextMarkerId = 0;
7372
7373 var TextMarker = function(doc, type) {
7374 this.lines = [];
7375 this.type = type;
7376 this.doc = doc;
7377 this.id = ++nextMarkerId;
7378 };
7379
7380 // Clear the marker.
7381 TextMarker.prototype.clear = function () {
7382 var this$1 = this;
7383
7384 if (this.explicitlyCleared) { return }
7385 var cm = this.doc.cm, withOp = cm && !cm.curOp;
7386 if (withOp) { startOperation(cm); }
7387 if (hasHandler(this, "clear")) {
7388 var found = this.find();
7389 if (found) { signalLater(this, "clear", found.from, found.to); }
7390 }
7391 var min = null, max = null;
7392 for (var i = 0; i < this.lines.length; ++i) {
7393 var line = this$1.lines[i];
7394 var span = getMarkedSpanFor(line.markedSpans, this$1);
7395 if (cm && !this$1.collapsed) { regLineChange(cm, lineNo(line), "text"); }
7396 else if (cm) {
7397 if (span.to != null) { max = lineNo(line); }
7398 if (span.from != null) { min = lineNo(line); }
7399 }
7400 line.markedSpans = removeMarkedSpan(line.markedSpans, span);
7401 if (span.from == null && this$1.collapsed && !lineIsHidden(this$1.doc, line) && cm)
7402 { updateLineHeight(line, textHeight(cm.display)); }
7403 }
7404 if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) {
7405 var visual = visualLine(this$1.lines[i$1]), len = lineLength(visual);
7406 if (len > cm.display.maxLineLength) {
7407 cm.display.maxLine = visual;
7408 cm.display.maxLineLength = len;
7409 cm.display.maxLineChanged = true;
7410 }
7411 } }
7412
7413 if (min != null && cm && this.collapsed) { regChange(cm, min, max + 1); }
7414 this.lines.length = 0;
7415 this.explicitlyCleared = true;
7416 if (this.atomic && this.doc.cantEdit) {
7417 this.doc.cantEdit = false;
7418 if (cm) { reCheckSelection(cm.doc); }
7419 }
7420 if (cm) { signalLater(cm, "markerCleared", cm, this, min, max); }
7421 if (withOp) { endOperation(cm); }
7422 if (this.parent) { this.parent.clear(); }
7423 };
7424
7425 // Find the position of the marker in the document. Returns a {from,
7426 // to} object by default. Side can be passed to get a specific side
7427 // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the
7428 // Pos objects returned contain a line object, rather than a line
7429 // number (used to prevent looking up the same line twice).
7430 TextMarker.prototype.find = function (side, lineObj) {
7431 var this$1 = this;
7432
7433 if (side == null && this.type == "bookmark") { side = 1; }
7434 var from, to;
7435 for (var i = 0; i < this.lines.length; ++i) {
7436 var line = this$1.lines[i];
7437 var span = getMarkedSpanFor(line.markedSpans, this$1);
7438 if (span.from != null) {
7439 from = Pos(lineObj ? line : lineNo(line), span.from);
7440 if (side == -1) { return from }
7441 }
7442 if (span.to != null) {
7443 to = Pos(lineObj ? line : lineNo(line), span.to);
7444 if (side == 1) { return to }
7445 }
7446 }
7447 return from && {from: from, to: to}
7448 };
7449
7450 // Signals that the marker's widget changed, and surrounding layout
7451 // should be recomputed.
7452 TextMarker.prototype.changed = function () {
7453 var this$1 = this;
7454
7455 var pos = this.find(-1, true), widget = this, cm = this.doc.cm;
7456 if (!pos || !cm) { return }
7457 runInOp(cm, function () {
7458 var line = pos.line, lineN = lineNo(pos.line);
7459 var view = findViewForLine(cm, lineN);
7460 if (view) {
7461 clearLineMeasurementCacheFor(view);
7462 cm.curOp.selectionChanged = cm.curOp.forceUpdate = true;
7463 }
7464 cm.curOp.updateMaxLine = true;
7465 if (!lineIsHidden(widget.doc, line) && widget.height != null) {
7466 var oldHeight = widget.height;
7467 widget.height = null;
7468 var dHeight = widgetHeight(widget) - oldHeight;
7469 if (dHeight)
7470 { updateLineHeight(line, line.height + dHeight); }
7471 }
7472 signalLater(cm, "markerChanged", cm, this$1);
7473 });
7474 };
7475
7476 TextMarker.prototype.attachLine = function (line) {
7477 if (!this.lines.length && this.doc.cm) {
7478 var op = this.doc.cm.curOp;
7479 if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
7480 { (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this); }
7481 }
7482 this.lines.push(line);
7483 };
7484
7485 TextMarker.prototype.detachLine = function (line) {
7486 this.lines.splice(indexOf(this.lines, line), 1);
7487 if (!this.lines.length && this.doc.cm) {
7488 var op = this.doc.cm.curOp
7489 ;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this);
7490 }
7491 };
7492 eventMixin(TextMarker);
7493
7494 // Create a marker, wire it up to the right lines, and
7495 function markText(doc, from, to, options, type) {
7496 // Shared markers (across linked documents) are handled separately
7497 // (markTextShared will call out to this again, once per
7498 // document).
7499 if (options && options.shared) { return markTextShared(doc, from, to, options, type) }
7500 // Ensure we are in an operation.
7501 if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, markText)(doc, from, to, options, type) }
7502
7503 var marker = new TextMarker(doc, type), diff = cmp(from, to);
7504 if (options) { copyObj(options, marker, false); }
7505 // Don't connect empty markers unless clearWhenEmpty is false
7506 if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false)
7507 { return marker }
7508 if (marker.replacedWith) {
7509 // Showing up as a widget implies collapsed (widget replaces text)
7510 marker.collapsed = true;
7511 marker.widgetNode = eltP("span", [marker.replacedWith], "CodeMirror-widget");
7512 if (!options.handleMouseEvents) { marker.widgetNode.setAttribute("cm-ignore-events", "true"); }
7513 if (options.insertLeft) { marker.widgetNode.insertLeft = true; }
7514 }
7515 if (marker.collapsed) {
7516 if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||
7517 from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker))
7518 { throw new Error("Inserting collapsed marker partially overlapping an existing one") }
7519 seeCollapsedSpans();
7520 }
7521
7522 if (marker.addToHistory)
7523 { addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN); }
7524
7525 var curLine = from.line, cm = doc.cm, updateMaxLine;
7526 doc.iter(curLine, to.line + 1, function (line) {
7527 if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine)
7528 { updateMaxLine = true; }
7529 if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0); }
7530 addMarkedSpan(line, new MarkedSpan(marker,
7531 curLine == from.line ? from.ch : null,
7532 curLine == to.line ? to.ch : null));
7533 ++curLine;
7534 });
7535 // lineIsHidden depends on the presence of the spans, so needs a second pass
7536 if (marker.collapsed) { doc.iter(from.line, to.line + 1, function (line) {
7537 if (lineIsHidden(doc, line)) { updateLineHeight(line, 0); }
7538 }); }
7539
7540 if (marker.clearOnEnter) { on(marker, "beforeCursorEnter", function () { return marker.clear(); }); }
7541
7542 if (marker.readOnly) {
7543 seeReadOnlySpans();
7544 if (doc.history.done.length || doc.history.undone.length)
7545 { doc.clearHistory(); }
7546 }
7547 if (marker.collapsed) {
7548 marker.id = ++nextMarkerId;
7549 marker.atomic = true;
7550 }
7551 if (cm) {
7552 // Sync editor state
7553 if (updateMaxLine) { cm.curOp.updateMaxLine = true; }
7554 if (marker.collapsed)
7555 { regChange(cm, from.line, to.line + 1); }
7556 else if (marker.className || marker.startStyle || marker.endStyle || marker.css ||
7557 marker.attributes || marker.title)
7558 { for (var i = from.line; i <= to.line; i++) { regLineChange(cm, i, "text"); } }
7559 if (marker.atomic) { reCheckSelection(cm.doc); }
7560 signalLater(cm, "markerAdded", cm, marker);
7561 }
7562 return marker
7563 }
7564
7565 // SHARED TEXTMARKERS
7566
7567 // A shared marker spans multiple linked documents. It is
7568 // implemented as a meta-marker-object controlling multiple normal
7569 // markers.
7570 var SharedTextMarker = function(markers, primary) {
7571 var this$1 = this;
7572
7573 this.markers = markers;
7574 this.primary = primary;
7575 for (var i = 0; i < markers.length; ++i)
7576 { markers[i].parent = this$1; }
7577 };
7578
7579 SharedTextMarker.prototype.clear = function () {
7580 var this$1 = this;
7581
7582 if (this.explicitlyCleared) { return }
7583 this.explicitlyCleared = true;
7584 for (var i = 0; i < this.markers.length; ++i)
7585 { this$1.markers[i].clear(); }
7586 signalLater(this, "clear");
7587 };
7588
7589 SharedTextMarker.prototype.find = function (side, lineObj) {
7590 return this.primary.find(side, lineObj)
7591 };
7592 eventMixin(SharedTextMarker);
7593
7594 function markTextShared(doc, from, to, options, type) {
7595 options = copyObj(options);
7596 options.shared = false;
7597 var markers = [markText(doc, from, to, options, type)], primary = markers[0];
7598 var widget = options.widgetNode;
7599 linkedDocs(doc, function (doc) {
7600 if (widget) { options.widgetNode = widget.cloneNode(true); }
7601 markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));
7602 for (var i = 0; i < doc.linked.length; ++i)
7603 { if (doc.linked[i].isParent) { return } }
7604 primary = lst(markers);
7605 });
7606 return new SharedTextMarker(markers, primary)
7607 }
7608
7609 function findSharedMarkers(doc) {
7610 return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function (m) { return m.parent; })
7611 }
7612
7613 function copySharedMarkers(doc, markers) {
7614 for (var i = 0; i < markers.length; i++) {
7615 var marker = markers[i], pos = marker.find();
7616 var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to);
7617 if (cmp(mFrom, mTo)) {
7618 var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type);
7619 marker.markers.push(subMark);
7620 subMark.parent = marker;
7621 }
7622 }
7623 }
7624
7625 function detachSharedMarkers(markers) {
7626 var loop = function ( i ) {
7627 var marker = markers[i], linked = [marker.primary.doc];
7628 linkedDocs(marker.primary.doc, function (d) { return linked.push(d); });
7629 for (var j = 0; j < marker.markers.length; j++) {
7630 var subMarker = marker.markers[j];
7631 if (indexOf(linked, subMarker.doc) == -1) {
7632 subMarker.parent = null;
7633 marker.markers.splice(j--, 1);
7634 }
7635 }
7636 };
7637
7638 for (var i = 0; i < markers.length; i++) loop( i );
7639 }
7640
7641 var nextDocId = 0;
7642 var Doc = function(text, mode, firstLine, lineSep, direction) {
7643 if (!(this instanceof Doc)) { return new Doc(text, mode, firstLine, lineSep, direction) }
7644 if (firstLine == null) { firstLine = 0; }
7645
7646 BranchChunk.call(this, [new LeafChunk([new Line("", null)])]);
7647 this.first = firstLine;
7648 this.scrollTop = this.scrollLeft = 0;
7649 this.cantEdit = false;
7650 this.cleanGeneration = 1;
7651 this.modeFrontier = this.highlightFrontier = firstLine;
7652 var start = Pos(firstLine, 0);
7653 this.sel = simpleSelection(start);
7654 this.history = new History(null);
7655 this.id = ++nextDocId;
7656 this.modeOption = mode;
7657 this.lineSep = lineSep;
7658 this.direction = (direction == "rtl") ? "rtl" : "ltr";
7659 this.extend = false;
7660
7661 if (typeof text == "string") { text = this.splitLines(text); }
7662 updateDoc(this, {from: start, to: start, text: text});
7663 setSelection(this, simpleSelection(start), sel_dontScroll);
7664 };
7665
7666 Doc.prototype = createObj(BranchChunk.prototype, {
7667 constructor: Doc,
7668 // Iterate over the document. Supports two forms -- with only one
7669 // argument, it calls that for each line in the document. With
7670 // three, it iterates over the range given by the first two (with
7671 // the second being non-inclusive).
7672 iter: function(from, to, op) {
7673 if (op) { this.iterN(from - this.first, to - from, op); }
7674 else { this.iterN(this.first, this.first + this.size, from); }
7675 },
7676
7677 // Non-public interface for adding and removing lines.
7678 insert: function(at, lines) {
7679 var height = 0;
7680 for (var i = 0; i < lines.length; ++i) { height += lines[i].height; }
7681 this.insertInner(at - this.first, lines, height);
7682 },
7683 remove: function(at, n) { this.removeInner(at - this.first, n); },
7684
7685 // From here, the methods are part of the public interface. Most
7686 // are also available from CodeMirror (editor) instances.
7687
7688 getValue: function(lineSep) {
7689 var lines = getLines(this, this.first, this.first + this.size);
7690 if (lineSep === false) { return lines }
7691 return lines.join(lineSep || this.lineSeparator())
7692 },
7693 setValue: docMethodOp(function(code) {
7694 var top = Pos(this.first, 0), last = this.first + this.size - 1;
7695 makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
7696 text: this.splitLines(code), origin: "setValue", full: true}, true);
7697 if (this.cm) { scrollToCoords(this.cm, 0, 0); }
7698 setSelection(this, simpleSelection(top), sel_dontScroll);
7699 }),
7700 replaceRange: function(code, from, to, origin) {
7701 from = clipPos(this, from);
7702 to = to ? clipPos(this, to) : from;
7703 replaceRange(this, code, from, to, origin);
7704 },
7705 getRange: function(from, to, lineSep) {
7706 var lines = getBetween(this, clipPos(this, from), clipPos(this, to));
7707 if (lineSep === false) { return lines }
7708 return lines.join(lineSep || this.lineSeparator())
7709 },
7710
7711 getLine: function(line) {var l = this.getLineHandle(line); return l && l.text},
7712
7713 getLineHandle: function(line) {if (isLine(this, line)) { return getLine(this, line) }},
7714 getLineNumber: function(line) {return lineNo(line)},
7715
7716 getLineHandleVisualStart: function(line) {
7717 if (typeof line == "number") { line = getLine(this, line); }
7718 return visualLine(line)
7719 },
7720
7721 lineCount: function() {return this.size},
7722 firstLine: function() {return this.first},
7723 lastLine: function() {return this.first + this.size - 1},
7724
7725 clipPos: function(pos) {return clipPos(this, pos)},
7726
7727 getCursor: function(start) {
7728 var range$$1 = this.sel.primary(), pos;
7729 if (start == null || start == "head") { pos = range$$1.head; }
7730 else if (start == "anchor") { pos = range$$1.anchor; }
7731 else if (start == "end" || start == "to" || start === false) { pos = range$$1.to(); }
7732 else { pos = range$$1.from(); }
7733 return pos
7734 },
7735 listSelections: function() { return this.sel.ranges },
7736 somethingSelected: function() {return this.sel.somethingSelected()},
7737
7738 setCursor: docMethodOp(function(line, ch, options) {
7739 setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options);
7740 }),
7741 setSelection: docMethodOp(function(anchor, head, options) {
7742 setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options);
7743 }),
7744 extendSelection: docMethodOp(function(head, other, options) {
7745 extendSelection(this, clipPos(this, head), other && clipPos(this, other), options);
7746 }),
7747 extendSelections: docMethodOp(function(heads, options) {
7748 extendSelections(this, clipPosArray(this, heads), options);
7749 }),
7750 extendSelectionsBy: docMethodOp(function(f, options) {
7751 var heads = map(this.sel.ranges, f);
7752 extendSelections(this, clipPosArray(this, heads), options);
7753 }),
7754 setSelections: docMethodOp(function(ranges, primary, options) {
7755 var this$1 = this;
7756
7757 if (!ranges.length) { return }
7758 var out = [];
7759 for (var i = 0; i < ranges.length; i++)
7760 { out[i] = new Range(clipPos(this$1, ranges[i].anchor),
7761 clipPos(this$1, ranges[i].head)); }
7762 if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex); }
7763 setSelection(this, normalizeSelection(this.cm, out, primary), options);
7764 }),
7765 addSelection: docMethodOp(function(anchor, head, options) {
7766 var ranges = this.sel.ranges.slice(0);
7767 ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)));
7768 setSelection(this, normalizeSelection(this.cm, ranges, ranges.length - 1), options);
7769 }),
7770
7771 getSelection: function(lineSep) {
7772 var this$1 = this;
7773
7774 var ranges = this.sel.ranges, lines;
7775 for (var i = 0; i < ranges.length; i++) {
7776 var sel = getBetween(this$1, ranges[i].from(), ranges[i].to());
7777 lines = lines ? lines.concat(sel) : sel;
7778 }
7779 if (lineSep === false) { return lines }
7780 else { return lines.join(lineSep || this.lineSeparator()) }
7781 },
7782 getSelections: function(lineSep) {
7783 var this$1 = this;
7784
7785 var parts = [], ranges = this.sel.ranges;
7786 for (var i = 0; i < ranges.length; i++) {
7787 var sel = getBetween(this$1, ranges[i].from(), ranges[i].to());
7788 if (lineSep !== false) { sel = sel.join(lineSep || this$1.lineSeparator()); }
7789 parts[i] = sel;
7790 }
7791 return parts
7792 },
7793 replaceSelection: function(code, collapse, origin) {
7794 var dup = [];
7795 for (var i = 0; i < this.sel.ranges.length; i++)
7796 { dup[i] = code; }
7797 this.replaceSelections(dup, collapse, origin || "+input");
7798 },
7799 replaceSelections: docMethodOp(function(code, collapse, origin) {
7800 var this$1 = this;
7801
7802 var changes = [], sel = this.sel;
7803 for (var i = 0; i < sel.ranges.length; i++) {
7804 var range$$1 = sel.ranges[i];
7805 changes[i] = {from: range$$1.from(), to: range$$1.to(), text: this$1.splitLines(code[i]), origin: origin};
7806 }
7807 var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse);
7808 for (var i$1 = changes.length - 1; i$1 >= 0; i$1--)
7809 { makeChange(this$1, changes[i$1]); }
7810 if (newSel) { setSelectionReplaceHistory(this, newSel); }
7811 else if (this.cm) { ensureCursorVisible(this.cm); }
7812 }),
7813 undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}),
7814 redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}),
7815 undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}),
7816 redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true);}),
7817
7818 setExtending: function(val) {this.extend = val;},
7819 getExtending: function() {return this.extend},
7820
7821 historySize: function() {
7822 var hist = this.history, done = 0, undone = 0;
7823 for (var i = 0; i < hist.done.length; i++) { if (!hist.done[i].ranges) { ++done; } }
7824 for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].ranges) { ++undone; } }
7825 return {undo: done, redo: undone}
7826 },
7827 clearHistory: function() {this.history = new History(this.history.maxGeneration);},
7828
7829 markClean: function() {
7830 this.cleanGeneration = this.changeGeneration(true);
7831 },
7832 changeGeneration: function(forceSplit) {
7833 if (forceSplit)
7834 { this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null; }
7835 return this.history.generation
7836 },
7837 isClean: function (gen) {
7838 return this.history.generation == (gen || this.cleanGeneration)
7839 },
7840
7841 getHistory: function() {
7842 return {done: copyHistoryArray(this.history.done),
7843 undone: copyHistoryArray(this.history.undone)}
7844 },
7845 setHistory: function(histData) {
7846 var hist = this.history = new History(this.history.maxGeneration);
7847 hist.done = copyHistoryArray(histData.done.slice(0), null, true);
7848 hist.undone = copyHistoryArray(histData.undone.slice(0), null, true);
7849 },
7850
7851 setGutterMarker: docMethodOp(function(line, gutterID, value) {
7852 return changeLine(this, line, "gutter", function (line) {
7853 var markers = line.gutterMarkers || (line.gutterMarkers = {});
7854 markers[gutterID] = value;
7855 if (!value && isEmpty(markers)) { line.gutterMarkers = null; }
7856 return true
7857 })
7858 }),
7859
7860 clearGutter: docMethodOp(function(gutterID) {
7861 var this$1 = this;
7862
7863 this.iter(function (line) {
7864 if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
7865 changeLine(this$1, line, "gutter", function () {
7866 line.gutterMarkers[gutterID] = null;
7867 if (isEmpty(line.gutterMarkers)) { line.gutterMarkers = null; }
7868 return true
7869 });
7870 }
7871 });
7872 }),
7873
7874 lineInfo: function(line) {
7875 var n;
7876 if (typeof line == "number") {
7877 if (!isLine(this, line)) { return null }
7878 n = line;
7879 line = getLine(this, line);
7880 if (!line) { return null }
7881 } else {
7882 n = lineNo(line);
7883 if (n == null) { return null }
7884 }
7885 return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
7886 textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
7887 widgets: line.widgets}
7888 },
7889
7890 addLineClass: docMethodOp(function(handle, where, cls) {
7891 return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
7892 var prop = where == "text" ? "textClass"
7893 : where == "background" ? "bgClass"
7894 : where == "gutter" ? "gutterClass" : "wrapClass";
7895 if (!line[prop]) { line[prop] = cls; }
7896 else if (classTest(cls).test(line[prop])) { return false }
7897 else { line[prop] += " " + cls; }
7898 return true
7899 })
7900 }),
7901 removeLineClass: docMethodOp(function(handle, where, cls) {
7902 return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
7903 var prop = where == "text" ? "textClass"
7904 : where == "background" ? "bgClass"
7905 : where == "gutter" ? "gutterClass" : "wrapClass";
7906 var cur = line[prop];
7907 if (!cur) { return false }
7908 else if (cls == null) { line[prop] = null; }
7909 else {
7910 var found = cur.match(classTest(cls));
7911 if (!found) { return false }
7912 var end = found.index + found[0].length;
7913 line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null;
7914 }
7915 return true
7916 })
7917 }),
7918
7919 addLineWidget: docMethodOp(function(handle, node, options) {
7920 return addLineWidget(this, handle, node, options)
7921 }),
7922 removeLineWidget: function(widget) { widget.clear(); },
7923
7924 markText: function(from, to, options) {
7925 return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range")
7926 },
7927 setBookmark: function(pos, options) {
7928 var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
7929 insertLeft: options && options.insertLeft,
7930 clearWhenEmpty: false, shared: options && options.shared,
7931 handleMouseEvents: options && options.handleMouseEvents};
7932 pos = clipPos(this, pos);
7933 return markText(this, pos, pos, realOpts, "bookmark")
7934 },
7935 findMarksAt: function(pos) {
7936 pos = clipPos(this, pos);
7937 var markers = [], spans = getLine(this, pos.line).markedSpans;
7938 if (spans) { for (var i = 0; i < spans.length; ++i) {
7939 var span = spans[i];
7940 if ((span.from == null || span.from <= pos.ch) &&
7941 (span.to == null || span.to >= pos.ch))
7942 { markers.push(span.marker.parent || span.marker); }
7943 } }
7944 return markers
7945 },
7946 findMarks: function(from, to, filter) {
7947 from = clipPos(this, from); to = clipPos(this, to);
7948 var found = [], lineNo$$1 = from.line;
7949 this.iter(from.line, to.line + 1, function (line) {
7950 var spans = line.markedSpans;
7951 if (spans) { for (var i = 0; i < spans.length; i++) {
7952 var span = spans[i];
7953 if (!(span.to != null && lineNo$$1 == from.line && from.ch >= span.to ||
7954 span.from == null && lineNo$$1 != from.line ||
7955 span.from != null && lineNo$$1 == to.line && span.from >= to.ch) &&
7956 (!filter || filter(span.marker)))
7957 { found.push(span.marker.parent || span.marker); }
7958 } }
7959 ++lineNo$$1;
7960 });
7961 return found
7962 },
7963 getAllMarks: function() {
7964 var markers = [];
7965 this.iter(function (line) {
7966 var sps = line.markedSpans;
7967 if (sps) { for (var i = 0; i < sps.length; ++i)
7968 { if (sps[i].from != null) { markers.push(sps[i].marker); } } }
7969 });
7970 return markers
7971 },
7972
7973 posFromIndex: function(off) {
7974 var ch, lineNo$$1 = this.first, sepSize = this.lineSeparator().length;
7975 this.iter(function (line) {
7976 var sz = line.text.length + sepSize;
7977 if (sz > off) { ch = off; return true }
7978 off -= sz;
7979 ++lineNo$$1;
7980 });
7981 return clipPos(this, Pos(lineNo$$1, ch))
7982 },
7983 indexFromPos: function (coords) {
7984 coords = clipPos(this, coords);
7985 var index = coords.ch;
7986 if (coords.line < this.first || coords.ch < 0) { return 0 }
7987 var sepSize = this.lineSeparator().length;
7988 this.iter(this.first, coords.line, function (line) { // iter aborts when callback returns a truthy value
7989 index += line.text.length + sepSize;
7990 });
7991 return index
7992 },
7993
7994 copy: function(copyHistory) {
7995 var doc = new Doc(getLines(this, this.first, this.first + this.size),
7996 this.modeOption, this.first, this.lineSep, this.direction);
7997 doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;
7998 doc.sel = this.sel;
7999 doc.extend = false;
8000 if (copyHistory) {
8001 doc.history.undoDepth = this.history.undoDepth;
8002 doc.setHistory(this.getHistory());
8003 }
8004 return doc
8005 },
8006
8007 linkedDoc: function(options) {
8008 if (!options) { options = {}; }
8009 var from = this.first, to = this.first + this.size;
8010 if (options.from != null && options.from > from) { from = options.from; }
8011 if (options.to != null && options.to < to) { to = options.to; }
8012 var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep, this.direction);
8013 if (options.sharedHist) { copy.history = this.history
8014 ; }(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});
8015 copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];
8016 copySharedMarkers(copy, findSharedMarkers(this));
8017 return copy
8018 },
8019 unlinkDoc: function(other) {
8020 var this$1 = this;
8021
8022 if (other instanceof CodeMirror) { other = other.doc; }
8023 if (this.linked) { for (var i = 0; i < this.linked.length; ++i) {
8024 var link = this$1.linked[i];
8025 if (link.doc != other) { continue }
8026 this$1.linked.splice(i, 1);
8027 other.unlinkDoc(this$1);
8028 detachSharedMarkers(findSharedMarkers(this$1));
8029 break
8030 } }
8031 // If the histories were shared, split them again
8032 if (other.history == this.history) {
8033 var splitIds = [other.id];
8034 linkedDocs(other, function (doc) { return splitIds.push(doc.id); }, true);
8035 other.history = new History(null);
8036 other.history.done = copyHistoryArray(this.history.done, splitIds);
8037 other.history.undone = copyHistoryArray(this.history.undone, splitIds);
8038 }
8039 },
8040 iterLinkedDocs: function(f) {linkedDocs(this, f);},
8041
8042 getMode: function() {return this.mode},
8043 getEditor: function() {return this.cm},
8044
8045 splitLines: function(str) {
8046 if (this.lineSep) { return str.split(this.lineSep) }
8047 return splitLinesAuto(str)
8048 },
8049 lineSeparator: function() { return this.lineSep || "\n" },
8050
8051 setDirection: docMethodOp(function (dir) {
8052 if (dir != "rtl") { dir = "ltr"; }
8053 if (dir == this.direction) { return }
8054 this.direction = dir;
8055 this.iter(function (line) { return line.order = null; });
8056 if (this.cm) { directionChanged(this.cm); }
8057 })
8058 });
8059
8060 // Public alias.
8061 Doc.prototype.eachLine = Doc.prototype.iter;
8062
8063 // Kludge to work around strange IE behavior where it'll sometimes
8064 // re-fire a series of drag-related events right after the drop (#1551)
8065 var lastDrop = 0;
8066
8067 function onDrop(e) {
8068 var cm = this;
8069 clearDragCursor(cm);
8070 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))
8071 { return }
8072 e_preventDefault(e);
8073 if (ie) { lastDrop = +new Date; }
8074 var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
8075 if (!pos || cm.isReadOnly()) { return }
8076 // Might be a file drop, in which case we simply extract the text
8077 // and insert it.
8078 if (files && files.length && window.FileReader && window.File) {
8079 var n = files.length, text = Array(n), read = 0;
8080 var loadFile = function (file, i) {
8081 if (cm.options.allowDropFileTypes &&
8082 indexOf(cm.options.allowDropFileTypes, file.type) == -1)
8083 { return }
8084
8085 var reader = new FileReader;
8086 reader.onload = operation(cm, function () {
8087 var content = reader.result;
8088 if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) { content = ""; }
8089 text[i] = content;
8090 if (++read == n) {
8091 pos = clipPos(cm.doc, pos);
8092 var change = {from: pos, to: pos,
8093 text: cm.doc.splitLines(text.join(cm.doc.lineSeparator())),
8094 origin: "paste"};
8095 makeChange(cm.doc, change);
8096 setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change)));
8097 }
8098 });
8099 reader.readAsText(file);
8100 };
8101 for (var i = 0; i < n; ++i) { loadFile(files[i], i); }
8102 } else { // Normal drop
8103 // Don't do a replace if the drop happened inside of the selected text.
8104 if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {
8105 cm.state.draggingText(e);
8106 // Ensure the editor is re-focused
8107 setTimeout(function () { return cm.display.input.focus(); }, 20);
8108 return
8109 }
8110 try {
8111 var text$1 = e.dataTransfer.getData("Text");
8112 if (text$1) {
8113 var selected;
8114 if (cm.state.draggingText && !cm.state.draggingText.copy)
8115 { selected = cm.listSelections(); }
8116 setSelectionNoUndo(cm.doc, simpleSelection(pos, pos));
8117 if (selected) { for (var i$1 = 0; i$1 < selected.length; ++i$1)
8118 { replaceRange(cm.doc, "", selected[i$1].anchor, selected[i$1].head, "drag"); } }
8119 cm.replaceSelection(text$1, "around", "paste");
8120 cm.display.input.focus();
8121 }
8122 }
8123 catch(e){}
8124 }
8125 }
8126
8127 function onDragStart(cm, e) {
8128 if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return }
8129 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return }
8130
8131 e.dataTransfer.setData("Text", cm.getSelection());
8132 e.dataTransfer.effectAllowed = "copyMove";
8133
8134 // Use dummy image instead of default browsers image.
8135 // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
8136 if (e.dataTransfer.setDragImage && !safari) {
8137 var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
8138 img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
8139 if (presto) {
8140 img.width = img.height = 1;
8141 cm.display.wrapper.appendChild(img);
8142 // Force a relayout, or Opera won't use our image for some obscure reason
8143 img._top = img.offsetTop;
8144 }
8145 e.dataTransfer.setDragImage(img, 0, 0);
8146 if (presto) { img.parentNode.removeChild(img); }
8147 }
8148 }
8149
8150 function onDragOver(cm, e) {
8151 var pos = posFromMouse(cm, e);
8152 if (!pos) { return }
8153 var frag = document.createDocumentFragment();
8154 drawSelectionCursor(cm, pos, frag);
8155 if (!cm.display.dragCursor) {
8156 cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors");
8157 cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv);
8158 }
8159 removeChildrenAndAdd(cm.display.dragCursor, frag);
8160 }
8161
8162 function clearDragCursor(cm) {
8163 if (cm.display.dragCursor) {
8164 cm.display.lineSpace.removeChild(cm.display.dragCursor);
8165 cm.display.dragCursor = null;
8166 }
8167 }
8168
8169 // These must be handled carefully, because naively registering a
8170 // handler for each editor will cause the editors to never be
8171 // garbage collected.
8172
8173 function forEachCodeMirror(f) {
8174 if (!document.getElementsByClassName) { return }
8175 var byClass = document.getElementsByClassName("CodeMirror"), editors = [];
8176 for (var i = 0; i < byClass.length; i++) {
8177 var cm = byClass[i].CodeMirror;
8178 if (cm) { editors.push(cm); }
8179 }
8180 if (editors.length) { editors[0].operation(function () {
8181 for (var i = 0; i < editors.length; i++) { f(editors[i]); }
8182 }); }
8183 }
8184
8185 var globalsRegistered = false;
8186 function ensureGlobalHandlers() {
8187 if (globalsRegistered) { return }
8188 registerGlobalHandlers();
8189 globalsRegistered = true;
8190 }
8191 function registerGlobalHandlers() {
8192 // When the window resizes, we need to refresh active editors.
8193 var resizeTimer;
8194 on(window, "resize", function () {
8195 if (resizeTimer == null) { resizeTimer = setTimeout(function () {
8196 resizeTimer = null;
8197 forEachCodeMirror(onResize);
8198 }, 100); }
8199 });
8200 // When the window loses focus, we want to show the editor as blurred
8201 on(window, "blur", function () { return forEachCodeMirror(onBlur); });
8202 }
8203 // Called when the window resizes
8204 function onResize(cm) {
8205 var d = cm.display;
8206 // Might be a text scaling operation, clear size caches.
8207 d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
8208 d.scrollbarsClipped = false;
8209 cm.setSize();
8210 }
8211
8212 var keyNames = {
8213 3: "Pause", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
8214 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
8215 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
8216 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod",
8217 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 127: "Delete", 145: "ScrollLock",
8218 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
8219 221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete",
8220 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert"
8221 };
8222
8223 // Number keys
8224 for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i); }
8225 // Alphabetic keys
8226 for (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1); }
8227 // Function keys
8228 for (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235] = "F" + i$2; }
8229
8230 var keyMap = {};
8231
8232 keyMap.basic = {
8233 "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
8234 "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
8235 "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore",
8236 "Tab": "defaultTab", "Shift-Tab": "indentAuto",
8237 "Enter": "newlineAndIndent", "Insert": "toggleOverwrite",
8238 "Esc": "singleSelection"
8239 };
8240 // Note that the save and find-related commands aren't defined by
8241 // default. User code or addons can define them. Unknown commands
8242 // are simply ignored.
8243 keyMap.pcDefault = {
8244 "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
8245 "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown",
8246 "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
8247 "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
8248 "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
8249 "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
8250 "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection",
8251 "fallthrough": "basic"
8252 };
8253 // Very basic readline/emacs-style bindings, which are standard on Mac.
8254 keyMap.emacsy = {
8255 "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
8256 "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
8257 "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
8258 "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars",
8259 "Ctrl-O": "openLine"
8260 };
8261 keyMap.macDefault = {
8262 "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
8263 "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
8264 "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore",
8265 "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
8266 "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
8267 "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight",
8268 "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd",
8269 "fallthrough": ["basic", "emacsy"]
8270 };
8271 keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
8272
8273 // KEYMAP DISPATCH
8274
8275 function normalizeKeyName(name) {
8276 var parts = name.split(/-(?!$)/);
8277 name = parts[parts.length - 1];
8278 var alt, ctrl, shift, cmd;
8279 for (var i = 0; i < parts.length - 1; i++) {
8280 var mod = parts[i];
8281 if (/^(cmd|meta|m)$/i.test(mod)) { cmd = true; }
8282 else if (/^a(lt)?$/i.test(mod)) { alt = true; }
8283 else if (/^(c|ctrl|control)$/i.test(mod)) { ctrl = true; }
8284 else if (/^s(hift)?$/i.test(mod)) { shift = true; }
8285 else { throw new Error("Unrecognized modifier name: " + mod) }
8286 }
8287 if (alt) { name = "Alt-" + name; }
8288 if (ctrl) { name = "Ctrl-" + name; }
8289 if (cmd) { name = "Cmd-" + name; }
8290 if (shift) { name = "Shift-" + name; }
8291 return name
8292 }
8293
8294 // This is a kludge to keep keymaps mostly working as raw objects
8295 // (backwards compatibility) while at the same time support features
8296 // like normalization and multi-stroke key bindings. It compiles a
8297 // new normalized keymap, and then updates the old object to reflect
8298 // this.
8299 function normalizeKeyMap(keymap) {
8300 var copy = {};
8301 for (var keyname in keymap) { if (keymap.hasOwnProperty(keyname)) {
8302 var value = keymap[keyname];
8303 if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) { continue }
8304 if (value == "...") { delete keymap[keyname]; continue }
8305
8306 var keys = map(keyname.split(" "), normalizeKeyName);
8307 for (var i = 0; i < keys.length; i++) {
8308 var val = (void 0), name = (void 0);
8309 if (i == keys.length - 1) {
8310 name = keys.join(" ");
8311 val = value;
8312 } else {
8313 name = keys.slice(0, i + 1).join(" ");
8314 val = "...";
8315 }
8316 var prev = copy[name];
8317 if (!prev) { copy[name] = val; }
8318 else if (prev != val) { throw new Error("Inconsistent bindings for " + name) }
8319 }
8320 delete keymap[keyname];
8321 } }
8322 for (var prop in copy) { keymap[prop] = copy[prop]; }
8323 return keymap
8324 }
8325
8326 function lookupKey(key, map$$1, handle, context) {
8327 map$$1 = getKeyMap(map$$1);
8328 var found = map$$1.call ? map$$1.call(key, context) : map$$1[key];
8329 if (found === false) { return "nothing" }
8330 if (found === "...") { return "multi" }
8331 if (found != null && handle(found)) { return "handled" }
8332
8333 if (map$$1.fallthrough) {
8334 if (Object.prototype.toString.call(map$$1.fallthrough) != "[object Array]")
8335 { return lookupKey(key, map$$1.fallthrough, handle, context) }
8336 for (var i = 0; i < map$$1.fallthrough.length; i++) {
8337 var result = lookupKey(key, map$$1.fallthrough[i], handle, context);
8338 if (result) { return result }
8339 }
8340 }
8341 }
8342
8343 // Modifier key presses don't count as 'real' key presses for the
8344 // purpose of keymap fallthrough.
8345 function isModifierKey(value) {
8346 var name = typeof value == "string" ? value : keyNames[value.keyCode];
8347 return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod"
8348 }
8349
8350 function addModifierNames(name, event, noShift) {
8351 var base = name;
8352 if (event.altKey && base != "Alt") { name = "Alt-" + name; }
8353 if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") { name = "Ctrl-" + name; }
8354 if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") { name = "Cmd-" + name; }
8355 if (!noShift && event.shiftKey && base != "Shift") { name = "Shift-" + name; }
8356 return name
8357 }
8358
8359 // Look up the name of a key as indicated by an event object.
8360 function keyName(event, noShift) {
8361 if (presto && event.keyCode == 34 && event["char"]) { return false }
8362 var name = keyNames[event.keyCode];
8363 if (name == null || event.altGraphKey) { return false }
8364 // Ctrl-ScrollLock has keyCode 3, same as Ctrl-Pause,
8365 // so we'll use event.code when available (Chrome 48+, FF 38+, Safari 10.1+)
8366 if (event.keyCode == 3 && event.code) { name = event.code; }
8367 return addModifierNames(name, event, noShift)
8368 }
8369
8370 function getKeyMap(val) {
8371 return typeof val == "string" ? keyMap[val] : val
8372 }
8373
8374 // Helper for deleting text near the selection(s), used to implement
8375 // backspace, delete, and similar functionality.
8376 function deleteNearSelection(cm, compute) {
8377 var ranges = cm.doc.sel.ranges, kill = [];
8378 // Build up a set of ranges to kill first, merging overlapping
8379 // ranges.
8380 for (var i = 0; i < ranges.length; i++) {
8381 var toKill = compute(ranges[i]);
8382 while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {
8383 var replaced = kill.pop();
8384 if (cmp(replaced.from, toKill.from) < 0) {
8385 toKill.from = replaced.from;
8386 break
8387 }
8388 }
8389 kill.push(toKill);
8390 }
8391 // Next, remove those actual ranges.
8392 runInOp(cm, function () {
8393 for (var i = kill.length - 1; i >= 0; i--)
8394 { replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete"); }
8395 ensureCursorVisible(cm);
8396 });
8397 }
8398
8399 function moveCharLogically(line, ch, dir) {
8400 var target = skipExtendingChars(line.text, ch + dir, dir);
8401 return target < 0 || target > line.text.length ? null : target
8402 }
8403
8404 function moveLogically(line, start, dir) {
8405 var ch = moveCharLogically(line, start.ch, dir);
8406 return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before")
8407 }
8408
8409 function endOfLine(visually, cm, lineObj, lineNo, dir) {
8410 if (visually) {
8411 var order = getOrder(lineObj, cm.doc.direction);
8412 if (order) {
8413 var part = dir < 0 ? lst(order) : order[0];
8414 var moveInStorageOrder = (dir < 0) == (part.level == 1);
8415 var sticky = moveInStorageOrder ? "after" : "before";
8416 var ch;
8417 // With a wrapped rtl chunk (possibly spanning multiple bidi parts),
8418 // it could be that the last bidi part is not on the last visual line,
8419 // since visual lines contain content order-consecutive chunks.
8420 // Thus, in rtl, we are looking for the first (content-order) character
8421 // in the rtl chunk that is on the last line (that is, the same line
8422 // as the last (content-order) character).
8423 if (part.level > 0 || cm.doc.direction == "rtl") {
8424 var prep = prepareMeasureForLine(cm, lineObj);
8425 ch = dir < 0 ? lineObj.text.length - 1 : 0;
8426 var targetTop = measureCharPrepared(cm, prep, ch).top;
8427 ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch);
8428 if (sticky == "before") { ch = moveCharLogically(lineObj, ch, 1); }
8429 } else { ch = dir < 0 ? part.to : part.from; }
8430 return new Pos(lineNo, ch, sticky)
8431 }
8432 }
8433 return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after")
8434 }
8435
8436 function moveVisually(cm, line, start, dir) {
8437 var bidi = getOrder(line, cm.doc.direction);
8438 if (!bidi) { return moveLogically(line, start, dir) }
8439 if (start.ch >= line.text.length) {
8440 start.ch = line.text.length;
8441 start.sticky = "before";
8442 } else if (start.ch <= 0) {
8443 start.ch = 0;
8444 start.sticky = "after";
8445 }
8446 var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos];
8447 if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) {
8448 // Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines,
8449 // nothing interesting happens.
8450 return moveLogically(line, start, dir)
8451 }
8452
8453 var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); };
8454 var prep;
8455 var getWrappedLineExtent = function (ch) {
8456 if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} }
8457 prep = prep || prepareMeasureForLine(cm, line);
8458 return wrappedLineExtentChar(cm, line, prep, ch)
8459 };
8460 var wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch);
8461
8462 if (cm.doc.direction == "rtl" || part.level == 1) {
8463 var moveInStorageOrder = (part.level == 1) == (dir < 0);
8464 var ch = mv(start, moveInStorageOrder ? 1 : -1);
8465 if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) {
8466 // Case 2: We move within an rtl part or in an rtl editor on the same visual line
8467 var sticky = moveInStorageOrder ? "before" : "after";
8468 return new Pos(start.line, ch, sticky)
8469 }
8470 }
8471
8472 // Case 3: Could not move within this bidi part in this visual line, so leave
8473 // the current bidi part
8474
8475 var searchInVisualLine = function (partPos, dir, wrappedLineExtent) {
8476 var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder
8477 ? new Pos(start.line, mv(ch, 1), "before")
8478 : new Pos(start.line, ch, "after"); };
8479
8480 for (; partPos >= 0 && partPos < bidi.length; partPos += dir) {
8481 var part = bidi[partPos];
8482 var moveInStorageOrder = (dir > 0) == (part.level != 1);
8483 var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1);
8484 if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) }
8485 ch = moveInStorageOrder ? part.from : mv(part.to, -1);
8486 if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) }
8487 }
8488 };
8489
8490 // Case 3a: Look for other bidi parts on the same visual line
8491 var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent);
8492 if (res) { return res }
8493
8494 // Case 3b: Look for other bidi parts on the next visual line
8495 var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1);
8496 if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) {
8497 res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh));
8498 if (res) { return res }
8499 }
8500
8501 // Case 4: Nowhere to move
8502 return null
8503 }
8504
8505 // Commands are parameter-less actions that can be performed on an
8506 // editor, mostly used for keybindings.
8507 var commands = {
8508 selectAll: selectAll,
8509 singleSelection: function (cm) { return cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll); },
8510 killLine: function (cm) { return deleteNearSelection(cm, function (range) {
8511 if (range.empty()) {
8512 var len = getLine(cm.doc, range.head.line).text.length;
8513 if (range.head.ch == len && range.head.line < cm.lastLine())
8514 { return {from: range.head, to: Pos(range.head.line + 1, 0)} }
8515 else
8516 { return {from: range.head, to: Pos(range.head.line, len)} }
8517 } else {
8518 return {from: range.from(), to: range.to()}
8519 }
8520 }); },
8521 deleteLine: function (cm) { return deleteNearSelection(cm, function (range) { return ({
8522 from: Pos(range.from().line, 0),
8523 to: clipPos(cm.doc, Pos(range.to().line + 1, 0))
8524 }); }); },
8525 delLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { return ({
8526 from: Pos(range.from().line, 0), to: range.from()
8527 }); }); },
8528 delWrappedLineLeft: function (cm) { return deleteNearSelection(cm, function (range) {
8529 var top = cm.charCoords(range.head, "div").top + 5;
8530 var leftPos = cm.coordsChar({left: 0, top: top}, "div");
8531 return {from: leftPos, to: range.from()}
8532 }); },
8533 delWrappedLineRight: function (cm) { return deleteNearSelection(cm, function (range) {
8534 var top = cm.charCoords(range.head, "div").top + 5;
8535 var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div");
8536 return {from: range.from(), to: rightPos }
8537 }); },
8538 undo: function (cm) { return cm.undo(); },
8539 redo: function (cm) { return cm.redo(); },
8540 undoSelection: function (cm) { return cm.undoSelection(); },
8541 redoSelection: function (cm) { return cm.redoSelection(); },
8542 goDocStart: function (cm) { return cm.extendSelection(Pos(cm.firstLine(), 0)); },
8543 goDocEnd: function (cm) { return cm.extendSelection(Pos(cm.lastLine())); },
8544 goLineStart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStart(cm, range.head.line); },
8545 {origin: "+move", bias: 1}
8546 ); },
8547 goLineStartSmart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStartSmart(cm, range.head); },
8548 {origin: "+move", bias: 1}
8549 ); },
8550 goLineEnd: function (cm) { return cm.extendSelectionsBy(function (range) { return lineEnd(cm, range.head.line); },
8551 {origin: "+move", bias: -1}
8552 ); },
8553 goLineRight: function (cm) { return cm.extendSelectionsBy(function (range) {
8554 var top = cm.cursorCoords(range.head, "div").top + 5;
8555 return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div")
8556 }, sel_move); },
8557 goLineLeft: function (cm) { return cm.extendSelectionsBy(function (range) {
8558 var top = cm.cursorCoords(range.head, "div").top + 5;
8559 return cm.coordsChar({left: 0, top: top}, "div")
8560 }, sel_move); },
8561 goLineLeftSmart: function (cm) { return cm.extendSelectionsBy(function (range) {
8562 var top = cm.cursorCoords(range.head, "div").top + 5;
8563 var pos = cm.coordsChar({left: 0, top: top}, "div");
8564 if (pos.ch < cm.getLine(pos.line).search(/\S/)) { return lineStartSmart(cm, range.head) }
8565 return pos
8566 }, sel_move); },
8567 goLineUp: function (cm) { return cm.moveV(-1, "line"); },
8568 goLineDown: function (cm) { return cm.moveV(1, "line"); },
8569 goPageUp: function (cm) { return cm.moveV(-1, "page"); },
8570 goPageDown: function (cm) { return cm.moveV(1, "page"); },
8571 goCharLeft: function (cm) { return cm.moveH(-1, "char"); },
8572 goCharRight: function (cm) { return cm.moveH(1, "char"); },
8573 goColumnLeft: function (cm) { return cm.moveH(-1, "column"); },
8574 goColumnRight: function (cm) { return cm.moveH(1, "column"); },
8575 goWordLeft: function (cm) { return cm.moveH(-1, "word"); },
8576 goGroupRight: function (cm) { return cm.moveH(1, "group"); },
8577 goGroupLeft: function (cm) { return cm.moveH(-1, "group"); },
8578 goWordRight: function (cm) { return cm.moveH(1, "word"); },
8579 delCharBefore: function (cm) { return cm.deleteH(-1, "char"); },
8580 delCharAfter: function (cm) { return cm.deleteH(1, "char"); },
8581 delWordBefore: function (cm) { return cm.deleteH(-1, "word"); },
8582 delWordAfter: function (cm) { return cm.deleteH(1, "word"); },
8583 delGroupBefore: function (cm) { return cm.deleteH(-1, "group"); },
8584 delGroupAfter: function (cm) { return cm.deleteH(1, "group"); },
8585 indentAuto: function (cm) { return cm.indentSelection("smart"); },
8586 indentMore: function (cm) { return cm.indentSelection("add"); },
8587 indentLess: function (cm) { return cm.indentSelection("subtract"); },
8588 insertTab: function (cm) { return cm.replaceSelection("\t"); },
8589 insertSoftTab: function (cm) {
8590 var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize;
8591 for (var i = 0; i < ranges.length; i++) {
8592 var pos = ranges[i].from();
8593 var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize);
8594 spaces.push(spaceStr(tabSize - col % tabSize));
8595 }
8596 cm.replaceSelections(spaces);
8597 },
8598 defaultTab: function (cm) {
8599 if (cm.somethingSelected()) { cm.indentSelection("add"); }
8600 else { cm.execCommand("insertTab"); }
8601 },
8602 // Swap the two chars left and right of each selection's head.
8603 // Move cursor behind the two swapped characters afterwards.
8604 //
8605 // Doesn't consider line feeds a character.
8606 // Doesn't scan more than one line above to find a character.
8607 // Doesn't do anything on an empty line.
8608 // Doesn't do anything with non-empty selections.
8609 transposeChars: function (cm) { return runInOp(cm, function () {
8610 var ranges = cm.listSelections(), newSel = [];
8611 for (var i = 0; i < ranges.length; i++) {
8612 if (!ranges[i].empty()) { continue }
8613 var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text;
8614 if (line) {
8615 if (cur.ch == line.length) { cur = new Pos(cur.line, cur.ch - 1); }
8616 if (cur.ch > 0) {
8617 cur = new Pos(cur.line, cur.ch + 1);
8618 cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),
8619 Pos(cur.line, cur.ch - 2), cur, "+transpose");
8620 } else if (cur.line > cm.doc.first) {
8621 var prev = getLine(cm.doc, cur.line - 1).text;
8622 if (prev) {
8623 cur = new Pos(cur.line, 1);
8624 cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() +
8625 prev.charAt(prev.length - 1),
8626 Pos(cur.line - 1, prev.length - 1), cur, "+transpose");
8627 }
8628 }
8629 }
8630 newSel.push(new Range(cur, cur));
8631 }
8632 cm.setSelections(newSel);
8633 }); },
8634 newlineAndIndent: function (cm) { return runInOp(cm, function () {
8635 var sels = cm.listSelections();
8636 for (var i = sels.length - 1; i >= 0; i--)
8637 { cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input"); }
8638 sels = cm.listSelections();
8639 for (var i$1 = 0; i$1 < sels.length; i$1++)
8640 { cm.indentLine(sels[i$1].from().line, null, true); }
8641 ensureCursorVisible(cm);
8642 }); },
8643 openLine: function (cm) { return cm.replaceSelection("\n", "start"); },
8644 toggleOverwrite: function (cm) { return cm.toggleOverwrite(); }
8645 };
8646
8647
8648 function lineStart(cm, lineN) {
8649 var line = getLine(cm.doc, lineN);
8650 var visual = visualLine(line);
8651 if (visual != line) { lineN = lineNo(visual); }
8652 return endOfLine(true, cm, visual, lineN, 1)
8653 }
8654 function lineEnd(cm, lineN) {
8655 var line = getLine(cm.doc, lineN);
8656 var visual = visualLineEnd(line);
8657 if (visual != line) { lineN = lineNo(visual); }
8658 return endOfLine(true, cm, line, lineN, -1)
8659 }
8660 function lineStartSmart(cm, pos) {
8661 var start = lineStart(cm, pos.line);
8662 var line = getLine(cm.doc, start.line);
8663 var order = getOrder(line, cm.doc.direction);
8664 if (!order || order[0].level == 0) {
8665 var firstNonWS = Math.max(0, line.text.search(/\S/));
8666 var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch;
8667 return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky)
8668 }
8669 return start
8670 }
8671
8672 // Run a handler that was bound to a key.
8673 function doHandleBinding(cm, bound, dropShift) {
8674 if (typeof bound == "string") {
8675 bound = commands[bound];
8676 if (!bound) { return false }
8677 }
8678 // Ensure previous input has been read, so that the handler sees a
8679 // consistent view of the document
8680 cm.display.input.ensurePolled();
8681 var prevShift = cm.display.shift, done = false;
8682 try {
8683 if (cm.isReadOnly()) { cm.state.suppressEdits = true; }
8684 if (dropShift) { cm.display.shift = false; }
8685 done = bound(cm) != Pass;
8686 } finally {
8687 cm.display.shift = prevShift;
8688 cm.state.suppressEdits = false;
8689 }
8690 return done
8691 }
8692
8693 function lookupKeyForEditor(cm, name, handle) {
8694 for (var i = 0; i < cm.state.keyMaps.length; i++) {
8695 var result = lookupKey(name, cm.state.keyMaps[i], handle, cm);
8696 if (result) { return result }
8697 }
8698 return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm))
8699 || lookupKey(name, cm.options.keyMap, handle, cm)
8700 }
8701
8702 // Note that, despite the name, this function is also used to check
8703 // for bound mouse clicks.
8704
8705 var stopSeq = new Delayed;
8706
8707 function dispatchKey(cm, name, e, handle) {
8708 var seq = cm.state.keySeq;
8709 if (seq) {
8710 if (isModifierKey(name)) { return "handled" }
8711 if (/\'$/.test(name))
8712 { cm.state.keySeq = null; }
8713 else
8714 { stopSeq.set(50, function () {
8715 if (cm.state.keySeq == seq) {
8716 cm.state.keySeq = null;
8717 cm.display.input.reset();
8718 }
8719 }); }
8720 if (dispatchKeyInner(cm, seq + " " + name, e, handle)) { return true }
8721 }
8722 return dispatchKeyInner(cm, name, e, handle)
8723 }
8724
8725 function dispatchKeyInner(cm, name, e, handle) {
8726 var result = lookupKeyForEditor(cm, name, handle);
8727
8728 if (result == "multi")
8729 { cm.state.keySeq = name; }
8730 if (result == "handled")
8731 { signalLater(cm, "keyHandled", cm, name, e); }
8732
8733 if (result == "handled" || result == "multi") {
8734 e_preventDefault(e);
8735 restartBlink(cm);
8736 }
8737
8738 return !!result
8739 }
8740
8741 // Handle a key from the keydown event.
8742 function handleKeyBinding(cm, e) {
8743 var name = keyName(e, true);
8744 if (!name) { return false }
8745
8746 if (e.shiftKey && !cm.state.keySeq) {
8747 // First try to resolve full name (including 'Shift-'). Failing
8748 // that, see if there is a cursor-motion command (starting with
8749 // 'go') bound to the keyname without 'Shift-'.
8750 return dispatchKey(cm, "Shift-" + name, e, function (b) { return doHandleBinding(cm, b, true); })
8751 || dispatchKey(cm, name, e, function (b) {
8752 if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
8753 { return doHandleBinding(cm, b) }
8754 })
8755 } else {
8756 return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b); })
8757 }
8758 }
8759
8760 // Handle a key from the keypress event
8761 function handleCharBinding(cm, e, ch) {
8762 return dispatchKey(cm, "'" + ch + "'", e, function (b) { return doHandleBinding(cm, b, true); })
8763 }
8764
8765 var lastStoppedKey = null;
8766 function onKeyDown(e) {
8767 var cm = this;
8768 cm.curOp.focus = activeElt();
8769 if (signalDOMEvent(cm, e)) { return }
8770 // IE does strange things with escape.
8771 if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false; }
8772 var code = e.keyCode;
8773 cm.display.shift = code == 16 || e.shiftKey;
8774 var handled = handleKeyBinding(cm, e);
8775 if (presto) {
8776 lastStoppedKey = handled ? code : null;
8777 // Opera has no cut event... we try to at least catch the key combo
8778 if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
8779 { cm.replaceSelection("", null, "cut"); }
8780 }
8781
8782 // Turn mouse into crosshair when Alt is held on Mac.
8783 if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className))
8784 { showCrossHair(cm); }
8785 }
8786
8787 function showCrossHair(cm) {
8788 var lineDiv = cm.display.lineDiv;
8789 addClass(lineDiv, "CodeMirror-crosshair");
8790
8791 function up(e) {
8792 if (e.keyCode == 18 || !e.altKey) {
8793 rmClass(lineDiv, "CodeMirror-crosshair");
8794 off(document, "keyup", up);
8795 off(document, "mouseover", up);
8796 }
8797 }
8798 on(document, "keyup", up);
8799 on(document, "mouseover", up);
8800 }
8801
8802 function onKeyUp(e) {
8803 if (e.keyCode == 16) { this.doc.sel.shift = false; }
8804 signalDOMEvent(this, e);
8805 }
8806
8807 function onKeyPress(e) {
8808 var cm = this;
8809 if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return }
8810 var keyCode = e.keyCode, charCode = e.charCode;
8811 if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return}
8812 if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) { return }
8813 var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
8814 // Some browsers fire keypress events for backspace
8815 if (ch == "\x08") { return }
8816 if (handleCharBinding(cm, e, ch)) { return }
8817 cm.display.input.onKeyPress(e);
8818 }
8819
8820 var DOUBLECLICK_DELAY = 400;
8821
8822 var PastClick = function(time, pos, button) {
8823 this.time = time;
8824 this.pos = pos;
8825 this.button = button;
8826 };
8827
8828 PastClick.prototype.compare = function (time, pos, button) {
8829 return this.time + DOUBLECLICK_DELAY > time &&
8830 cmp(pos, this.pos) == 0 && button == this.button
8831 };
8832
8833 var lastClick, lastDoubleClick;
8834 function clickRepeat(pos, button) {
8835 var now = +new Date;
8836 if (lastDoubleClick && lastDoubleClick.compare(now, pos, button)) {
8837 lastClick = lastDoubleClick = null;
8838 return "triple"
8839 } else if (lastClick && lastClick.compare(now, pos, button)) {
8840 lastDoubleClick = new PastClick(now, pos, button);
8841 lastClick = null;
8842 return "double"
8843 } else {
8844 lastClick = new PastClick(now, pos, button);
8845 lastDoubleClick = null;
8846 return "single"
8847 }
8848 }
8849
8850 // A mouse down can be a single click, double click, triple click,
8851 // start of selection drag, start of text drag, new cursor
8852 // (ctrl-click), rectangle drag (alt-drag), or xwin
8853 // middle-click-paste. Or it might be a click on something we should
8854 // not interfere with, such as a scrollbar or widget.
8855 function onMouseDown(e) {
8856 var cm = this, display = cm.display;
8857 if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) { return }
8858 display.input.ensurePolled();
8859 display.shift = e.shiftKey;
8860
8861 if (eventInWidget(display, e)) {
8862 if (!webkit) {
8863 // Briefly turn off draggability, to allow widgets to do
8864 // normal dragging things.
8865 display.scroller.draggable = false;
8866 setTimeout(function () { return display.scroller.draggable = true; }, 100);
8867 }
8868 return
8869 }
8870 if (clickInGutter(cm, e)) { return }
8871 var pos = posFromMouse(cm, e), button = e_button(e), repeat = pos ? clickRepeat(pos, button) : "single";
8872 window.focus();
8873
8874 // #3261: make sure, that we're not starting a second selection
8875 if (button == 1 && cm.state.selectingText)
8876 { cm.state.selectingText(e); }
8877
8878 if (pos && handleMappedButton(cm, button, pos, repeat, e)) { return }
8879
8880 if (button == 1) {
8881 if (pos) { leftButtonDown(cm, pos, repeat, e); }
8882 else if (e_target(e) == display.scroller) { e_preventDefault(e); }
8883 } else if (button == 2) {
8884 if (pos) { extendSelection(cm.doc, pos); }
8885 setTimeout(function () { return display.input.focus(); }, 20);
8886 } else if (button == 3) {
8887 if (captureRightClick) { cm.display.input.onContextMenu(e); }
8888 else { delayBlurEvent(cm); }
8889 }
8890 }
8891
8892 function handleMappedButton(cm, button, pos, repeat, event) {
8893 var name = "Click";
8894 if (repeat == "double") { name = "Double" + name; }
8895 else if (repeat == "triple") { name = "Triple" + name; }
8896 name = (button == 1 ? "Left" : button == 2 ? "Middle" : "Right") + name;
8897
8898 return dispatchKey(cm, addModifierNames(name, event), event, function (bound) {
8899 if (typeof bound == "string") { bound = commands[bound]; }
8900 if (!bound) { return false }
8901 var done = false;
8902 try {
8903 if (cm.isReadOnly()) { cm.state.suppressEdits = true; }
8904 done = bound(cm, pos) != Pass;
8905 } finally {
8906 cm.state.suppressEdits = false;
8907 }
8908 return done
8909 })
8910 }
8911
8912 function configureMouse(cm, repeat, event) {
8913 var option = cm.getOption("configureMouse");
8914 var value = option ? option(cm, repeat, event) : {};
8915 if (value.unit == null) {
8916 var rect = chromeOS ? event.shiftKey && event.metaKey : event.altKey;
8917 value.unit = rect ? "rectangle" : repeat == "single" ? "char" : repeat == "double" ? "word" : "line";
8918 }
8919 if (value.extend == null || cm.doc.extend) { value.extend = cm.doc.extend || event.shiftKey; }
8920 if (value.addNew == null) { value.addNew = mac ? event.metaKey : event.ctrlKey; }
8921 if (value.moveOnDrag == null) { value.moveOnDrag = !(mac ? event.altKey : event.ctrlKey); }
8922 return value
8923 }
8924
8925 function leftButtonDown(cm, pos, repeat, event) {
8926 if (ie) { setTimeout(bind(ensureFocus, cm), 0); }
8927 else { cm.curOp.focus = activeElt(); }
8928
8929 var behavior = configureMouse(cm, repeat, event);
8930
8931 var sel = cm.doc.sel, contained;
8932 if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() &&
8933 repeat == "single" && (contained = sel.contains(pos)) > -1 &&
8934 (cmp((contained = sel.ranges[contained]).from(), pos) < 0 || pos.xRel > 0) &&
8935 (cmp(contained.to(), pos) > 0 || pos.xRel < 0))
8936 { leftButtonStartDrag(cm, event, pos, behavior); }
8937 else
8938 { leftButtonSelect(cm, event, pos, behavior); }
8939 }
8940
8941 // Start a text drag. When it ends, see if any dragging actually
8942 // happen, and treat as a click if it didn't.
8943 function leftButtonStartDrag(cm, event, pos, behavior) {
8944 var display = cm.display, moved = false;
8945 var dragEnd = operation(cm, function (e) {
8946 if (webkit) { display.scroller.draggable = false; }
8947 cm.state.draggingText = false;
8948 off(display.wrapper.ownerDocument, "mouseup", dragEnd);
8949 off(display.wrapper.ownerDocument, "mousemove", mouseMove);
8950 off(display.scroller, "dragstart", dragStart);
8951 off(display.scroller, "drop", dragEnd);
8952 if (!moved) {
8953 e_preventDefault(e);
8954 if (!behavior.addNew)
8955 { extendSelection(cm.doc, pos, null, null, behavior.extend); }
8956 // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)
8957 if (webkit || ie && ie_version == 9)
8958 { setTimeout(function () {display.wrapper.ownerDocument.body.focus(); display.input.focus();}, 20); }
8959 else
8960 { display.input.focus(); }
8961 }
8962 });
8963 var mouseMove = function(e2) {
8964 moved = moved || Math.abs(event.clientX - e2.clientX) + Math.abs(event.clientY - e2.clientY) >= 10;
8965 };
8966 var dragStart = function () { return moved = true; };
8967 // Let the drag handler handle this.
8968 if (webkit) { display.scroller.draggable = true; }
8969 cm.state.draggingText = dragEnd;
8970 dragEnd.copy = !behavior.moveOnDrag;
8971 // IE's approach to draggable
8972 if (display.scroller.dragDrop) { display.scroller.dragDrop(); }
8973 on(display.wrapper.ownerDocument, "mouseup", dragEnd);
8974 on(display.wrapper.ownerDocument, "mousemove", mouseMove);
8975 on(display.scroller, "dragstart", dragStart);
8976 on(display.scroller, "drop", dragEnd);
8977
8978 delayBlurEvent(cm);
8979 setTimeout(function () { return display.input.focus(); }, 20);
8980 }
8981
8982 function rangeForUnit(cm, pos, unit) {
8983 if (unit == "char") { return new Range(pos, pos) }
8984 if (unit == "word") { return cm.findWordAt(pos) }
8985 if (unit == "line") { return new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) }
8986 var result = unit(cm, pos);
8987 return new Range(result.from, result.to)
8988 }
8989
8990 // Normal selection, as opposed to text dragging.
8991 function leftButtonSelect(cm, event, start, behavior) {
8992 var display = cm.display, doc = cm.doc;
8993 e_preventDefault(event);
8994
8995 var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges;
8996 if (behavior.addNew && !behavior.extend) {
8997 ourIndex = doc.sel.contains(start);
8998 if (ourIndex > -1)
8999 { ourRange = ranges[ourIndex]; }
9000 else
9001 { ourRange = new Range(start, start); }
9002 } else {
9003 ourRange = doc.sel.primary();
9004 ourIndex = doc.sel.primIndex;
9005 }
9006
9007 if (behavior.unit == "rectangle") {
9008 if (!behavior.addNew) { ourRange = new Range(start, start); }
9009 start = posFromMouse(cm, event, true, true);
9010 ourIndex = -1;
9011 } else {
9012 var range$$1 = rangeForUnit(cm, start, behavior.unit);
9013 if (behavior.extend)
9014 { ourRange = extendRange(ourRange, range$$1.anchor, range$$1.head, behavior.extend); }
9015 else
9016 { ourRange = range$$1; }
9017 }
9018
9019 if (!behavior.addNew) {
9020 ourIndex = 0;
9021 setSelection(doc, new Selection([ourRange], 0), sel_mouse);
9022 startSel = doc.sel;
9023 } else if (ourIndex == -1) {
9024 ourIndex = ranges.length;
9025 setSelection(doc, normalizeSelection(cm, ranges.concat([ourRange]), ourIndex),
9026 {scroll: false, origin: "*mouse"});
9027 } else if (ranges.length > 1 && ranges[ourIndex].empty() && behavior.unit == "char" && !behavior.extend) {
9028 setSelection(doc, normalizeSelection(cm, ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0),
9029 {scroll: false, origin: "*mouse"});
9030 startSel = doc.sel;
9031 } else {
9032 replaceOneSelection(doc, ourIndex, ourRange, sel_mouse);
9033 }
9034
9035 var lastPos = start;
9036 function extendTo(pos) {
9037 if (cmp(lastPos, pos) == 0) { return }
9038 lastPos = pos;
9039
9040 if (behavior.unit == "rectangle") {
9041 var ranges = [], tabSize = cm.options.tabSize;
9042 var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize);
9043 var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize);
9044 var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol);
9045 for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line));
9046 line <= end; line++) {
9047 var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize);
9048 if (left == right)
9049 { ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))); }
9050 else if (text.length > leftPos)
9051 { ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))); }
9052 }
9053 if (!ranges.length) { ranges.push(new Range(start, start)); }
9054 setSelection(doc, normalizeSelection(cm, startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex),
9055 {origin: "*mouse", scroll: false});
9056 cm.scrollIntoView(pos);
9057 } else {
9058 var oldRange = ourRange;
9059 var range$$1 = rangeForUnit(cm, pos, behavior.unit);
9060 var anchor = oldRange.anchor, head;
9061 if (cmp(range$$1.anchor, anchor) > 0) {
9062 head = range$$1.head;
9063 anchor = minPos(oldRange.from(), range$$1.anchor);
9064 } else {
9065 head = range$$1.anchor;
9066 anchor = maxPos(oldRange.to(), range$$1.head);
9067 }
9068 var ranges$1 = startSel.ranges.slice(0);
9069 ranges$1[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc, anchor), head));
9070 setSelection(doc, normalizeSelection(cm, ranges$1, ourIndex), sel_mouse);
9071 }
9072 }
9073
9074 var editorSize = display.wrapper.getBoundingClientRect();
9075 // Used to ensure timeout re-tries don't fire when another extend
9076 // happened in the meantime (clearTimeout isn't reliable -- at
9077 // least on Chrome, the timeouts still happen even when cleared,
9078 // if the clear happens after their scheduled firing time).
9079 var counter = 0;
9080
9081 function extend(e) {
9082 var curCount = ++counter;
9083 var cur = posFromMouse(cm, e, true, behavior.unit == "rectangle");
9084 if (!cur) { return }
9085 if (cmp(cur, lastPos) != 0) {
9086 cm.curOp.focus = activeElt();
9087 extendTo(cur);
9088 var visible = visibleLines(display, doc);
9089 if (cur.line >= visible.to || cur.line < visible.from)
9090 { setTimeout(operation(cm, function () {if (counter == curCount) { extend(e); }}), 150); }
9091 } else {
9092 var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;
9093 if (outside) { setTimeout(operation(cm, function () {
9094 if (counter != curCount) { return }
9095 display.scroller.scrollTop += outside;
9096 extend(e);
9097 }), 50); }
9098 }
9099 }
9100
9101 function done(e) {
9102 cm.state.selectingText = false;
9103 counter = Infinity;
9104 e_preventDefault(e);
9105 display.input.focus();
9106 off(display.wrapper.ownerDocument, "mousemove", move);
9107 off(display.wrapper.ownerDocument, "mouseup", up);
9108 doc.history.lastSelOrigin = null;
9109 }
9110
9111 var move = operation(cm, function (e) {
9112 if (e.buttons === 0 || !e_button(e)) { done(e); }
9113 else { extend(e); }
9114 });
9115 var up = operation(cm, done);
9116 cm.state.selectingText = up;
9117 on(display.wrapper.ownerDocument, "mousemove", move);
9118 on(display.wrapper.ownerDocument, "mouseup", up);
9119 }
9120
9121 // Used when mouse-selecting to adjust the anchor to the proper side
9122 // of a bidi jump depending on the visual position of the head.
9123 function bidiSimplify(cm, range$$1) {
9124 var anchor = range$$1.anchor;
9125 var head = range$$1.head;
9126 var anchorLine = getLine(cm.doc, anchor.line);
9127 if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) { return range$$1 }
9128 var order = getOrder(anchorLine);
9129 if (!order) { return range$$1 }
9130 var index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index];
9131 if (part.from != anchor.ch && part.to != anchor.ch) { return range$$1 }
9132 var boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1);
9133 if (boundary == 0 || boundary == order.length) { return range$$1 }
9134
9135 // Compute the relative visual position of the head compared to the
9136 // anchor (<0 is to the left, >0 to the right)
9137 var leftSide;
9138 if (head.line != anchor.line) {
9139 leftSide = (head.line - anchor.line) * (cm.doc.direction == "ltr" ? 1 : -1) > 0;
9140 } else {
9141 var headIndex = getBidiPartAt(order, head.ch, head.sticky);
9142 var dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1);
9143 if (headIndex == boundary - 1 || headIndex == boundary)
9144 { leftSide = dir < 0; }
9145 else
9146 { leftSide = dir > 0; }
9147 }
9148
9149 var usePart = order[boundary + (leftSide ? -1 : 0)];
9150 var from = leftSide == (usePart.level == 1);
9151 var ch = from ? usePart.from : usePart.to, sticky = from ? "after" : "before";
9152 return anchor.ch == ch && anchor.sticky == sticky ? range$$1 : new Range(new Pos(anchor.line, ch, sticky), head)
9153 }
9154
9155
9156 // Determines whether an event happened in the gutter, and fires the
9157 // handlers for the corresponding event.
9158 function gutterEvent(cm, e, type, prevent) {
9159 var mX, mY;
9160 if (e.touches) {
9161 mX = e.touches[0].clientX;
9162 mY = e.touches[0].clientY;
9163 } else {
9164 try { mX = e.clientX; mY = e.clientY; }
9165 catch(e) { return false }
9166 }
9167 if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false }
9168 if (prevent) { e_preventDefault(e); }
9169
9170 var display = cm.display;
9171 var lineBox = display.lineDiv.getBoundingClientRect();
9172
9173 if (mY > lineBox.bottom || !hasHandler(cm, type)) { return e_defaultPrevented(e) }
9174 mY -= lineBox.top - display.viewOffset;
9175
9176 for (var i = 0; i < cm.options.gutters.length; ++i) {
9177 var g = display.gutters.childNodes[i];
9178 if (g && g.getBoundingClientRect().right >= mX) {
9179 var line = lineAtHeight(cm.doc, mY);
9180 var gutter = cm.options.gutters[i];
9181 signal(cm, type, cm, line, gutter, e);
9182 return e_defaultPrevented(e)
9183 }
9184 }
9185 }
9186
9187 function clickInGutter(cm, e) {
9188 return gutterEvent(cm, e, "gutterClick", true)
9189 }
9190
9191 // CONTEXT MENU HANDLING
9192
9193 // To make the context menu work, we need to briefly unhide the
9194 // textarea (making it as unobtrusive as possible) to let the
9195 // right-click take effect on it.
9196 function onContextMenu(cm, e) {
9197 if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) { return }
9198 if (signalDOMEvent(cm, e, "contextmenu")) { return }
9199 if (!captureRightClick) { cm.display.input.onContextMenu(e); }
9200 }
9201
9202 function contextMenuInGutter(cm, e) {
9203 if (!hasHandler(cm, "gutterContextMenu")) { return false }
9204 return gutterEvent(cm, e, "gutterContextMenu", false)
9205 }
9206
9207 function themeChanged(cm) {
9208 cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
9209 cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
9210 clearCaches(cm);
9211 }
9212
9213 var Init = {toString: function(){return "CodeMirror.Init"}};
9214
9215 var defaults = {};
9216 var optionHandlers = {};
9217
9218 function defineOptions(CodeMirror) {
9219 var optionHandlers = CodeMirror.optionHandlers;
9220
9221 function option(name, deflt, handle, notOnInit) {
9222 CodeMirror.defaults[name] = deflt;
9223 if (handle) { optionHandlers[name] =
9224 notOnInit ? function (cm, val, old) {if (old != Init) { handle(cm, val, old); }} : handle; }
9225 }
9226
9227 CodeMirror.defineOption = option;
9228
9229 // Passed to option handlers when there is no old value.
9230 CodeMirror.Init = Init;
9231
9232 // These two are, on init, called from the constructor because they
9233 // have to be initialized before the editor can start at all.
9234 option("value", "", function (cm, val) { return cm.setValue(val); }, true);
9235 option("mode", null, function (cm, val) {
9236 cm.doc.modeOption = val;
9237 loadMode(cm);
9238 }, true);
9239
9240 option("indentUnit", 2, loadMode, true);
9241 option("indentWithTabs", false);
9242 option("smartIndent", true);
9243 option("tabSize", 4, function (cm) {
9244 resetModeState(cm);
9245 clearCaches(cm);
9246 regChange(cm);
9247 }, true);
9248
9249 option("lineSeparator", null, function (cm, val) {
9250 cm.doc.lineSep = val;
9251 if (!val) { return }
9252 var newBreaks = [], lineNo = cm.doc.first;
9253 cm.doc.iter(function (line) {
9254 for (var pos = 0;;) {
9255 var found = line.text.indexOf(val, pos);
9256 if (found == -1) { break }
9257 pos = found + val.length;
9258 newBreaks.push(Pos(lineNo, found));
9259 }
9260 lineNo++;
9261 });
9262 for (var i = newBreaks.length - 1; i >= 0; i--)
9263 { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)); }
9264 });
9265 option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff]/g, function (cm, val, old) {
9266 cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g");
9267 if (old != Init) { cm.refresh(); }
9268 });
9269 option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function (cm) { return cm.refresh(); }, true);
9270 option("electricChars", true);
9271 option("inputStyle", mobile ? "contenteditable" : "textarea", function () {
9272 throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME
9273 }, true);
9274 option("spellcheck", false, function (cm, val) { return cm.getInputField().spellcheck = val; }, true);
9275 option("autocorrect", false, function (cm, val) { return cm.getInputField().autocorrect = val; }, true);
9276 option("autocapitalize", false, function (cm, val) { return cm.getInputField().autocapitalize = val; }, true);
9277 option("rtlMoveVisually", !windows);
9278 option("wholeLineUpdateBefore", true);
9279
9280 option("theme", "default", function (cm) {
9281 themeChanged(cm);
9282 guttersChanged(cm);
9283 }, true);
9284 option("keyMap", "default", function (cm, val, old) {
9285 var next = getKeyMap(val);
9286 var prev = old != Init && getKeyMap(old);
9287 if (prev && prev.detach) { prev.detach(cm, next); }
9288 if (next.attach) { next.attach(cm, prev || null); }
9289 });
9290 option("extraKeys", null);
9291 option("configureMouse", null);
9292
9293 option("lineWrapping", false, wrappingChanged, true);
9294 option("gutters", [], function (cm) {
9295 setGuttersForLineNumbers(cm.options);
9296 guttersChanged(cm);
9297 }, true);
9298 option("fixedGutter", true, function (cm, val) {
9299 cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
9300 cm.refresh();
9301 }, true);
9302 option("coverGutterNextToScrollbar", false, function (cm) { return updateScrollbars(cm); }, true);
9303 option("scrollbarStyle", "native", function (cm) {
9304 initScrollbars(cm);
9305 updateScrollbars(cm);
9306 cm.display.scrollbars.setScrollTop(cm.doc.scrollTop);
9307 cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft);
9308 }, true);
9309 option("lineNumbers", false, function (cm) {
9310 setGuttersForLineNumbers(cm.options);
9311 guttersChanged(cm);
9312 }, true);
9313 option("firstLineNumber", 1, guttersChanged, true);
9314 option("lineNumberFormatter", function (integer) { return integer; }, guttersChanged, true);
9315 option("showCursorWhenSelecting", false, updateSelection, true);
9316
9317 option("resetSelectionOnContextMenu", true);
9318 option("lineWiseCopyCut", true);
9319 option("pasteLinesPerSelection", true);
9320 option("selectionsMayTouch", false);
9321
9322 option("readOnly", false, function (cm, val) {
9323 if (val == "nocursor") {
9324 onBlur(cm);
9325 cm.display.input.blur();
9326 }
9327 cm.display.input.readOnlyChanged(val);
9328 });
9329 option("disableInput", false, function (cm, val) {if (!val) { cm.display.input.reset(); }}, true);
9330 option("dragDrop", true, dragDropChanged);
9331 option("allowDropFileTypes", null);
9332
9333 option("cursorBlinkRate", 530);
9334 option("cursorScrollMargin", 0);
9335 option("cursorHeight", 1, updateSelection, true);
9336 option("singleCursorHeightPerLine", true, updateSelection, true);
9337 option("workTime", 100);
9338 option("workDelay", 100);
9339 option("flattenSpans", true, resetModeState, true);
9340 option("addModeClass", false, resetModeState, true);
9341 option("pollInterval", 100);
9342 option("undoDepth", 200, function (cm, val) { return cm.doc.history.undoDepth = val; });
9343 option("historyEventDelay", 1250);
9344 option("viewportMargin", 10, function (cm) { return cm.refresh(); }, true);
9345 option("maxHighlightLength", 10000, resetModeState, true);
9346 option("moveInputWithCursor", true, function (cm, val) {
9347 if (!val) { cm.display.input.resetPosition(); }
9348 });
9349
9350 option("tabindex", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || ""; });
9351 option("autofocus", null);
9352 option("direction", "ltr", function (cm, val) { return cm.doc.setDirection(val); }, true);
9353 option("phrases", null);
9354 }
9355
9356 function guttersChanged(cm) {
9357 updateGutters(cm);
9358 regChange(cm);
9359 alignHorizontally(cm);
9360 }
9361
9362 function dragDropChanged(cm, value, old) {
9363 var wasOn = old && old != Init;
9364 if (!value != !wasOn) {
9365 var funcs = cm.display.dragFunctions;
9366 var toggle = value ? on : off;
9367 toggle(cm.display.scroller, "dragstart", funcs.start);
9368 toggle(cm.display.scroller, "dragenter", funcs.enter);
9369 toggle(cm.display.scroller, "dragover", funcs.over);
9370 toggle(cm.display.scroller, "dragleave", funcs.leave);
9371 toggle(cm.display.scroller, "drop", funcs.drop);
9372 }
9373 }
9374
9375 function wrappingChanged(cm) {
9376 if (cm.options.lineWrapping) {
9377 addClass(cm.display.wrapper, "CodeMirror-wrap");
9378 cm.display.sizer.style.minWidth = "";
9379 cm.display.sizerWidth = null;
9380 } else {
9381 rmClass(cm.display.wrapper, "CodeMirror-wrap");
9382 findMaxLine(cm);
9383 }
9384 estimateLineHeights(cm);
9385 regChange(cm);
9386 clearCaches(cm);
9387 setTimeout(function () { return updateScrollbars(cm); }, 100);
9388 }
9389
9390 // A CodeMirror instance represents an editor. This is the object
9391 // that user code is usually dealing with.
9392
9393 function CodeMirror(place, options) {
9394 var this$1 = this;
9395
9396 if (!(this instanceof CodeMirror)) { return new CodeMirror(place, options) }
9397
9398 this.options = options = options ? copyObj(options) : {};
9399 // Determine effective options based on given values and defaults.
9400 copyObj(defaults, options, false);
9401 setGuttersForLineNumbers(options);
9402
9403 var doc = options.value;
9404 if (typeof doc == "string") { doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction); }
9405 else if (options.mode) { doc.modeOption = options.mode; }
9406 this.doc = doc;
9407
9408 var input = new CodeMirror.inputStyles[options.inputStyle](this);
9409 var display = this.display = new Display(place, doc, input);
9410 display.wrapper.CodeMirror = this;
9411 updateGutters(this);
9412 themeChanged(this);
9413 if (options.lineWrapping)
9414 { this.display.wrapper.className += " CodeMirror-wrap"; }
9415 initScrollbars(this);
9416
9417 this.state = {
9418 keyMaps: [], // stores maps added by addKeyMap
9419 overlays: [], // highlighting overlays, as added by addOverlay
9420 modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info
9421 overwrite: false,
9422 delayingBlurEvent: false,
9423 focused: false,
9424 suppressEdits: false, // used to disable editing during key handlers when in readOnly mode
9425 pasteIncoming: -1, cutIncoming: -1, // help recognize paste/cut edits in input.poll
9426 selectingText: false,
9427 draggingText: false,
9428 highlight: new Delayed(), // stores highlight worker timeout
9429 keySeq: null, // Unfinished key sequence
9430 specialChars: null
9431 };
9432
9433 if (options.autofocus && !mobile) { display.input.focus(); }
9434
9435 // Override magic textarea content restore that IE sometimes does
9436 // on our hidden textarea on reload
9437 if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20); }
9438
9439 registerEventHandlers(this);
9440 ensureGlobalHandlers();
9441
9442 startOperation(this);
9443 this.curOp.forceUpdate = true;
9444 attachDoc(this, doc);
9445
9446 if ((options.autofocus && !mobile) || this.hasFocus())
9447 { setTimeout(bind(onFocus, this), 20); }
9448 else
9449 { onBlur(this); }
9450
9451 for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt))
9452 { optionHandlers[opt](this$1, options[opt], Init); } }
9453 maybeUpdateLineNumberWidth(this);
9454 if (options.finishInit) { options.finishInit(this); }
9455 for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this$1); }
9456 endOperation(this);
9457 // Suppress optimizelegibility in Webkit, since it breaks text
9458 // measuring on line wrapping boundaries.
9459 if (webkit && options.lineWrapping &&
9460 getComputedStyle(display.lineDiv).textRendering == "optimizelegibility")
9461 { display.lineDiv.style.textRendering = "auto"; }
9462 }
9463
9464 // The default configuration options.
9465 CodeMirror.defaults = defaults;
9466 // Functions to run when options are changed.
9467 CodeMirror.optionHandlers = optionHandlers;
9468
9469 // Attach the necessary event handlers when initializing the editor
9470 function registerEventHandlers(cm) {
9471 var d = cm.display;
9472 on(d.scroller, "mousedown", operation(cm, onMouseDown));
9473 // Older IE's will not fire a second mousedown for a double click
9474 if (ie && ie_version < 11)
9475 { on(d.scroller, "dblclick", operation(cm, function (e) {
9476 if (signalDOMEvent(cm, e)) { return }
9477 var pos = posFromMouse(cm, e);
9478 if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) { return }
9479 e_preventDefault(e);
9480 var word = cm.findWordAt(pos);
9481 extendSelection(cm.doc, word.anchor, word.head);
9482 })); }
9483 else
9484 { on(d.scroller, "dblclick", function (e) { return signalDOMEvent(cm, e) || e_preventDefault(e); }); }
9485 // Some browsers fire contextmenu *after* opening the menu, at
9486 // which point we can't mess with it anymore. Context menu is
9487 // handled in onMouseDown for these browsers.
9488 on(d.scroller, "contextmenu", function (e) { return onContextMenu(cm, e); });
9489
9490 // Used to suppress mouse event handling when a touch happens
9491 var touchFinished, prevTouch = {end: 0};
9492 function finishTouch() {
9493 if (d.activeTouch) {
9494 touchFinished = setTimeout(function () { return d.activeTouch = null; }, 1000);
9495 prevTouch = d.activeTouch;
9496 prevTouch.end = +new Date;
9497 }
9498 }
9499 function isMouseLikeTouchEvent(e) {
9500 if (e.touches.length != 1) { return false }
9501 var touch = e.touches[0];
9502 return touch.radiusX <= 1 && touch.radiusY <= 1
9503 }
9504 function farAway(touch, other) {
9505 if (other.left == null) { return true }
9506 var dx = other.left - touch.left, dy = other.top - touch.top;
9507 return dx * dx + dy * dy > 20 * 20
9508 }
9509 on(d.scroller, "touchstart", function (e) {
9510 if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e) && !clickInGutter(cm, e)) {
9511 d.input.ensurePolled();
9512 clearTimeout(touchFinished);
9513 var now = +new Date;
9514 d.activeTouch = {start: now, moved: false,
9515 prev: now - prevTouch.end <= 300 ? prevTouch : null};
9516 if (e.touches.length == 1) {
9517 d.activeTouch.left = e.touches[0].pageX;
9518 d.activeTouch.top = e.touches[0].pageY;
9519 }
9520 }
9521 });
9522 on(d.scroller, "touchmove", function () {
9523 if (d.activeTouch) { d.activeTouch.moved = true; }
9524 });
9525 on(d.scroller, "touchend", function (e) {
9526 var touch = d.activeTouch;
9527 if (touch && !eventInWidget(d, e) && touch.left != null &&
9528 !touch.moved && new Date - touch.start < 300) {
9529 var pos = cm.coordsChar(d.activeTouch, "page"), range;
9530 if (!touch.prev || farAway(touch, touch.prev)) // Single tap
9531 { range = new Range(pos, pos); }
9532 else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap
9533 { range = cm.findWordAt(pos); }
9534 else // Triple tap
9535 { range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))); }
9536 cm.setSelection(range.anchor, range.head);
9537 cm.focus();
9538 e_preventDefault(e);
9539 }
9540 finishTouch();
9541 });
9542 on(d.scroller, "touchcancel", finishTouch);
9543
9544 // Sync scrolling between fake scrollbars and real scrollable
9545 // area, ensure viewport is updated when scrolling.
9546 on(d.scroller, "scroll", function () {
9547 if (d.scroller.clientHeight) {
9548 updateScrollTop(cm, d.scroller.scrollTop);
9549 setScrollLeft(cm, d.scroller.scrollLeft, true);
9550 signal(cm, "scroll", cm);
9551 }
9552 });
9553
9554 // Listen to wheel events in order to try and update the viewport on time.
9555 on(d.scroller, "mousewheel", function (e) { return onScrollWheel(cm, e); });
9556 on(d.scroller, "DOMMouseScroll", function (e) { return onScrollWheel(cm, e); });
9557
9558 // Prevent wrapper from ever scrolling
9559 on(d.wrapper, "scroll", function () { return d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
9560
9561 d.dragFunctions = {
9562 enter: function (e) {if (!signalDOMEvent(cm, e)) { e_stop(e); }},
9563 over: function (e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e); }},
9564 start: function (e) { return onDragStart(cm, e); },
9565 drop: operation(cm, onDrop),
9566 leave: function (e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm); }}
9567 };
9568
9569 var inp = d.input.getField();
9570 on(inp, "keyup", function (e) { return onKeyUp.call(cm, e); });
9571 on(inp, "keydown", operation(cm, onKeyDown));
9572 on(inp, "keypress", operation(cm, onKeyPress));
9573 on(inp, "focus", function (e) { return onFocus(cm, e); });
9574 on(inp, "blur", function (e) { return onBlur(cm, e); });
9575 }
9576
9577 var initHooks = [];
9578 CodeMirror.defineInitHook = function (f) { return initHooks.push(f); };
9579
9580 // Indent the given line. The how parameter can be "smart",
9581 // "add"/null, "subtract", or "prev". When aggressive is false
9582 // (typically set to true for forced single-line indents), empty
9583 // lines are not indented, and places where the mode returns Pass
9584 // are left alone.
9585 function indentLine(cm, n, how, aggressive) {
9586 var doc = cm.doc, state;
9587 if (how == null) { how = "add"; }
9588 if (how == "smart") {
9589 // Fall back to "prev" when the mode doesn't have an indentation
9590 // method.
9591 if (!doc.mode.indent) { how = "prev"; }
9592 else { state = getContextBefore(cm, n).state; }
9593 }
9594
9595 var tabSize = cm.options.tabSize;
9596 var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
9597 if (line.stateAfter) { line.stateAfter = null; }
9598 var curSpaceString = line.text.match(/^\s*/)[0], indentation;
9599 if (!aggressive && !/\S/.test(line.text)) {
9600 indentation = 0;
9601 how = "not";
9602 } else if (how == "smart") {
9603 indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
9604 if (indentation == Pass || indentation > 150) {
9605 if (!aggressive) { return }
9606 how = "prev";
9607 }
9608 }
9609 if (how == "prev") {
9610 if (n > doc.first) { indentation = countColumn(getLine(doc, n-1).text, null, tabSize); }
9611 else { indentation = 0; }
9612 } else if (how == "add") {
9613 indentation = curSpace + cm.options.indentUnit;
9614 } else if (how == "subtract") {
9615 indentation = curSpace - cm.options.indentUnit;
9616 } else if (typeof how == "number") {
9617 indentation = curSpace + how;
9618 }
9619 indentation = Math.max(0, indentation);
9620
9621 var indentString = "", pos = 0;
9622 if (cm.options.indentWithTabs)
9623 { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";} }
9624 if (pos < indentation) { indentString += spaceStr(indentation - pos); }
9625
9626 if (indentString != curSpaceString) {
9627 replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
9628 line.stateAfter = null;
9629 return true
9630 } else {
9631 // Ensure that, if the cursor was in the whitespace at the start
9632 // of the line, it is moved to the end of that space.
9633 for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) {
9634 var range = doc.sel.ranges[i$1];
9635 if (range.head.line == n && range.head.ch < curSpaceString.length) {
9636 var pos$1 = Pos(n, curSpaceString.length);
9637 replaceOneSelection(doc, i$1, new Range(pos$1, pos$1));
9638 break
9639 }
9640 }
9641 }
9642 }
9643
9644 // This will be set to a {lineWise: bool, text: [string]} object, so
9645 // that, when pasting, we know what kind of selections the copied
9646 // text was made out of.
9647 var lastCopied = null;
9648
9649 function setLastCopied(newLastCopied) {
9650 lastCopied = newLastCopied;
9651 }
9652
9653 function applyTextInput(cm, inserted, deleted, sel, origin) {
9654 var doc = cm.doc;
9655 cm.display.shift = false;
9656 if (!sel) { sel = doc.sel; }
9657
9658 var recent = +new Date - 200;
9659 var paste = origin == "paste" || cm.state.pasteIncoming > recent;
9660 var textLines = splitLinesAuto(inserted), multiPaste = null;
9661 // When pasting N lines into N selections, insert one line per selection
9662 if (paste && sel.ranges.length > 1) {
9663 if (lastCopied && lastCopied.text.join("\n") == inserted) {
9664 if (sel.ranges.length % lastCopied.text.length == 0) {
9665 multiPaste = [];
9666 for (var i = 0; i < lastCopied.text.length; i++)
9667 { multiPaste.push(doc.splitLines(lastCopied.text[i])); }
9668 }
9669 } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) {
9670 multiPaste = map(textLines, function (l) { return [l]; });
9671 }
9672 }
9673
9674 var updateInput = cm.curOp.updateInput;
9675 // Normal behavior is to insert the new text into every selection
9676 for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) {
9677 var range$$1 = sel.ranges[i$1];
9678 var from = range$$1.from(), to = range$$1.to();
9679 if (range$$1.empty()) {
9680 if (deleted && deleted > 0) // Handle deletion
9681 { from = Pos(from.line, from.ch - deleted); }
9682 else if (cm.state.overwrite && !paste) // Handle overwrite
9683 { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); }
9684 else if (paste && lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == inserted)
9685 { from = to = Pos(from.line, 0); }
9686 }
9687 var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines,
9688 origin: origin || (paste ? "paste" : cm.state.cutIncoming > recent ? "cut" : "+input")};
9689 makeChange(cm.doc, changeEvent);
9690 signalLater(cm, "inputRead", cm, changeEvent);
9691 }
9692 if (inserted && !paste)
9693 { triggerElectric(cm, inserted); }
9694
9695 ensureCursorVisible(cm);
9696 if (cm.curOp.updateInput < 2) { cm.curOp.updateInput = updateInput; }
9697 cm.curOp.typing = true;
9698 cm.state.pasteIncoming = cm.state.cutIncoming = -1;
9699 }
9700
9701 function handlePaste(e, cm) {
9702 var pasted = e.clipboardData && e.clipboardData.getData("Text");
9703 if (pasted) {
9704 e.preventDefault();
9705 if (!cm.isReadOnly() && !cm.options.disableInput)
9706 { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, "paste"); }); }
9707 return true
9708 }
9709 }
9710
9711 function triggerElectric(cm, inserted) {
9712 // When an 'electric' character is inserted, immediately trigger a reindent
9713 if (!cm.options.electricChars || !cm.options.smartIndent) { return }
9714 var sel = cm.doc.sel;
9715
9716 for (var i = sel.ranges.length - 1; i >= 0; i--) {
9717 var range$$1 = sel.ranges[i];
9718 if (range$$1.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range$$1.head.line)) { continue }
9719 var mode = cm.getModeAt(range$$1.head);
9720 var indented = false;
9721 if (mode.electricChars) {
9722 for (var j = 0; j < mode.electricChars.length; j++)
9723 { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
9724 indented = indentLine(cm, range$$1.head.line, "smart");
9725 break
9726 } }
9727 } else if (mode.electricInput) {
9728 if (mode.electricInput.test(getLine(cm.doc, range$$1.head.line).text.slice(0, range$$1.head.ch)))
9729 { indented = indentLine(cm, range$$1.head.line, "smart"); }
9730 }
9731 if (indented) { signalLater(cm, "electricInput", cm, range$$1.head.line); }
9732 }
9733 }
9734
9735 function copyableRanges(cm) {
9736 var text = [], ranges = [];
9737 for (var i = 0; i < cm.doc.sel.ranges.length; i++) {
9738 var line = cm.doc.sel.ranges[i].head.line;
9739 var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)};
9740 ranges.push(lineRange);
9741 text.push(cm.getRange(lineRange.anchor, lineRange.head));
9742 }
9743 return {text: text, ranges: ranges}
9744 }
9745
9746 function disableBrowserMagic(field, spellcheck, autocorrect, autocapitalize) {
9747 field.setAttribute("autocorrect", !!autocorrect);
9748 field.setAttribute("autocapitalize", !!autocapitalize);
9749 field.setAttribute("spellcheck", !!spellcheck);
9750 }
9751
9752 function hiddenTextarea() {
9753 var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none");
9754 var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
9755 // The textarea is kept positioned near the cursor to prevent the
9756 // fact that it'll be scrolled into view on input from scrolling
9757 // our fake cursor out of view. On webkit, when wrap=off, paste is
9758 // very slow. So make the area wide instead.
9759 if (webkit) { te.style.width = "1000px"; }
9760 else { te.setAttribute("wrap", "off"); }
9761 // If border: 0; -- iOS fails to open keyboard (issue #1287)
9762 if (ios) { te.style.border = "1px solid black"; }
9763 disableBrowserMagic(te);
9764 return div
9765 }
9766
9767 // The publicly visible API. Note that methodOp(f) means
9768 // 'wrap f in an operation, performed on its `this` parameter'.
9769
9770 // This is not the complete set of editor methods. Most of the
9771 // methods defined on the Doc type are also injected into
9772 // CodeMirror.prototype, for backwards compatibility and
9773 // convenience.
9774
9775 function addEditorMethods(CodeMirror) {
9776 var optionHandlers = CodeMirror.optionHandlers;
9777
9778 var helpers = CodeMirror.helpers = {};
9779
9780 CodeMirror.prototype = {
9781 constructor: CodeMirror,
9782 focus: function(){window.focus(); this.display.input.focus();},
9783
9784 setOption: function(option, value) {
9785 var options = this.options, old = options[option];
9786 if (options[option] == value && option != "mode") { return }
9787 options[option] = value;
9788 if (optionHandlers.hasOwnProperty(option))
9789 { operation(this, optionHandlers[option])(this, value, old); }
9790 signal(this, "optionChange", this, option);
9791 },
9792
9793 getOption: function(option) {return this.options[option]},
9794 getDoc: function() {return this.doc},
9795
9796 addKeyMap: function(map$$1, bottom) {
9797 this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map$$1));
9798 },
9799 removeKeyMap: function(map$$1) {
9800 var maps = this.state.keyMaps;
9801 for (var i = 0; i < maps.length; ++i)
9802 { if (maps[i] == map$$1 || maps[i].name == map$$1) {
9803 maps.splice(i, 1);
9804 return true
9805 } }
9806 },
9807
9808 addOverlay: methodOp(function(spec, options) {
9809 var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
9810 if (mode.startState) { throw new Error("Overlays may not be stateful.") }
9811 insertSorted(this.state.overlays,
9812 {mode: mode, modeSpec: spec, opaque: options && options.opaque,
9813 priority: (options && options.priority) || 0},
9814 function (overlay) { return overlay.priority; });
9815 this.state.modeGen++;
9816 regChange(this);
9817 }),
9818 removeOverlay: methodOp(function(spec) {
9819 var this$1 = this;
9820
9821 var overlays = this.state.overlays;
9822 for (var i = 0; i < overlays.length; ++i) {
9823 var cur = overlays[i].modeSpec;
9824 if (cur == spec || typeof spec == "string" && cur.name == spec) {
9825 overlays.splice(i, 1);
9826 this$1.state.modeGen++;
9827 regChange(this$1);
9828 return
9829 }
9830 }
9831 }),
9832
9833 indentLine: methodOp(function(n, dir, aggressive) {
9834 if (typeof dir != "string" && typeof dir != "number") {
9835 if (dir == null) { dir = this.options.smartIndent ? "smart" : "prev"; }
9836 else { dir = dir ? "add" : "subtract"; }
9837 }
9838 if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive); }
9839 }),
9840 indentSelection: methodOp(function(how) {
9841 var this$1 = this;
9842
9843 var ranges = this.doc.sel.ranges, end = -1;
9844 for (var i = 0; i < ranges.length; i++) {
9845 var range$$1 = ranges[i];
9846 if (!range$$1.empty()) {
9847 var from = range$$1.from(), to = range$$1.to();
9848 var start = Math.max(end, from.line);
9849 end = Math.min(this$1.lastLine(), to.line - (to.ch ? 0 : 1)) + 1;
9850 for (var j = start; j < end; ++j)
9851 { indentLine(this$1, j, how); }
9852 var newRanges = this$1.doc.sel.ranges;
9853 if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0)
9854 { replaceOneSelection(this$1.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll); }
9855 } else if (range$$1.head.line > end) {
9856 indentLine(this$1, range$$1.head.line, how, true);
9857 end = range$$1.head.line;
9858 if (i == this$1.doc.sel.primIndex) { ensureCursorVisible(this$1); }
9859 }
9860 }
9861 }),
9862
9863 // Fetch the parser token for a given character. Useful for hacks
9864 // that want to inspect the mode state (say, for completion).
9865 getTokenAt: function(pos, precise) {
9866 return takeToken(this, pos, precise)
9867 },
9868
9869 getLineTokens: function(line, precise) {
9870 return takeToken(this, Pos(line), precise, true)
9871 },
9872
9873 getTokenTypeAt: function(pos) {
9874 pos = clipPos(this.doc, pos);
9875 var styles = getLineStyles(this, getLine(this.doc, pos.line));
9876 var before = 0, after = (styles.length - 1) / 2, ch = pos.ch;
9877 var type;
9878 if (ch == 0) { type = styles[2]; }
9879 else { for (;;) {
9880 var mid = (before + after) >> 1;
9881 if ((mid ? styles[mid * 2 - 1] : 0) >= ch) { after = mid; }
9882 else if (styles[mid * 2 + 1] < ch) { before = mid + 1; }
9883 else { type = styles[mid * 2 + 2]; break }
9884 } }
9885 var cut = type ? type.indexOf("overlay ") : -1;
9886 return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1)
9887 },
9888
9889 getModeAt: function(pos) {
9890 var mode = this.doc.mode;
9891 if (!mode.innerMode) { return mode }
9892 return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode
9893 },
9894
9895 getHelper: function(pos, type) {
9896 return this.getHelpers(pos, type)[0]
9897 },
9898
9899 getHelpers: function(pos, type) {
9900 var this$1 = this;
9901
9902 var found = [];
9903 if (!helpers.hasOwnProperty(type)) { return found }
9904 var help = helpers[type], mode = this.getModeAt(pos);
9905 if (typeof mode[type] == "string") {
9906 if (help[mode[type]]) { found.push(help[mode[type]]); }
9907 } else if (mode[type]) {
9908 for (var i = 0; i < mode[type].length; i++) {
9909 var val = help[mode[type][i]];
9910 if (val) { found.push(val); }
9911 }
9912 } else if (mode.helperType && help[mode.helperType]) {
9913 found.push(help[mode.helperType]);
9914 } else if (help[mode.name]) {
9915 found.push(help[mode.name]);
9916 }
9917 for (var i$1 = 0; i$1 < help._global.length; i$1++) {
9918 var cur = help._global[i$1];
9919 if (cur.pred(mode, this$1) && indexOf(found, cur.val) == -1)
9920 { found.push(cur.val); }
9921 }
9922 return found
9923 },
9924
9925 getStateAfter: function(line, precise) {
9926 var doc = this.doc;
9927 line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);
9928 return getContextBefore(this, line + 1, precise).state
9929 },
9930
9931 cursorCoords: function(start, mode) {
9932 var pos, range$$1 = this.doc.sel.primary();
9933 if (start == null) { pos = range$$1.head; }
9934 else if (typeof start == "object") { pos = clipPos(this.doc, start); }
9935 else { pos = start ? range$$1.from() : range$$1.to(); }
9936 return cursorCoords(this, pos, mode || "page")
9937 },
9938
9939 charCoords: function(pos, mode) {
9940 return charCoords(this, clipPos(this.doc, pos), mode || "page")
9941 },
9942
9943 coordsChar: function(coords, mode) {
9944 coords = fromCoordSystem(this, coords, mode || "page");
9945 return coordsChar(this, coords.left, coords.top)
9946 },
9947
9948 lineAtHeight: function(height, mode) {
9949 height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top;
9950 return lineAtHeight(this.doc, height + this.display.viewOffset)
9951 },
9952 heightAtLine: function(line, mode, includeWidgets) {
9953 var end = false, lineObj;
9954 if (typeof line == "number") {
9955 var last = this.doc.first + this.doc.size - 1;
9956 if (line < this.doc.first) { line = this.doc.first; }
9957 else if (line > last) { line = last; end = true; }
9958 lineObj = getLine(this.doc, line);
9959 } else {
9960 lineObj = line;
9961 }
9962 return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page", includeWidgets || end).top +
9963 (end ? this.doc.height - heightAtLine(lineObj) : 0)
9964 },
9965
9966 defaultTextHeight: function() { return textHeight(this.display) },
9967 defaultCharWidth: function() { return charWidth(this.display) },
9968
9969 getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}},
9970
9971 addWidget: function(pos, node, scroll, vert, horiz) {
9972 var display = this.display;
9973 pos = cursorCoords(this, clipPos(this.doc, pos));
9974 var top = pos.bottom, left = pos.left;
9975 node.style.position = "absolute";
9976 node.setAttribute("cm-ignore-events", "true");
9977 this.display.input.setUneditable(node);
9978 display.sizer.appendChild(node);
9979 if (vert == "over") {
9980 top = pos.top;
9981 } else if (vert == "above" || vert == "near") {
9982 var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
9983 hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
9984 // Default to positioning above (if specified and possible); otherwise default to positioning below
9985 if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
9986 { top = pos.top - node.offsetHeight; }
9987 else if (pos.bottom + node.offsetHeight <= vspace)
9988 { top = pos.bottom; }
9989 if (left + node.offsetWidth > hspace)
9990 { left = hspace - node.offsetWidth; }
9991 }
9992 node.style.top = top + "px";
9993 node.style.left = node.style.right = "";
9994 if (horiz == "right") {
9995 left = display.sizer.clientWidth - node.offsetWidth;
9996 node.style.right = "0px";
9997 } else {
9998 if (horiz == "left") { left = 0; }
9999 else if (horiz == "middle") { left = (display.sizer.clientWidth - node.offsetWidth) / 2; }
10000 node.style.left = left + "px";
10001 }
10002 if (scroll)
10003 { scrollIntoView(this, {left: left, top: top, right: left + node.offsetWidth, bottom: top + node.offsetHeight}); }
10004 },
10005
10006 triggerOnKeyDown: methodOp(onKeyDown),
10007 triggerOnKeyPress: methodOp(onKeyPress),
10008 triggerOnKeyUp: onKeyUp,
10009 triggerOnMouseDown: methodOp(onMouseDown),
10010
10011 execCommand: function(cmd) {
10012 if (commands.hasOwnProperty(cmd))
10013 { return commands[cmd].call(null, this) }
10014 },
10015
10016 triggerElectric: methodOp(function(text) { triggerElectric(this, text); }),
10017
10018 findPosH: function(from, amount, unit, visually) {
10019 var this$1 = this;
10020
10021 var dir = 1;
10022 if (amount < 0) { dir = -1; amount = -amount; }
10023 var cur = clipPos(this.doc, from);
10024 for (var i = 0; i < amount; ++i) {
10025 cur = findPosH(this$1.doc, cur, dir, unit, visually);
10026 if (cur.hitSide) { break }
10027 }
10028 return cur
10029 },
10030
10031 moveH: methodOp(function(dir, unit) {
10032 var this$1 = this;
10033
10034 this.extendSelectionsBy(function (range$$1) {
10035 if (this$1.display.shift || this$1.doc.extend || range$$1.empty())
10036 { return findPosH(this$1.doc, range$$1.head, dir, unit, this$1.options.rtlMoveVisually) }
10037 else
10038 { return dir < 0 ? range$$1.from() : range$$1.to() }
10039 }, sel_move);
10040 }),
10041
10042 deleteH: methodOp(function(dir, unit) {
10043 var sel = this.doc.sel, doc = this.doc;
10044 if (sel.somethingSelected())
10045 { doc.replaceSelection("", null, "+delete"); }
10046 else
10047 { deleteNearSelection(this, function (range$$1) {
10048 var other = findPosH(doc, range$$1.head, dir, unit, false);
10049 return dir < 0 ? {from: other, to: range$$1.head} : {from: range$$1.head, to: other}
10050 }); }
10051 }),
10052
10053 findPosV: function(from, amount, unit, goalColumn) {
10054 var this$1 = this;
10055
10056 var dir = 1, x = goalColumn;
10057 if (amount < 0) { dir = -1; amount = -amount; }
10058 var cur = clipPos(this.doc, from);
10059 for (var i = 0; i < amount; ++i) {
10060 var coords = cursorCoords(this$1, cur, "div");
10061 if (x == null) { x = coords.left; }
10062 else { coords.left = x; }
10063 cur = findPosV(this$1, coords, dir, unit);
10064 if (cur.hitSide) { break }
10065 }
10066 return cur
10067 },
10068
10069 moveV: methodOp(function(dir, unit) {
10070 var this$1 = this;
10071
10072 var doc = this.doc, goals = [];
10073 var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected();
10074 doc.extendSelectionsBy(function (range$$1) {
10075 if (collapse)
10076 { return dir < 0 ? range$$1.from() : range$$1.to() }
10077 var headPos = cursorCoords(this$1, range$$1.head, "div");
10078 if (range$$1.goalColumn != null) { headPos.left = range$$1.goalColumn; }
10079 goals.push(headPos.left);
10080 var pos = findPosV(this$1, headPos, dir, unit);
10081 if (unit == "page" && range$$1 == doc.sel.primary())
10082 { addToScrollTop(this$1, charCoords(this$1, pos, "div").top - headPos.top); }
10083 return pos
10084 }, sel_move);
10085 if (goals.length) { for (var i = 0; i < doc.sel.ranges.length; i++)
10086 { doc.sel.ranges[i].goalColumn = goals[i]; } }
10087 }),
10088
10089 // Find the word at the given position (as returned by coordsChar).
10090 findWordAt: function(pos) {
10091 var doc = this.doc, line = getLine(doc, pos.line).text;
10092 var start = pos.ch, end = pos.ch;
10093 if (line) {
10094 var helper = this.getHelper(pos, "wordChars");
10095 if ((pos.sticky == "before" || end == line.length) && start) { --start; } else { ++end; }
10096 var startChar = line.charAt(start);
10097 var check = isWordChar(startChar, helper)
10098 ? function (ch) { return isWordChar(ch, helper); }
10099 : /\s/.test(startChar) ? function (ch) { return /\s/.test(ch); }
10100 : function (ch) { return (!/\s/.test(ch) && !isWordChar(ch)); };
10101 while (start > 0 && check(line.charAt(start - 1))) { --start; }
10102 while (end < line.length && check(line.charAt(end))) { ++end; }
10103 }
10104 return new Range(Pos(pos.line, start), Pos(pos.line, end))
10105 },
10106
10107 toggleOverwrite: function(value) {
10108 if (value != null && value == this.state.overwrite) { return }
10109 if (this.state.overwrite = !this.state.overwrite)
10110 { addClass(this.display.cursorDiv, "CodeMirror-overwrite"); }
10111 else
10112 { rmClass(this.display.cursorDiv, "CodeMirror-overwrite"); }
10113
10114 signal(this, "overwriteToggle", this, this.state.overwrite);
10115 },
10116 hasFocus: function() { return this.display.input.getField() == activeElt() },
10117 isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) },
10118
10119 scrollTo: methodOp(function (x, y) { scrollToCoords(this, x, y); }),
10120 getScrollInfo: function() {
10121 var scroller = this.display.scroller;
10122 return {left: scroller.scrollLeft, top: scroller.scrollTop,
10123 height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight,
10124 width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth,
10125 clientHeight: displayHeight(this), clientWidth: displayWidth(this)}
10126 },
10127
10128 scrollIntoView: methodOp(function(range$$1, margin) {
10129 if (range$$1 == null) {
10130 range$$1 = {from: this.doc.sel.primary().head, to: null};
10131 if (margin == null) { margin = this.options.cursorScrollMargin; }
10132 } else if (typeof range$$1 == "number") {
10133 range$$1 = {from: Pos(range$$1, 0), to: null};
10134 } else if (range$$1.from == null) {
10135 range$$1 = {from: range$$1, to: null};
10136 }
10137 if (!range$$1.to) { range$$1.to = range$$1.from; }
10138 range$$1.margin = margin || 0;
10139
10140 if (range$$1.from.line != null) {
10141 scrollToRange(this, range$$1);
10142 } else {
10143 scrollToCoordsRange(this, range$$1.from, range$$1.to, range$$1.margin);
10144 }
10145 }),
10146
10147 setSize: methodOp(function(width, height) {
10148 var this$1 = this;
10149
10150 var interpret = function (val) { return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; };
10151 if (width != null) { this.display.wrapper.style.width = interpret(width); }
10152 if (height != null) { this.display.wrapper.style.height = interpret(height); }
10153 if (this.options.lineWrapping) { clearLineMeasurementCache(this); }
10154 var lineNo$$1 = this.display.viewFrom;
10155 this.doc.iter(lineNo$$1, this.display.viewTo, function (line) {
10156 if (line.widgets) { for (var i = 0; i < line.widgets.length; i++)
10157 { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo$$1, "widget"); break } } }
10158 ++lineNo$$1;
10159 });
10160 this.curOp.forceUpdate = true;
10161 signal(this, "refresh", this);
10162 }),
10163
10164 operation: function(f){return runInOp(this, f)},
10165 startOperation: function(){return startOperation(this)},
10166 endOperation: function(){return endOperation(this)},
10167
10168 refresh: methodOp(function() {
10169 var oldHeight = this.display.cachedTextHeight;
10170 regChange(this);
10171 this.curOp.forceUpdate = true;
10172 clearCaches(this);
10173 scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop);
10174 updateGutterSpace(this);
10175 if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5)
10176 { estimateLineHeights(this); }
10177 signal(this, "refresh", this);
10178 }),
10179
10180 swapDoc: methodOp(function(doc) {
10181 var old = this.doc;
10182 old.cm = null;
10183 attachDoc(this, doc);
10184 clearCaches(this);
10185 this.display.input.reset();
10186 scrollToCoords(this, doc.scrollLeft, doc.scrollTop);
10187 this.curOp.forceScroll = true;
10188 signalLater(this, "swapDoc", this, old);
10189 return old
10190 }),
10191
10192 phrase: function(phraseText) {
10193 var phrases = this.options.phrases;
10194 return phrases && Object.prototype.hasOwnProperty.call(phrases, phraseText) ? phrases[phraseText] : phraseText
10195 },
10196
10197 getInputField: function(){return this.display.input.getField()},
10198 getWrapperElement: function(){return this.display.wrapper},
10199 getScrollerElement: function(){return this.display.scroller},
10200 getGutterElement: function(){return this.display.gutters}
10201 };
10202 eventMixin(CodeMirror);
10203
10204 CodeMirror.registerHelper = function(type, name, value) {
10205 if (!helpers.hasOwnProperty(type)) { helpers[type] = CodeMirror[type] = {_global: []}; }
10206 helpers[type][name] = value;
10207 };
10208 CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {
10209 CodeMirror.registerHelper(type, name, value);
10210 helpers[type]._global.push({pred: predicate, val: value});
10211 };
10212 }
10213
10214 // Used for horizontal relative motion. Dir is -1 or 1 (left or
10215 // right), unit can be "char", "column" (like char, but doesn't
10216 // cross line boundaries), "word" (across next word), or "group" (to
10217 // the start of next group of word or non-word-non-whitespace
10218 // chars). The visually param controls whether, in right-to-left
10219 // text, direction 1 means to move towards the next index in the
10220 // string, or towards the character to the right of the current
10221 // position. The resulting position will have a hitSide=true
10222 // property if it reached the end of the document.
10223 function findPosH(doc, pos, dir, unit, visually) {
10224 var oldPos = pos;
10225 var origDir = dir;
10226 var lineObj = getLine(doc, pos.line);
10227 function findNextLine() {
10228 var l = pos.line + dir;
10229 if (l < doc.first || l >= doc.first + doc.size) { return false }
10230 pos = new Pos(l, pos.ch, pos.sticky);
10231 return lineObj = getLine(doc, l)
10232 }
10233 function moveOnce(boundToLine) {
10234 var next;
10235 if (visually) {
10236 next = moveVisually(doc.cm, lineObj, pos, dir);
10237 } else {
10238 next = moveLogically(lineObj, pos, dir);
10239 }
10240 if (next == null) {
10241 if (!boundToLine && findNextLine())
10242 { pos = endOfLine(visually, doc.cm, lineObj, pos.line, dir); }
10243 else
10244 { return false }
10245 } else {
10246 pos = next;
10247 }
10248 return true
10249 }
10250
10251 if (unit == "char") {
10252 moveOnce();
10253 } else if (unit == "column") {
10254 moveOnce(true);
10255 } else if (unit == "word" || unit == "group") {
10256 var sawType = null, group = unit == "group";
10257 var helper = doc.cm && doc.cm.getHelper(pos, "wordChars");
10258 for (var first = true;; first = false) {
10259 if (dir < 0 && !moveOnce(!first)) { break }
10260 var cur = lineObj.text.charAt(pos.ch) || "\n";
10261 var type = isWordChar(cur, helper) ? "w"
10262 : group && cur == "\n" ? "n"
10263 : !group || /\s/.test(cur) ? null
10264 : "p";
10265 if (group && !first && !type) { type = "s"; }
10266 if (sawType && sawType != type) {
10267 if (dir < 0) {dir = 1; moveOnce(); pos.sticky = "after";}
10268 break
10269 }
10270
10271 if (type) { sawType = type; }
10272 if (dir > 0 && !moveOnce(!first)) { break }
10273 }
10274 }
10275 var result = skipAtomic(doc, pos, oldPos, origDir, true);
10276 if (equalCursorPos(oldPos, result)) { result.hitSide = true; }
10277 return result
10278 }
10279
10280 // For relative vertical movement. Dir may be -1 or 1. Unit can be
10281 // "page" or "line". The resulting position will have a hitSide=true
10282 // property if it reached the end of the document.
10283 function findPosV(cm, pos, dir, unit) {
10284 var doc = cm.doc, x = pos.left, y;
10285 if (unit == "page") {
10286 var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
10287 var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3);
10288 y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount;
10289
10290 } else if (unit == "line") {
10291 y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
10292 }
10293 var target;
10294 for (;;) {
10295 target = coordsChar(cm, x, y);
10296 if (!target.outside) { break }
10297 if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break }
10298 y += dir * 5;
10299 }
10300 return target
10301 }
10302
10303 // CONTENTEDITABLE INPUT STYLE
10304
10305 var ContentEditableInput = function(cm) {
10306 this.cm = cm;
10307 this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null;
10308 this.polling = new Delayed();
10309 this.composing = null;
10310 this.gracePeriod = false;
10311 this.readDOMTimeout = null;
10312 };
10313
10314 ContentEditableInput.prototype.init = function (display) {
10315 var this$1 = this;
10316
10317 var input = this, cm = input.cm;
10318 var div = input.div = display.lineDiv;
10319 disableBrowserMagic(div, cm.options.spellcheck, cm.options.autocorrect, cm.options.autocapitalize);
10320
10321 on(div, "paste", function (e) {
10322 if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
10323 // IE doesn't fire input events, so we schedule a read for the pasted content in this way
10324 if (ie_version <= 11) { setTimeout(operation(cm, function () { return this$1.updateFromDOM(); }), 20); }
10325 });
10326
10327 on(div, "compositionstart", function (e) {
10328 this$1.composing = {data: e.data, done: false};
10329 });
10330 on(div, "compositionupdate", function (e) {
10331 if (!this$1.composing) { this$1.composing = {data: e.data, done: false}; }
10332 });
10333 on(div, "compositionend", function (e) {
10334 if (this$1.composing) {
10335 if (e.data != this$1.composing.data) { this$1.readFromDOMSoon(); }
10336 this$1.composing.done = true;
10337 }
10338 });
10339
10340 on(div, "touchstart", function () { return input.forceCompositionEnd(); });
10341
10342 on(div, "input", function () {
10343 if (!this$1.composing) { this$1.readFromDOMSoon(); }
10344 });
10345
10346 function onCopyCut(e) {
10347 if (signalDOMEvent(cm, e)) { return }
10348 if (cm.somethingSelected()) {
10349 setLastCopied({lineWise: false, text: cm.getSelections()});
10350 if (e.type == "cut") { cm.replaceSelection("", null, "cut"); }
10351 } else if (!cm.options.lineWiseCopyCut) {
10352 return
10353 } else {
10354 var ranges = copyableRanges(cm);
10355 setLastCopied({lineWise: true, text: ranges.text});
10356 if (e.type == "cut") {
10357 cm.operation(function () {
10358 cm.setSelections(ranges.ranges, 0, sel_dontScroll);
10359 cm.replaceSelection("", null, "cut");
10360 });
10361 }
10362 }
10363 if (e.clipboardData) {
10364 e.clipboardData.clearData();
10365 var content = lastCopied.text.join("\n");
10366 // iOS exposes the clipboard API, but seems to discard content inserted into it
10367 e.clipboardData.setData("Text", content);
10368 if (e.clipboardData.getData("Text") == content) {
10369 e.preventDefault();
10370 return
10371 }
10372 }
10373 // Old-fashioned briefly-focus-a-textarea hack
10374 var kludge = hiddenTextarea(), te = kludge.firstChild;
10375 cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild);
10376 te.value = lastCopied.text.join("\n");
10377 var hadFocus = document.activeElement;
10378 selectInput(te);
10379 setTimeout(function () {
10380 cm.display.lineSpace.removeChild(kludge);
10381 hadFocus.focus();
10382 if (hadFocus == div) { input.showPrimarySelection(); }
10383 }, 50);
10384 }
10385 on(div, "copy", onCopyCut);
10386 on(div, "cut", onCopyCut);
10387 };
10388
10389 ContentEditableInput.prototype.prepareSelection = function () {
10390 var result = prepareSelection(this.cm, false);
10391 result.focus = this.cm.state.focused;
10392 return result
10393 };
10394
10395 ContentEditableInput.prototype.showSelection = function (info, takeFocus) {
10396 if (!info || !this.cm.display.view.length) { return }
10397 if (info.focus || takeFocus) { this.showPrimarySelection(); }
10398 this.showMultipleSelections(info);
10399 };
10400
10401 ContentEditableInput.prototype.getSelection = function () {
10402 return this.cm.display.wrapper.ownerDocument.getSelection()
10403 };
10404
10405 ContentEditableInput.prototype.showPrimarySelection = function () {
10406 var sel = this.getSelection(), cm = this.cm, prim = cm.doc.sel.primary();
10407 var from = prim.from(), to = prim.to();
10408
10409 if (cm.display.viewTo == cm.display.viewFrom || from.line >= cm.display.viewTo || to.line < cm.display.viewFrom) {
10410 sel.removeAllRanges();
10411 return
10412 }
10413
10414 var curAnchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
10415 var curFocus = domToPos(cm, sel.focusNode, sel.focusOffset);
10416 if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad &&
10417 cmp(minPos(curAnchor, curFocus), from) == 0 &&
10418 cmp(maxPos(curAnchor, curFocus), to) == 0)
10419 { return }
10420
10421 var view = cm.display.view;
10422 var start = (from.line >= cm.display.viewFrom && posToDOM(cm, from)) ||
10423 {node: view[0].measure.map[2], offset: 0};
10424 var end = to.line < cm.display.viewTo && posToDOM(cm, to);
10425 if (!end) {
10426 var measure = view[view.length - 1].measure;
10427 var map$$1 = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map;
10428 end = {node: map$$1[map$$1.length - 1], offset: map$$1[map$$1.length - 2] - map$$1[map$$1.length - 3]};
10429 }
10430
10431 if (!start || !end) {
10432 sel.removeAllRanges();
10433 return
10434 }
10435
10436 var old = sel.rangeCount && sel.getRangeAt(0), rng;
10437 try { rng = range(start.node, start.offset, end.offset, end.node); }
10438 catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible
10439 if (rng) {
10440 if (!gecko && cm.state.focused) {
10441 sel.collapse(start.node, start.offset);
10442 if (!rng.collapsed) {
10443 sel.removeAllRanges();
10444 sel.addRange(rng);
10445 }
10446 } else {
10447 sel.removeAllRanges();
10448 sel.addRange(rng);
10449 }
10450 if (old && sel.anchorNode == null) { sel.addRange(old); }
10451 else if (gecko) { this.startGracePeriod(); }
10452 }
10453 this.rememberSelection();
10454 };
10455
10456 ContentEditableInput.prototype.startGracePeriod = function () {
10457 var this$1 = this;
10458
10459 clearTimeout(this.gracePeriod);
10460 this.gracePeriod = setTimeout(function () {
10461 this$1.gracePeriod = false;
10462 if (this$1.selectionChanged())
10463 { this$1.cm.operation(function () { return this$1.cm.curOp.selectionChanged = true; }); }
10464 }, 20);
10465 };
10466
10467 ContentEditableInput.prototype.showMultipleSelections = function (info) {
10468 removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors);
10469 removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection);
10470 };
10471
10472 ContentEditableInput.prototype.rememberSelection = function () {
10473 var sel = this.getSelection();
10474 this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset;
10475 this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset;
10476 };
10477
10478 ContentEditableInput.prototype.selectionInEditor = function () {
10479 var sel = this.getSelection();
10480 if (!sel.rangeCount) { return false }
10481 var node = sel.getRangeAt(0).commonAncestorContainer;
10482 return contains(this.div, node)
10483 };
10484
10485 ContentEditableInput.prototype.focus = function () {
10486 if (this.cm.options.readOnly != "nocursor") {
10487 if (!this.selectionInEditor())
10488 { this.showSelection(this.prepareSelection(), true); }
10489 this.div.focus();
10490 }
10491 };
10492 ContentEditableInput.prototype.blur = function () { this.div.blur(); };
10493 ContentEditableInput.prototype.getField = function () { return this.div };
10494
10495 ContentEditableInput.prototype.supportsTouch = function () { return true };
10496
10497 ContentEditableInput.prototype.receivedFocus = function () {
10498 var input = this;
10499 if (this.selectionInEditor())
10500 { this.pollSelection(); }
10501 else
10502 { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }); }
10503
10504 function poll() {
10505 if (input.cm.state.focused) {
10506 input.pollSelection();
10507 input.polling.set(input.cm.options.pollInterval, poll);
10508 }
10509 }
10510 this.polling.set(this.cm.options.pollInterval, poll);
10511 };
10512
10513 ContentEditableInput.prototype.selectionChanged = function () {
10514 var sel = this.getSelection();
10515 return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||
10516 sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset
10517 };
10518
10519 ContentEditableInput.prototype.pollSelection = function () {
10520 if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) { return }
10521 var sel = this.getSelection(), cm = this.cm;
10522 // On Android Chrome (version 56, at least), backspacing into an
10523 // uneditable block element will put the cursor in that element,
10524 // and then, because it's not editable, hide the virtual keyboard.
10525 // Because Android doesn't allow us to actually detect backspace
10526 // presses in a sane way, this code checks for when that happens
10527 // and simulates a backspace press in this case.
10528 if (android && chrome && this.cm.options.gutters.length && isInGutter(sel.anchorNode)) {
10529 this.cm.triggerOnKeyDown({type: "keydown", keyCode: 8, preventDefault: Math.abs});
10530 this.blur();
10531 this.focus();
10532 return
10533 }
10534 if (this.composing) { return }
10535 this.rememberSelection();
10536 var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
10537 var head = domToPos(cm, sel.focusNode, sel.focusOffset);
10538 if (anchor && head) { runInOp(cm, function () {
10539 setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll);
10540 if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true; }
10541 }); }
10542 };
10543
10544 ContentEditableInput.prototype.pollContent = function () {
10545 if (this.readDOMTimeout != null) {
10546 clearTimeout(this.readDOMTimeout);
10547 this.readDOMTimeout = null;
10548 }
10549
10550 var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary();
10551 var from = sel.from(), to = sel.to();
10552 if (from.ch == 0 && from.line > cm.firstLine())
10553 { from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length); }
10554 if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine())
10555 { to = Pos(to.line + 1, 0); }
10556 if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return false }
10557
10558 var fromIndex, fromLine, fromNode;
10559 if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) {
10560 fromLine = lineNo(display.view[0].line);
10561 fromNode = display.view[0].node;
10562 } else {
10563 fromLine = lineNo(display.view[fromIndex].line);
10564 fromNode = display.view[fromIndex - 1].node.nextSibling;
10565 }
10566 var toIndex = findViewIndex(cm, to.line);
10567 var toLine, toNode;
10568 if (toIndex == display.view.length - 1) {
10569 toLine = display.viewTo - 1;
10570 toNode = display.lineDiv.lastChild;
10571 } else {
10572 toLine = lineNo(display.view[toIndex + 1].line) - 1;
10573 toNode = display.view[toIndex + 1].node.previousSibling;
10574 }
10575
10576 if (!fromNode) { return false }
10577 var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine));
10578 var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length));
10579 while (newText.length > 1 && oldText.length > 1) {
10580 if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; }
10581 else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; }
10582 else { break }
10583 }
10584
10585 var cutFront = 0, cutEnd = 0;
10586 var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length);
10587 while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront))
10588 { ++cutFront; }
10589 var newBot = lst(newText), oldBot = lst(oldText);
10590 var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0),
10591 oldBot.length - (oldText.length == 1 ? cutFront : 0));
10592 while (cutEnd < maxCutEnd &&
10593 newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1))
10594 { ++cutEnd; }
10595 // Try to move start of change to start of selection if ambiguous
10596 if (newText.length == 1 && oldText.length == 1 && fromLine == from.line) {
10597 while (cutFront && cutFront > from.ch &&
10598 newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) {
10599 cutFront--;
10600 cutEnd++;
10601 }
10602 }
10603
10604 newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, "");
10605 newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, "");
10606
10607 var chFrom = Pos(fromLine, cutFront);
10608 var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0);
10609 if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {
10610 replaceRange(cm.doc, newText, chFrom, chTo, "+input");
10611 return true
10612 }
10613 };
10614
10615 ContentEditableInput.prototype.ensurePolled = function () {
10616 this.forceCompositionEnd();
10617 };
10618 ContentEditableInput.prototype.reset = function () {
10619 this.forceCompositionEnd();
10620 };
10621 ContentEditableInput.prototype.forceCompositionEnd = function () {
10622 if (!this.composing) { return }
10623 clearTimeout(this.readDOMTimeout);
10624 this.composing = null;
10625 this.updateFromDOM();
10626 this.div.blur();
10627 this.div.focus();
10628 };
10629 ContentEditableInput.prototype.readFromDOMSoon = function () {
10630 var this$1 = this;
10631
10632 if (this.readDOMTimeout != null) { return }
10633 this.readDOMTimeout = setTimeout(function () {
10634 this$1.readDOMTimeout = null;
10635 if (this$1.composing) {
10636 if (this$1.composing.done) { this$1.composing = null; }
10637 else { return }
10638 }
10639 this$1.updateFromDOM();
10640 }, 80);
10641 };
10642
10643 ContentEditableInput.prototype.updateFromDOM = function () {
10644 var this$1 = this;
10645
10646 if (this.cm.isReadOnly() || !this.pollContent())
10647 { runInOp(this.cm, function () { return regChange(this$1.cm); }); }
10648 };
10649
10650 ContentEditableInput.prototype.setUneditable = function (node) {
10651 node.contentEditable = "false";
10652 };
10653
10654 ContentEditableInput.prototype.onKeyPress = function (e) {
10655 if (e.charCode == 0 || this.composing) { return }
10656 e.preventDefault();
10657 if (!this.cm.isReadOnly())
10658 { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0); }
10659 };
10660
10661 ContentEditableInput.prototype.readOnlyChanged = function (val) {
10662 this.div.contentEditable = String(val != "nocursor");
10663 };
10664
10665 ContentEditableInput.prototype.onContextMenu = function () {};
10666 ContentEditableInput.prototype.resetPosition = function () {};
10667
10668 ContentEditableInput.prototype.needsContentAttribute = true;
10669
10670 function posToDOM(cm, pos) {
10671 var view = findViewForLine(cm, pos.line);
10672 if (!view || view.hidden) { return null }
10673 var line = getLine(cm.doc, pos.line);
10674 var info = mapFromLineView(view, line, pos.line);
10675
10676 var order = getOrder(line, cm.doc.direction), side = "left";
10677 if (order) {
10678 var partPos = getBidiPartAt(order, pos.ch);
10679 side = partPos % 2 ? "right" : "left";
10680 }
10681 var result = nodeAndOffsetInLineMap(info.map, pos.ch, side);
10682 result.offset = result.collapse == "right" ? result.end : result.start;
10683 return result
10684 }
10685
10686 function isInGutter(node) {
10687 for (var scan = node; scan; scan = scan.parentNode)
10688 { if (/CodeMirror-gutter-wrapper/.test(scan.className)) { return true } }
10689 return false
10690 }
10691
10692 function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos }
10693
10694 function domTextBetween(cm, from, to, fromLine, toLine) {
10695 var text = "", closing = false, lineSep = cm.doc.lineSeparator(), extraLinebreak = false;
10696 function recognizeMarker(id) { return function (marker) { return marker.id == id; } }
10697 function close() {
10698 if (closing) {
10699 text += lineSep;
10700 if (extraLinebreak) { text += lineSep; }
10701 closing = extraLinebreak = false;
10702 }
10703 }
10704 function addText(str) {
10705 if (str) {
10706 close();
10707 text += str;
10708 }
10709 }
10710 function walk(node) {
10711 if (node.nodeType == 1) {
10712 var cmText = node.getAttribute("cm-text");
10713 if (cmText) {
10714 addText(cmText);
10715 return
10716 }
10717 var markerID = node.getAttribute("cm-marker"), range$$1;
10718 if (markerID) {
10719 var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID));
10720 if (found.length && (range$$1 = found[0].find(0)))
10721 { addText(getBetween(cm.doc, range$$1.from, range$$1.to).join(lineSep)); }
10722 return
10723 }
10724 if (node.getAttribute("contenteditable") == "false") { return }
10725 var isBlock = /^(pre|div|p|li|table|br)$/i.test(node.nodeName);
10726 if (!/^br$/i.test(node.nodeName) && node.textContent.length == 0) { return }
10727
10728 if (isBlock) { close(); }
10729 for (var i = 0; i < node.childNodes.length; i++)
10730 { walk(node.childNodes[i]); }
10731
10732 if (/^(pre|p)$/i.test(node.nodeName)) { extraLinebreak = true; }
10733 if (isBlock) { closing = true; }
10734 } else if (node.nodeType == 3) {
10735 addText(node.nodeValue.replace(/\u200b/g, "").replace(/\u00a0/g, " "));
10736 }
10737 }
10738 for (;;) {
10739 walk(from);
10740 if (from == to) { break }
10741 from = from.nextSibling;
10742 extraLinebreak = false;
10743 }
10744 return text
10745 }
10746
10747 function domToPos(cm, node, offset) {
10748 var lineNode;
10749 if (node == cm.display.lineDiv) {
10750 lineNode = cm.display.lineDiv.childNodes[offset];
10751 if (!lineNode) { return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) }
10752 node = null; offset = 0;
10753 } else {
10754 for (lineNode = node;; lineNode = lineNode.parentNode) {
10755 if (!lineNode || lineNode == cm.display.lineDiv) { return null }
10756 if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) { break }
10757 }
10758 }
10759 for (var i = 0; i < cm.display.view.length; i++) {
10760 var lineView = cm.display.view[i];
10761 if (lineView.node == lineNode)
10762 { return locateNodeInLineView(lineView, node, offset) }
10763 }
10764 }
10765
10766 function locateNodeInLineView(lineView, node, offset) {
10767 var wrapper = lineView.text.firstChild, bad = false;
10768 if (!node || !contains(wrapper, node)) { return badPos(Pos(lineNo(lineView.line), 0), true) }
10769 if (node == wrapper) {
10770 bad = true;
10771 node = wrapper.childNodes[offset];
10772 offset = 0;
10773 if (!node) {
10774 var line = lineView.rest ? lst(lineView.rest) : lineView.line;
10775 return badPos(Pos(lineNo(line), line.text.length), bad)
10776 }
10777 }
10778
10779 var textNode = node.nodeType == 3 ? node : null, topNode = node;
10780 if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {
10781 textNode = node.firstChild;
10782 if (offset) { offset = textNode.nodeValue.length; }
10783 }
10784 while (topNode.parentNode != wrapper) { topNode = topNode.parentNode; }
10785 var measure = lineView.measure, maps = measure.maps;
10786
10787 function find(textNode, topNode, offset) {
10788 for (var i = -1; i < (maps ? maps.length : 0); i++) {
10789 var map$$1 = i < 0 ? measure.map : maps[i];
10790 for (var j = 0; j < map$$1.length; j += 3) {
10791 var curNode = map$$1[j + 2];
10792 if (curNode == textNode || curNode == topNode) {
10793 var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]);
10794 var ch = map$$1[j] + offset;
10795 if (offset < 0 || curNode != textNode) { ch = map$$1[j + (offset ? 1 : 0)]; }
10796 return Pos(line, ch)
10797 }
10798 }
10799 }
10800 }
10801 var found = find(textNode, topNode, offset);
10802 if (found) { return badPos(found, bad) }
10803
10804 // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems
10805 for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) {
10806 found = find(after, after.firstChild, 0);
10807 if (found)
10808 { return badPos(Pos(found.line, found.ch - dist), bad) }
10809 else
10810 { dist += after.textContent.length; }
10811 }
10812 for (var before = topNode.previousSibling, dist$1 = offset; before; before = before.previousSibling) {
10813 found = find(before, before.firstChild, -1);
10814 if (found)
10815 { return badPos(Pos(found.line, found.ch + dist$1), bad) }
10816 else
10817 { dist$1 += before.textContent.length; }
10818 }
10819 }
10820
10821 // TEXTAREA INPUT STYLE
10822
10823 var TextareaInput = function(cm) {
10824 this.cm = cm;
10825 // See input.poll and input.reset
10826 this.prevInput = "";
10827
10828 // Flag that indicates whether we expect input to appear real soon
10829 // now (after some event like 'keypress' or 'input') and are
10830 // polling intensively.
10831 this.pollingFast = false;
10832 // Self-resetting timeout for the poller
10833 this.polling = new Delayed();
10834 // Used to work around IE issue with selection being forgotten when focus moves away from textarea
10835 this.hasSelection = false;
10836 this.composing = null;
10837 };
10838
10839 TextareaInput.prototype.init = function (display) {
10840 var this$1 = this;
10841
10842 var input = this, cm = this.cm;
10843 this.createField(display);
10844 var te = this.textarea;
10845
10846 display.wrapper.insertBefore(this.wrapper, display.wrapper.firstChild);
10847
10848 // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)
10849 if (ios) { te.style.width = "0px"; }
10850
10851 on(te, "input", function () {
10852 if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = null; }
10853 input.poll();
10854 });
10855
10856 on(te, "paste", function (e) {
10857 if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
10858
10859 cm.state.pasteIncoming = +new Date;
10860 input.fastPoll();
10861 });
10862
10863 function prepareCopyCut(e) {
10864 if (signalDOMEvent(cm, e)) { return }
10865 if (cm.somethingSelected()) {
10866 setLastCopied({lineWise: false, text: cm.getSelections()});
10867 } else if (!cm.options.lineWiseCopyCut) {
10868 return
10869 } else {
10870 var ranges = copyableRanges(cm);
10871 setLastCopied({lineWise: true, text: ranges.text});
10872 if (e.type == "cut") {
10873 cm.setSelections(ranges.ranges, null, sel_dontScroll);
10874 } else {
10875 input.prevInput = "";
10876 te.value = ranges.text.join("\n");
10877 selectInput(te);
10878 }
10879 }
10880 if (e.type == "cut") { cm.state.cutIncoming = +new Date; }
10881 }
10882 on(te, "cut", prepareCopyCut);
10883 on(te, "copy", prepareCopyCut);
10884
10885 on(display.scroller, "paste", function (e) {
10886 if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return }
10887 if (!te.dispatchEvent) {
10888 cm.state.pasteIncoming = +new Date;
10889 input.focus();
10890 return
10891 }
10892
10893 // Pass the `paste` event to the textarea so it's handled by its event listener.
10894 var event = new Event("paste");
10895 event.clipboardData = e.clipboardData;
10896 te.dispatchEvent(event);
10897 });
10898
10899 // Prevent normal selection in the editor (we handle our own)
10900 on(display.lineSpace, "selectstart", function (e) {
10901 if (!eventInWidget(display, e)) { e_preventDefault(e); }
10902 });
10903
10904 on(te, "compositionstart", function () {
10905 var start = cm.getCursor("from");
10906 if (input.composing) { input.composing.range.clear(); }
10907 input.composing = {
10908 start: start,
10909 range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"})
10910 };
10911 });
10912 on(te, "compositionend", function () {
10913 if (input.composing) {
10914 input.poll();
10915 input.composing.range.clear();
10916 input.composing = null;
10917 }
10918 });
10919 };
10920
10921 TextareaInput.prototype.createField = function (_display) {
10922 // Wraps and hides input textarea
10923 this.wrapper = hiddenTextarea();
10924 // The semihidden textarea that is focused when the editor is
10925 // focused, and receives input.
10926 this.textarea = this.wrapper.firstChild;
10927 };
10928
10929 TextareaInput.prototype.prepareSelection = function () {
10930 // Redraw the selection and/or cursor
10931 var cm = this.cm, display = cm.display, doc = cm.doc;
10932 var result = prepareSelection(cm);
10933
10934 // Move the hidden textarea near the cursor to prevent scrolling artifacts
10935 if (cm.options.moveInputWithCursor) {
10936 var headPos = cursorCoords(cm, doc.sel.primary().head, "div");
10937 var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();
10938 result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
10939 headPos.top + lineOff.top - wrapOff.top));
10940 result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
10941 headPos.left + lineOff.left - wrapOff.left));
10942 }
10943
10944 return result
10945 };
10946
10947 TextareaInput.prototype.showSelection = function (drawn) {
10948 var cm = this.cm, display = cm.display;
10949 removeChildrenAndAdd(display.cursorDiv, drawn.cursors);
10950 removeChildrenAndAdd(display.selectionDiv, drawn.selection);
10951 if (drawn.teTop != null) {
10952 this.wrapper.style.top = drawn.teTop + "px";
10953 this.wrapper.style.left = drawn.teLeft + "px";
10954 }
10955 };
10956
10957 // Reset the input to correspond to the selection (or to be empty,
10958 // when not typing and nothing is selected)
10959 TextareaInput.prototype.reset = function (typing) {
10960 if (this.contextMenuPending || this.composing) { return }
10961 var cm = this.cm;
10962 if (cm.somethingSelected()) {
10963 this.prevInput = "";
10964 var content = cm.getSelection();
10965 this.textarea.value = content;
10966 if (cm.state.focused) { selectInput(this.textarea); }
10967 if (ie && ie_version >= 9) { this.hasSelection = content; }
10968 } else if (!typing) {
10969 this.prevInput = this.textarea.value = "";
10970 if (ie && ie_version >= 9) { this.hasSelection = null; }
10971 }
10972 };
10973
10974 TextareaInput.prototype.getField = function () { return this.textarea };
10975
10976 TextareaInput.prototype.supportsTouch = function () { return false };
10977
10978 TextareaInput.prototype.focus = function () {
10979 if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) {
10980 try { this.textarea.focus(); }
10981 catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM
10982 }
10983 };
10984
10985 TextareaInput.prototype.blur = function () { this.textarea.blur(); };
10986
10987 TextareaInput.prototype.resetPosition = function () {
10988 this.wrapper.style.top = this.wrapper.style.left = 0;
10989 };
10990
10991 TextareaInput.prototype.receivedFocus = function () { this.slowPoll(); };
10992
10993 // Poll for input changes, using the normal rate of polling. This
10994 // runs as long as the editor is focused.
10995 TextareaInput.prototype.slowPoll = function () {
10996 var this$1 = this;
10997
10998 if (this.pollingFast) { return }
10999 this.polling.set(this.cm.options.pollInterval, function () {
11000 this$1.poll();
11001 if (this$1.cm.state.focused) { this$1.slowPoll(); }
11002 });
11003 };
11004
11005 // When an event has just come in that is likely to add or change
11006 // something in the input textarea, we poll faster, to ensure that
11007 // the change appears on the screen quickly.
11008 TextareaInput.prototype.fastPoll = function () {
11009 var missed = false, input = this;
11010 input.pollingFast = true;
11011 function p() {
11012 var changed = input.poll();
11013 if (!changed && !missed) {missed = true; input.polling.set(60, p);}
11014 else {input.pollingFast = false; input.slowPoll();}
11015 }
11016 input.polling.set(20, p);
11017 };
11018
11019 // Read input from the textarea, and update the document to match.
11020 // When something is selected, it is present in the textarea, and
11021 // selected (unless it is huge, in which case a placeholder is
11022 // used). When nothing is selected, the cursor sits after previously
11023 // seen text (can be empty), which is stored in prevInput (we must
11024 // not reset the textarea when typing, because that breaks IME).
11025 TextareaInput.prototype.poll = function () {
11026 var this$1 = this;
11027
11028 var cm = this.cm, input = this.textarea, prevInput = this.prevInput;
11029 // Since this is called a *lot*, try to bail out as cheaply as
11030 // possible when it is clear that nothing happened. hasSelection
11031 // will be the case when there is a lot of text in the textarea,
11032 // in which case reading its value would be expensive.
11033 if (this.contextMenuPending || !cm.state.focused ||
11034 (hasSelection(input) && !prevInput && !this.composing) ||
11035 cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq)
11036 { return false }
11037
11038 var text = input.value;
11039 // If nothing changed, bail.
11040 if (text == prevInput && !cm.somethingSelected()) { return false }
11041 // Work around nonsensical selection resetting in IE9/10, and
11042 // inexplicable appearance of private area unicode characters on
11043 // some key combos in Mac (#2689).
11044 if (ie && ie_version >= 9 && this.hasSelection === text ||
11045 mac && /[\uf700-\uf7ff]/.test(text)) {
11046 cm.display.input.reset();
11047 return false
11048 }
11049
11050 if (cm.doc.sel == cm.display.selForContextMenu) {
11051 var first = text.charCodeAt(0);
11052 if (first == 0x200b && !prevInput) { prevInput = "\u200b"; }
11053 if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") }
11054 }
11055 // Find the part of the input that is actually new
11056 var same = 0, l = Math.min(prevInput.length, text.length);
11057 while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same; }
11058
11059 runInOp(cm, function () {
11060 applyTextInput(cm, text.slice(same), prevInput.length - same,
11061 null, this$1.composing ? "*compose" : null);
11062
11063 // Don't leave long text in the textarea, since it makes further polling slow
11064 if (text.length > 1000 || text.indexOf("\n") > -1) { input.value = this$1.prevInput = ""; }
11065 else { this$1.prevInput = text; }
11066
11067 if (this$1.composing) {
11068 this$1.composing.range.clear();
11069 this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor("to"),
11070 {className: "CodeMirror-composing"});
11071 }
11072 });
11073 return true
11074 };
11075
11076 TextareaInput.prototype.ensurePolled = function () {
11077 if (this.pollingFast && this.poll()) { this.pollingFast = false; }
11078 };
11079
11080 TextareaInput.prototype.onKeyPress = function () {
11081 if (ie && ie_version >= 9) { this.hasSelection = null; }
11082 this.fastPoll();
11083 };
11084
11085 TextareaInput.prototype.onContextMenu = function (e) {
11086 var input = this, cm = input.cm, display = cm.display, te = input.textarea;
11087 if (input.contextMenuPending) { input.contextMenuPending(); }
11088 var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
11089 if (!pos || presto) { return } // Opera is difficult.
11090
11091 // Reset the current text selection only if the click is done outside of the selection
11092 // and 'resetSelectionOnContextMenu' option is true.
11093 var reset = cm.options.resetSelectionOnContextMenu;
11094 if (reset && cm.doc.sel.contains(pos) == -1)
11095 { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll); }
11096
11097 var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText;
11098 var wrapperBox = input.wrapper.offsetParent.getBoundingClientRect();
11099 input.wrapper.style.cssText = "position: static";
11100 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);";
11101 var oldScrollY;
11102 if (webkit) { oldScrollY = window.scrollY; } // Work around Chrome issue (#2712)
11103 display.input.focus();
11104 if (webkit) { window.scrollTo(null, oldScrollY); }
11105 display.input.reset();
11106 // Adds "Select all" to context menu in FF
11107 if (!cm.somethingSelected()) { te.value = input.prevInput = " "; }
11108 input.contextMenuPending = rehide;
11109 display.selForContextMenu = cm.doc.sel;
11110 clearTimeout(display.detectingSelectAll);
11111
11112 // Select-all will be greyed out if there's nothing to select, so
11113 // this adds a zero-width space so that we can later check whether
11114 // it got selected.
11115 function prepareSelectAllHack() {
11116 if (te.selectionStart != null) {
11117 var selected = cm.somethingSelected();
11118 var extval = "\u200b" + (selected ? te.value : "");
11119 te.value = "\u21da"; // Used to catch context-menu undo
11120 te.value = extval;
11121 input.prevInput = selected ? "" : "\u200b";
11122 te.selectionStart = 1; te.selectionEnd = extval.length;
11123 // Re-set this, in case some other handler touched the
11124 // selection in the meantime.
11125 display.selForContextMenu = cm.doc.sel;
11126 }
11127 }
11128 function rehide() {
11129 if (input.contextMenuPending != rehide) { return }
11130 input.contextMenuPending = false;
11131 input.wrapper.style.cssText = oldWrapperCSS;
11132 te.style.cssText = oldCSS;
11133 if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos); }
11134
11135 // Try to detect the user choosing select-all
11136 if (te.selectionStart != null) {
11137 if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack(); }
11138 var i = 0, poll = function () {
11139 if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 &&
11140 te.selectionEnd > 0 && input.prevInput == "\u200b") {
11141 operation(cm, selectAll)(cm);
11142 } else if (i++ < 10) {
11143 display.detectingSelectAll = setTimeout(poll, 500);
11144 } else {
11145 display.selForContextMenu = null;
11146 display.input.reset();
11147 }
11148 };
11149 display.detectingSelectAll = setTimeout(poll, 200);
11150 }
11151 }
11152
11153 if (ie && ie_version >= 9) { prepareSelectAllHack(); }
11154 if (captureRightClick) {
11155 e_stop(e);
11156 var mouseup = function () {
11157 off(window, "mouseup", mouseup);
11158 setTimeout(rehide, 20);
11159 };
11160 on(window, "mouseup", mouseup);
11161 } else {
11162 setTimeout(rehide, 50);
11163 }
11164 };
11165
11166 TextareaInput.prototype.readOnlyChanged = function (val) {
11167 if (!val) { this.reset(); }
11168 this.textarea.disabled = val == "nocursor";
11169 };
11170
11171 TextareaInput.prototype.setUneditable = function () {};
11172
11173 TextareaInput.prototype.needsContentAttribute = false;
11174
11175 function fromTextArea(textarea, options) {
11176 options = options ? copyObj(options) : {};
11177 options.value = textarea.value;
11178 if (!options.tabindex && textarea.tabIndex)
11179 { options.tabindex = textarea.tabIndex; }
11180 if (!options.placeholder && textarea.placeholder)
11181 { options.placeholder = textarea.placeholder; }
11182 // Set autofocus to true if this textarea is focused, or if it has
11183 // autofocus and no other element is focused.
11184 if (options.autofocus == null) {
11185 var hasFocus = activeElt();
11186 options.autofocus = hasFocus == textarea ||
11187 textarea.getAttribute("autofocus") != null && hasFocus == document.body;
11188 }
11189
11190 function save() {textarea.value = cm.getValue();}
11191
11192 var realSubmit;
11193 if (textarea.form) {
11194 on(textarea.form, "submit", save);
11195 // Deplorable hack to make the submit method do the right thing.
11196 if (!options.leaveSubmitMethodAlone) {
11197 var form = textarea.form;
11198 realSubmit = form.submit;
11199 try {
11200 var wrappedSubmit = form.submit = function () {
11201 save();
11202 form.submit = realSubmit;
11203 form.submit();
11204 form.submit = wrappedSubmit;
11205 };
11206 } catch(e) {}
11207 }
11208 }
11209
11210 options.finishInit = function (cm) {
11211 cm.save = save;
11212 cm.getTextArea = function () { return textarea; };
11213 cm.toTextArea = function () {
11214 cm.toTextArea = isNaN; // Prevent this from being ran twice
11215 save();
11216 textarea.parentNode.removeChild(cm.getWrapperElement());
11217 textarea.style.display = "";
11218 if (textarea.form) {
11219 off(textarea.form, "submit", save);
11220 if (typeof textarea.form.submit == "function")
11221 { textarea.form.submit = realSubmit; }
11222 }
11223 };
11224 };
11225
11226 textarea.style.display = "none";
11227 var cm = CodeMirror(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); },
11228 options);
11229 return cm
11230 }
11231
11232 function addLegacyProps(CodeMirror) {
11233 CodeMirror.off = off;
11234 CodeMirror.on = on;
11235 CodeMirror.wheelEventPixels = wheelEventPixels;
11236 CodeMirror.Doc = Doc;
11237 CodeMirror.splitLines = splitLinesAuto;
11238 CodeMirror.countColumn = countColumn;
11239 CodeMirror.findColumn = findColumn;
11240 CodeMirror.isWordChar = isWordCharBasic;
11241 CodeMirror.Pass = Pass;
11242 CodeMirror.signal = signal;
11243 CodeMirror.Line = Line;
11244 CodeMirror.changeEnd = changeEnd;
11245 CodeMirror.scrollbarModel = scrollbarModel;
11246 CodeMirror.Pos = Pos;
11247 CodeMirror.cmpPos = cmp;
11248 CodeMirror.modes = modes;
11249 CodeMirror.mimeModes = mimeModes;
11250 CodeMirror.resolveMode = resolveMode;
11251 CodeMirror.getMode = getMode;
11252 CodeMirror.modeExtensions = modeExtensions;
11253 CodeMirror.extendMode = extendMode;
11254 CodeMirror.copyState = copyState;
11255 CodeMirror.startState = startState;
11256 CodeMirror.innerMode = innerMode;
11257 CodeMirror.commands = commands;
11258 CodeMirror.keyMap = keyMap;
11259 CodeMirror.keyName = keyName;
11260 CodeMirror.isModifierKey = isModifierKey;
11261 CodeMirror.lookupKey = lookupKey;
11262 CodeMirror.normalizeKeyMap = normalizeKeyMap;
11263 CodeMirror.StringStream = StringStream;
11264 CodeMirror.SharedTextMarker = SharedTextMarker;
11265 CodeMirror.TextMarker = TextMarker;
11266 CodeMirror.LineWidget = LineWidget;
11267 CodeMirror.e_preventDefault = e_preventDefault;
11268 CodeMirror.e_stopPropagation = e_stopPropagation;
11269 CodeMirror.e_stop = e_stop;
11270 CodeMirror.addClass = addClass;
11271 CodeMirror.contains = contains;
11272 CodeMirror.rmClass = rmClass;
11273 CodeMirror.keyNames = keyNames;
11274 }
11275
11276 // EDITOR CONSTRUCTOR
11277
11278 defineOptions(CodeMirror);
11279
11280 addEditorMethods(CodeMirror);
11281
11282 // Set up methods on CodeMirror's prototype to redirect to the editor's document.
11283 var dontDelegate = "iter insert remove copy getEditor constructor".split(" ");
11284 for (var prop in Doc.prototype) { if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
11285 { CodeMirror.prototype[prop] = (function(method) {
11286 return function() {return method.apply(this.doc, arguments)}
11287 })(Doc.prototype[prop]); } }
11288
11289 eventMixin(Doc);
11290 CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput};
11291
11292 // Extra arguments are stored as the mode's dependencies, which is
11293 // used by (legacy) mechanisms like loadmode.js to automatically
11294 // load a mode. (Preferred mechanism is the require/define calls.)
11295 CodeMirror.defineMode = function(name/*, mode, …*/) {
11296 if (!CodeMirror.defaults.mode && name != "null") { CodeMirror.defaults.mode = name; }
11297 defineMode.apply(this, arguments);
11298 };
11299
11300 CodeMirror.defineMIME = defineMIME;
11301
11302 // Minimal default mode.
11303 CodeMirror.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); });
11304 CodeMirror.defineMIME("text/plain", "null");
11305
11306 // EXTENSIONS
11307
11308 CodeMirror.defineExtension = function (name, func) {
11309 CodeMirror.prototype[name] = func;
11310 };
11311 CodeMirror.defineDocExtension = function (name, func) {
11312 Doc.prototype[name] = func;
11313 };
11314
11315 CodeMirror.fromTextArea = fromTextArea;
11316
11317 addLegacyProps(CodeMirror);
11318
11319 CodeMirror.version = "5.45.0";
11320
11321 return CodeMirror;
11322
11323 })));
11324
11325 },{}],15:[function(require,module,exports){
11326 // CodeMirror, copyright (c) by Marijn Haverbeke and others
11327 // Distributed under an MIT license: https://codemirror.net/LICENSE
11328
11329 (function(mod) {
11330 if (typeof exports == "object" && typeof module == "object") // CommonJS
11331 mod(require("../../lib/codemirror"));
11332 else if (typeof define == "function" && define.amd) // AMD
11333 define(["../../lib/codemirror"], mod);
11334 else // Plain browser env
11335 mod(CodeMirror);
11336 })(function(CodeMirror) {
11337 "use strict";
11338
11339 CodeMirror.defineMode("css", function(config, parserConfig) {
11340 var inline = parserConfig.inline
11341 if (!parserConfig.propertyKeywords) parserConfig = CodeMirror.resolveMode("text/css");
11342
11343 var indentUnit = config.indentUnit,
11344 tokenHooks = parserConfig.tokenHooks,
11345 documentTypes = parserConfig.documentTypes || {},
11346 mediaTypes = parserConfig.mediaTypes || {},
11347 mediaFeatures = parserConfig.mediaFeatures || {},
11348 mediaValueKeywords = parserConfig.mediaValueKeywords || {},
11349 propertyKeywords = parserConfig.propertyKeywords || {},
11350 nonStandardPropertyKeywords = parserConfig.nonStandardPropertyKeywords || {},
11351 fontProperties = parserConfig.fontProperties || {},
11352 counterDescriptors = parserConfig.counterDescriptors || {},
11353 colorKeywords = parserConfig.colorKeywords || {},
11354 valueKeywords = parserConfig.valueKeywords || {},
11355 allowNested = parserConfig.allowNested,
11356 lineComment = parserConfig.lineComment,
11357 supportsAtComponent = parserConfig.supportsAtComponent === true;
11358
11359 var type, override;
11360 function ret(style, tp) { type = tp; return style; }
11361
11362 // Tokenizers
11363
11364 function tokenBase(stream, state) {
11365 var ch = stream.next();
11366 if (tokenHooks[ch]) {
11367 var result = tokenHooks[ch](stream, state);
11368 if (result !== false) return result;
11369 }
11370 if (ch == "@") {
11371 stream.eatWhile(/[\w\\\-]/);
11372 return ret("def", stream.current());
11373 } else if (ch == "=" || (ch == "~" || ch == "|") && stream.eat("=")) {
11374 return ret(null, "compare");
11375 } else if (ch == "\"" || ch == "'") {
11376 state.tokenize = tokenString(ch);
11377 return state.tokenize(stream, state);
11378 } else if (ch == "#") {
11379 stream.eatWhile(/[\w\\\-]/);
11380 return ret("atom", "hash");
11381 } else if (ch == "!") {
11382 stream.match(/^\s*\w*/);
11383 return ret("keyword", "important");
11384 } else if (/\d/.test(ch) || ch == "." && stream.eat(/\d/)) {
11385 stream.eatWhile(/[\w.%]/);
11386 return ret("number", "unit");
11387 } else if (ch === "-") {
11388 if (/[\d.]/.test(stream.peek())) {
11389 stream.eatWhile(/[\w.%]/);
11390 return ret("number", "unit");
11391 } else if (stream.match(/^-[\w\\\-]*/)) {
11392 stream.eatWhile(/[\w\\\-]/);
11393 if (stream.match(/^\s*:/, false))
11394 return ret("variable-2", "variable-definition");
11395 return ret("variable-2", "variable");
11396 } else if (stream.match(/^\w+-/)) {
11397 return ret("meta", "meta");
11398 }
11399 } else if (/[,+>*\/]/.test(ch)) {
11400 return ret(null, "select-op");
11401 } else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {
11402 return ret("qualifier", "qualifier");
11403 } else if (/[:;{}\[\]\(\)]/.test(ch)) {
11404 return ret(null, ch);
11405 } else if (stream.match(/[\w-.]+(?=\()/)) {
11406 if (/^(url(-prefix)?|domain|regexp)$/.test(stream.current().toLowerCase())) {
11407 state.tokenize = tokenParenthesized;
11408 }
11409 return ret("variable callee", "variable");
11410 } else if (/[\w\\\-]/.test(ch)) {
11411 stream.eatWhile(/[\w\\\-]/);
11412 return ret("property", "word");
11413 } else {
11414 return ret(null, null);
11415 }
11416 }
11417
11418 function tokenString(quote) {
11419 return function(stream, state) {
11420 var escaped = false, ch;
11421 while ((ch = stream.next()) != null) {
11422 if (ch == quote && !escaped) {
11423 if (quote == ")") stream.backUp(1);
11424 break;
11425 }
11426 escaped = !escaped && ch == "\\";
11427 }
11428 if (ch == quote || !escaped && quote != ")") state.tokenize = null;
11429 return ret("string", "string");
11430 };
11431 }
11432
11433 function tokenParenthesized(stream, state) {
11434 stream.next(); // Must be '('
11435 if (!stream.match(/\s*[\"\')]/, false))
11436 state.tokenize = tokenString(")");
11437 else
11438 state.tokenize = null;
11439 return ret(null, "(");
11440 }
11441
11442 // Context management
11443
11444 function Context(type, indent, prev) {
11445 this.type = type;
11446 this.indent = indent;
11447 this.prev = prev;
11448 }
11449
11450 function pushContext(state, stream, type, indent) {
11451 state.context = new Context(type, stream.indentation() + (indent === false ? 0 : indentUnit), state.context);
11452 return type;
11453 }
11454
11455 function popContext(state) {
11456 if (state.context.prev)
11457 state.context = state.context.prev;
11458 return state.context.type;
11459 }
11460
11461 function pass(type, stream, state) {
11462 return states[state.context.type](type, stream, state);
11463 }
11464 function popAndPass(type, stream, state, n) {
11465 for (var i = n || 1; i > 0; i--)
11466 state.context = state.context.prev;
11467 return pass(type, stream, state);
11468 }
11469
11470 // Parser
11471
11472 function wordAsValue(stream) {
11473 var word = stream.current().toLowerCase();
11474 if (valueKeywords.hasOwnProperty(word))
11475 override = "atom";
11476 else if (colorKeywords.hasOwnProperty(word))
11477 override = "keyword";
11478 else
11479 override = "variable";
11480 }
11481
11482 var states = {};
11483
11484 states.top = function(type, stream, state) {
11485 if (type == "{") {
11486 return pushContext(state, stream, "block");
11487 } else if (type == "}" && state.context.prev) {
11488 return popContext(state);
11489 } else if (supportsAtComponent && /@component/i.test(type)) {
11490 return pushContext(state, stream, "atComponentBlock");
11491 } else if (/^@(-moz-)?document$/i.test(type)) {
11492 return pushContext(state, stream, "documentTypes");
11493 } else if (/^@(media|supports|(-moz-)?document|import)$/i.test(type)) {
11494 return pushContext(state, stream, "atBlock");
11495 } else if (/^@(font-face|counter-style)/i.test(type)) {
11496 state.stateArg = type;
11497 return "restricted_atBlock_before";
11498 } else if (/^@(-(moz|ms|o|webkit)-)?keyframes$/i.test(type)) {
11499 return "keyframes";
11500 } else if (type && type.charAt(0) == "@") {
11501 return pushContext(state, stream, "at");
11502 } else if (type == "hash") {
11503 override = "builtin";
11504 } else if (type == "word") {
11505 override = "tag";
11506 } else if (type == "variable-definition") {
11507 return "maybeprop";
11508 } else if (type == "interpolation") {
11509 return pushContext(state, stream, "interpolation");
11510 } else if (type == ":") {
11511 return "pseudo";
11512 } else if (allowNested && type == "(") {
11513 return pushContext(state, stream, "parens");
11514 }
11515 return state.context.type;
11516 };
11517
11518 states.block = function(type, stream, state) {
11519 if (type == "word") {
11520 var word = stream.current().toLowerCase();
11521 if (propertyKeywords.hasOwnProperty(word)) {
11522 override = "property";
11523 return "maybeprop";
11524 } else if (nonStandardPropertyKeywords.hasOwnProperty(word)) {
11525 override = "string-2";
11526 return "maybeprop";
11527 } else if (allowNested) {
11528 override = stream.match(/^\s*:(?:\s|$)/, false) ? "property" : "tag";
11529 return "block";
11530 } else {
11531 override += " error";
11532 return "maybeprop";
11533 }
11534 } else if (type == "meta") {
11535 return "block";
11536 } else if (!allowNested && (type == "hash" || type == "qualifier")) {
11537 override = "error";
11538 return "block";
11539 } else {
11540 return states.top(type, stream, state);
11541 }
11542 };
11543
11544 states.maybeprop = function(type, stream, state) {
11545 if (type == ":") return pushContext(state, stream, "prop");
11546 return pass(type, stream, state);
11547 };
11548
11549 states.prop = function(type, stream, state) {
11550 if (type == ";") return popContext(state);
11551 if (type == "{" && allowNested) return pushContext(state, stream, "propBlock");
11552 if (type == "}" || type == "{") return popAndPass(type, stream, state);
11553 if (type == "(") return pushContext(state, stream, "parens");
11554
11555 if (type == "hash" && !/^#([0-9a-fA-f]{3,4}|[0-9a-fA-f]{6}|[0-9a-fA-f]{8})$/.test(stream.current())) {
11556 override += " error";
11557 } else if (type == "word") {
11558 wordAsValue(stream);
11559 } else if (type == "interpolation") {
11560 return pushContext(state, stream, "interpolation");
11561 }
11562 return "prop";
11563 };
11564
11565 states.propBlock = function(type, _stream, state) {
11566 if (type == "}") return popContext(state);
11567 if (type == "word") { override = "property"; return "maybeprop"; }
11568 return state.context.type;
11569 };
11570
11571 states.parens = function(type, stream, state) {
11572 if (type == "{" || type == "}") return popAndPass(type, stream, state);
11573 if (type == ")") return popContext(state);
11574 if (type == "(") return pushContext(state, stream, "parens");
11575 if (type == "interpolation") return pushContext(state, stream, "interpolation");
11576 if (type == "word") wordAsValue(stream);
11577 return "parens";
11578 };
11579
11580 states.pseudo = function(type, stream, state) {
11581 if (type == "meta") return "pseudo";
11582
11583 if (type == "word") {
11584 override = "variable-3";
11585 return state.context.type;
11586 }
11587 return pass(type, stream, state);
11588 };
11589
11590 states.documentTypes = function(type, stream, state) {
11591 if (type == "word" && documentTypes.hasOwnProperty(stream.current())) {
11592 override = "tag";
11593 return state.context.type;
11594 } else {
11595 return states.atBlock(type, stream, state);
11596 }
11597 };
11598
11599 states.atBlock = function(type, stream, state) {
11600 if (type == "(") return pushContext(state, stream, "atBlock_parens");
11601 if (type == "}" || type == ";") return popAndPass(type, stream, state);
11602 if (type == "{") return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top");
11603
11604 if (type == "interpolation") return pushContext(state, stream, "interpolation");
11605
11606 if (type == "word") {
11607 var word = stream.current().toLowerCase();
11608 if (word == "only" || word == "not" || word == "and" || word == "or")
11609 override = "keyword";
11610 else if (mediaTypes.hasOwnProperty(word))
11611 override = "attribute";
11612 else if (mediaFeatures.hasOwnProperty(word))
11613 override = "property";
11614 else if (mediaValueKeywords.hasOwnProperty(word))
11615 override = "keyword";
11616 else if (propertyKeywords.hasOwnProperty(word))
11617 override = "property";
11618 else if (nonStandardPropertyKeywords.hasOwnProperty(word))
11619 override = "string-2";
11620 else if (valueKeywords.hasOwnProperty(word))
11621 override = "atom";
11622 else if (colorKeywords.hasOwnProperty(word))
11623 override = "keyword";
11624 else
11625 override = "error";
11626 }
11627 return state.context.type;
11628 };
11629
11630 states.atComponentBlock = function(type, stream, state) {
11631 if (type == "}")
11632 return popAndPass(type, stream, state);
11633 if (type == "{")
11634 return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top", false);
11635 if (type == "word")
11636 override = "error";
11637 return state.context.type;
11638 };
11639
11640 states.atBlock_parens = function(type, stream, state) {
11641 if (type == ")") return popContext(state);
11642 if (type == "{" || type == "}") return popAndPass(type, stream, state, 2);
11643 return states.atBlock(type, stream, state);
11644 };
11645
11646 states.restricted_atBlock_before = function(type, stream, state) {
11647 if (type == "{")
11648 return pushContext(state, stream, "restricted_atBlock");
11649 if (type == "word" && state.stateArg == "@counter-style") {
11650 override = "variable";
11651 return "restricted_atBlock_before";
11652 }
11653 return pass(type, stream, state);
11654 };
11655
11656 states.restricted_atBlock = function(type, stream, state) {
11657 if (type == "}") {
11658 state.stateArg = null;
11659 return popContext(state);
11660 }
11661 if (type == "word") {
11662 if ((state.stateArg == "@font-face" && !fontProperties.hasOwnProperty(stream.current().toLowerCase())) ||
11663 (state.stateArg == "@counter-style" && !counterDescriptors.hasOwnProperty(stream.current().toLowerCase())))
11664 override = "error";
11665 else
11666 override = "property";
11667 return "maybeprop";
11668 }
11669 return "restricted_atBlock";
11670 };
11671
11672 states.keyframes = function(type, stream, state) {
11673 if (type == "word") { override = "variable"; return "keyframes"; }
11674 if (type == "{") return pushContext(state, stream, "top");
11675 return pass(type, stream, state);
11676 };
11677
11678 states.at = function(type, stream, state) {
11679 if (type == ";") return popContext(state);
11680 if (type == "{" || type == "}") return popAndPass(type, stream, state);
11681 if (type == "word") override = "tag";
11682 else if (type == "hash") override = "builtin";
11683 return "at";
11684 };
11685
11686 states.interpolation = function(type, stream, state) {
11687 if (type == "}") return popContext(state);
11688 if (type == "{" || type == ";") return popAndPass(type, stream, state);
11689 if (type == "word") override = "variable";
11690 else if (type != "variable" && type != "(" && type != ")") override = "error";
11691 return "interpolation";
11692 };
11693
11694 return {
11695 startState: function(base) {
11696 return {tokenize: null,
11697 state: inline ? "block" : "top",
11698 stateArg: null,
11699 context: new Context(inline ? "block" : "top", base || 0, null)};
11700 },
11701
11702 token: function(stream, state) {
11703 if (!state.tokenize && stream.eatSpace()) return null;
11704 var style = (state.tokenize || tokenBase)(stream, state);
11705 if (style && typeof style == "object") {
11706 type = style[1];
11707 style = style[0];
11708 }
11709 override = style;
11710 if (type != "comment")
11711 state.state = states[state.state](type, stream, state);
11712 return override;
11713 },
11714
11715 indent: function(state, textAfter) {
11716 var cx = state.context, ch = textAfter && textAfter.charAt(0);
11717 var indent = cx.indent;
11718 if (cx.type == "prop" && (ch == "}" || ch == ")")) cx = cx.prev;
11719 if (cx.prev) {
11720 if (ch == "}" && (cx.type == "block" || cx.type == "top" ||
11721 cx.type == "interpolation" || cx.type == "restricted_atBlock")) {
11722 // Resume indentation from parent context.
11723 cx = cx.prev;
11724 indent = cx.indent;
11725 } else if (ch == ")" && (cx.type == "parens" || cx.type == "atBlock_parens") ||
11726 ch == "{" && (cx.type == "at" || cx.type == "atBlock")) {
11727 // Dedent relative to current context.
11728 indent = Math.max(0, cx.indent - indentUnit);
11729 }
11730 }
11731 return indent;
11732 },
11733
11734 electricChars: "}",
11735 blockCommentStart: "/*",
11736 blockCommentEnd: "*/",
11737 blockCommentContinue: " * ",
11738 lineComment: lineComment,
11739 fold: "brace"
11740 };
11741 });
11742
11743 function keySet(array) {
11744 var keys = {};
11745 for (var i = 0; i < array.length; ++i) {
11746 keys[array[i].toLowerCase()] = true;
11747 }
11748 return keys;
11749 }
11750
11751 var documentTypes_ = [
11752 "domain", "regexp", "url", "url-prefix"
11753 ], documentTypes = keySet(documentTypes_);
11754
11755 var mediaTypes_ = [
11756 "all", "aural", "braille", "handheld", "print", "projection", "screen",
11757 "tty", "tv", "embossed"
11758 ], mediaTypes = keySet(mediaTypes_);
11759
11760 var mediaFeatures_ = [
11761 "width", "min-width", "max-width", "height", "min-height", "max-height",
11762 "device-width", "min-device-width", "max-device-width", "device-height",
11763 "min-device-height", "max-device-height", "aspect-ratio",
11764 "min-aspect-ratio", "max-aspect-ratio", "device-aspect-ratio",
11765 "min-device-aspect-ratio", "max-device-aspect-ratio", "color", "min-color",
11766 "max-color", "color-index", "min-color-index", "max-color-index",
11767 "monochrome", "min-monochrome", "max-monochrome", "resolution",
11768 "min-resolution", "max-resolution", "scan", "grid", "orientation",
11769 "device-pixel-ratio", "min-device-pixel-ratio", "max-device-pixel-ratio",
11770 "pointer", "any-pointer", "hover", "any-hover"
11771 ], mediaFeatures = keySet(mediaFeatures_);
11772
11773 var mediaValueKeywords_ = [
11774 "landscape", "portrait", "none", "coarse", "fine", "on-demand", "hover",
11775 "interlace", "progressive"
11776 ], mediaValueKeywords = keySet(mediaValueKeywords_);
11777
11778 var propertyKeywords_ = [
11779 "align-content", "align-items", "align-self", "alignment-adjust",
11780 "alignment-baseline", "anchor-point", "animation", "animation-delay",
11781 "animation-direction", "animation-duration", "animation-fill-mode",
11782 "animation-iteration-count", "animation-name", "animation-play-state",
11783 "animation-timing-function", "appearance", "azimuth", "backface-visibility",
11784 "background", "background-attachment", "background-blend-mode", "background-clip",
11785 "background-color", "background-image", "background-origin", "background-position",
11786 "background-repeat", "background-size", "baseline-shift", "binding",
11787 "bleed", "bookmark-label", "bookmark-level", "bookmark-state",
11788 "bookmark-target", "border", "border-bottom", "border-bottom-color",
11789 "border-bottom-left-radius", "border-bottom-right-radius",
11790 "border-bottom-style", "border-bottom-width", "border-collapse",
11791 "border-color", "border-image", "border-image-outset",
11792 "border-image-repeat", "border-image-slice", "border-image-source",
11793 "border-image-width", "border-left", "border-left-color",
11794 "border-left-style", "border-left-width", "border-radius", "border-right",
11795 "border-right-color", "border-right-style", "border-right-width",
11796 "border-spacing", "border-style", "border-top", "border-top-color",
11797 "border-top-left-radius", "border-top-right-radius", "border-top-style",
11798 "border-top-width", "border-width", "bottom", "box-decoration-break",
11799 "box-shadow", "box-sizing", "break-after", "break-before", "break-inside",
11800 "caption-side", "caret-color", "clear", "clip", "color", "color-profile", "column-count",
11801 "column-fill", "column-gap", "column-rule", "column-rule-color",
11802 "column-rule-style", "column-rule-width", "column-span", "column-width",
11803 "columns", "content", "counter-increment", "counter-reset", "crop", "cue",
11804 "cue-after", "cue-before", "cursor", "direction", "display",
11805 "dominant-baseline", "drop-initial-after-adjust",
11806 "drop-initial-after-align", "drop-initial-before-adjust",
11807 "drop-initial-before-align", "drop-initial-size", "drop-initial-value",
11808 "elevation", "empty-cells", "fit", "fit-position", "flex", "flex-basis",
11809 "flex-direction", "flex-flow", "flex-grow", "flex-shrink", "flex-wrap",
11810 "float", "float-offset", "flow-from", "flow-into", "font", "font-feature-settings",
11811 "font-family", "font-kerning", "font-language-override", "font-size", "font-size-adjust",
11812 "font-stretch", "font-style", "font-synthesis", "font-variant",
11813 "font-variant-alternates", "font-variant-caps", "font-variant-east-asian",
11814 "font-variant-ligatures", "font-variant-numeric", "font-variant-position",
11815 "font-weight", "grid", "grid-area", "grid-auto-columns", "grid-auto-flow",
11816 "grid-auto-rows", "grid-column", "grid-column-end", "grid-column-gap",
11817 "grid-column-start", "grid-gap", "grid-row", "grid-row-end", "grid-row-gap",
11818 "grid-row-start", "grid-template", "grid-template-areas", "grid-template-columns",
11819 "grid-template-rows", "hanging-punctuation", "height", "hyphens",
11820 "icon", "image-orientation", "image-rendering", "image-resolution",
11821 "inline-box-align", "justify-content", "justify-items", "justify-self", "left", "letter-spacing",
11822 "line-break", "line-height", "line-stacking", "line-stacking-ruby",
11823 "line-stacking-shift", "line-stacking-strategy", "list-style",
11824 "list-style-image", "list-style-position", "list-style-type", "margin",
11825 "margin-bottom", "margin-left", "margin-right", "margin-top",
11826 "marks", "marquee-direction", "marquee-loop",
11827 "marquee-play-count", "marquee-speed", "marquee-style", "max-height",
11828 "max-width", "min-height", "min-width", "mix-blend-mode", "move-to", "nav-down", "nav-index",
11829 "nav-left", "nav-right", "nav-up", "object-fit", "object-position",
11830 "opacity", "order", "orphans", "outline",
11831 "outline-color", "outline-offset", "outline-style", "outline-width",
11832 "overflow", "overflow-style", "overflow-wrap", "overflow-x", "overflow-y",
11833 "padding", "padding-bottom", "padding-left", "padding-right", "padding-top",
11834 "page", "page-break-after", "page-break-before", "page-break-inside",
11835 "page-policy", "pause", "pause-after", "pause-before", "perspective",
11836 "perspective-origin", "pitch", "pitch-range", "place-content", "place-items", "place-self", "play-during", "position",
11837 "presentation-level", "punctuation-trim", "quotes", "region-break-after",
11838 "region-break-before", "region-break-inside", "region-fragment",
11839 "rendering-intent", "resize", "rest", "rest-after", "rest-before", "richness",
11840 "right", "rotation", "rotation-point", "ruby-align", "ruby-overhang",
11841 "ruby-position", "ruby-span", "shape-image-threshold", "shape-inside", "shape-margin",
11842 "shape-outside", "size", "speak", "speak-as", "speak-header",
11843 "speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set",
11844 "tab-size", "table-layout", "target", "target-name", "target-new",
11845 "target-position", "text-align", "text-align-last", "text-decoration",
11846 "text-decoration-color", "text-decoration-line", "text-decoration-skip",
11847 "text-decoration-style", "text-emphasis", "text-emphasis-color",
11848 "text-emphasis-position", "text-emphasis-style", "text-height",
11849 "text-indent", "text-justify", "text-outline", "text-overflow", "text-shadow",
11850 "text-size-adjust", "text-space-collapse", "text-transform", "text-underline-position",
11851 "text-wrap", "top", "transform", "transform-origin", "transform-style",
11852 "transition", "transition-delay", "transition-duration",
11853 "transition-property", "transition-timing-function", "unicode-bidi",
11854 "user-select", "vertical-align", "visibility", "voice-balance", "voice-duration",
11855 "voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress",
11856 "voice-volume", "volume", "white-space", "widows", "width", "will-change", "word-break",
11857 "word-spacing", "word-wrap", "z-index",
11858 // SVG-specific
11859 "clip-path", "clip-rule", "mask", "enable-background", "filter", "flood-color",
11860 "flood-opacity", "lighting-color", "stop-color", "stop-opacity", "pointer-events",
11861 "color-interpolation", "color-interpolation-filters",
11862 "color-rendering", "fill", "fill-opacity", "fill-rule", "image-rendering",
11863 "marker", "marker-end", "marker-mid", "marker-start", "shape-rendering", "stroke",
11864 "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin",
11865 "stroke-miterlimit", "stroke-opacity", "stroke-width", "text-rendering",
11866 "baseline-shift", "dominant-baseline", "glyph-orientation-horizontal",
11867 "glyph-orientation-vertical", "text-anchor", "writing-mode"
11868 ], propertyKeywords = keySet(propertyKeywords_);
11869
11870 var nonStandardPropertyKeywords_ = [
11871 "scrollbar-arrow-color", "scrollbar-base-color", "scrollbar-dark-shadow-color",
11872 "scrollbar-face-color", "scrollbar-highlight-color", "scrollbar-shadow-color",
11873 "scrollbar-3d-light-color", "scrollbar-track-color", "shape-inside",
11874 "searchfield-cancel-button", "searchfield-decoration", "searchfield-results-button",
11875 "searchfield-results-decoration", "zoom"
11876 ], nonStandardPropertyKeywords = keySet(nonStandardPropertyKeywords_);
11877
11878 var fontProperties_ = [
11879 "font-family", "src", "unicode-range", "font-variant", "font-feature-settings",
11880 "font-stretch", "font-weight", "font-style"
11881 ], fontProperties = keySet(fontProperties_);
11882
11883 var counterDescriptors_ = [
11884 "additive-symbols", "fallback", "negative", "pad", "prefix", "range",
11885 "speak-as", "suffix", "symbols", "system"
11886 ], counterDescriptors = keySet(counterDescriptors_);
11887
11888 var colorKeywords_ = [
11889 "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige",
11890 "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown",
11891 "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue",
11892 "cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod",
11893 "darkgray", "darkgreen", "darkkhaki", "darkmagenta", "darkolivegreen",
11894 "darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen",
11895 "darkslateblue", "darkslategray", "darkturquoise", "darkviolet",
11896 "deeppink", "deepskyblue", "dimgray", "dodgerblue", "firebrick",
11897 "floralwhite", "forestgreen", "fuchsia", "gainsboro", "ghostwhite",
11898 "gold", "goldenrod", "gray", "grey", "green", "greenyellow", "honeydew",
11899 "hotpink", "indianred", "indigo", "ivory", "khaki", "lavender",
11900 "lavenderblush", "lawngreen", "lemonchiffon", "lightblue", "lightcoral",
11901 "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightpink",
11902 "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray",
11903 "lightsteelblue", "lightyellow", "lime", "limegreen", "linen", "magenta",
11904 "maroon", "mediumaquamarine", "mediumblue", "mediumorchid", "mediumpurple",
11905 "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise",
11906 "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin",
11907 "navajowhite", "navy", "oldlace", "olive", "olivedrab", "orange", "orangered",
11908 "orchid", "palegoldenrod", "palegreen", "paleturquoise", "palevioletred",
11909 "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue",
11910 "purple", "rebeccapurple", "red", "rosybrown", "royalblue", "saddlebrown",
11911 "salmon", "sandybrown", "seagreen", "seashell", "sienna", "silver", "skyblue",
11912 "slateblue", "slategray", "snow", "springgreen", "steelblue", "tan",
11913 "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white",
11914 "whitesmoke", "yellow", "yellowgreen"
11915 ], colorKeywords = keySet(colorKeywords_);
11916
11917 var valueKeywords_ = [
11918 "above", "absolute", "activeborder", "additive", "activecaption", "afar",
11919 "after-white-space", "ahead", "alias", "all", "all-scroll", "alphabetic", "alternate",
11920 "always", "amharic", "amharic-abegede", "antialiased", "appworkspace",
11921 "arabic-indic", "armenian", "asterisks", "attr", "auto", "auto-flow", "avoid", "avoid-column", "avoid-page",
11922 "avoid-region", "background", "backwards", "baseline", "below", "bidi-override", "binary",
11923 "bengali", "blink", "block", "block-axis", "bold", "bolder", "border", "border-box",
11924 "both", "bottom", "break", "break-all", "break-word", "bullets", "button", "button-bevel",
11925 "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "calc", "cambodian",
11926 "capitalize", "caps-lock-indicator", "caption", "captiontext", "caret",
11927 "cell", "center", "checkbox", "circle", "cjk-decimal", "cjk-earthly-branch",
11928 "cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote",
11929 "col-resize", "collapse", "color", "color-burn", "color-dodge", "column", "column-reverse",
11930 "compact", "condensed", "contain", "content", "contents",
11931 "content-box", "context-menu", "continuous", "copy", "counter", "counters", "cover", "crop",
11932 "cross", "crosshair", "currentcolor", "cursive", "cyclic", "darken", "dashed", "decimal",
11933 "decimal-leading-zero", "default", "default-button", "dense", "destination-atop",
11934 "destination-in", "destination-out", "destination-over", "devanagari", "difference",
11935 "disc", "discard", "disclosure-closed", "disclosure-open", "document",
11936 "dot-dash", "dot-dot-dash",
11937 "dotted", "double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out",
11938 "element", "ellipse", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede",
11939 "ethiopic-abegede-am-et", "ethiopic-abegede-gez", "ethiopic-abegede-ti-er",
11940 "ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er",
11941 "ethiopic-halehame-aa-et", "ethiopic-halehame-am-et",
11942 "ethiopic-halehame-gez", "ethiopic-halehame-om-et",
11943 "ethiopic-halehame-sid-et", "ethiopic-halehame-so-et",
11944 "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et", "ethiopic-halehame-tig",
11945 "ethiopic-numeric", "ew-resize", "exclusion", "expanded", "extends", "extra-condensed",
11946 "extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "flex", "flex-end", "flex-start", "footnotes",
11947 "forwards", "from", "geometricPrecision", "georgian", "graytext", "grid", "groove",
11948 "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hard-light", "hebrew",
11949 "help", "hidden", "hide", "higher", "highlight", "highlighttext",
11950 "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "hue", "icon", "ignore",
11951 "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite",
11952 "infobackground", "infotext", "inherit", "initial", "inline", "inline-axis",
11953 "inline-block", "inline-flex", "inline-grid", "inline-table", "inset", "inside", "intrinsic", "invert",
11954 "italic", "japanese-formal", "japanese-informal", "justify", "kannada",
11955 "katakana", "katakana-iroha", "keep-all", "khmer",
11956 "korean-hangul-formal", "korean-hanja-formal", "korean-hanja-informal",
11957 "landscape", "lao", "large", "larger", "left", "level", "lighter", "lighten",
11958 "line-through", "linear", "linear-gradient", "lines", "list-item", "listbox", "listitem",
11959 "local", "logical", "loud", "lower", "lower-alpha", "lower-armenian",
11960 "lower-greek", "lower-hexadecimal", "lower-latin", "lower-norwegian",
11961 "lower-roman", "lowercase", "ltr", "luminosity", "malayalam", "match", "matrix", "matrix3d",
11962 "media-controls-background", "media-current-time-display",
11963 "media-fullscreen-button", "media-mute-button", "media-play-button",
11964 "media-return-to-realtime-button", "media-rewind-button",
11965 "media-seek-back-button", "media-seek-forward-button", "media-slider",
11966 "media-sliderthumb", "media-time-remaining-display", "media-volume-slider",
11967 "media-volume-slider-container", "media-volume-sliderthumb", "medium",
11968 "menu", "menulist", "menulist-button", "menulist-text",
11969 "menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic",
11970 "mix", "mongolian", "monospace", "move", "multiple", "multiply", "myanmar", "n-resize",
11971 "narrower", "ne-resize", "nesw-resize", "no-close-quote", "no-drop",
11972 "no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap",
11973 "ns-resize", "numbers", "numeric", "nw-resize", "nwse-resize", "oblique", "octal", "opacity", "open-quote",
11974 "optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset",
11975 "outside", "outside-shape", "overlay", "overline", "padding", "padding-box",
11976 "painted", "page", "paused", "persian", "perspective", "plus-darker", "plus-lighter",
11977 "pointer", "polygon", "portrait", "pre", "pre-line", "pre-wrap", "preserve-3d",
11978 "progress", "push-button", "radial-gradient", "radio", "read-only",
11979 "read-write", "read-write-plaintext-only", "rectangle", "region",
11980 "relative", "repeat", "repeating-linear-gradient",
11981 "repeating-radial-gradient", "repeat-x", "repeat-y", "reset", "reverse",
11982 "rgb", "rgba", "ridge", "right", "rotate", "rotate3d", "rotateX", "rotateY",
11983 "rotateZ", "round", "row", "row-resize", "row-reverse", "rtl", "run-in", "running",
11984 "s-resize", "sans-serif", "saturation", "scale", "scale3d", "scaleX", "scaleY", "scaleZ", "screen",
11985 "scroll", "scrollbar", "scroll-position", "se-resize", "searchfield",
11986 "searchfield-cancel-button", "searchfield-decoration",
11987 "searchfield-results-button", "searchfield-results-decoration", "self-start", "self-end",
11988 "semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama",
11989 "simp-chinese-formal", "simp-chinese-informal", "single",
11990 "skew", "skewX", "skewY", "skip-white-space", "slide", "slider-horizontal",
11991 "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow",
11992 "small", "small-caps", "small-caption", "smaller", "soft-light", "solid", "somali",
11993 "source-atop", "source-in", "source-out", "source-over", "space", "space-around", "space-between", "space-evenly", "spell-out", "square",
11994 "square-button", "start", "static", "status-bar", "stretch", "stroke", "sub",
11995 "subpixel-antialiased", "super", "sw-resize", "symbolic", "symbols", "system-ui", "table",
11996 "table-caption", "table-cell", "table-column", "table-column-group",
11997 "table-footer-group", "table-header-group", "table-row", "table-row-group",
11998 "tamil",
11999 "telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai",
12000 "thick", "thin", "threeddarkshadow", "threedface", "threedhighlight",
12001 "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er",
12002 "tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top",
12003 "trad-chinese-formal", "trad-chinese-informal", "transform",
12004 "translate", "translate3d", "translateX", "translateY", "translateZ",
12005 "transparent", "ultra-condensed", "ultra-expanded", "underline", "unset", "up",
12006 "upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal",
12007 "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url",
12008 "var", "vertical", "vertical-text", "visible", "visibleFill", "visiblePainted",
12009 "visibleStroke", "visual", "w-resize", "wait", "wave", "wider",
12010 "window", "windowframe", "windowtext", "words", "wrap", "wrap-reverse", "x-large", "x-small", "xor",
12011 "xx-large", "xx-small"
12012 ], valueKeywords = keySet(valueKeywords_);
12013
12014 var allWords = documentTypes_.concat(mediaTypes_).concat(mediaFeatures_).concat(mediaValueKeywords_)
12015 .concat(propertyKeywords_).concat(nonStandardPropertyKeywords_).concat(colorKeywords_)
12016 .concat(valueKeywords_);
12017 CodeMirror.registerHelper("hintWords", "css", allWords);
12018
12019 function tokenCComment(stream, state) {
12020 var maybeEnd = false, ch;
12021 while ((ch = stream.next()) != null) {
12022 if (maybeEnd && ch == "/") {
12023 state.tokenize = null;
12024 break;
12025 }
12026 maybeEnd = (ch == "*");
12027 }
12028 return ["comment", "comment"];
12029 }
12030
12031 CodeMirror.defineMIME("text/css", {
12032 documentTypes: documentTypes,
12033 mediaTypes: mediaTypes,
12034 mediaFeatures: mediaFeatures,
12035 mediaValueKeywords: mediaValueKeywords,
12036 propertyKeywords: propertyKeywords,
12037 nonStandardPropertyKeywords: nonStandardPropertyKeywords,
12038 fontProperties: fontProperties,
12039 counterDescriptors: counterDescriptors,
12040 colorKeywords: colorKeywords,
12041 valueKeywords: valueKeywords,
12042 tokenHooks: {
12043 "/": function(stream, state) {
12044 if (!stream.eat("*")) return false;
12045 state.tokenize = tokenCComment;
12046 return tokenCComment(stream, state);
12047 }
12048 },
12049 name: "css"
12050 });
12051
12052 CodeMirror.defineMIME("text/x-scss", {
12053 mediaTypes: mediaTypes,
12054 mediaFeatures: mediaFeatures,
12055 mediaValueKeywords: mediaValueKeywords,
12056 propertyKeywords: propertyKeywords,
12057 nonStandardPropertyKeywords: nonStandardPropertyKeywords,
12058 colorKeywords: colorKeywords,
12059 valueKeywords: valueKeywords,
12060 fontProperties: fontProperties,
12061 allowNested: true,
12062 lineComment: "//",
12063 tokenHooks: {
12064 "/": function(stream, state) {
12065 if (stream.eat("/")) {
12066 stream.skipToEnd();
12067 return ["comment", "comment"];
12068 } else if (stream.eat("*")) {
12069 state.tokenize = tokenCComment;
12070 return tokenCComment(stream, state);
12071 } else {
12072 return ["operator", "operator"];
12073 }
12074 },
12075 ":": function(stream) {
12076 if (stream.match(/\s*\{/, false))
12077 return [null, null]
12078 return false;
12079 },
12080 "$": function(stream) {
12081 stream.match(/^[\w-]+/);
12082 if (stream.match(/^\s*:/, false))
12083 return ["variable-2", "variable-definition"];
12084 return ["variable-2", "variable"];
12085 },
12086 "#": function(stream) {
12087 if (!stream.eat("{")) return false;
12088 return [null, "interpolation"];
12089 }
12090 },
12091 name: "css",
12092 helperType: "scss"
12093 });
12094
12095 CodeMirror.defineMIME("text/x-less", {
12096 mediaTypes: mediaTypes,
12097 mediaFeatures: mediaFeatures,
12098 mediaValueKeywords: mediaValueKeywords,
12099 propertyKeywords: propertyKeywords,
12100 nonStandardPropertyKeywords: nonStandardPropertyKeywords,
12101 colorKeywords: colorKeywords,
12102 valueKeywords: valueKeywords,
12103 fontProperties: fontProperties,
12104 allowNested: true,
12105 lineComment: "//",
12106 tokenHooks: {
12107 "/": function(stream, state) {
12108 if (stream.eat("/")) {
12109 stream.skipToEnd();
12110 return ["comment", "comment"];
12111 } else if (stream.eat("*")) {
12112 state.tokenize = tokenCComment;
12113 return tokenCComment(stream, state);
12114 } else {
12115 return ["operator", "operator"];
12116 }
12117 },
12118 "@": function(stream) {
12119 if (stream.eat("{")) return [null, "interpolation"];
12120 if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/i, false)) return false;
12121 stream.eatWhile(/[\w\\\-]/);
12122 if (stream.match(/^\s*:/, false))
12123 return ["variable-2", "variable-definition"];
12124 return ["variable-2", "variable"];
12125 },
12126 "&": function() {
12127 return ["atom", "atom"];
12128 }
12129 },
12130 name: "css",
12131 helperType: "less"
12132 });
12133
12134 CodeMirror.defineMIME("text/x-gss", {
12135 documentTypes: documentTypes,
12136 mediaTypes: mediaTypes,
12137 mediaFeatures: mediaFeatures,
12138 propertyKeywords: propertyKeywords,
12139 nonStandardPropertyKeywords: nonStandardPropertyKeywords,
12140 fontProperties: fontProperties,
12141 counterDescriptors: counterDescriptors,
12142 colorKeywords: colorKeywords,
12143 valueKeywords: valueKeywords,
12144 supportsAtComponent: true,
12145 tokenHooks: {
12146 "/": function(stream, state) {
12147 if (!stream.eat("*")) return false;
12148 state.tokenize = tokenCComment;
12149 return tokenCComment(stream, state);
12150 }
12151 },
12152 name: "css",
12153 helperType: "gss"
12154 });
12155
12156 });
12157
12158 },{"../../lib/codemirror":14}],16:[function(require,module,exports){
12159 // CodeMirror, copyright (c) by Marijn Haverbeke and others
12160 // Distributed under an MIT license: https://codemirror.net/LICENSE
12161
12162 (function(mod) {
12163 if (typeof exports == "object" && typeof module == "object") // CommonJS
12164 mod(require("../../lib/codemirror"), require("../xml/xml"), require("../javascript/javascript"), require("../css/css"));
12165 else if (typeof define == "function" && define.amd) // AMD
12166 define(["../../lib/codemirror", "../xml/xml", "../javascript/javascript", "../css/css"], mod);
12167 else // Plain browser env
12168 mod(CodeMirror);
12169 })(function(CodeMirror) {
12170 "use strict";
12171
12172 var defaultTags = {
12173 script: [
12174 ["lang", /(javascript|babel)/i, "javascript"],
12175 ["type", /^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^module$|^$/i, "javascript"],
12176 ["type", /./, "text/plain"],
12177 [null, null, "javascript"]
12178 ],
12179 style: [
12180 ["lang", /^css$/i, "css"],
12181 ["type", /^(text\/)?(x-)?(stylesheet|css)$/i, "css"],
12182 ["type", /./, "text/plain"],
12183 [null, null, "css"]
12184 ]
12185 };
12186
12187 function maybeBackup(stream, pat, style) {
12188 var cur = stream.current(), close = cur.search(pat);
12189 if (close > -1) {
12190 stream.backUp(cur.length - close);
12191 } else if (cur.match(/<\/?$/)) {
12192 stream.backUp(cur.length);
12193 if (!stream.match(pat, false)) stream.match(cur);
12194 }
12195 return style;
12196 }
12197
12198 var attrRegexpCache = {};
12199 function getAttrRegexp(attr) {
12200 var regexp = attrRegexpCache[attr];
12201 if (regexp) return regexp;
12202 return attrRegexpCache[attr] = new RegExp("\\s+" + attr + "\\s*=\\s*('|\")?([^'\"]+)('|\")?\\s*");
12203 }
12204
12205 function getAttrValue(text, attr) {
12206 var match = text.match(getAttrRegexp(attr))
12207 return match ? /^\s*(.*?)\s*$/.exec(match[2])[1] : ""
12208 }
12209
12210 function getTagRegexp(tagName, anchored) {
12211 return new RegExp((anchored ? "^" : "") + "<\/\s*" + tagName + "\s*>", "i");
12212 }
12213
12214 function addTags(from, to) {
12215 for (var tag in from) {
12216 var dest = to[tag] || (to[tag] = []);
12217 var source = from[tag];
12218 for (var i = source.length - 1; i >= 0; i--)
12219 dest.unshift(source[i])
12220 }
12221 }
12222
12223 function findMatchingMode(tagInfo, tagText) {
12224 for (var i = 0; i < tagInfo.length; i++) {
12225 var spec = tagInfo[i];
12226 if (!spec[0] || spec[1].test(getAttrValue(tagText, spec[0]))) return spec[2];
12227 }
12228 }
12229
12230 CodeMirror.defineMode("htmlmixed", function (config, parserConfig) {
12231 var htmlMode = CodeMirror.getMode(config, {
12232 name: "xml",
12233 htmlMode: true,
12234 multilineTagIndentFactor: parserConfig.multilineTagIndentFactor,
12235 multilineTagIndentPastTag: parserConfig.multilineTagIndentPastTag
12236 });
12237
12238 var tags = {};
12239 var configTags = parserConfig && parserConfig.tags, configScript = parserConfig && parserConfig.scriptTypes;
12240 addTags(defaultTags, tags);
12241 if (configTags) addTags(configTags, tags);
12242 if (configScript) for (var i = configScript.length - 1; i >= 0; i--)
12243 tags.script.unshift(["type", configScript[i].matches, configScript[i].mode])
12244
12245 function html(stream, state) {
12246 var style = htmlMode.token(stream, state.htmlState), tag = /\btag\b/.test(style), tagName
12247 if (tag && !/[<>\s\/]/.test(stream.current()) &&
12248 (tagName = state.htmlState.tagName && state.htmlState.tagName.toLowerCase()) &&
12249 tags.hasOwnProperty(tagName)) {
12250 state.inTag = tagName + " "
12251 } else if (state.inTag && tag && />$/.test(stream.current())) {
12252 var inTag = /^([\S]+) (.*)/.exec(state.inTag)
12253 state.inTag = null
12254 var modeSpec = stream.current() == ">" && findMatchingMode(tags[inTag[1]], inTag[2])
12255 var mode = CodeMirror.getMode(config, modeSpec)
12256 var endTagA = getTagRegexp(inTag[1], true), endTag = getTagRegexp(inTag[1], false);
12257 state.token = function (stream, state) {
12258 if (stream.match(endTagA, false)) {
12259 state.token = html;
12260 state.localState = state.localMode = null;
12261 return null;
12262 }
12263 return maybeBackup(stream, endTag, state.localMode.token(stream, state.localState));
12264 };
12265 state.localMode = mode;
12266 state.localState = CodeMirror.startState(mode, htmlMode.indent(state.htmlState, "", ""));
12267 } else if (state.inTag) {
12268 state.inTag += stream.current()
12269 if (stream.eol()) state.inTag += " "
12270 }
12271 return style;
12272 };
12273
12274 return {
12275 startState: function () {
12276 var state = CodeMirror.startState(htmlMode);
12277 return {token: html, inTag: null, localMode: null, localState: null, htmlState: state};
12278 },
12279
12280 copyState: function (state) {
12281 var local;
12282 if (state.localState) {
12283 local = CodeMirror.copyState(state.localMode, state.localState);
12284 }
12285 return {token: state.token, inTag: state.inTag,
12286 localMode: state.localMode, localState: local,
12287 htmlState: CodeMirror.copyState(htmlMode, state.htmlState)};
12288 },
12289
12290 token: function (stream, state) {
12291 return state.token(stream, state);
12292 },
12293
12294 indent: function (state, textAfter, line) {
12295 if (!state.localMode || /^\s*<\//.test(textAfter))
12296 return htmlMode.indent(state.htmlState, textAfter, line);
12297 else if (state.localMode.indent)
12298 return state.localMode.indent(state.localState, textAfter, line);
12299 else
12300 return CodeMirror.Pass;
12301 },
12302
12303 innerMode: function (state) {
12304 return {state: state.localState || state.htmlState, mode: state.localMode || htmlMode};
12305 }
12306 };
12307 }, "xml", "javascript", "css");
12308
12309 CodeMirror.defineMIME("text/html", "htmlmixed");
12310 });
12311
12312 },{"../../lib/codemirror":14,"../css/css":15,"../javascript/javascript":17,"../xml/xml":18}],17:[function(require,module,exports){
12313 // CodeMirror, copyright (c) by Marijn Haverbeke and others
12314 // Distributed under an MIT license: https://codemirror.net/LICENSE
12315
12316 (function(mod) {
12317 if (typeof exports == "object" && typeof module == "object") // CommonJS
12318 mod(require("../../lib/codemirror"));
12319 else if (typeof define == "function" && define.amd) // AMD
12320 define(["../../lib/codemirror"], mod);
12321 else // Plain browser env
12322 mod(CodeMirror);
12323 })(function(CodeMirror) {
12324 "use strict";
12325
12326 CodeMirror.defineMode("javascript", function(config, parserConfig) {
12327 var indentUnit = config.indentUnit;
12328 var statementIndent = parserConfig.statementIndent;
12329 var jsonldMode = parserConfig.jsonld;
12330 var jsonMode = parserConfig.json || jsonldMode;
12331 var isTS = parserConfig.typescript;
12332 var wordRE = parserConfig.wordCharacters || /[\w$\xa1-\uffff]/;
12333
12334 // Tokenizer
12335
12336 var keywords = function(){
12337 function kw(type) {return {type: type, style: "keyword"};}
12338 var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"), D = kw("keyword d");
12339 var operator = kw("operator"), atom = {type: "atom", style: "atom"};
12340
12341 return {
12342 "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
12343 "return": D, "break": D, "continue": D, "new": kw("new"), "delete": C, "void": C, "throw": C,
12344 "debugger": kw("debugger"), "var": kw("var"), "const": kw("var"), "let": kw("var"),
12345 "function": kw("function"), "catch": kw("catch"),
12346 "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
12347 "in": operator, "typeof": operator, "instanceof": operator,
12348 "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom,
12349 "this": kw("this"), "class": kw("class"), "super": kw("atom"),
12350 "yield": C, "export": kw("export"), "import": kw("import"), "extends": C,
12351 "await": C
12352 };
12353 }();
12354
12355 var isOperatorChar = /[+\-*&%=<>!?|~^@]/;
12356 var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/;
12357
12358 function readRegexp(stream) {
12359 var escaped = false, next, inSet = false;
12360 while ((next = stream.next()) != null) {
12361 if (!escaped) {
12362 if (next == "/" && !inSet) return;
12363 if (next == "[") inSet = true;
12364 else if (inSet && next == "]") inSet = false;
12365 }
12366 escaped = !escaped && next == "\\";
12367 }
12368 }
12369
12370 // Used as scratch variables to communicate multiple values without
12371 // consing up tons of objects.
12372 var type, content;
12373 function ret(tp, style, cont) {
12374 type = tp; content = cont;
12375 return style;
12376 }
12377 function tokenBase(stream, state) {
12378 var ch = stream.next();
12379 if (ch == '"' || ch == "'") {
12380 state.tokenize = tokenString(ch);
12381 return state.tokenize(stream, state);
12382 } else if (ch == "." && stream.match(/^\d+(?:[eE][+\-]?\d+)?/)) {
12383 return ret("number", "number");
12384 } else if (ch == "." && stream.match("..")) {
12385 return ret("spread", "meta");
12386 } else if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
12387 return ret(ch);
12388 } else if (ch == "=" && stream.eat(">")) {
12389 return ret("=>", "operator");
12390 } else if (ch == "0" && stream.match(/^(?:x[\da-f]+|o[0-7]+|b[01]+)n?/i)) {
12391 return ret("number", "number");
12392 } else if (/\d/.test(ch)) {
12393 stream.match(/^\d*(?:n|(?:\.\d*)?(?:[eE][+\-]?\d+)?)?/);
12394 return ret("number", "number");
12395 } else if (ch == "/") {
12396 if (stream.eat("*")) {
12397 state.tokenize = tokenComment;
12398 return tokenComment(stream, state);
12399 } else if (stream.eat("/")) {
12400 stream.skipToEnd();
12401 return ret("comment", "comment");
12402 } else if (expressionAllowed(stream, state, 1)) {
12403 readRegexp(stream);
12404 stream.match(/^\b(([gimyus])(?![gimyus]*\2))+\b/);
12405 return ret("regexp", "string-2");
12406 } else {
12407 stream.eat("=");
12408 return ret("operator", "operator", stream.current());
12409 }
12410 } else if (ch == "`") {
12411 state.tokenize = tokenQuasi;
12412 return tokenQuasi(stream, state);
12413 } else if (ch == "#") {
12414 stream.skipToEnd();
12415 return ret("error", "error");
12416 } else if (isOperatorChar.test(ch)) {
12417 if (ch != ">" || !state.lexical || state.lexical.type != ">") {
12418 if (stream.eat("=")) {
12419 if (ch == "!" || ch == "=") stream.eat("=")
12420 } else if (/[<>*+\-]/.test(ch)) {
12421 stream.eat(ch)
12422 if (ch == ">") stream.eat(ch)
12423 }
12424 }
12425 return ret("operator", "operator", stream.current());
12426 } else if (wordRE.test(ch)) {
12427 stream.eatWhile(wordRE);
12428 var word = stream.current()
12429 if (state.lastType != ".") {
12430 if (keywords.propertyIsEnumerable(word)) {
12431 var kw = keywords[word]
12432 return ret(kw.type, kw.style, word)
12433 }
12434 if (word == "async" && stream.match(/^(\s|\/\*.*?\*\/)*[\[\(\w]/, false))
12435 return ret("async", "keyword", word)
12436 }
12437 return ret("variable", "variable", word)
12438 }
12439 }
12440
12441 function tokenString(quote) {
12442 return function(stream, state) {
12443 var escaped = false, next;
12444 if (jsonldMode && stream.peek() == "@" && stream.match(isJsonldKeyword)){
12445 state.tokenize = tokenBase;
12446 return ret("jsonld-keyword", "meta");
12447 }
12448 while ((next = stream.next()) != null) {
12449 if (next == quote && !escaped) break;
12450 escaped = !escaped && next == "\\";
12451 }
12452 if (!escaped) state.tokenize = tokenBase;
12453 return ret("string", "string");
12454 };
12455 }
12456
12457 function tokenComment(stream, state) {
12458 var maybeEnd = false, ch;
12459 while (ch = stream.next()) {
12460 if (ch == "/" && maybeEnd) {
12461 state.tokenize = tokenBase;
12462 break;
12463 }
12464 maybeEnd = (ch == "*");
12465 }
12466 return ret("comment", "comment");
12467 }
12468
12469 function tokenQuasi(stream, state) {
12470 var escaped = false, next;
12471 while ((next = stream.next()) != null) {
12472 if (!escaped && (next == "`" || next == "$" && stream.eat("{"))) {
12473 state.tokenize = tokenBase;
12474 break;
12475 }
12476 escaped = !escaped && next == "\\";
12477 }
12478 return ret("quasi", "string-2", stream.current());
12479 }
12480
12481 var brackets = "([{}])";
12482 // This is a crude lookahead trick to try and notice that we're
12483 // parsing the argument patterns for a fat-arrow function before we
12484 // actually hit the arrow token. It only works if the arrow is on
12485 // the same line as the arguments and there's no strange noise
12486 // (comments) in between. Fallback is to only notice when we hit the
12487 // arrow, and not declare the arguments as locals for the arrow
12488 // body.
12489 function findFatArrow(stream, state) {
12490 if (state.fatArrowAt) state.fatArrowAt = null;
12491 var arrow = stream.string.indexOf("=>", stream.start);
12492 if (arrow < 0) return;
12493
12494 if (isTS) { // Try to skip TypeScript return type declarations after the arguments
12495 var m = /:\s*(?:\w+(?:<[^>]*>|\[\])?|\{[^}]*\})\s*$/.exec(stream.string.slice(stream.start, arrow))
12496 if (m) arrow = m.index
12497 }
12498
12499 var depth = 0, sawSomething = false;
12500 for (var pos = arrow - 1; pos >= 0; --pos) {
12501 var ch = stream.string.charAt(pos);
12502 var bracket = brackets.indexOf(ch);
12503 if (bracket >= 0 && bracket < 3) {
12504 if (!depth) { ++pos; break; }
12505 if (--depth == 0) { if (ch == "(") sawSomething = true; break; }
12506 } else if (bracket >= 3 && bracket < 6) {
12507 ++depth;
12508 } else if (wordRE.test(ch)) {
12509 sawSomething = true;
12510 } else if (/["'\/]/.test(ch)) {
12511 return;
12512 } else if (sawSomething && !depth) {
12513 ++pos;
12514 break;
12515 }
12516 }
12517 if (sawSomething && !depth) state.fatArrowAt = pos;
12518 }
12519
12520 // Parser
12521
12522 var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true, "jsonld-keyword": true};
12523
12524 function JSLexical(indented, column, type, align, prev, info) {
12525 this.indented = indented;
12526 this.column = column;
12527 this.type = type;
12528 this.prev = prev;
12529 this.info = info;
12530 if (align != null) this.align = align;
12531 }
12532
12533 function inScope(state, varname) {
12534 for (var v = state.localVars; v; v = v.next)
12535 if (v.name == varname) return true;
12536 for (var cx = state.context; cx; cx = cx.prev) {
12537 for (var v = cx.vars; v; v = v.next)
12538 if (v.name == varname) return true;
12539 }
12540 }
12541
12542 function parseJS(state, style, type, content, stream) {
12543 var cc = state.cc;
12544 // Communicate our context to the combinators.
12545 // (Less wasteful than consing up a hundred closures on every call.)
12546 cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; cx.style = style;
12547
12548 if (!state.lexical.hasOwnProperty("align"))
12549 state.lexical.align = true;
12550
12551 while(true) {
12552 var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;
12553 if (combinator(type, content)) {
12554 while(cc.length && cc[cc.length - 1].lex)
12555 cc.pop()();
12556 if (cx.marked) return cx.marked;
12557 if (type == "variable" && inScope(state, content)) return "variable-2";
12558 return style;
12559 }
12560 }
12561 }
12562
12563 // Combinator utils
12564
12565 var cx = {state: null, column: null, marked: null, cc: null};
12566 function pass() {
12567 for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
12568 }
12569 function cont() {
12570 pass.apply(null, arguments);
12571 return true;
12572 }
12573 function inList(name, list) {
12574 for (var v = list; v; v = v.next) if (v.name == name) return true
12575 return false;
12576 }
12577 function register(varname) {
12578 var state = cx.state;
12579 cx.marked = "def";
12580 if (state.context) {
12581 if (state.lexical.info == "var" && state.context && state.context.block) {
12582 // FIXME function decls are also not block scoped
12583 var newContext = registerVarScoped(varname, state.context)
12584 if (newContext != null) {
12585 state.context = newContext
12586 return
12587 }
12588 } else if (!inList(varname, state.localVars)) {
12589 state.localVars = new Var(varname, state.localVars)
12590 return
12591 }
12592 }
12593 // Fall through means this is global
12594 if (parserConfig.globalVars && !inList(varname, state.globalVars))
12595 state.globalVars = new Var(varname, state.globalVars)
12596 }
12597 function registerVarScoped(varname, context) {
12598 if (!context) {
12599 return null
12600 } else if (context.block) {
12601 var inner = registerVarScoped(varname, context.prev)
12602 if (!inner) return null
12603 if (inner == context.prev) return context
12604 return new Context(inner, context.vars, true)
12605 } else if (inList(varname, context.vars)) {
12606 return context
12607 } else {
12608 return new Context(context.prev, new Var(varname, context.vars), false)
12609 }
12610 }
12611
12612 function isModifier(name) {
12613 return name == "public" || name == "private" || name == "protected" || name == "abstract" || name == "readonly"
12614 }
12615
12616 // Combinators
12617
12618 function Context(prev, vars, block) { this.prev = prev; this.vars = vars; this.block = block }
12619 function Var(name, next) { this.name = name; this.next = next }
12620
12621 var defaultVars = new Var("this", new Var("arguments", null))
12622 function pushcontext() {
12623 cx.state.context = new Context(cx.state.context, cx.state.localVars, false)
12624 cx.state.localVars = defaultVars
12625 }
12626 function pushblockcontext() {
12627 cx.state.context = new Context(cx.state.context, cx.state.localVars, true)
12628 cx.state.localVars = null
12629 }
12630 function popcontext() {
12631 cx.state.localVars = cx.state.context.vars
12632 cx.state.context = cx.state.context.prev
12633 }
12634 popcontext.lex = true
12635 function pushlex(type, info) {
12636 var result = function() {
12637 var state = cx.state, indent = state.indented;
12638 if (state.lexical.type == "stat") indent = state.lexical.indented;
12639 else for (var outer = state.lexical; outer && outer.type == ")" && outer.align; outer = outer.prev)
12640 indent = outer.indented;
12641 state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info);
12642 };
12643 result.lex = true;
12644 return result;
12645 }
12646 function poplex() {
12647 var state = cx.state;
12648 if (state.lexical.prev) {
12649 if (state.lexical.type == ")")
12650 state.indented = state.lexical.indented;
12651 state.lexical = state.lexical.prev;
12652 }
12653 }
12654 poplex.lex = true;
12655
12656 function expect(wanted) {
12657 function exp(type) {
12658 if (type == wanted) return cont();
12659 else if (wanted == ";" || type == "}" || type == ")" || type == "]") return pass();
12660 else return cont(exp);
12661 };
12662 return exp;
12663 }
12664
12665 function statement(type, value) {
12666 if (type == "var") return cont(pushlex("vardef", value), vardef, expect(";"), poplex);
12667 if (type == "keyword a") return cont(pushlex("form"), parenExpr, statement, poplex);
12668 if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
12669 if (type == "keyword d") return cx.stream.match(/^\s*$/, false) ? cont() : cont(pushlex("stat"), maybeexpression, expect(";"), poplex);
12670 if (type == "debugger") return cont(expect(";"));
12671 if (type == "{") return cont(pushlex("}"), pushblockcontext, block, poplex, popcontext);
12672 if (type == ";") return cont();
12673 if (type == "if") {
12674 if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex)
12675 cx.state.cc.pop()();
12676 return cont(pushlex("form"), parenExpr, statement, poplex, maybeelse);
12677 }
12678 if (type == "function") return cont(functiondef);
12679 if (type == "for") return cont(pushlex("form"), forspec, statement, poplex);
12680 if (type == "class" || (isTS && value == "interface")) {
12681 cx.marked = "keyword"
12682 return cont(pushlex("form", type == "class" ? type : value), className, poplex)
12683 }
12684 if (type == "variable") {
12685 if (isTS && value == "declare") {
12686 cx.marked = "keyword"
12687 return cont(statement)
12688 } else if (isTS && (value == "module" || value == "enum" || value == "type") && cx.stream.match(/^\s*\w/, false)) {
12689 cx.marked = "keyword"
12690 if (value == "enum") return cont(enumdef);
12691 else if (value == "type") return cont(typename, expect("operator"), typeexpr, expect(";"));
12692 else return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex)
12693 } else if (isTS && value == "namespace") {
12694 cx.marked = "keyword"
12695 return cont(pushlex("form"), expression, statement, poplex)
12696 } else if (isTS && value == "abstract") {
12697 cx.marked = "keyword"
12698 return cont(statement)
12699 } else {
12700 return cont(pushlex("stat"), maybelabel);
12701 }
12702 }
12703 if (type == "switch") return cont(pushlex("form"), parenExpr, expect("{"), pushlex("}", "switch"), pushblockcontext,
12704 block, poplex, poplex, popcontext);
12705 if (type == "case") return cont(expression, expect(":"));
12706 if (type == "default") return cont(expect(":"));
12707 if (type == "catch") return cont(pushlex("form"), pushcontext, maybeCatchBinding, statement, poplex, popcontext);
12708 if (type == "export") return cont(pushlex("stat"), afterExport, poplex);
12709 if (type == "import") return cont(pushlex("stat"), afterImport, poplex);
12710 if (type == "async") return cont(statement)
12711 if (value == "@") return cont(expression, statement)
12712 return pass(pushlex("stat"), expression, expect(";"), poplex);
12713 }
12714 function maybeCatchBinding(type) {
12715 if (type == "(") return cont(funarg, expect(")"))
12716 }
12717 function expression(type, value) {
12718 return expressionInner(type, value, false);
12719 }
12720 function expressionNoComma(type, value) {
12721 return expressionInner(type, value, true);
12722 }
12723 function parenExpr(type) {
12724 if (type != "(") return pass()
12725 return cont(pushlex(")"), expression, expect(")"), poplex)
12726 }
12727 function expressionInner(type, value, noComma) {
12728 if (cx.state.fatArrowAt == cx.stream.start) {
12729 var body = noComma ? arrowBodyNoComma : arrowBody;
12730 if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, expect("=>"), body, popcontext);
12731 else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext);
12732 }
12733
12734 var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
12735 if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
12736 if (type == "function") return cont(functiondef, maybeop);
12737 if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword"; return cont(pushlex("form"), classExpression, poplex); }
12738 if (type == "keyword c" || type == "async") return cont(noComma ? expressionNoComma : expression);
12739 if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop);
12740 if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression);
12741 if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop);
12742 if (type == "{") return contCommasep(objprop, "}", null, maybeop);
12743 if (type == "quasi") return pass(quasi, maybeop);
12744 if (type == "new") return cont(maybeTarget(noComma));
12745 if (type == "import") return cont(expression);
12746 return cont();
12747 }
12748 function maybeexpression(type) {
12749 if (type.match(/[;\}\)\],]/)) return pass();
12750 return pass(expression);
12751 }
12752
12753 function maybeoperatorComma(type, value) {
12754 if (type == ",") return cont(expression);
12755 return maybeoperatorNoComma(type, value, false);
12756 }
12757 function maybeoperatorNoComma(type, value, noComma) {
12758 var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma;
12759 var expr = noComma == false ? expression : expressionNoComma;
12760 if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext);
12761 if (type == "operator") {
12762 if (/\+\+|--/.test(value) || isTS && value == "!") return cont(me);
12763 if (isTS && value == "<" && cx.stream.match(/^([^>]|<.*?>)*>\s*\(/, false))
12764 return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, me);
12765 if (value == "?") return cont(expression, expect(":"), expr);
12766 return cont(expr);
12767 }
12768 if (type == "quasi") { return pass(quasi, me); }
12769 if (type == ";") return;
12770 if (type == "(") return contCommasep(expressionNoComma, ")", "call", me);
12771 if (type == ".") return cont(property, me);
12772 if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me);
12773 if (isTS && value == "as") { cx.marked = "keyword"; return cont(typeexpr, me) }
12774 if (type == "regexp") {
12775 cx.state.lastType = cx.marked = "operator"
12776 cx.stream.backUp(cx.stream.pos - cx.stream.start - 1)
12777 return cont(expr)
12778 }
12779 }
12780 function quasi(type, value) {
12781 if (type != "quasi") return pass();
12782 if (value.slice(value.length - 2) != "${") return cont(quasi);
12783 return cont(expression, continueQuasi);
12784 }
12785 function continueQuasi(type) {
12786 if (type == "}") {
12787 cx.marked = "string-2";
12788 cx.state.tokenize = tokenQuasi;
12789 return cont(quasi);
12790 }
12791 }
12792 function arrowBody(type) {
12793 findFatArrow(cx.stream, cx.state);
12794 return pass(type == "{" ? statement : expression);
12795 }
12796 function arrowBodyNoComma(type) {
12797 findFatArrow(cx.stream, cx.state);
12798 return pass(type == "{" ? statement : expressionNoComma);
12799 }
12800 function maybeTarget(noComma) {
12801 return function(type) {
12802 if (type == ".") return cont(noComma ? targetNoComma : target);
12803 else if (type == "variable" && isTS) return cont(maybeTypeArgs, noComma ? maybeoperatorNoComma : maybeoperatorComma)
12804 else return pass(noComma ? expressionNoComma : expression);
12805 };
12806 }
12807 function target(_, value) {
12808 if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorComma); }
12809 }
12810 function targetNoComma(_, value) {
12811 if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorNoComma); }
12812 }
12813 function maybelabel(type) {
12814 if (type == ":") return cont(poplex, statement);
12815 return pass(maybeoperatorComma, expect(";"), poplex);
12816 }
12817 function property(type) {
12818 if (type == "variable") {cx.marked = "property"; return cont();}
12819 }
12820 function objprop(type, value) {
12821 if (type == "async") {
12822 cx.marked = "property";
12823 return cont(objprop);
12824 } else if (type == "variable" || cx.style == "keyword") {
12825 cx.marked = "property";
12826 if (value == "get" || value == "set") return cont(getterSetter);
12827 var m // Work around fat-arrow-detection complication for detecting typescript typed arrow params
12828 if (isTS && cx.state.fatArrowAt == cx.stream.start && (m = cx.stream.match(/^\s*:\s*/, false)))
12829 cx.state.fatArrowAt = cx.stream.pos + m[0].length
12830 return cont(afterprop);
12831 } else if (type == "number" || type == "string") {
12832 cx.marked = jsonldMode ? "property" : (cx.style + " property");
12833 return cont(afterprop);
12834 } else if (type == "jsonld-keyword") {
12835 return cont(afterprop);
12836 } else if (isTS && isModifier(value)) {
12837 cx.marked = "keyword"
12838 return cont(objprop)
12839 } else if (type == "[") {
12840 return cont(expression, maybetype, expect("]"), afterprop);
12841 } else if (type == "spread") {
12842 return cont(expressionNoComma, afterprop);
12843 } else if (value == "*") {
12844 cx.marked = "keyword";
12845 return cont(objprop);
12846 } else if (type == ":") {
12847 return pass(afterprop)
12848 }
12849 }
12850 function getterSetter(type) {
12851 if (type != "variable") return pass(afterprop);
12852 cx.marked = "property";
12853 return cont(functiondef);
12854 }
12855 function afterprop(type) {
12856 if (type == ":") return cont(expressionNoComma);
12857 if (type == "(") return pass(functiondef);
12858 }
12859 function commasep(what, end, sep) {
12860 function proceed(type, value) {
12861 if (sep ? sep.indexOf(type) > -1 : type == ",") {
12862 var lex = cx.state.lexical;
12863 if (lex.info == "call") lex.pos = (lex.pos || 0) + 1;
12864 return cont(function(type, value) {
12865 if (type == end || value == end) return pass()
12866 return pass(what)
12867 }, proceed);
12868 }
12869 if (type == end || value == end) return cont();
12870 if (sep && sep.indexOf(";") > -1) return pass(what)
12871 return cont(expect(end));
12872 }
12873 return function(type, value) {
12874 if (type == end || value == end) return cont();
12875 return pass(what, proceed);
12876 };
12877 }
12878 function contCommasep(what, end, info) {
12879 for (var i = 3; i < arguments.length; i++)
12880 cx.cc.push(arguments[i]);
12881 return cont(pushlex(end, info), commasep(what, end), poplex);
12882 }
12883 function block(type) {
12884 if (type == "}") return cont();
12885 return pass(statement, block);
12886 }
12887 function maybetype(type, value) {
12888 if (isTS) {
12889 if (type == ":" || value == "in") return cont(typeexpr);
12890 if (value == "?") return cont(maybetype);
12891 }
12892 }
12893 function mayberettype(type) {
12894 if (isTS && type == ":") {
12895 if (cx.stream.match(/^\s*\w+\s+is\b/, false)) return cont(expression, isKW, typeexpr)
12896 else return cont(typeexpr)
12897 }
12898 }
12899 function isKW(_, value) {
12900 if (value == "is") {
12901 cx.marked = "keyword"
12902 return cont()
12903 }
12904 }
12905 function typeexpr(type, value) {
12906 if (value == "keyof" || value == "typeof" || value == "infer") {
12907 cx.marked = "keyword"
12908 return cont(value == "typeof" ? expressionNoComma : typeexpr)
12909 }
12910 if (type == "variable" || value == "void") {
12911 cx.marked = "type"
12912 return cont(afterType)
12913 }
12914 if (value == "|" || value == "&") return cont(typeexpr)
12915 if (type == "string" || type == "number" || type == "atom") return cont(afterType);
12916 if (type == "[") return cont(pushlex("]"), commasep(typeexpr, "]", ","), poplex, afterType)
12917 if (type == "{") return cont(pushlex("}"), commasep(typeprop, "}", ",;"), poplex, afterType)
12918 if (type == "(") return cont(commasep(typearg, ")"), maybeReturnType, afterType)
12919 if (type == "<") return cont(commasep(typeexpr, ">"), typeexpr)
12920 }
12921 function maybeReturnType(type) {
12922 if (type == "=>") return cont(typeexpr)
12923 }
12924 function typeprop(type, value) {
12925 if (type == "variable" || cx.style == "keyword") {
12926 cx.marked = "property"
12927 return cont(typeprop)
12928 } else if (value == "?" || type == "number" || type == "string") {
12929 return cont(typeprop)
12930 } else if (type == ":") {
12931 return cont(typeexpr)
12932 } else if (type == "[") {
12933 return cont(expect("variable"), maybetype, expect("]"), typeprop)
12934 } else if (type == "(") {
12935 return pass(functiondecl, typeprop)
12936 }
12937 }
12938 function typearg(type, value) {
12939 if (type == "variable" && cx.stream.match(/^\s*[?:]/, false) || value == "?") return cont(typearg)
12940 if (type == ":") return cont(typeexpr)
12941 if (type == "spread") return cont(typearg)
12942 return pass(typeexpr)
12943 }
12944 function afterType(type, value) {
12945 if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
12946 if (value == "|" || type == "." || value == "&") return cont(typeexpr)
12947 if (type == "[") return cont(typeexpr, expect("]"), afterType)
12948 if (value == "extends" || value == "implements") { cx.marked = "keyword"; return cont(typeexpr) }
12949 if (value == "?") return cont(typeexpr, expect(":"), typeexpr)
12950 }
12951 function maybeTypeArgs(_, value) {
12952 if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
12953 }
12954 function typeparam() {
12955 return pass(typeexpr, maybeTypeDefault)
12956 }
12957 function maybeTypeDefault(_, value) {
12958 if (value == "=") return cont(typeexpr)
12959 }
12960 function vardef(_, value) {
12961 if (value == "enum") {cx.marked = "keyword"; return cont(enumdef)}
12962 return pass(pattern, maybetype, maybeAssign, vardefCont);
12963 }
12964 function pattern(type, value) {
12965 if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(pattern) }
12966 if (type == "variable") { register(value); return cont(); }
12967 if (type == "spread") return cont(pattern);
12968 if (type == "[") return contCommasep(eltpattern, "]");
12969 if (type == "{") return contCommasep(proppattern, "}");
12970 }
12971 function proppattern(type, value) {
12972 if (type == "variable" && !cx.stream.match(/^\s*:/, false)) {
12973 register(value);
12974 return cont(maybeAssign);
12975 }
12976 if (type == "variable") cx.marked = "property";
12977 if (type == "spread") return cont(pattern);
12978 if (type == "}") return pass();
12979 if (type == "[") return cont(expression, expect(']'), expect(':'), proppattern);
12980 return cont(expect(":"), pattern, maybeAssign);
12981 }
12982 function eltpattern() {
12983 return pass(pattern, maybeAssign)
12984 }
12985 function maybeAssign(_type, value) {
12986 if (value == "=") return cont(expressionNoComma);
12987 }
12988 function vardefCont(type) {
12989 if (type == ",") return cont(vardef);
12990 }
12991 function maybeelse(type, value) {
12992 if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex);
12993 }
12994 function forspec(type, value) {
12995 if (value == "await") return cont(forspec);
12996 if (type == "(") return cont(pushlex(")"), forspec1, poplex);
12997 }
12998 function forspec1(type) {
12999 if (type == "var") return cont(vardef, forspec2);
13000 if (type == "variable") return cont(forspec2);
13001 return pass(forspec2)
13002 }
13003 function forspec2(type, value) {
13004 if (type == ")") return cont()
13005 if (type == ";") return cont(forspec2)
13006 if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression, forspec2) }
13007 return pass(expression, forspec2)
13008 }
13009 function functiondef(type, value) {
13010 if (value == "*") {cx.marked = "keyword"; return cont(functiondef);}
13011 if (type == "variable") {register(value); return cont(functiondef);}
13012 if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, statement, popcontext);
13013 if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondef)
13014 }
13015 function functiondecl(type, value) {
13016 if (value == "*") {cx.marked = "keyword"; return cont(functiondecl);}
13017 if (type == "variable") {register(value); return cont(functiondecl);}
13018 if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, popcontext);
13019 if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondecl)
13020 }
13021 function typename(type, value) {
13022 if (type == "keyword" || type == "variable") {
13023 cx.marked = "type"
13024 return cont(typename)
13025 } else if (value == "<") {
13026 return cont(pushlex(">"), commasep(typeparam, ">"), poplex)
13027 }
13028 }
13029 function funarg(type, value) {
13030 if (value == "@") cont(expression, funarg)
13031 if (type == "spread") return cont(funarg);
13032 if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(funarg); }
13033 if (isTS && type == "this") return cont(maybetype, maybeAssign)
13034 return pass(pattern, maybetype, maybeAssign);
13035 }
13036 function classExpression(type, value) {
13037 // Class expressions may have an optional name.
13038 if (type == "variable") return className(type, value);
13039 return classNameAfter(type, value);
13040 }
13041 function className(type, value) {
13042 if (type == "variable") {register(value); return cont(classNameAfter);}
13043 }
13044 function classNameAfter(type, value) {
13045 if (value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, classNameAfter)
13046 if (value == "extends" || value == "implements" || (isTS && type == ",")) {
13047 if (value == "implements") cx.marked = "keyword";
13048 return cont(isTS ? typeexpr : expression, classNameAfter);
13049 }
13050 if (type == "{") return cont(pushlex("}"), classBody, poplex);
13051 }
13052 function classBody(type, value) {
13053 if (type == "async" ||
13054 (type == "variable" &&
13055 (value == "static" || value == "get" || value == "set" || (isTS && isModifier(value))) &&
13056 cx.stream.match(/^\s+[\w$\xa1-\uffff]/, false))) {
13057 cx.marked = "keyword";
13058 return cont(classBody);
13059 }
13060 if (type == "variable" || cx.style == "keyword") {
13061 cx.marked = "property";
13062 return cont(isTS ? classfield : functiondef, classBody);
13063 }
13064 if (type == "number" || type == "string") return cont(isTS ? classfield : functiondef, classBody);
13065 if (type == "[")
13066 return cont(expression, maybetype, expect("]"), isTS ? classfield : functiondef, classBody)
13067 if (value == "*") {
13068 cx.marked = "keyword";
13069 return cont(classBody);
13070 }
13071 if (isTS && type == "(") return pass(functiondecl, classBody)
13072 if (type == ";" || type == ",") return cont(classBody);
13073 if (type == "}") return cont();
13074 if (value == "@") return cont(expression, classBody)
13075 }
13076 function classfield(type, value) {
13077 if (value == "?") return cont(classfield)
13078 if (type == ":") return cont(typeexpr, maybeAssign)
13079 if (value == "=") return cont(expressionNoComma)
13080 var context = cx.state.lexical.prev, isInterface = context && context.info == "interface"
13081 return pass(isInterface ? functiondecl : functiondef)
13082 }
13083 function afterExport(type, value) {
13084 if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); }
13085 if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); }
13086 if (type == "{") return cont(commasep(exportField, "}"), maybeFrom, expect(";"));
13087 return pass(statement);
13088 }
13089 function exportField(type, value) {
13090 if (value == "as") { cx.marked = "keyword"; return cont(expect("variable")); }
13091 if (type == "variable") return pass(expressionNoComma, exportField);
13092 }
13093 function afterImport(type) {
13094 if (type == "string") return cont();
13095 if (type == "(") return pass(expression);
13096 return pass(importSpec, maybeMoreImports, maybeFrom);
13097 }
13098 function importSpec(type, value) {
13099 if (type == "{") return contCommasep(importSpec, "}");
13100 if (type == "variable") register(value);
13101 if (value == "*") cx.marked = "keyword";
13102 return cont(maybeAs);
13103 }
13104 function maybeMoreImports(type) {
13105 if (type == ",") return cont(importSpec, maybeMoreImports)
13106 }
13107 function maybeAs(_type, value) {
13108 if (value == "as") { cx.marked = "keyword"; return cont(importSpec); }
13109 }
13110 function maybeFrom(_type, value) {
13111 if (value == "from") { cx.marked = "keyword"; return cont(expression); }
13112 }
13113 function arrayLiteral(type) {
13114 if (type == "]") return cont();
13115 return pass(commasep(expressionNoComma, "]"));
13116 }
13117 function enumdef() {
13118 return pass(pushlex("form"), pattern, expect("{"), pushlex("}"), commasep(enummember, "}"), poplex, poplex)
13119 }
13120 function enummember() {
13121 return pass(pattern, maybeAssign);
13122 }
13123
13124 function isContinuedStatement(state, textAfter) {
13125 return state.lastType == "operator" || state.lastType == "," ||
13126 isOperatorChar.test(textAfter.charAt(0)) ||
13127 /[,.]/.test(textAfter.charAt(0));
13128 }
13129
13130 function expressionAllowed(stream, state, backUp) {
13131 return state.tokenize == tokenBase &&
13132 /^(?:operator|sof|keyword [bcd]|case|new|export|default|spread|[\[{}\(,;:]|=>)$/.test(state.lastType) ||
13133 (state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0))))
13134 }
13135
13136 // Interface
13137
13138 return {
13139 startState: function(basecolumn) {
13140 var state = {
13141 tokenize: tokenBase,
13142 lastType: "sof",
13143 cc: [],
13144 lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
13145 localVars: parserConfig.localVars,
13146 context: parserConfig.localVars && new Context(null, null, false),
13147 indented: basecolumn || 0
13148 };
13149 if (parserConfig.globalVars && typeof parserConfig.globalVars == "object")
13150 state.globalVars = parserConfig.globalVars;
13151 return state;
13152 },
13153
13154 token: function(stream, state) {
13155 if (stream.sol()) {
13156 if (!state.lexical.hasOwnProperty("align"))
13157 state.lexical.align = false;
13158 state.indented = stream.indentation();
13159 findFatArrow(stream, state);
13160 }
13161 if (state.tokenize != tokenComment && stream.eatSpace()) return null;
13162 var style = state.tokenize(stream, state);
13163 if (type == "comment") return style;
13164 state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type;
13165 return parseJS(state, style, type, content, stream);
13166 },
13167
13168 indent: function(state, textAfter) {
13169 if (state.tokenize == tokenComment) return CodeMirror.Pass;
13170 if (state.tokenize != tokenBase) return 0;
13171 var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical, top
13172 // Kludge to prevent 'maybelse' from blocking lexical scope pops
13173 if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) {
13174 var c = state.cc[i];
13175 if (c == poplex) lexical = lexical.prev;
13176 else if (c != maybeelse) break;
13177 }
13178 while ((lexical.type == "stat" || lexical.type == "form") &&
13179 (firstChar == "}" || ((top = state.cc[state.cc.length - 1]) &&
13180 (top == maybeoperatorComma || top == maybeoperatorNoComma) &&
13181 !/^[,\.=+\-*:?[\(]/.test(textAfter))))
13182 lexical = lexical.prev;
13183 if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat")
13184 lexical = lexical.prev;
13185 var type = lexical.type, closing = firstChar == type;
13186
13187 if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info.length + 1 : 0);
13188 else if (type == "form" && firstChar == "{") return lexical.indented;
13189 else if (type == "form") return lexical.indented + indentUnit;
13190 else if (type == "stat")
13191 return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0);
13192 else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false)
13193 return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
13194 else if (lexical.align) return lexical.column + (closing ? 0 : 1);
13195 else return lexical.indented + (closing ? 0 : indentUnit);
13196 },
13197
13198 electricInput: /^\s*(?:case .*?:|default:|\{|\})$/,
13199 blockCommentStart: jsonMode ? null : "/*",
13200 blockCommentEnd: jsonMode ? null : "*/",
13201 blockCommentContinue: jsonMode ? null : " * ",
13202 lineComment: jsonMode ? null : "//",
13203 fold: "brace",
13204 closeBrackets: "()[]{}''\"\"``",
13205
13206 helperType: jsonMode ? "json" : "javascript",
13207 jsonldMode: jsonldMode,
13208 jsonMode: jsonMode,
13209
13210 expressionAllowed: expressionAllowed,
13211
13212 skipExpression: function(state) {
13213 var top = state.cc[state.cc.length - 1]
13214 if (top == expression || top == expressionNoComma) state.cc.pop()
13215 }
13216 };
13217 });
13218
13219 CodeMirror.registerHelper("wordChars", "javascript", /[\w$]/);
13220
13221 CodeMirror.defineMIME("text/javascript", "javascript");
13222 CodeMirror.defineMIME("text/ecmascript", "javascript");
13223 CodeMirror.defineMIME("application/javascript", "javascript");
13224 CodeMirror.defineMIME("application/x-javascript", "javascript");
13225 CodeMirror.defineMIME("application/ecmascript", "javascript");
13226 CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
13227 CodeMirror.defineMIME("application/x-json", {name: "javascript", json: true});
13228 CodeMirror.defineMIME("application/ld+json", {name: "javascript", jsonld: true});
13229 CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true });
13230 CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true });
13231
13232 });
13233
13234 },{"../../lib/codemirror":14}],18:[function(require,module,exports){
13235 // CodeMirror, copyright (c) by Marijn Haverbeke and others
13236 // Distributed under an MIT license: https://codemirror.net/LICENSE
13237
13238 (function(mod) {
13239 if (typeof exports == "object" && typeof module == "object") // CommonJS
13240 mod(require("../../lib/codemirror"));
13241 else if (typeof define == "function" && define.amd) // AMD
13242 define(["../../lib/codemirror"], mod);
13243 else // Plain browser env
13244 mod(CodeMirror);
13245 })(function(CodeMirror) {
13246 "use strict";
13247
13248 var htmlConfig = {
13249 autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true,
13250 'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true,
13251 'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true,
13252 'track': true, 'wbr': true, 'menuitem': true},
13253 implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true,
13254 'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true,
13255 'th': true, 'tr': true},
13256 contextGrabbers: {
13257 'dd': {'dd': true, 'dt': true},
13258 'dt': {'dd': true, 'dt': true},
13259 'li': {'li': true},
13260 'option': {'option': true, 'optgroup': true},
13261 'optgroup': {'optgroup': true},
13262 'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true,
13263 'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true,
13264 'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true,
13265 'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true,
13266 'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true},
13267 'rp': {'rp': true, 'rt': true},
13268 'rt': {'rp': true, 'rt': true},
13269 'tbody': {'tbody': true, 'tfoot': true},
13270 'td': {'td': true, 'th': true},
13271 'tfoot': {'tbody': true},
13272 'th': {'td': true, 'th': true},
13273 'thead': {'tbody': true, 'tfoot': true},
13274 'tr': {'tr': true}
13275 },
13276 doNotIndent: {"pre": true},
13277 allowUnquoted: true,
13278 allowMissing: true,
13279 caseFold: true
13280 }
13281
13282 var xmlConfig = {
13283 autoSelfClosers: {},
13284 implicitlyClosed: {},
13285 contextGrabbers: {},
13286 doNotIndent: {},
13287 allowUnquoted: false,
13288 allowMissing: false,
13289 allowMissingTagName: false,
13290 caseFold: false
13291 }
13292
13293 CodeMirror.defineMode("xml", function(editorConf, config_) {
13294 var indentUnit = editorConf.indentUnit
13295 var config = {}
13296 var defaults = config_.htmlMode ? htmlConfig : xmlConfig
13297 for (var prop in defaults) config[prop] = defaults[prop]
13298 for (var prop in config_) config[prop] = config_[prop]
13299
13300 // Return variables for tokenizers
13301 var type, setStyle;
13302
13303 function inText(stream, state) {
13304 function chain(parser) {
13305 state.tokenize = parser;
13306 return parser(stream, state);
13307 }
13308
13309 var ch = stream.next();
13310 if (ch == "<") {
13311 if (stream.eat("!")) {
13312 if (stream.eat("[")) {
13313 if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));
13314 else return null;
13315 } else if (stream.match("--")) {
13316 return chain(inBlock("comment", "-->"));
13317 } else if (stream.match("DOCTYPE", true, true)) {
13318 stream.eatWhile(/[\w\._\-]/);
13319 return chain(doctype(1));
13320 } else {
13321 return null;
13322 }
13323 } else if (stream.eat("?")) {
13324 stream.eatWhile(/[\w\._\-]/);
13325 state.tokenize = inBlock("meta", "?>");
13326 return "meta";
13327 } else {
13328 type = stream.eat("/") ? "closeTag" : "openTag";
13329 state.tokenize = inTag;
13330 return "tag bracket";
13331 }
13332 } else if (ch == "&") {
13333 var ok;
13334 if (stream.eat("#")) {
13335 if (stream.eat("x")) {
13336 ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";");
13337 } else {
13338 ok = stream.eatWhile(/[\d]/) && stream.eat(";");
13339 }
13340 } else {
13341 ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";");
13342 }
13343 return ok ? "atom" : "error";
13344 } else {
13345 stream.eatWhile(/[^&<]/);
13346 return null;
13347 }
13348 }
13349 inText.isInText = true;
13350
13351 function inTag(stream, state) {
13352 var ch = stream.next();
13353 if (ch == ">" || (ch == "/" && stream.eat(">"))) {
13354 state.tokenize = inText;
13355 type = ch == ">" ? "endTag" : "selfcloseTag";
13356 return "tag bracket";
13357 } else if (ch == "=") {
13358 type = "equals";
13359 return null;
13360 } else if (ch == "<") {
13361 state.tokenize = inText;
13362 state.state = baseState;
13363 state.tagName = state.tagStart = null;
13364 var next = state.tokenize(stream, state);
13365 return next ? next + " tag error" : "tag error";
13366 } else if (/[\'\"]/.test(ch)) {
13367 state.tokenize = inAttribute(ch);
13368 state.stringStartCol = stream.column();
13369 return state.tokenize(stream, state);
13370 } else {
13371 stream.match(/^[^\s\u00a0=<>\"\']*[^\s\u00a0=<>\"\'\/]/);
13372 return "word";
13373 }
13374 }
13375
13376 function inAttribute(quote) {
13377 var closure = function(stream, state) {
13378 while (!stream.eol()) {
13379 if (stream.next() == quote) {
13380 state.tokenize = inTag;
13381 break;
13382 }
13383 }
13384 return "string";
13385 };
13386 closure.isInAttribute = true;
13387 return closure;
13388 }
13389
13390 function inBlock(style, terminator) {
13391 return function(stream, state) {
13392 while (!stream.eol()) {
13393 if (stream.match(terminator)) {
13394 state.tokenize = inText;
13395 break;
13396 }
13397 stream.next();
13398 }
13399 return style;
13400 }
13401 }
13402
13403 function doctype(depth) {
13404 return function(stream, state) {
13405 var ch;
13406 while ((ch = stream.next()) != null) {
13407 if (ch == "<") {
13408 state.tokenize = doctype(depth + 1);
13409 return state.tokenize(stream, state);
13410 } else if (ch == ">") {
13411 if (depth == 1) {
13412 state.tokenize = inText;
13413 break;
13414 } else {
13415 state.tokenize = doctype(depth - 1);
13416 return state.tokenize(stream, state);
13417 }
13418 }
13419 }
13420 return "meta";
13421 };
13422 }
13423
13424 function Context(state, tagName, startOfLine) {
13425 this.prev = state.context;
13426 this.tagName = tagName;
13427 this.indent = state.indented;
13428 this.startOfLine = startOfLine;
13429 if (config.doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.noIndent))
13430 this.noIndent = true;
13431 }
13432 function popContext(state) {
13433 if (state.context) state.context = state.context.prev;
13434 }
13435 function maybePopContext(state, nextTagName) {
13436 var parentTagName;
13437 while (true) {
13438 if (!state.context) {
13439 return;
13440 }
13441 parentTagName = state.context.tagName;
13442 if (!config.contextGrabbers.hasOwnProperty(parentTagName) ||
13443 !config.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
13444 return;
13445 }
13446 popContext(state);
13447 }
13448 }
13449
13450 function baseState(type, stream, state) {
13451 if (type == "openTag") {
13452 state.tagStart = stream.column();
13453 return tagNameState;
13454 } else if (type == "closeTag") {
13455 return closeTagNameState;
13456 } else {
13457 return baseState;
13458 }
13459 }
13460 function tagNameState(type, stream, state) {
13461 if (type == "word") {
13462 state.tagName = stream.current();
13463 setStyle = "tag";
13464 return attrState;
13465 } else if (config.allowMissingTagName && type == "endTag") {
13466 setStyle = "tag bracket";
13467 return attrState(type, stream, state);
13468 } else {
13469 setStyle = "error";
13470 return tagNameState;
13471 }
13472 }
13473 function closeTagNameState(type, stream, state) {
13474 if (type == "word") {
13475 var tagName = stream.current();
13476 if (state.context && state.context.tagName != tagName &&
13477 config.implicitlyClosed.hasOwnProperty(state.context.tagName))
13478 popContext(state);
13479 if ((state.context && state.context.tagName == tagName) || config.matchClosing === false) {
13480 setStyle = "tag";
13481 return closeState;
13482 } else {
13483 setStyle = "tag error";
13484 return closeStateErr;
13485 }
13486 } else if (config.allowMissingTagName && type == "endTag") {
13487 setStyle = "tag bracket";
13488 return closeState(type, stream, state);
13489 } else {
13490 setStyle = "error";
13491 return closeStateErr;
13492 }
13493 }
13494
13495 function closeState(type, _stream, state) {
13496 if (type != "endTag") {
13497 setStyle = "error";
13498 return closeState;
13499 }
13500 popContext(state);
13501 return baseState;
13502 }
13503 function closeStateErr(type, stream, state) {
13504 setStyle = "error";
13505 return closeState(type, stream, state);
13506 }
13507
13508 function attrState(type, _stream, state) {
13509 if (type == "word") {
13510 setStyle = "attribute";
13511 return attrEqState;
13512 } else if (type == "endTag" || type == "selfcloseTag") {
13513 var tagName = state.tagName, tagStart = state.tagStart;
13514 state.tagName = state.tagStart = null;
13515 if (type == "selfcloseTag" ||
13516 config.autoSelfClosers.hasOwnProperty(tagName)) {
13517 maybePopContext(state, tagName);
13518 } else {
13519 maybePopContext(state, tagName);
13520 state.context = new Context(state, tagName, tagStart == state.indented);
13521 }
13522 return baseState;
13523 }
13524 setStyle = "error";
13525 return attrState;
13526 }
13527 function attrEqState(type, stream, state) {
13528 if (type == "equals") return attrValueState;
13529 if (!config.allowMissing) setStyle = "error";
13530 return attrState(type, stream, state);
13531 }
13532 function attrValueState(type, stream, state) {
13533 if (type == "string") return attrContinuedState;
13534 if (type == "word" && config.allowUnquoted) {setStyle = "string"; return attrState;}
13535 setStyle = "error";
13536 return attrState(type, stream, state);
13537 }
13538 function attrContinuedState(type, stream, state) {
13539 if (type == "string") return attrContinuedState;
13540 return attrState(type, stream, state);
13541 }
13542
13543 return {
13544 startState: function(baseIndent) {
13545 var state = {tokenize: inText,
13546 state: baseState,
13547 indented: baseIndent || 0,
13548 tagName: null, tagStart: null,
13549 context: null}
13550 if (baseIndent != null) state.baseIndent = baseIndent
13551 return state
13552 },
13553
13554 token: function(stream, state) {
13555 if (!state.tagName && stream.sol())
13556 state.indented = stream.indentation();
13557
13558 if (stream.eatSpace()) return null;
13559 type = null;
13560 var style = state.tokenize(stream, state);
13561 if ((style || type) && style != "comment") {
13562 setStyle = null;
13563 state.state = state.state(type || style, stream, state);
13564 if (setStyle)
13565 style = setStyle == "error" ? style + " error" : setStyle;
13566 }
13567 return style;
13568 },
13569
13570 indent: function(state, textAfter, fullLine) {
13571 var context = state.context;
13572 // Indent multi-line strings (e.g. css).
13573 if (state.tokenize.isInAttribute) {
13574 if (state.tagStart == state.indented)
13575 return state.stringStartCol + 1;
13576 else
13577 return state.indented + indentUnit;
13578 }
13579 if (context && context.noIndent) return CodeMirror.Pass;
13580 if (state.tokenize != inTag && state.tokenize != inText)
13581 return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
13582 // Indent the starts of attribute names.
13583 if (state.tagName) {
13584 if (config.multilineTagIndentPastTag !== false)
13585 return state.tagStart + state.tagName.length + 2;
13586 else
13587 return state.tagStart + indentUnit * (config.multilineTagIndentFactor || 1);
13588 }
13589 if (config.alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
13590 var tagAfter = textAfter && /^<(\/)?([\w_:\.-]*)/.exec(textAfter);
13591 if (tagAfter && tagAfter[1]) { // Closing tag spotted
13592 while (context) {
13593 if (context.tagName == tagAfter[2]) {
13594 context = context.prev;
13595 break;
13596 } else if (config.implicitlyClosed.hasOwnProperty(context.tagName)) {
13597 context = context.prev;
13598 } else {
13599 break;
13600 }
13601 }
13602 } else if (tagAfter) { // Opening tag spotted
13603 while (context) {
13604 var grabbers = config.contextGrabbers[context.tagName];
13605 if (grabbers && grabbers.hasOwnProperty(tagAfter[2]))
13606 context = context.prev;
13607 else
13608 break;
13609 }
13610 }
13611 while (context && context.prev && !context.startOfLine)
13612 context = context.prev;
13613 if (context) return context.indent + indentUnit;
13614 else return state.baseIndent || 0;
13615 },
13616
13617 electricInput: /<\/[\s\w:]+>$/,
13618 blockCommentStart: "<!--",
13619 blockCommentEnd: "-->",
13620
13621 configuration: config.htmlMode ? "html" : "xml",
13622 helperType: config.htmlMode ? "html" : "xml",
13623
13624 skipAttribute: function(state) {
13625 if (state.state == attrValueState)
13626 state.state = attrState
13627 }
13628 };
13629 });
13630
13631 CodeMirror.defineMIME("text/xml", "xml");
13632 CodeMirror.defineMIME("application/xml", "xml");
13633 if (!CodeMirror.mimeModes.hasOwnProperty("text/html"))
13634 CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
13635
13636 });
13637
13638 },{"../../lib/codemirror":14}],19:[function(require,module,exports){
13639 (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);};};}});
13640
13641
13642 },{}],20:[function(require,module,exports){
13643 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;
13644
13645
13646 },{}],21:[function(require,module,exports){
13647 if("function"!=typeof Symbol){var t=0;Symbol=function(n){return"@@"+n+ ++t},Symbol.for=function(t){return"@@"+t}}var n=/acit|ex(?:s|g|n|p|$)|rph|ows|mnc|ntw|ine[ch]|zoo|^ord/i,e=function(t){return String(t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},r=function(t,n){return String(t).replace(/(\n+)/g,"$1"+(n||"\t"))},o=function(t,n,e){return String(t).length>(n||40)||!e&&-1!==String(t).indexOf("\n")||-1!==String(t).indexOf("<")},i={};function a(t){var e="";for(var r in t){var o=t[r];null!=o&&(e&&(e+=" "),e+=i[r]||(i[r]=r.replace(/([A-Z])/g,"-$1").toLowerCase()),e+=": ",e+=o,"number"==typeof o&&!1===n.test(r)&&(e+="px"),e+=";")}return e||void 0}function u(t,n){for(var e in n)t[e]=n[e];return t}var c={shallow:!0},f=[],l=/^(area|base|br|col|embed|hr|img|input|link|meta|param|source|track|wbr)$/;function s(t,n,i,c,p){if(null==t||"boolean"==typeof t)return"";var g=t.nodeName,b=t.attributes,m=!1;n=n||{};var y,v=(i=i||{}).pretty,d=v&&"string"==typeof v?v:"\t";if("object"!=typeof t&&!g)return e(t);if("function"==typeof g){if(m=!0,!i.shallow||!c&&!1!==i.renderRootComponent){var h,j=function(t){var n=u({},t.attributes);n.children=t.children;var e=t.nodeName.defaultProps;if(void 0!==e)for(var r in e)void 0===n[r]&&(n[r]=e[r]);return n}(t);if(g.prototype&&"function"==typeof g.prototype.render){var S=new g(j,n);S._disable=S.__x=!0,S.props=j,S.context=n,g.getDerivedStateFromProps?S.state=u(u({},S.state),g.getDerivedStateFromProps(S.props,S.state)):S.componentWillMount&&S.componentWillMount(),h=S.render(S.props,S.state,S.context),S.getChildContext&&(n=u(u({},n),S.getChildContext()))}else h=g(j,n);return s(h,n,i,!1!==i.shallowHighOrder)}g=(y=g).displayName||y!==Function&&y.name||function(t){var n=(Function.prototype.toString.call(t).match(/^\s*function\s+([^( ]+)/)||"")[1];if(!n){for(var e=-1,r=f.length;r--;)if(f[r]===t){e=r;break}e<0&&(e=f.push(t)-1),n="UnnamedComponent"+e}return n}(y)}var x,O="";if(b){var A=Object.keys(b);i&&!0===i.sortAttributes&&A.sort();for(var k=0;k<A.length;k++){var w=A[k],F=b[w];if("children"!==w&&!w.match(/[\s\n\\/='"\0<>]/)&&(i&&i.allAttributes||"key"!==w&&"ref"!==w)){if("className"===w){if(b.class)continue;w="class"}else p&&w.match(/^xlink:?./)&&(w=w.toLowerCase().replace(/^xlink:?/,"xlink:"));"style"===w&&F&&"object"==typeof F&&(F=a(F));var N=i.attributeHook&&i.attributeHook(w,F,n,i,m);if(N||""===N)O+=N;else if("dangerouslySetInnerHTML"===w)x=F&&F.__html;else if((F||0===F||""===F)&&"function"!=typeof F){if(!(!0!==F&&""!==F||(F=w,i&&i.xml))){O+=" "+w;continue}O+=" "+w+'="'+e(F)+'"'}}}}if(v){var C=O.replace(/^\n\s*/," ");C===O||~C.indexOf("\n")?v&&~O.indexOf("\n")&&(O+="\n"):O=C}if(O="<"+g+O+">",String(g).match(/[\s\n\\/='"\0<>]/))throw O;var E=String(g).match(l);E&&(O=O.replace(/>$/," />"));var M=[];if(x)v&&o(x)&&(x="\n"+d+r(x,d)),O+=x;else if(t.children){for(var D=v&&~O.indexOf("\n"),I=0;I<t.children.length;I++){var $=t.children[I];if(null!=$&&!1!==$){var H=s($,n,i,!0,"svg"===g||"foreignObject"!==g&&p);v&&!D&&o(H)&&(D=!0),H&&M.push(H)}}if(v&&D)for(var P=M.length;P--;)M[P]="\n"+d+r(M[P],d)}if(M.length)O+=M.join("");else if(i&&i.xml)return O.substring(0,O.length-1)+" />";return E||(v&&~O.indexOf("\n")&&(O+="\n"),O+="</"+g+">"),O}s.render=s,s.shallowRender=function(t,n){return s(t,n,c)};var p=/(\\|\"|\')/g,g=function(t){return t.replace(p,"\\$1")},b=Object.prototype.toString,m=Date.prototype.toISOString,y=Error.prototype.toString,v=RegExp.prototype.toString,d=Symbol.prototype.toString,h=/^Symbol\((.*)\)(.*)$/,j=/\n/gi,S=Object.getOwnPropertySymbols||function(t){return[]};function x(t){return"[object Array]"===t||"[object ArrayBuffer]"===t||"[object DataView]"===t||"[object Float32Array]"===t||"[object Float64Array]"===t||"[object Int8Array]"===t||"[object Int16Array]"===t||"[object Int32Array]"===t||"[object Uint8Array]"===t||"[object Uint8ClampedArray]"===t||"[object Uint16Array]"===t||"[object Uint32Array]"===t}function O(t){return""===t.name?"[Function anonymous]":"[Function "+t.name+"]"}function A(t){return d.call(t).replace(h,"Symbol($1)")}function k(t){return"["+y.call(t)+"]"}function w(t){if(!0===t||!1===t)return""+t;if(void 0===t)return"undefined";if(null===t)return"null";var n=typeof t;if("number"===n)return function(t){return t!=+t?"NaN":0===t&&1/t<0?"-0":""+t}(t);if("string"===n)return'"'+g(t)+'"';if("function"===n)return O(t);if("symbol"===n)return A(t);var e=b.call(t);return"[object WeakMap]"===e?"WeakMap {}":"[object WeakSet]"===e?"WeakSet {}":"[object Function]"===e||"[object GeneratorFunction]"===e?O(t,min):"[object Symbol]"===e?A(t):"[object Date]"===e?m.call(t):"[object Error]"===e?k(t):"[object RegExp]"===e?v.call(t):"[object Arguments]"===e&&0===t.length?"Arguments []":x(e)&&0===t.length?t.constructor.name+" []":t instanceof Error&&k(t)}function F(t,n,e,r,o,i,a,u,c,f){var l="";if(t.length){l+=o;for(var s=e+n,p=0;p<t.length;p++)l+=s+E(t[p],n,s,r,o,i,a,u,c,f),p<t.length-1&&(l+=","+r);l+=o+e}return"["+l+"]"}function N(t,n,e,r,o,i,a,u,c,f){if((i=i.slice()).indexOf(t)>-1)return"[Circular]";i.push(t);var l=++u>a;if(!l&&t.toJSON&&"function"==typeof t.toJSON)return E(t.toJSON(),n,e,r,o,i,a,u,c,f);var s=b.call(t);return"[object Arguments]"===s?l?"[Arguments]":function(t,n,e,r,o,i,a,u,c,f){return(f?"":"Arguments ")+F(t,n,e,r,o,i,a,u,c,f)}(t,n,e,r,o,i,a,u,c,f):x(s)?l?"[Array]":function(t,n,e,r,o,i,a,u,c,f){return(f?"":t.constructor.name+" ")+F(t,n,e,r,o,i,a,u,c,f)}(t,n,e,r,o,i,a,u,c,f):"[object Map]"===s?l?"[Map]":function(t,n,e,r,o,i,a,u,c,f){var l="Map {",s=t.entries(),p=s.next();if(!p.done){l+=o;for(var g=e+n;!p.done;)l+=g+E(p.value[0],n,g,r,o,i,a,u,c,f)+" => "+E(p.value[1],n,g,r,o,i,a,u,c,f),(p=s.next()).done||(l+=","+r);l+=o+e}return l+"}"}(t,n,e,r,o,i,a,u,c,f):"[object Set]"===s?l?"[Set]":function(t,n,e,r,o,i,a,u,c,f){var l="Set {",s=t.entries(),p=s.next();if(!p.done){l+=o;for(var g=e+n;!p.done;)l+=g+E(p.value[1],n,g,r,o,i,a,u,c,f),(p=s.next()).done||(l+=","+r);l+=o+e}return l+"}"}(t,n,e,r,o,i,a,u,c,f):"object"==typeof t?l?"[Object]":function(t,n,e,r,o,i,a,u,c,f){var l=(f?"":t.constructor?t.constructor.name+" ":"Object ")+"{",s=Object.keys(t).sort(),p=S(t);if(p.length&&(s=s.filter(function(t){return!("symbol"==typeof t||"[object Symbol]"===b.call(t))}).concat(p)),s.length){l+=o;for(var g=e+n,m=0;m<s.length;m++){var y=s[m];l+=g+E(y,n,g,r,o,i,a,u,c,f)+": "+E(t[y],n,g,r,o,i,a,u,c,f),m<s.length-1&&(l+=","+r)}l+=o+e}return l+"}"}(t,n,e,r,o,i,a,u,c,f):void 0}function C(t,n,e,r,o,i,a,u,c,f){for(var l,s=!1,p=0;p<c.length;p++)if((l=c[p]).test(t)){s=!0;break}return!!s&&l.print(t,function(t){return E(t,n,e,r,o,i,a,u,c,f)},function(t){var r=e+n;return r+t.replace(j,"\n"+r)},{edgeSpacing:o,spacing:r})}function E(t,n,e,r,o,i,a,u,c,f){var l=w(t);return l||(C(t,n,e,r,o,i,a,u,c,f)||N(t,n,e,r,o,i,a,u,c,f))}var M={indent:2,min:!1,maxDepth:Infinity,plugins:[]};function D(t){return new Array(t+1).join(" ")}var I={test:function(t){return t&&"object"==typeof t&&"nodeName"in t&&"attributes"in t&&"children"in t&&!("nodeType"in t)},print:function(t,n,e){return s(t,I.context,I.opts,!0)}},$={plugins:[I]},H={attributeHook:function(t,n,o,i,a){var u=typeof n;if("dangerouslySetInnerHTML"===t)return!1;if(null==n||"function"===u&&!i.functions)return"";if(i.skipFalseAttributes&&!a&&(!1===n||("class"===t||"style"===t)&&""===n))return"";var c="string"==typeof i.pretty?i.pretty:"\t";return"string"!==u?("function"!==u||i.functionNames?(I.context=o,I.opts=i,~(n=function(t,n){var e,r;n?(function(t){if(Object.keys(t).forEach(function(t){if(!M.hasOwnProperty(t))throw new Error("prettyFormat: Invalid option: "+t)}),t.min&&void 0!==t.indent&&0!==t.indent)throw new Error("prettyFormat: Cannot run with min option and indent")}(n),n=function(t){var n={};return Object.keys(M).forEach(function(e){return n[e]=t.hasOwnProperty(e)?t[e]:M[e]}),n.min&&(n.indent=0),n}(n)):n=M;var o=n.min?" ":"\n",i=n.min?"":"\n";if(n&&n.plugins.length){var a=C(t,e=D(n.indent),"",o,i,r=[],n.maxDepth,0,n.plugins,n.min);if(a)return a}return w(t)||(e||(e=D(n.indent)),r||(r=[]),N(t,e,"",o,i,r,n.maxDepth,0,n.plugins,n.min))}(n,$)).indexOf("\n")&&(n=r("\n"+n,c)+"\n")):n="Function",r("\n"+t+"={"+n+"}",c)):"\n"+c+t+'="'+e(n)+'"'},jsx:!0,xml:!1,functions:!0,functionNames:!0,skipFalseAttributes:!0,pretty:" "};module.exports=function(t,n,e,r){return s(t,n,e=u(u({},H),e||{}),r)};
13648
13649
13650 },{}],22:[function(require,module,exports){
13651 module.exports = require('./dist/jsx'); // eslint-disable-line
13652
13653 },{"./dist/jsx":21}],23:[function(require,module,exports){
13654 !function() {
13655 'use strict';
13656 function h(nodeName, attributes) {
13657 var lastSimple, child, simple, i, children = EMPTY_CHILDREN;
13658 for (i = arguments.length; i-- > 2; ) stack.push(arguments[i]);
13659 if (attributes && null != attributes.children) {
13660 if (!stack.length) stack.push(attributes.children);
13661 delete attributes.children;
13662 }
13663 while (stack.length) if ((child = stack.pop()) && void 0 !== child.pop) for (i = child.length; i--; ) stack.push(child[i]); else {
13664 if ('boolean' == typeof child) child = null;
13665 if (simple = 'function' != typeof nodeName) if (null == child) child = ''; else if ('number' == typeof child) child = String(child); else if ('string' != typeof child) simple = !1;
13666 if (simple && lastSimple) children[children.length - 1] += child; else if (children === EMPTY_CHILDREN) children = [ child ]; else children.push(child);
13667 lastSimple = simple;
13668 }
13669 var p = new VNode();
13670 p.nodeName = nodeName;
13671 p.children = children;
13672 p.attributes = null == attributes ? void 0 : attributes;
13673 p.key = null == attributes ? void 0 : attributes.key;
13674 if (void 0 !== options.vnode) options.vnode(p);
13675 return p;
13676 }
13677 function extend(obj, props) {
13678 for (var i in props) obj[i] = props[i];
13679 return obj;
13680 }
13681 function applyRef(ref, value) {
13682 if (null != ref) if ('function' == typeof ref) ref(value); else ref.current = value;
13683 }
13684 function cloneElement(vnode, props) {
13685 return h(vnode.nodeName, extend(extend({}, vnode.attributes), props), arguments.length > 2 ? [].slice.call(arguments, 2) : vnode.children);
13686 }
13687 function enqueueRender(component) {
13688 if (!component.__d && (component.__d = !0) && 1 == items.push(component)) (options.debounceRendering || defer)(rerender);
13689 }
13690 function rerender() {
13691 var p;
13692 while (p = items.pop()) if (p.__d) renderComponent(p);
13693 }
13694 function isSameNodeType(node, vnode, hydrating) {
13695 if ('string' == typeof vnode || 'number' == typeof vnode) return void 0 !== node.splitText;
13696 if ('string' == typeof vnode.nodeName) return !node._componentConstructor && isNamedNode(node, vnode.nodeName); else return hydrating || node._componentConstructor === vnode.nodeName;
13697 }
13698 function isNamedNode(node, nodeName) {
13699 return node.__n === nodeName || node.nodeName.toLowerCase() === nodeName.toLowerCase();
13700 }
13701 function getNodeProps(vnode) {
13702 var props = extend({}, vnode.attributes);
13703 props.children = vnode.children;
13704 var defaultProps = vnode.nodeName.defaultProps;
13705 if (void 0 !== defaultProps) for (var i in defaultProps) if (void 0 === props[i]) props[i] = defaultProps[i];
13706 return props;
13707 }
13708 function createNode(nodeName, isSvg) {
13709 var node = isSvg ? document.createElementNS('http://www.w3.org/2000/svg', nodeName) : document.createElement(nodeName);
13710 node.__n = nodeName;
13711 return node;
13712 }
13713 function removeNode(node) {
13714 var parentNode = node.parentNode;
13715 if (parentNode) parentNode.removeChild(node);
13716 }
13717 function setAccessor(node, name, old, value, isSvg) {
13718 if ('className' === name) name = 'class';
13719 if ('key' === name) ; else if ('ref' === name) {
13720 applyRef(old, null);
13721 applyRef(value, node);
13722 } else if ('class' === name && !isSvg) node.className = value || ''; else if ('style' === name) {
13723 if (!value || 'string' == typeof value || 'string' == typeof old) node.style.cssText = value || '';
13724 if (value && 'object' == typeof value) {
13725 if ('string' != typeof old) for (var i in old) if (!(i in value)) node.style[i] = '';
13726 for (var i in value) node.style[i] = 'number' == typeof value[i] && !1 === IS_NON_DIMENSIONAL.test(i) ? value[i] + 'px' : value[i];
13727 }
13728 } else if ('dangerouslySetInnerHTML' === name) {
13729 if (value) node.innerHTML = value.__html || '';
13730 } else if ('o' == name[0] && 'n' == name[1]) {
13731 var useCapture = name !== (name = name.replace(/Capture$/, ''));
13732 name = name.toLowerCase().substring(2);
13733 if (value) {
13734 if (!old) node.addEventListener(name, eventProxy, useCapture);
13735 } else node.removeEventListener(name, eventProxy, useCapture);
13736 (node.__l || (node.__l = {}))[name] = value;
13737 } else if ('list' !== name && 'type' !== name && !isSvg && name in node) {
13738 try {
13739 node[name] = null == value ? '' : value;
13740 } catch (e) {}
13741 if ((null == value || !1 === value) && 'spellcheck' != name) node.removeAttribute(name);
13742 } else {
13743 var ns = isSvg && name !== (name = name.replace(/^xlink:?/, ''));
13744 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);
13745 }
13746 }
13747 function eventProxy(e) {
13748 return this.__l[e.type](options.event && options.event(e) || e);
13749 }
13750 function flushMounts() {
13751 var c;
13752 while (c = mounts.shift()) {
13753 if (options.afterMount) options.afterMount(c);
13754 if (c.componentDidMount) c.componentDidMount();
13755 }
13756 }
13757 function diff(dom, vnode, context, mountAll, parent, componentRoot) {
13758 if (!diffLevel++) {
13759 isSvgMode = null != parent && void 0 !== parent.ownerSVGElement;
13760 hydrating = null != dom && !('__preactattr_' in dom);
13761 }
13762 var ret = idiff(dom, vnode, context, mountAll, componentRoot);
13763 if (parent && ret.parentNode !== parent) parent.appendChild(ret);
13764 if (!--diffLevel) {
13765 hydrating = !1;
13766 if (!componentRoot) flushMounts();
13767 }
13768 return ret;
13769 }
13770 function idiff(dom, vnode, context, mountAll, componentRoot) {
13771 var out = dom, prevSvgMode = isSvgMode;
13772 if (null == vnode || 'boolean' == typeof vnode) vnode = '';
13773 if ('string' == typeof vnode || 'number' == typeof vnode) {
13774 if (dom && void 0 !== dom.splitText && dom.parentNode && (!dom._component || componentRoot)) {
13775 if (dom.nodeValue != vnode) dom.nodeValue = vnode;
13776 } else {
13777 out = document.createTextNode(vnode);
13778 if (dom) {
13779 if (dom.parentNode) dom.parentNode.replaceChild(out, dom);
13780 recollectNodeTree(dom, !0);
13781 }
13782 }
13783 out.__preactattr_ = !0;
13784 return out;
13785 }
13786 var vnodeName = vnode.nodeName;
13787 if ('function' == typeof vnodeName) return buildComponentFromVNode(dom, vnode, context, mountAll);
13788 isSvgMode = 'svg' === vnodeName ? !0 : 'foreignObject' === vnodeName ? !1 : isSvgMode;
13789 vnodeName = String(vnodeName);
13790 if (!dom || !isNamedNode(dom, vnodeName)) {
13791 out = createNode(vnodeName, isSvgMode);
13792 if (dom) {
13793 while (dom.firstChild) out.appendChild(dom.firstChild);
13794 if (dom.parentNode) dom.parentNode.replaceChild(out, dom);
13795 recollectNodeTree(dom, !0);
13796 }
13797 }
13798 var fc = out.firstChild, props = out.__preactattr_, vchildren = vnode.children;
13799 if (null == props) {
13800 props = out.__preactattr_ = {};
13801 for (var a = out.attributes, i = a.length; i--; ) props[a[i].name] = a[i].value;
13802 }
13803 if (!hydrating && vchildren && 1 === vchildren.length && 'string' == typeof vchildren[0] && null != fc && void 0 !== fc.splitText && null == fc.nextSibling) {
13804 if (fc.nodeValue != vchildren[0]) fc.nodeValue = vchildren[0];
13805 } else if (vchildren && vchildren.length || null != fc) innerDiffNode(out, vchildren, context, mountAll, hydrating || null != props.dangerouslySetInnerHTML);
13806 diffAttributes(out, vnode.attributes, props);
13807 isSvgMode = prevSvgMode;
13808 return out;
13809 }
13810 function innerDiffNode(dom, vchildren, context, mountAll, isHydrating) {
13811 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;
13812 if (0 !== len) for (var i = 0; i < len; i++) {
13813 var _child = originalChildren[i], props = _child.__preactattr_, key = vlen && props ? _child._component ? _child._component.__k : props.key : null;
13814 if (null != key) {
13815 keyedLen++;
13816 keyed[key] = _child;
13817 } else if (props || (void 0 !== _child.splitText ? isHydrating ? _child.nodeValue.trim() : !0 : isHydrating)) children[childrenLen++] = _child;
13818 }
13819 if (0 !== vlen) for (var i = 0; i < vlen; i++) {
13820 vchild = vchildren[i];
13821 child = null;
13822 var key = vchild.key;
13823 if (null != key) {
13824 if (keyedLen && void 0 !== keyed[key]) {
13825 child = keyed[key];
13826 keyed[key] = void 0;
13827 keyedLen--;
13828 }
13829 } else if (min < childrenLen) for (j = min; j < childrenLen; j++) if (void 0 !== children[j] && isSameNodeType(c = children[j], vchild, isHydrating)) {
13830 child = c;
13831 children[j] = void 0;
13832 if (j === childrenLen - 1) childrenLen--;
13833 if (j === min) min++;
13834 break;
13835 }
13836 child = idiff(child, vchild, context, mountAll);
13837 f = originalChildren[i];
13838 if (child && child !== dom && child !== f) if (null == f) dom.appendChild(child); else if (child === f.nextSibling) removeNode(f); else dom.insertBefore(child, f);
13839 }
13840 if (keyedLen) for (var i in keyed) if (void 0 !== keyed[i]) recollectNodeTree(keyed[i], !1);
13841 while (min <= childrenLen) if (void 0 !== (child = children[childrenLen--])) recollectNodeTree(child, !1);
13842 }
13843 function recollectNodeTree(node, unmountOnly) {
13844 var component = node._component;
13845 if (component) unmountComponent(component); else {
13846 if (null != node.__preactattr_) applyRef(node.__preactattr_.ref, null);
13847 if (!1 === unmountOnly || null == node.__preactattr_) removeNode(node);
13848 removeChildren(node);
13849 }
13850 }
13851 function removeChildren(node) {
13852 node = node.lastChild;
13853 while (node) {
13854 var next = node.previousSibling;
13855 recollectNodeTree(node, !0);
13856 node = next;
13857 }
13858 }
13859 function diffAttributes(dom, attrs, old) {
13860 var name;
13861 for (name in old) if ((!attrs || null == attrs[name]) && null != old[name]) setAccessor(dom, name, old[name], old[name] = void 0, isSvgMode);
13862 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);
13863 }
13864 function createComponent(Ctor, props, context) {
13865 var inst, i = recyclerComponents.length;
13866 if (Ctor.prototype && Ctor.prototype.render) {
13867 inst = new Ctor(props, context);
13868 Component.call(inst, props, context);
13869 } else {
13870 inst = new Component(props, context);
13871 inst.constructor = Ctor;
13872 inst.render = doRender;
13873 }
13874 while (i--) if (recyclerComponents[i].constructor === Ctor) {
13875 inst.__b = recyclerComponents[i].__b;
13876 recyclerComponents.splice(i, 1);
13877 return inst;
13878 }
13879 return inst;
13880 }
13881 function doRender(props, state, context) {
13882 return this.constructor(props, context);
13883 }
13884 function setComponentProps(component, props, renderMode, context, mountAll) {
13885 if (!component.__x) {
13886 component.__x = !0;
13887 component.__r = props.ref;
13888 component.__k = props.key;
13889 delete props.ref;
13890 delete props.key;
13891 if (void 0 === component.constructor.getDerivedStateFromProps) if (!component.base || mountAll) {
13892 if (component.componentWillMount) component.componentWillMount();
13893 } else if (component.componentWillReceiveProps) component.componentWillReceiveProps(props, context);
13894 if (context && context !== component.context) {
13895 if (!component.__c) component.__c = component.context;
13896 component.context = context;
13897 }
13898 if (!component.__p) component.__p = component.props;
13899 component.props = props;
13900 component.__x = !1;
13901 if (0 !== renderMode) if (1 === renderMode || !1 !== options.syncComponentUpdates || !component.base) renderComponent(component, 1, mountAll); else enqueueRender(component);
13902 applyRef(component.__r, component);
13903 }
13904 }
13905 function renderComponent(component, renderMode, mountAll, isChild) {
13906 if (!component.__x) {
13907 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;
13908 if (component.constructor.getDerivedStateFromProps) {
13909 state = extend(extend({}, state), component.constructor.getDerivedStateFromProps(props, state));
13910 component.state = state;
13911 }
13912 if (isUpdate) {
13913 component.props = previousProps;
13914 component.state = previousState;
13915 component.context = previousContext;
13916 if (2 !== renderMode && component.shouldComponentUpdate && !1 === component.shouldComponentUpdate(props, state, context)) skip = !0; else if (component.componentWillUpdate) component.componentWillUpdate(props, state, context);
13917 component.props = props;
13918 component.state = state;
13919 component.context = context;
13920 }
13921 component.__p = component.__s = component.__c = component.__b = null;
13922 component.__d = !1;
13923 if (!skip) {
13924 rendered = component.render(props, state, context);
13925 if (component.getChildContext) context = extend(extend({}, context), component.getChildContext());
13926 if (isUpdate && component.getSnapshotBeforeUpdate) snapshot = component.getSnapshotBeforeUpdate(previousProps, previousState);
13927 var toUnmount, base, childComponent = rendered && rendered.nodeName;
13928 if ('function' == typeof childComponent) {
13929 var childProps = getNodeProps(rendered);
13930 inst = initialChildComponent;
13931 if (inst && inst.constructor === childComponent && childProps.key == inst.__k) setComponentProps(inst, childProps, 1, context, !1); else {
13932 toUnmount = inst;
13933 component._component = inst = createComponent(childComponent, childProps, context);
13934 inst.__b = inst.__b || nextBase;
13935 inst.__u = component;
13936 setComponentProps(inst, childProps, 0, context, !1);
13937 renderComponent(inst, 1, mountAll, !0);
13938 }
13939 base = inst.base;
13940 } else {
13941 cbase = initialBase;
13942 toUnmount = initialChildComponent;
13943 if (toUnmount) cbase = component._component = null;
13944 if (initialBase || 1 === renderMode) {
13945 if (cbase) cbase._component = null;
13946 base = diff(cbase, rendered, context, mountAll || !isUpdate, initialBase && initialBase.parentNode, !0);
13947 }
13948 }
13949 if (initialBase && base !== initialBase && inst !== initialChildComponent) {
13950 var baseParent = initialBase.parentNode;
13951 if (baseParent && base !== baseParent) {
13952 baseParent.replaceChild(base, initialBase);
13953 if (!toUnmount) {
13954 initialBase._component = null;
13955 recollectNodeTree(initialBase, !1);
13956 }
13957 }
13958 }
13959 if (toUnmount) unmountComponent(toUnmount);
13960 component.base = base;
13961 if (base && !isChild) {
13962 var componentRef = component, t = component;
13963 while (t = t.__u) (componentRef = t).base = base;
13964 base._component = componentRef;
13965 base._componentConstructor = componentRef.constructor;
13966 }
13967 }
13968 if (!isUpdate || mountAll) mounts.push(component); else if (!skip) {
13969 if (component.componentDidUpdate) component.componentDidUpdate(previousProps, previousState, snapshot);
13970 if (options.afterUpdate) options.afterUpdate(component);
13971 }
13972 while (component.__h.length) component.__h.pop().call(component);
13973 if (!diffLevel && !isChild) flushMounts();
13974 }
13975 }
13976 function buildComponentFromVNode(dom, vnode, context, mountAll) {
13977 var c = dom && dom._component, originalComponent = c, oldDom = dom, isDirectOwner = c && dom._componentConstructor === vnode.nodeName, isOwner = isDirectOwner, props = getNodeProps(vnode);
13978 while (c && !isOwner && (c = c.__u)) isOwner = c.constructor === vnode.nodeName;
13979 if (c && isOwner && (!mountAll || c._component)) {
13980 setComponentProps(c, props, 3, context, mountAll);
13981 dom = c.base;
13982 } else {
13983 if (originalComponent && !isDirectOwner) {
13984 unmountComponent(originalComponent);
13985 dom = oldDom = null;
13986 }
13987 c = createComponent(vnode.nodeName, props, context);
13988 if (dom && !c.__b) {
13989 c.__b = dom;
13990 oldDom = null;
13991 }
13992 setComponentProps(c, props, 1, context, mountAll);
13993 dom = c.base;
13994 if (oldDom && dom !== oldDom) {
13995 oldDom._component = null;
13996 recollectNodeTree(oldDom, !1);
13997 }
13998 }
13999 return dom;
14000 }
14001 function unmountComponent(component) {
14002 if (options.beforeUnmount) options.beforeUnmount(component);
14003 var base = component.base;
14004 component.__x = !0;
14005 if (component.componentWillUnmount) component.componentWillUnmount();
14006 component.base = null;
14007 var inner = component._component;
14008 if (inner) unmountComponent(inner); else if (base) {
14009 if (null != base.__preactattr_) applyRef(base.__preactattr_.ref, null);
14010 component.__b = base;
14011 removeNode(base);
14012 recyclerComponents.push(component);
14013 removeChildren(base);
14014 }
14015 applyRef(component.__r, null);
14016 }
14017 function Component(props, context) {
14018 this.__d = !0;
14019 this.context = context;
14020 this.props = props;
14021 this.state = this.state || {};
14022 this.__h = [];
14023 }
14024 function render(vnode, parent, merge) {
14025 return diff(merge, vnode, {}, !1, parent, !1);
14026 }
14027 function createRef() {
14028 return {};
14029 }
14030 var VNode = function() {};
14031 var options = {};
14032 var stack = [];
14033 var EMPTY_CHILDREN = [];
14034 var defer = 'function' == typeof Promise ? Promise.resolve().then.bind(Promise.resolve()) : setTimeout;
14035 var IS_NON_DIMENSIONAL = /acit|ex(?:s|g|n|p|$)|rph|ows|mnc|ntw|ine[ch]|zoo|^ord/i;
14036 var items = [];
14037 var mounts = [];
14038 var diffLevel = 0;
14039 var isSvgMode = !1;
14040 var hydrating = !1;
14041 var recyclerComponents = [];
14042 extend(Component.prototype, {
14043 setState: function(state, callback) {
14044 if (!this.__s) this.__s = this.state;
14045 this.state = extend(extend({}, this.state), 'function' == typeof state ? state(this.state, this.props) : state);
14046 if (callback) this.__h.push(callback);
14047 enqueueRender(this);
14048 },
14049 forceUpdate: function(callback) {
14050 if (callback) this.__h.push(callback);
14051 renderComponent(this, 2);
14052 },
14053 render: function() {}
14054 });
14055 var preact = {
14056 h: h,
14057 createElement: h,
14058 cloneElement: cloneElement,
14059 createRef: createRef,
14060 Component: Component,
14061 render: render,
14062 rerender: rerender,
14063 options: options
14064 };
14065 if ('undefined' != typeof module) module.exports = preact; else self.preact = preact;
14066 }();
14067
14068 },{}],24:[function(require,module,exports){
14069 function tlite(getTooltipOpts) {
14070 document.addEventListener('mouseover', function (e) {
14071 var el = e.target;
14072 var opts = getTooltipOpts(el);
14073
14074 if (!opts) {
14075 el = el.parentElement;
14076 opts = el && getTooltipOpts(el);
14077 }
14078
14079 opts && tlite.show(el, opts, true);
14080 });
14081 }
14082
14083 tlite.show = function (el, opts, isAuto) {
14084 var fallbackAttrib = 'data-tlite';
14085 opts = opts || {};
14086
14087 (el.tooltip || Tooltip(el, opts)).show();
14088
14089 function Tooltip(el, opts) {
14090 var tooltipEl;
14091 var showTimer;
14092 var text;
14093
14094 el.addEventListener('mousedown', autoHide);
14095 el.addEventListener('mouseleave', autoHide);
14096
14097 function show() {
14098 text = el.title || el.getAttribute(fallbackAttrib) || text;
14099 el.title = '';
14100 el.setAttribute(fallbackAttrib, '');
14101 text && !showTimer && (showTimer = setTimeout(fadeIn, isAuto ? 150 : 1))
14102 }
14103
14104 function autoHide() {
14105 tlite.hide(el, true);
14106 }
14107
14108 function hide(isAutoHiding) {
14109 if (isAuto === isAutoHiding) {
14110 showTimer = clearTimeout(showTimer);
14111 var parent = tooltipEl && tooltipEl.parentNode;
14112 parent && parent.removeChild(tooltipEl);
14113 tooltipEl = undefined;
14114 }
14115 }
14116
14117 function fadeIn() {
14118 if (!tooltipEl) {
14119 tooltipEl = createTooltip(el, text, opts);
14120 }
14121 }
14122
14123 return el.tooltip = {
14124 show: show,
14125 hide: hide
14126 };
14127 }
14128
14129 function createTooltip(el, text, opts) {
14130 var tooltipEl = document.createElement('span');
14131 var grav = opts.grav || el.getAttribute('data-tlite') || 'n';
14132
14133 tooltipEl.innerHTML = text;
14134
14135 el.appendChild(tooltipEl);
14136
14137 var vertGrav = grav[0] || '';
14138 var horzGrav = grav[1] || '';
14139
14140 function positionTooltip() {
14141 tooltipEl.className = 'tlite ' + 'tlite-' + vertGrav + horzGrav;
14142
14143 var arrowSize = 10;
14144 var top = el.offsetTop;
14145 var left = el.offsetLeft;
14146
14147 if (tooltipEl.offsetParent === el) {
14148 top = left = 0;
14149 }
14150
14151 var width = el.offsetWidth;
14152 var height = el.offsetHeight;
14153 var tooltipHeight = tooltipEl.offsetHeight;
14154 var tooltipWidth = tooltipEl.offsetWidth;
14155 var centerEl = left + (width / 2);
14156
14157 tooltipEl.style.top = (
14158 vertGrav === 's' ? (top - tooltipHeight - arrowSize) :
14159 vertGrav === 'n' ? (top + height + arrowSize) :
14160 (top + (height / 2) - (tooltipHeight / 2))
14161 ) + 'px';
14162
14163 tooltipEl.style.left = (
14164 horzGrav === 'w' ? left :
14165 horzGrav === 'e' ? left + width - tooltipWidth :
14166 vertGrav === 'w' ? (left + width + arrowSize) :
14167 vertGrav === 'e' ? (left - tooltipWidth - arrowSize) :
14168 (centerEl - tooltipWidth / 2)
14169 ) + 'px';
14170 }
14171
14172 positionTooltip();
14173
14174 var rect = tooltipEl.getBoundingClientRect();
14175
14176 if (vertGrav === 's' && rect.top < 0) {
14177 vertGrav = 'n';
14178 positionTooltip();
14179 } else if (vertGrav === 'n' && rect.bottom > window.innerHeight) {
14180 vertGrav = 's';
14181 positionTooltip();
14182 } else if (vertGrav === 'e' && rect.left < 0) {
14183 vertGrav = 'w';
14184 positionTooltip();
14185 } else if (vertGrav === 'w' && rect.right > window.innerWidth) {
14186 vertGrav = 'e';
14187 positionTooltip();
14188 }
14189
14190 tooltipEl.className += ' tlite-visible';
14191
14192 return tooltipEl;
14193 }
14194 };
14195
14196 tlite.hide = function (el, isAuto) {
14197 el.tooltip && el.tooltip.hide(isAuto);
14198 };
14199
14200 if (typeof module !== 'undefined' && module.exports) {
14201 module.exports = tlite;
14202 }
14203
14204 },{}]},{},[2]);
14205 ; })();