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

14,212 lines 555.4 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 var _preact = require("preact");
48
49 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
50
51 window.html_forms = {}; // init the various components
52
53 _tabs["default"].init();
54
55 _actionConfirmations["default"].init();
56
57 if (document.getElementById('hf-form-editor')) {
58 _formEditor["default"].init();
59
60 _formActions["default"].init();
61
62 _fieldBuilder["default"].init(_formEditor["default"]);
63 }
64
65 (0, _tlite["default"])(function (el) {
66 return el.className.indexOf('hf-tooltip') > -1;
67 });
68 window.html_forms.FieldBuilder = _fieldBuilder["default"];
69 window.html_forms.Editor = _formEditor["default"]; // tell WP common.js to override the method used for determining hidden columns (screen options)
70
71 if (window.hf_options.view === 'edit') {
72 window.columns.useCheckboxesForHidden();
73 }
74
75 },{"./action-confirmations.js":1,"./field-builder.js":6,"./form-actions.js":8,"./form-editor.js":9,"./tabs.js":10,"preact":20,"tlite":21}],3:[function(require,module,exports){
76 'use strict';
77
78 Object.defineProperty(exports, "__esModule", {
79 value: true
80 });
81 exports.FieldBuilder = void 0;
82
83 var _preact = require("preact");
84
85 var _fieldConfigurator = require("./field-configurator.js");
86
87 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); }
88
89 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
90
91 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); } }
92
93 function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
94
95 function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }
96
97 function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
98
99 function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
100
101 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); }
102
103 function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
104
105 var FieldBuilder =
106 /*#__PURE__*/
107 function (_Component) {
108 _inherits(FieldBuilder, _Component);
109
110 function FieldBuilder(props) {
111 var _this;
112
113 _classCallCheck(this, FieldBuilder);
114
115 _this = _possibleConstructorReturn(this, _getPrototypeOf(FieldBuilder).call(this, props));
116 _this.state = {
117 activeField: null
118 };
119 _this.handleCancel = _this.handleCancel.bind(_assertThisInitialized(_this));
120 _this.openFieldConfig = _this.openFieldConfig.bind(_assertThisInitialized(_this));
121 return _this;
122 }
123
124 _createClass(FieldBuilder, [{
125 key: "handleCancel",
126 value: function handleCancel() {
127 this.setState({
128 activeField: null
129 });
130 }
131 }, {
132 key: "openFieldConfig",
133 value: function openFieldConfig(e) {
134 var field = this.props.fields[e.target.value];
135
136 if (this.state.activeField === field) {
137 this.setState({
138 activeField: null
139 });
140 } else {
141 this.setState({
142 activeField: field
143 });
144 }
145 }
146 }, {
147 key: "render",
148 value: function render(props, state) {
149 var _this2 = this;
150
151 var fieldButtons = props.fields.map(function (f, i) {
152 return (0, _preact.h)("button", {
153 type: "button",
154 value: i,
155 className: 'button ' + (state.activeField === f ? 'active' : ''),
156 onClick: _this2.openFieldConfig
157 }, f.label);
158 });
159 var fieldType = state.activeField ? state.activeField.key : '';
160 var rows = state.activeField ? state.activeField.configRows : [];
161 return (0, _preact.h)("div", {
162 "class": "hf-field-builder"
163 }, (0, _preact.h)("h4", null, "Add field"), (0, _preact.h)("div", {
164 "class": "available-fields"
165 }, fieldButtons), (0, _preact.h)("div", {
166 style: "max-width: 480px;"
167 }, (0, _preact.h)(_fieldConfigurator.FieldConfigurator, {
168 fieldType: fieldType,
169 rows: rows,
170 onCancel: this.handleCancel
171 })), state.activeField === null ? (0, _preact.h)("p", {
172 "class": "help",
173 style: "margin-bottom: 0;"
174 }, "Use the buttons above to generate your field HTML, or manually modify your form below.") : '');
175 }
176 }]);
177
178 return FieldBuilder;
179 }(_preact.Component);
180
181 exports.FieldBuilder = FieldBuilder;
182
183 },{"./field-configurator.js":4,"preact":20}],4:[function(require,module,exports){
184 'use strict';
185
186 Object.defineProperty(exports, "__esModule", {
187 value: true
188 });
189 exports.FieldConfigurator = void 0;
190
191 var _preact = require("preact");
192
193 var _html = require("../field-builder/html.js");
194
195 var FS = _interopRequireWildcard(require("./field-settings.js"));
196
197 var _linkstate = _interopRequireDefault(require("linkstate"));
198
199 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
200
201 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; } }
202
203 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); }
204
205 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
206
207 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); } }
208
209 function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
210
211 function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }
212
213 function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
214
215 function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
216
217 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); }
218
219 function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
220
221 var FieldConfigurator =
222 /*#__PURE__*/
223 function (_Component) {
224 _inherits(FieldConfigurator, _Component);
225
226 function FieldConfigurator(props) {
227 var _this;
228
229 _classCallCheck(this, FieldConfigurator);
230
231 _this = _possibleConstructorReturn(this, _getPrototypeOf(FieldConfigurator).call(this, props));
232 _this.state = _this.getInitialState();
233 _this.choiceHandlers = {
234 add: _this.addChoice.bind(_assertThisInitialized(_this)),
235 "delete": _this.deleteChoice.bind(_assertThisInitialized(_this)),
236 changeLabel: _this.changeChoiceLabel.bind(_assertThisInitialized(_this)),
237 toggleChecked: _this.toggleChoiceChecked.bind(_assertThisInitialized(_this))
238 };
239 _this.addToForm = _this.addToForm.bind(_assertThisInitialized(_this));
240 _this.handleCancel = _this.handleCancel.bind(_assertThisInitialized(_this));
241 return _this;
242 }
243
244 _createClass(FieldConfigurator, [{
245 key: "getInitialState",
246 value: function getInitialState() {
247 return {
248 formId: document.querySelector('input[name="form_id"]').value,
249 formSlug: document.querySelector('input[name="form[slug]"]').value,
250 fieldType: '',
251 fieldLabel: '',
252 placeholder: '',
253 value: '',
254 multiple: false,
255 wrap: true,
256 required: false,
257 choices: [{
258 checked: false,
259 label: 'One'
260 }, {
261 checked: false,
262 label: 'Two'
263 }],
264 accept: ''
265 };
266 }
267 }, {
268 key: "componentWillReceiveProps",
269 value: function componentWillReceiveProps(props) {
270 var newState = {
271 fieldType: props.fieldType // when changing from field that accepts multiple values to single-value field, reset all pre-selections
272
273 };
274
275 if (this.state.fieldType === 'checkbox' && props.fieldType !== 'checkbox') {
276 newState.choices = this.state.choices.map(function (c, i) {
277 c.checked = false;
278 return c;
279 });
280 }
281
282 this.setState(newState);
283 }
284 }, {
285 key: "addToForm",
286 value: function addToForm() {
287 var html = (0, _html.htmlgenerate)(this.state);
288 window.html_forms.Editor.replaceSelection(html);
289 }
290 }, {
291 key: "addChoice",
292 value: function addChoice() {
293 var arr = this.state.choices;
294 arr.push({
295 checked: false,
296 label: '...'
297 });
298 this.setState({
299 choices: arr
300 });
301 }
302 }, {
303 key: "deleteChoice",
304 value: function deleteChoice(e) {
305 var arr = this.state.choices;
306 var index = e.target.parentElement.getAttribute('data-key');
307 arr.splice(index, 1);
308 this.setState({
309 choices: arr
310 });
311 }
312 }, {
313 key: "changeChoiceLabel",
314 value: function changeChoiceLabel(e) {
315 var arr = this.state.choices;
316 var index = e.target.parentElement.getAttribute('data-key');
317 arr[index].label = e.target.value;
318 this.setState({
319 choices: arr
320 });
321 }
322 }, {
323 key: "toggleChoiceChecked",
324 value: function toggleChoiceChecked(e) {
325 var arr = this.state.choices;
326 var index = e.target.parentElement.getAttribute('data-key');
327 arr[index].checked = !arr[index].checked;
328 this.setState({
329 choices: arr
330 });
331 }
332 }, {
333 key: "handleCancel",
334 value: function handleCancel() {
335 // revert back to initial state
336 this.setState(this.getInitialState());
337 this.props.onCancel();
338 }
339 }, {
340 key: "render",
341 value: function render(props, state) {
342 if (props.rows.length === 0) {
343 return '';
344 }
345
346 var formFields = [];
347
348 for (var i = 0; i < props.rows.length; i++) {
349 switch (props.rows[i]) {
350 case 'label':
351 formFields.push((0, _preact.h)(FS.Label, {
352 value: state.fieldLabel,
353 onChange: (0, _linkstate["default"])(this, 'fieldLabel')
354 }));
355 break;
356
357 case 'placeholder':
358 formFields.push((0, _preact.h)(FS.Placeholder, {
359 value: state.placeholder,
360 onChange: (0, _linkstate["default"])(this, 'placeholder')
361 }));
362 break;
363
364 case 'default-value':
365 formFields.push((0, _preact.h)(FS.DefaultValue, {
366 value: state.value,
367 onChange: (0, _linkstate["default"])(this, 'value')
368 }));
369 break;
370
371 case 'multiple':
372 formFields.push((0, _preact.h)(FS.Multiple, {
373 checked: state.multiple,
374 onChange: (0, _linkstate["default"])(this, 'multiple')
375 }));
376 break;
377
378 case 'required':
379 formFields.push((0, _preact.h)(FS.Required, {
380 checked: state.required,
381 onChange: (0, _linkstate["default"])(this, 'required')
382 }));
383 break;
384
385 case 'wrap':
386 formFields.push((0, _preact.h)(FS.Wrap, {
387 checked: state.wrap,
388 onChange: (0, _linkstate["default"])(this, 'wrap')
389 }));
390 break;
391
392 case 'add-to-form':
393 formFields.push((0, _preact.h)(FS.AddToForm, {
394 onSubmit: this.addToForm,
395 onCancel: this.handleCancel
396 }));
397 break;
398
399 case 'choices':
400 formFields.push((0, _preact.h)(FS.Choices, {
401 multiple: state.fieldType === 'checkbox',
402 choices: state.choices,
403 handlers: this.choiceHandlers
404 }));
405 break;
406
407 case 'button-text':
408 formFields.push((0, _preact.h)(FS.ButtonText, {
409 value: state.value,
410 onChange: (0, _linkstate["default"])(this, 'value')
411 }));
412 break;
413
414 case 'accept':
415 formFields.push((0, _preact.h)(FS.Accept, {
416 value: state.accept,
417 onChange: (0, _linkstate["default"])(this, 'accept')
418 }));
419 break;
420 }
421 }
422
423 return (0, _preact.h)("div", {
424 "class": "field-config",
425 onKeyPress: FieldConfigurator.handleKeyPress
426 }, formFields);
427 }
428 }], [{
429 key: "handleKeyPress",
430 value: function handleKeyPress(e) {
431 // stop RETURN from submitting the parent form.
432 if (e.keyCode === 13) {
433 e.preventDefault();
434 }
435 }
436 }]);
437
438 return FieldConfigurator;
439 }(_preact.Component);
440
441 exports.FieldConfigurator = FieldConfigurator;
442
443 },{"../field-builder/html.js":7,"./field-settings.js":5,"linkstate":19,"preact":20}],5:[function(require,module,exports){
444 "use strict";
445
446 Object.defineProperty(exports, "__esModule", {
447 value: true
448 });
449 exports.AddToForm = AddToForm;
450 exports.Label = Label;
451 exports.Placeholder = Placeholder;
452 exports.DefaultValue = DefaultValue;
453 exports.Wrap = Wrap;
454 exports.Required = Required;
455 exports.Choices = Choices;
456 exports.ButtonText = ButtonText;
457 exports.Accept = Accept;
458 exports.Multiple = Multiple;
459
460 var _preact = require("preact");
461
462 function AddToForm(props) {
463 return (0, _preact.h)("div", {
464 "class": "hf-small-margin"
465 }, (0, _preact.h)("button", {
466 "class": "button",
467 type: "button",
468 onClick: props.onSubmit
469 }, "Add field to form"), " \xA0 ", (0, _preact.h)("a", {
470 href: "javascript:void(0);",
471 "class": "hf-small",
472 style: "vertical-align: middle;",
473 onClick: props.onCancel
474 }, "or close field helper"));
475 }
476
477 function Label(props) {
478 return (0, _preact.h)("div", {
479 "class": "hf-small-margin"
480 }, (0, _preact.h)("label", {
481 "for": "hf-fg-field-label"
482 }, "Field label ", (0, _preact.h)("span", {
483 "class": "hf-required"
484 }, "*")), (0, _preact.h)("input", {
485 id: "hf-fg-field-label",
486 type: "text",
487 value: props.value,
488 onChange: props.onChange
489 }));
490 }
491
492 function Placeholder(props) {
493 return (0, _preact.h)("div", {
494 "class": "hf-small-margin"
495 }, (0, _preact.h)("label", {
496 "for": "hf-fg-placeholder"
497 }, "Placeholder ", (0, _preact.h)("span", {
498 "class": "hf-italic hf-pull-right"
499 }, "Optional")), (0, _preact.h)("input", {
500 id: "hf-fg-placeholder",
501 type: "text",
502 value: props.value,
503 onChange: props.onChange
504 }), (0, _preact.h)("p", {
505 "class": "help"
506 }, "Text to show when field has no value."));
507 }
508
509 function ButtonText(props) {
510 return (0, _preact.h)("div", {
511 "class": "hf-small-margin"
512 }, (0, _preact.h)("label", {
513 "for": "hf-fg-default-value"
514 }, "Button text ", (0, _preact.h)("span", {
515 "class": "hf-required"
516 }, "*")), (0, _preact.h)("input", {
517 id: "hf-fg-default-value",
518 type: "text",
519 value: props.value,
520 onChange: props.onChange
521 }), (0, _preact.h)("p", {
522 "class": "help"
523 }, "Text to show on the button."));
524 }
525
526 function DefaultValue(props) {
527 return (0, _preact.h)("div", {
528 "class": "hf-small-margin"
529 }, (0, _preact.h)("label", {
530 "for": "hf-fg-default-value"
531 }, "Default value ", (0, _preact.h)("span", {
532 "class": "hf-italic hf-pull-right"
533 }, "Optional")), (0, _preact.h)("input", {
534 id: "hf-fg-default-value",
535 type: "text",
536 value: props.value,
537 onChange: props.onChange
538 }), (0, _preact.h)("p", {
539 "class": "help"
540 }, "Text to pre-fill this field with."));
541 }
542
543 function Multiple(props) {
544 return (0, _preact.h)("div", {
545 "class": "hf-small-margin"
546 }, (0, _preact.h)("label", {
547 "class": "inline"
548 }, (0, _preact.h)("input", {
549 type: "checkbox",
550 value: "1",
551 defaultChecked: props.checked,
552 onChange: props.onChange
553 }), "Accept multiple values."));
554 }
555
556 function Wrap(props) {
557 return (0, _preact.h)("div", {
558 "class": "hf-small-margin"
559 }, (0, _preact.h)("label", {
560 "class": "inline"
561 }, (0, _preact.h)("input", {
562 type: "checkbox",
563 value: "1",
564 defaultChecked: props.checked,
565 onChange: props.onChange
566 }), "Wrap this field in paragraph tags."));
567 }
568
569 function Required(props) {
570 return (0, _preact.h)("div", {
571 "class": "hf-small-margin"
572 }, (0, _preact.h)("label", {
573 "class": "inline"
574 }, (0, _preact.h)("input", {
575 type: "checkbox",
576 value: "1",
577 defaultChecked: props.checked,
578 onChange: props.onChange
579 }), "This field is required."));
580 }
581
582 function Choices(props) {
583 var choiceFields = props.choices.map(function (choice, k) {
584 return (0, _preact.h)("div", {
585 "data-key": k
586 }, (0, _preact.h)("input", {
587 type: props.multiple ? 'checkbox' : 'radio',
588 name: "selected",
589 defaultChecked: choice.checked,
590 onChange: props.handlers.toggleChecked,
591 title: "Pre-select this choice?"
592 }), (0, _preact.h)("input", {
593 type: "text",
594 value: choice.label,
595 placeholder: "Choice label",
596 style: "width: 80%;",
597 onChange: props.handlers.changeLabel
598 }), (0, _preact.h)("a", {
599 href: "javascript:void(0);",
600 onClick: props.handlers["delete"],
601 style: "text-decoration: none;",
602 title: "Delete choice"
603 }, "\u2715"));
604 });
605 return (0, _preact.h)("div", {
606 "class": "hf-small-margin"
607 }, (0, _preact.h)("label", null, "Choices"), choiceFields, (0, _preact.h)("input", {
608 type: props.multiple ? 'checkbox' : 'radio',
609 style: "visibility: hidden;"
610 }), (0, _preact.h)("a", {
611 href: "javascript:void(0);",
612 onClick: props.handlers.add
613 }, "Add choice"));
614 }
615
616 function Accept(props) {
617 return (0, _preact.h)("div", {
618 "class": "hf-small-margin"
619 }, (0, _preact.h)("label", null, "Accepted file types"), (0, _preact.h)("input", {
620 type: "text",
621 value: props.value,
622 onChange: props.onChange
623 }), (0, _preact.h)("p", {
624 "class": "help"
625 }, "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."));
626 }
627
628 },{"preact":20}],6:[function(require,module,exports){
629 'use strict'; // imports
630
631 Object.defineProperty(exports, "__esModule", {
632 value: true
633 });
634 exports["default"] = void 0;
635
636 var _preact = require("preact");
637
638 var _fieldBuilder = require("./components/field-builder.js");
639
640 // vars
641 var rootElement; // functions
642
643 function Field(key, label, configRows) {
644 this.key = key;
645 this.label = label;
646 this.configRows = configRows || [];
647 }
648
649 function mount() {
650 rootElement = (0, _preact.render)((0, _preact.h)(_fieldBuilder.FieldBuilder, {
651 fields: fields
652 }), document.getElementById('hf-field-builder'), rootElement);
653 } // bootstrap
654
655
656 var 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'])];
657 var _default = {
658 init: function init() {
659 mount();
660 },
661 registerField: function registerField(key, label, configRows) {
662 fields.push(new Field(key, label, configRows));
663 mount();
664 }
665 };
666 exports["default"] = _default;
667
668 },{"./components/field-builder.js":3,"preact":20}],7:[function(require,module,exports){
669 'use strict';
670
671 Object.defineProperty(exports, "__esModule", {
672 value: true
673 });
674 exports.htmlgenerate = htmlgenerate;
675
676 var _preact = require("preact");
677
678 function htmlgenerate(conf) {
679 var fieldName = namify(conf.fieldLabel);
680 var fieldId = conf.formSlug + '-' + fieldName;
681 var label = conf.fieldLabel.length && conf.fieldType !== 'submit' ? (0, _preact.h)('label', {
682 "for": fieldId
683 }, conf.fieldLabel) : '';
684 var fieldAttr, field;
685
686 switch (conf.fieldType) {
687 case 'text':
688 default:
689 fieldAttr = {
690 type: conf.fieldType,
691 name: fieldName,
692 value: conf.value,
693 placeholder: conf.placeholder,
694 required: conf.required,
695 id: fieldId
696 };
697 field = html('input', fieldAttr);
698 break;
699
700 case 'textarea':
701 fieldAttr = {
702 name: fieldName,
703 placeholder: conf.placeholder,
704 required: conf.required,
705 id: fieldId
706 };
707 field = html('textarea', fieldAttr, conf.value);
708 break;
709
710 case 'dropdown':
711 fieldAttr = {
712 name: fieldName + (conf.multiple ? '[]' : ''),
713 required: conf.required,
714 multiple: conf.multiple,
715 id: fieldId
716 };
717 field = html('select', fieldAttr, conf.choices.map(function (choice) {
718 return html('option', {
719 selected: choice.checked
720 }, choice.label);
721 }));
722 break;
723
724 case 'radio':
725 field = conf.choices.map(function (choice) {
726 return html('label', {}, [html('input', {
727 type: 'radio',
728 name: fieldName,
729 value: choice.label,
730 selected: choice.checked
731 }), ' ', html('span', {}, choice.label)]);
732 });
733 break;
734
735 case 'checkbox':
736 field = conf.choices.map(function (choice) {
737 return html('label', {}, [html('input', {
738 type: 'checkbox',
739 name: fieldName + '[]',
740 value: choice.label,
741 checked: choice.checked
742 }), ' ', html('span', {}, choice.label)]);
743 });
744 break;
745
746 case 'file':
747 fieldAttr = {
748 type: 'file',
749 name: fieldName,
750 required: conf.required,
751 id: fieldId
752 };
753
754 if (conf.accept) {
755 fieldAttr.accept = conf.accept;
756 }
757
758 field = html('input', fieldAttr);
759 break;
760
761 case 'submit':
762 fieldAttr = {
763 type: 'submit',
764 value: conf.value
765 };
766 field = html('input', fieldAttr);
767 break;
768 }
769
770 var str = '';
771
772 if (conf.wrap) {
773 var children = label !== '' ? ['\n\t', label] : [];
774 children = children.concat(['\n\t', field, '\n']);
775 var tmpl = (0, _preact.h)('p', {}, children);
776 str = renderToString(tmpl);
777 } else {
778 str += renderToString(label);
779 str += '\n';
780 str += renderToString(field);
781 }
782
783 str += '\n';
784 return str;
785 }
786
787 function renderToString(vdom) {
788 var el = document.createElement('div');
789 (0, _preact.render)(vdom, el);
790 return el.innerHTML;
791 }
792
793 function html(tag, attr, children) {
794 attr = filterEmptyObjectValues(attr);
795 return (0, _preact.h)(tag, attr, children);
796 }
797
798 function namify(str) {
799 return str.replace(/ /g, '_').replace(/[^\w[\]_]*/g, '').toUpperCase();
800 }
801
802 function filterEmptyObjectValues(obj) {
803 var newObj = {};
804
805 for (var propName in obj) {
806 if (obj[propName] !== false && obj[propName] !== '') {
807 newObj[propName] = obj[propName];
808 }
809 }
810
811 return newObj;
812 }
813
814 },{"preact":20}],8:[function(require,module,exports){
815 'use strict';
816
817 Object.defineProperty(exports, "__esModule", {
818 value: true
819 });
820 exports["default"] = void 0;
821 var availableActions, actionTemplates, actions;
822
823 function init() {
824 actions = document.getElementById('hf-form-actions');
825 availableActions = document.getElementById('hf-available-form-actions');
826 actionTemplates = document.getElementById('hf-form-action-templates'); // turn settings into accordions
827
828 [].forEach.call(actions.querySelectorAll('.hf-action-settings'), function (el) {
829 el.parentNode.removeChild(el);
830 var heading = el.getAttribute('data-title');
831 var summary = el.querySelector('.hf-action-summary');
832
833 if (summary) {
834 heading += ' &mdash; <span class="hf-muted">' + summary.innerHTML + '</span>';
835 }
836
837 var wrap = createAccordion(heading, el.innerHTML);
838 actions.appendChild(wrap);
839 actions.querySelector('#hf-form-actions-empty').style.display = 'none';
840 });
841 availableActions.addEventListener('click', addAction, true);
842 }
843
844 function createAccordion(headingHTML, contentHTML) {
845 var wrap = document.createElement('div');
846 wrap.className = 'hf-accordion expanded ';
847 var heading = document.createElement('h4');
848 heading.className = 'hf-accordion-heading';
849 heading.innerHTML = headingHTML;
850 wrap.appendChild(heading);
851 var content = document.createElement('div');
852 content.className = 'hf-accordion-content';
853 content.innerHTML = contentHTML;
854 wrap.appendChild(content);
855 var deleteWrap = document.createElement('p');
856 deleteWrap.style.textAlign = 'right';
857 var deleteLink = document.createElement('a');
858 deleteLink.href = 'javascript:void(0);';
859 deleteLink.className = 'danger';
860 deleteLink.innerText = 'Delete this action';
861 deleteWrap.appendChild(deleteLink);
862 content.appendChild(deleteWrap); // bind handlers
863
864 heading.addEventListener('click', createToggleActionHandler(wrap, content));
865 deleteLink.addEventListener('click', createDeleteActionHandler(wrap));
866 return wrap;
867 }
868
869 function addAction(e) {
870 var el = e.target || e.srcElement;
871
872 if (el.tagName !== 'INPUT') {
873 return;
874 }
875
876 var actionType = el.getAttribute('data-action-type');
877 var actionTemplate = actionTemplates.querySelector("#hf-action-type-".concat(actionType, "-template")); // append HTML to actions wrapper
878
879 var index = actions.querySelectorAll('div').length - 1;
880 var wrap = createAccordion(el.value, actionTemplate.innerHTML.replace(/\$index/g, index));
881 actions.appendChild(wrap); // hide "no form actions" message
882
883 actions.querySelector('#hf-form-actions-empty').style.display = 'none';
884 window.html_forms.Editor.updateFieldVariables();
885 }
886
887 function createDeleteActionHandler(wrap) {
888 return function () {
889 actions.removeChild(wrap);
890
891 if (actions.childElementCount === 1) {
892 actions.querySelector('#hf-form-actions-empty').style.display = '';
893 }
894 };
895 }
896
897 function createToggleActionHandler(wrap, content) {
898 return function () {
899 var show = content.offsetParent === null;
900 wrap.className = wrap.className.replace('expanded', '') + (show ? ' expanded' : '');
901 content.style.display = show ? 'block' : 'none';
902 };
903 }
904
905 var _default = {
906 init: init
907 };
908 exports["default"] = _default;
909
910 },{}],9:[function(require,module,exports){
911 'use strict'; // load CodeMirror & plugins
912
913 Object.defineProperty(exports, "__esModule", {
914 value: true
915 });
916 exports["default"] = void 0;
917
918 var CodeMirror = require('codemirror');
919
920 require('codemirror/mode/xml/xml');
921
922 require('codemirror/mode/javascript/javascript');
923
924 require('codemirror/mode/css/css');
925
926 require('codemirror/mode/htmlmixed/htmlmixed');
927
928 require('codemirror/addon/fold/xml-fold');
929
930 require('codemirror/addon/edit/matchtags');
931
932 require('codemirror/addon/edit/closetag.js');
933
934 var editor, element, dom, requiredFieldsInput, emailFieldsInput, previewFrame, previewDom;
935 var templateRegex = /\{\{ *(\w+)(?:\.([\w.]+))? *(?:\|\| *(\w+))? *\}\}/g;
936
937 function init() {
938 previewFrame = document.getElementById('hf-form-preview');
939 element = document.getElementById('hf-form-editor');
940 dom = document.createElement('form');
941 requiredFieldsInput = document.getElementById('hf-required-fields');
942 emailFieldsInput = document.getElementById('hf-email-fields');
943 dom.innerHTML = element.value;
944 editor = CodeMirror.fromTextArea(element, {
945 selectionPointer: true,
946 matchTags: {
947 bothTags: true
948 },
949 mode: 'htmlmixed',
950 htmlMode: true,
951 autoCloseTags: true,
952 autoRefresh: true,
953 styleActiveLine: true,
954 matchBrackets: true
955 });
956 editor.on('changes', debounce(updatePreview, 500));
957 editor.on('changes', debounce(updateShadowDOM, 100));
958 editor.on('changes', debounce(updateFieldVariables, 500));
959 editor.on('blur', updatePreview);
960 editor.on('blur', updateShadowDOM);
961 editor.on('blur', updateFieldVariables);
962 editor.on('blur', updateRequiredFields);
963 editor.on('blur', updateEmailFields);
964 previewFrame.addEventListener('load', setPreviewDom);
965 setPreviewDom();
966 updateFieldVariables();
967 }
968
969 function setPreviewDom() {
970 var frameContent = previewFrame.contentDocument || previewFrame.contentWindow.document;
971 previewDom = frameContent.querySelector('.hf-fields-wrap');
972
973 if (previewDom) {
974 updatePreview();
975 }
976 }
977
978 function getFieldVariableName(f) {
979 return f.name.replace('[]', '').replace(/\[(\w+)\]/g, '.$1');
980 }
981
982 function updateFieldVariables() {
983 var fields = dom.querySelectorAll('input[name], select[name], textarea[name], button[name]');
984 var fieldVariables = uniq([].map.call(fields, function (f) {
985 return '[' + getFieldVariableName(f) + ']';
986 }));
987 var wpbody = document.getElementById('wpbody-content');
988 [].forEach.call(document.querySelectorAll('.hf-field-names'), function (el) {
989 // remove existing variables
990 while (el.firstChild) {
991 el.removeChild(el.firstChild);
992 }
993
994 var variableElements = fieldVariables.map(function (n) {
995 // measure width of actual font size for prettiness
996 var sizeEl = document.createElement('span');
997 sizeEl.style.visibility = 'hidden';
998 sizeEl.innerText = n;
999 wpbody.appendChild(sizeEl);
1000 var width = sizeEl.offsetWidth;
1001 wpbody.removeChild(sizeEl); // add input el
1002
1003 var el = document.createElement('input');
1004 el.type = 'text';
1005 el.style.maxWidth = width * 1.1 + 14 + 'px';
1006 el.value = n;
1007 el.readonly = true;
1008
1009 el.onfocus = function () {
1010 return el.select();
1011 };
1012
1013 return el;
1014 });
1015 variableElements.forEach(function (vel, i, arr) {
1016 el.appendChild(vel);
1017 });
1018 });
1019 }
1020
1021 function updatePreview() {
1022 var markup = editor.getValue(); // replace template tags
1023
1024 markup = markup.replace(templateRegex, function (s, m) {
1025 // if a default value was provided, use that
1026 if (arguments[3]) {
1027 return arguments[3];
1028 }
1029
1030 return '';
1031 }); // update dom
1032
1033 previewDom.innerHTML = markup;
1034 previewDom.dispatchEvent(new Event('hf-refresh'));
1035 }
1036
1037 function updateShadowDOM() {
1038 dom.innerHTML = editor.getValue();
1039 }
1040
1041 function updateRequiredFields() {
1042 var fields = dom.querySelectorAll('[required]');
1043 var fieldNames = [].map.call(fields, getFieldVariableName);
1044 requiredFieldsInput.value = fieldNames.join(',');
1045 }
1046
1047 function updateEmailFields() {
1048 var fields = dom.querySelectorAll('input[type="email"]');
1049 var fieldNames = [].map.call(fields, getFieldVariableName);
1050 emailFieldsInput.value = fieldNames.join(',');
1051 }
1052
1053 function replaceSelection(str) {
1054 editor.replaceSelection(str);
1055 editor.focus();
1056 }
1057
1058 function debounce(func, wait, immediate) {
1059 var timeout;
1060 return function () {
1061 var context = this;
1062 var args = arguments;
1063
1064 var later = function later() {
1065 timeout = null;
1066 if (!immediate) func.apply(context, args);
1067 };
1068
1069 var callNow = immediate && !timeout;
1070 clearTimeout(timeout);
1071 timeout = setTimeout(later, wait);
1072 if (callNow) func.apply(context, args);
1073 };
1074 }
1075
1076 ;
1077
1078 function uniq(a) {
1079 var seen = {};
1080 return a.filter(function (item) {
1081 return seen.hasOwnProperty(item) ? false : seen[item] = true;
1082 });
1083 }
1084
1085 var _default = {
1086 init: init,
1087 replaceSelection: replaceSelection,
1088 updateFieldVariables: updateFieldVariables
1089 };
1090 exports["default"] = _default;
1091
1092 },{"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){
1093 'use strict';
1094
1095 Object.defineProperty(exports, "__esModule", {
1096 value: true
1097 });
1098 exports["default"] = void 0;
1099 var tabs, tabNavs;
1100
1101 function init() {
1102 tabs = document.querySelectorAll('.hf-tab');
1103 tabNavs = document.querySelectorAll('#hf-tabs-nav a');
1104
1105 for (var i = 0; i < tabNavs.length; i++) {
1106 tabNavs[i].addEventListener('click', open);
1107 }
1108 }
1109
1110 ;
1111
1112 function open(e) {
1113 var tabTarget = this.getAttribute('data-tab-target');
1114
1115 for (var i = 0; i < tabNavs.length; i++) {
1116 tabNavs[i].classList.toggle('nav-tab-active', tabNavs[i] === this);
1117 }
1118
1119 this.blur();
1120
1121 for (var _i = 0; _i < tabs.length; _i++) {
1122 var tab = tabs[_i];
1123 tab.classList.toggle('hf-tab-active', tab.getAttribute('data-tab') === tabTarget);
1124 }
1125
1126 document.title = document.title.replace(document.title.split(' - ').shift(), this.innerText + ' ');
1127
1128 if (window.history) {
1129 var newUrl = window.location.href;
1130 newUrl = newUrl.replace(/&tab=\w+/, '');
1131 newUrl += '&tab=' + tabTarget;
1132 window.history.replaceState({
1133 tab: tabTarget
1134 }, document.title, newUrl);
1135 }
1136
1137 e.preventDefault();
1138 }
1139
1140 ;
1141 var _default = {
1142 init: init,
1143 open: open
1144 };
1145 exports["default"] = _default;
1146
1147 },{}],11:[function(require,module,exports){
1148 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1149 // Distributed under an MIT license: https://codemirror.net/LICENSE
1150
1151 /**
1152 * Tag-closer extension for CodeMirror.
1153 *
1154 * This extension adds an "autoCloseTags" option that can be set to
1155 * either true to get the default behavior, or an object to further
1156 * configure its behavior.
1157 *
1158 * These are supported options:
1159 *
1160 * `whenClosing` (default true)
1161 * Whether to autoclose when the '/' of a closing tag is typed.
1162 * `whenOpening` (default true)
1163 * Whether to autoclose the tag when the final '>' of an opening
1164 * tag is typed.
1165 * `dontCloseTags` (default is empty tags for HTML, none for XML)
1166 * An array of tag names that should not be autoclosed.
1167 * `indentTags` (default is block tags for HTML, none for XML)
1168 * An array of tag names that should, when opened, cause a
1169 * blank line to be added inside the tag, and the blank line and
1170 * closing line to be indented.
1171 * `emptyTags` (default is none)
1172 * An array of XML tag names that should be autoclosed with '/>'.
1173 *
1174 * See demos/closetag.html for a usage example.
1175 */
1176
1177 (function(mod) {
1178 if (typeof exports == "object" && typeof module == "object") // CommonJS
1179 mod(require("../../lib/codemirror"), require("../fold/xml-fold"));
1180 else if (typeof define == "function" && define.amd) // AMD
1181 define(["../../lib/codemirror", "../fold/xml-fold"], mod);
1182 else // Plain browser env
1183 mod(CodeMirror);
1184 })(function(CodeMirror) {
1185 CodeMirror.defineOption("autoCloseTags", false, function(cm, val, old) {
1186 if (old != CodeMirror.Init && old)
1187 cm.removeKeyMap("autoCloseTags");
1188 if (!val) return;
1189 var map = {name: "autoCloseTags"};
1190 if (typeof val != "object" || val.whenClosing)
1191 map["'/'"] = function(cm) { return autoCloseSlash(cm); };
1192 if (typeof val != "object" || val.whenOpening)
1193 map["'>'"] = function(cm) { return autoCloseGT(cm); };
1194 cm.addKeyMap(map);
1195 });
1196
1197 var htmlDontClose = ["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param",
1198 "source", "track", "wbr"];
1199 var htmlIndent = ["applet", "blockquote", "body", "button", "div", "dl", "fieldset", "form", "frameset", "h1", "h2", "h3", "h4",
1200 "h5", "h6", "head", "html", "iframe", "layer", "legend", "object", "ol", "p", "select", "table", "ul"];
1201
1202 function autoCloseGT(cm) {
1203 if (cm.getOption("disableInput")) return CodeMirror.Pass;
1204 var ranges = cm.listSelections(), replacements = [];
1205 var opt = cm.getOption("autoCloseTags");
1206 for (var i = 0; i < ranges.length; i++) {
1207 if (!ranges[i].empty()) return CodeMirror.Pass;
1208 var pos = ranges[i].head, tok = cm.getTokenAt(pos);
1209 var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state;
1210 if (inner.mode.name != "xml" || !state.tagName) return CodeMirror.Pass;
1211
1212 var html = inner.mode.configuration == "html";
1213 var dontCloseTags = (typeof opt == "object" && opt.dontCloseTags) || (html && htmlDontClose);
1214 var indentTags = (typeof opt == "object" && opt.indentTags) || (html && htmlIndent);
1215
1216 var tagName = state.tagName;
1217 if (tok.end > pos.ch) tagName = tagName.slice(0, tagName.length - tok.end + pos.ch);
1218 var lowerTagName = tagName.toLowerCase();
1219 // Don't process the '>' at the end of an end-tag or self-closing tag
1220 if (!tagName ||
1221 tok.type == "string" && (tok.end != pos.ch || !/[\"\']/.test(tok.string.charAt(tok.string.length - 1)) || tok.string.length == 1) ||
1222 tok.type == "tag" && state.type == "closeTag" ||
1223 tok.string.indexOf("/") == (tok.string.length - 1) || // match something like <someTagName />
1224 dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1 ||
1225 closingTagExists(cm, tagName, pos, state, true))
1226 return CodeMirror.Pass;
1227
1228 var emptyTags = typeof opt == "object" && opt.emptyTags;
1229 if (emptyTags && indexOf(emptyTags, tagName) > -1) {
1230 replacements[i] = { text: "/>", newPos: CodeMirror.Pos(pos.line, pos.ch + 2) };
1231 continue;
1232 }
1233
1234 var indent = indentTags && indexOf(indentTags, lowerTagName) > -1;
1235 replacements[i] = {indent: indent,
1236 text: ">" + (indent ? "\n\n" : "") + "</" + tagName + ">",
1237 newPos: indent ? CodeMirror.Pos(pos.line + 1, 0) : CodeMirror.Pos(pos.line, pos.ch + 1)};
1238 }
1239
1240 var dontIndentOnAutoClose = (typeof opt == "object" && opt.dontIndentOnAutoClose);
1241 for (var i = ranges.length - 1; i >= 0; i--) {
1242 var info = replacements[i];
1243 cm.replaceRange(info.text, ranges[i].head, ranges[i].anchor, "+insert");
1244 var sel = cm.listSelections().slice(0);
1245 sel[i] = {head: info.newPos, anchor: info.newPos};
1246 cm.setSelections(sel);
1247 if (!dontIndentOnAutoClose && info.indent) {
1248 cm.indentLine(info.newPos.line, null, true);
1249 cm.indentLine(info.newPos.line + 1, null, true);
1250 }
1251 }
1252 }
1253
1254 function autoCloseCurrent(cm, typingSlash) {
1255 var ranges = cm.listSelections(), replacements = [];
1256 var head = typingSlash ? "/" : "</";
1257 var opt = cm.getOption("autoCloseTags");
1258 var dontIndentOnAutoClose = (typeof opt == "object" && opt.dontIndentOnSlash);
1259 for (var i = 0; i < ranges.length; i++) {
1260 if (!ranges[i].empty()) return CodeMirror.Pass;
1261 var pos = ranges[i].head, tok = cm.getTokenAt(pos);
1262 var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state;
1263 if (typingSlash && (tok.type == "string" || tok.string.charAt(0) != "<" ||
1264 tok.start != pos.ch - 1))
1265 return CodeMirror.Pass;
1266 // Kludge to get around the fact that we are not in XML mode
1267 // when completing in JS/CSS snippet in htmlmixed mode. Does not
1268 // work for other XML embedded languages (there is no general
1269 // way to go from a mixed mode to its current XML state).
1270 var replacement;
1271 if (inner.mode.name != "xml") {
1272 if (cm.getMode().name == "htmlmixed" && inner.mode.name == "javascript")
1273 replacement = head + "script";
1274 else if (cm.getMode().name == "htmlmixed" && inner.mode.name == "css")
1275 replacement = head + "style";
1276 else
1277 return CodeMirror.Pass;
1278 } else {
1279 if (!state.context || !state.context.tagName ||
1280 closingTagExists(cm, state.context.tagName, pos, state))
1281 return CodeMirror.Pass;
1282 replacement = head + state.context.tagName;
1283 }
1284 if (cm.getLine(pos.line).charAt(tok.end) != ">") replacement += ">";
1285 replacements[i] = replacement;
1286 }
1287 cm.replaceSelections(replacements);
1288 ranges = cm.listSelections();
1289 if (!dontIndentOnAutoClose) {
1290 for (var i = 0; i < ranges.length; i++)
1291 if (i == ranges.length - 1 || ranges[i].head.line < ranges[i + 1].head.line)
1292 cm.indentLine(ranges[i].head.line);
1293 }
1294 }
1295
1296 function autoCloseSlash(cm) {
1297 if (cm.getOption("disableInput")) return CodeMirror.Pass;
1298 return autoCloseCurrent(cm, true);
1299 }
1300
1301 CodeMirror.commands.closeTag = function(cm) { return autoCloseCurrent(cm); };
1302
1303 function indexOf(collection, elt) {
1304 if (collection.indexOf) return collection.indexOf(elt);
1305 for (var i = 0, e = collection.length; i < e; ++i)
1306 if (collection[i] == elt) return i;
1307 return -1;
1308 }
1309
1310 // If xml-fold is loaded, we use its functionality to try and verify
1311 // whether a given tag is actually unclosed.
1312 function closingTagExists(cm, tagName, pos, state, newTag) {
1313 if (!CodeMirror.scanForClosingTag) return false;
1314 var end = Math.min(cm.lastLine() + 1, pos.line + 500);
1315 var nextClose = CodeMirror.scanForClosingTag(cm, pos, null, end);
1316 if (!nextClose || nextClose.tag != tagName) return false;
1317 var cx = state.context;
1318 // If the immediate wrapping context contains onCx instances of
1319 // the same tag, a closing tag only exists if there are at least
1320 // that many closing tags of that type following.
1321 for (var onCx = newTag ? 1 : 0; cx && cx.tagName == tagName; cx = cx.prev) ++onCx;
1322 pos = nextClose.to;
1323 for (var i = 1; i < onCx; i++) {
1324 var next = CodeMirror.scanForClosingTag(cm, pos, null, end);
1325 if (!next || next.tag != tagName) return false;
1326 pos = next.to;
1327 }
1328 return true;
1329 }
1330 });
1331
1332 },{"../../lib/codemirror":14,"../fold/xml-fold":13}],12:[function(require,module,exports){
1333 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1334 // Distributed under an MIT license: https://codemirror.net/LICENSE
1335
1336 (function(mod) {
1337 if (typeof exports == "object" && typeof module == "object") // CommonJS
1338 mod(require("../../lib/codemirror"), require("../fold/xml-fold"));
1339 else if (typeof define == "function" && define.amd) // AMD
1340 define(["../../lib/codemirror", "../fold/xml-fold"], mod);
1341 else // Plain browser env
1342 mod(CodeMirror);
1343 })(function(CodeMirror) {
1344 "use strict";
1345
1346 CodeMirror.defineOption("matchTags", false, function(cm, val, old) {
1347 if (old && old != CodeMirror.Init) {
1348 cm.off("cursorActivity", doMatchTags);
1349 cm.off("viewportChange", maybeUpdateMatch);
1350 clear(cm);
1351 }
1352 if (val) {
1353 cm.state.matchBothTags = typeof val == "object" && val.bothTags;
1354 cm.on("cursorActivity", doMatchTags);
1355 cm.on("viewportChange", maybeUpdateMatch);
1356 doMatchTags(cm);
1357 }
1358 });
1359
1360 function clear(cm) {
1361 if (cm.state.tagHit) cm.state.tagHit.clear();
1362 if (cm.state.tagOther) cm.state.tagOther.clear();
1363 cm.state.tagHit = cm.state.tagOther = null;
1364 }
1365
1366 function doMatchTags(cm) {
1367 cm.state.failedTagMatch = false;
1368 cm.operation(function() {
1369 clear(cm);
1370 if (cm.somethingSelected()) return;
1371 var cur = cm.getCursor(), range = cm.getViewport();
1372 range.from = Math.min(range.from, cur.line); range.to = Math.max(cur.line + 1, range.to);
1373 var match = CodeMirror.findMatchingTag(cm, cur, range);
1374 if (!match) return;
1375 if (cm.state.matchBothTags) {
1376 var hit = match.at == "open" ? match.open : match.close;
1377 if (hit) cm.state.tagHit = cm.markText(hit.from, hit.to, {className: "CodeMirror-matchingtag"});
1378 }
1379 var other = match.at == "close" ? match.open : match.close;
1380 if (other)
1381 cm.state.tagOther = cm.markText(other.from, other.to, {className: "CodeMirror-matchingtag"});
1382 else
1383 cm.state.failedTagMatch = true;
1384 });
1385 }
1386
1387 function maybeUpdateMatch(cm) {
1388 if (cm.state.failedTagMatch) doMatchTags(cm);
1389 }
1390
1391 CodeMirror.commands.toMatchingTag = function(cm) {
1392 var found = CodeMirror.findMatchingTag(cm, cm.getCursor());
1393 if (found) {
1394 var other = found.at == "close" ? found.open : found.close;
1395 if (other) cm.extendSelection(other.to, other.from);
1396 }
1397 };
1398 });
1399
1400 },{"../../lib/codemirror":14,"../fold/xml-fold":13}],13:[function(require,module,exports){
1401 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1402 // Distributed under an MIT license: https://codemirror.net/LICENSE
1403
1404 (function(mod) {
1405 if (typeof exports == "object" && typeof module == "object") // CommonJS
1406 mod(require("../../lib/codemirror"));
1407 else if (typeof define == "function" && define.amd) // AMD
1408 define(["../../lib/codemirror"], mod);
1409 else // Plain browser env
1410 mod(CodeMirror);
1411 })(function(CodeMirror) {
1412 "use strict";
1413
1414 var Pos = CodeMirror.Pos;
1415 function cmp(a, b) { return a.line - b.line || a.ch - b.ch; }
1416
1417 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";
1418 var nameChar = nameStartChar + "\-\:\.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040";
1419 var xmlTagStart = new RegExp("<(/?)([" + nameStartChar + "][" + nameChar + "]*)", "g");
1420
1421 function Iter(cm, line, ch, range) {
1422 this.line = line; this.ch = ch;
1423 this.cm = cm; this.text = cm.getLine(line);
1424 this.min = range ? Math.max(range.from, cm.firstLine()) : cm.firstLine();
1425 this.max = range ? Math.min(range.to - 1, cm.lastLine()) : cm.lastLine();
1426 }
1427
1428 function tagAt(iter, ch) {
1429 var type = iter.cm.getTokenTypeAt(Pos(iter.line, ch));
1430 return type && /\btag\b/.test(type);
1431 }
1432
1433 function nextLine(iter) {
1434 if (iter.line >= iter.max) return;
1435 iter.ch = 0;
1436 iter.text = iter.cm.getLine(++iter.line);
1437 return true;
1438 }
1439 function prevLine(iter) {
1440 if (iter.line <= iter.min) return;
1441 iter.text = iter.cm.getLine(--iter.line);
1442 iter.ch = iter.text.length;
1443 return true;
1444 }
1445
1446 function toTagEnd(iter) {
1447 for (;;) {
1448 var gt = iter.text.indexOf(">", iter.ch);
1449 if (gt == -1) { if (nextLine(iter)) continue; else return; }
1450 if (!tagAt(iter, gt + 1)) { iter.ch = gt + 1; continue; }
1451 var lastSlash = iter.text.lastIndexOf("/", gt);
1452 var selfClose = lastSlash > -1 && !/\S/.test(iter.text.slice(lastSlash + 1, gt));
1453 iter.ch = gt + 1;
1454 return selfClose ? "selfClose" : "regular";
1455 }
1456 }
1457 function toTagStart(iter) {
1458 for (;;) {
1459 var lt = iter.ch ? iter.text.lastIndexOf("<", iter.ch - 1) : -1;
1460 if (lt == -1) { if (prevLine(iter)) continue; else return; }
1461 if (!tagAt(iter, lt + 1)) { iter.ch = lt; continue; }
1462 xmlTagStart.lastIndex = lt;
1463 iter.ch = lt;
1464 var match = xmlTagStart.exec(iter.text);
1465 if (match && match.index == lt) return match;
1466 }
1467 }
1468
1469 function toNextTag(iter) {
1470 for (;;) {
1471 xmlTagStart.lastIndex = iter.ch;
1472 var found = xmlTagStart.exec(iter.text);
1473 if (!found) { if (nextLine(iter)) continue; else return; }
1474 if (!tagAt(iter, found.index + 1)) { iter.ch = found.index + 1; continue; }
1475 iter.ch = found.index + found[0].length;
1476 return found;
1477 }
1478 }
1479 function toPrevTag(iter) {
1480 for (;;) {
1481 var gt = iter.ch ? iter.text.lastIndexOf(">", iter.ch - 1) : -1;
1482 if (gt == -1) { if (prevLine(iter)) continue; else return; }
1483 if (!tagAt(iter, gt + 1)) { iter.ch = gt; continue; }
1484 var lastSlash = iter.text.lastIndexOf("/", gt);
1485 var selfClose = lastSlash > -1 && !/\S/.test(iter.text.slice(lastSlash + 1, gt));
1486 iter.ch = gt + 1;
1487 return selfClose ? "selfClose" : "regular";
1488 }
1489 }
1490
1491 function findMatchingClose(iter, tag) {
1492 var stack = [];
1493 for (;;) {
1494 var next = toNextTag(iter), end, startLine = iter.line, startCh = iter.ch - (next ? next[0].length : 0);
1495 if (!next || !(end = toTagEnd(iter))) return;
1496 if (end == "selfClose") continue;
1497 if (next[1]) { // closing tag
1498 for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == next[2]) {
1499 stack.length = i;
1500 break;
1501 }
1502 if (i < 0 && (!tag || tag == next[2])) return {
1503 tag: next[2],
1504 from: Pos(startLine, startCh),
1505 to: Pos(iter.line, iter.ch)
1506 };
1507 } else { // opening tag
1508 stack.push(next[2]);
1509 }
1510 }
1511 }
1512 function findMatchingOpen(iter, tag) {
1513 var stack = [];
1514 for (;;) {
1515 var prev = toPrevTag(iter);
1516 if (!prev) return;
1517 if (prev == "selfClose") { toTagStart(iter); continue; }
1518 var endLine = iter.line, endCh = iter.ch;
1519 var start = toTagStart(iter);
1520 if (!start) return;
1521 if (start[1]) { // closing tag
1522 stack.push(start[2]);
1523 } else { // opening tag
1524 for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == start[2]) {
1525 stack.length = i;
1526 break;
1527 }
1528 if (i < 0 && (!tag || tag == start[2])) return {
1529 tag: start[2],
1530 from: Pos(iter.line, iter.ch),
1531 to: Pos(endLine, endCh)
1532 };
1533 }
1534 }
1535 }
1536
1537 CodeMirror.registerHelper("fold", "xml", function(cm, start) {
1538 var iter = new Iter(cm, start.line, 0);
1539 for (;;) {
1540 var openTag = toNextTag(iter)
1541 if (!openTag || iter.line != start.line) return
1542 var end = toTagEnd(iter)
1543 if (!end) return
1544 if (!openTag[1] && end != "selfClose") {
1545 var startPos = Pos(iter.line, iter.ch);
1546 var endPos = findMatchingClose(iter, openTag[2]);
1547 return endPos && cmp(endPos.from, startPos) > 0 ? {from: startPos, to: endPos.from} : null
1548 }
1549 }
1550 });
1551 CodeMirror.findMatchingTag = function(cm, pos, range) {
1552 var iter = new Iter(cm, pos.line, pos.ch, range);
1553 if (iter.text.indexOf(">") == -1 && iter.text.indexOf("<") == -1) return;
1554 var end = toTagEnd(iter), to = end && Pos(iter.line, iter.ch);
1555 var start = end && toTagStart(iter);
1556 if (!end || !start || cmp(iter, pos) > 0) return;
1557 var here = {from: Pos(iter.line, iter.ch), to: to, tag: start[2]};
1558 if (end == "selfClose") return {open: here, close: null, at: "open"};
1559
1560 if (start[1]) { // closing tag
1561 return {open: findMatchingOpen(iter, start[2]), close: here, at: "close"};
1562 } else { // opening tag
1563 iter = new Iter(cm, to.line, to.ch, range);
1564 return {open: here, close: findMatchingClose(iter, start[2]), at: "open"};
1565 }
1566 };
1567
1568 CodeMirror.findEnclosingTag = function(cm, pos, range, tag) {
1569 var iter = new Iter(cm, pos.line, pos.ch, range);
1570 for (;;) {
1571 var open = findMatchingOpen(iter, tag);
1572 if (!open) break;
1573 var forward = new Iter(cm, pos.line, pos.ch, range);
1574 var close = findMatchingClose(forward, open.tag);
1575 if (close) return {open: open, close: close};
1576 }
1577 };
1578
1579 // Used by addon/edit/closetag.js
1580 CodeMirror.scanForClosingTag = function(cm, pos, name, end) {
1581 var iter = new Iter(cm, pos.line, pos.ch, end ? {from: 0, to: end} : null);
1582 return findMatchingClose(iter, name);
1583 };
1584 });
1585
1586 },{"../../lib/codemirror":14}],14:[function(require,module,exports){
1587 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1588 // Distributed under an MIT license: https://codemirror.net/LICENSE
1589
1590 // This is CodeMirror (https://codemirror.net), a code editor
1591 // implemented in JavaScript on top of the browser's DOM.
1592 //
1593 // You can find some technical background for some of the code below
1594 // at http://marijnhaverbeke.nl/blog/#cm-internals .
1595
1596 (function (global, factory) {
1597 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
1598 typeof define === 'function' && define.amd ? define(factory) :
1599 (global.CodeMirror = factory());
1600 }(this, (function () { 'use strict';
1601
1602 // Kludges for bugs and behavior differences that can't be feature
1603 // detected are enabled based on userAgent etc sniffing.
1604 var userAgent = navigator.userAgent;
1605 var platform = navigator.platform;
1606
1607 var gecko = /gecko\/\d/i.test(userAgent);
1608 var ie_upto10 = /MSIE \d/.test(userAgent);
1609 var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent);
1610 var edge = /Edge\/(\d+)/.exec(userAgent);
1611 var ie = ie_upto10 || ie_11up || edge;
1612 var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1]);
1613 var webkit = !edge && /WebKit\//.test(userAgent);
1614 var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent);
1615 var chrome = !edge && /Chrome\//.test(userAgent);
1616 var presto = /Opera\//.test(userAgent);
1617 var safari = /Apple Computer/.test(navigator.vendor);
1618 var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent);
1619 var phantom = /PhantomJS/.test(userAgent);
1620
1621 var ios = !edge && /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent);
1622 var android = /Android/.test(userAgent);
1623 // This is woefully incomplete. Suggestions for alternative methods welcome.
1624 var mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent);
1625 var mac = ios || /Mac/.test(platform);
1626 var chromeOS = /\bCrOS\b/.test(userAgent);
1627 var windows = /win/i.test(platform);
1628
1629 var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/);
1630 if (presto_version) { presto_version = Number(presto_version[1]); }
1631 if (presto_version && presto_version >= 15) { presto = false; webkit = true; }
1632 // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
1633 var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11));
1634 var captureRightClick = gecko || (ie && ie_version >= 9);
1635
1636 function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") }
1637
1638 var rmClass = function(node, cls) {
1639 var current = node.className;
1640 var match = classTest(cls).exec(current);
1641 if (match) {
1642 var after = current.slice(match.index + match[0].length);
1643 node.className = current.slice(0, match.index) + (after ? match[1] + after : "");
1644 }
1645 };
1646
1647 function removeChildren(e) {
1648 for (var count = e.childNodes.length; count > 0; --count)
1649 { e.removeChild(e.firstChild); }
1650 return e
1651 }
1652
1653 function removeChildrenAndAdd(parent, e) {
1654 return removeChildren(parent).appendChild(e)
1655 }
1656
1657 function elt(tag, content, className, style) {
1658 var e = document.createElement(tag);
1659 if (className) { e.className = className; }
1660 if (style) { e.style.cssText = style; }
1661 if (typeof content == "string") { e.appendChild(document.createTextNode(content)); }
1662 else if (content) { for (var i = 0; i < content.length; ++i) { e.appendChild(content[i]); } }
1663 return e
1664 }
1665 // wrapper for elt, which removes the elt from the accessibility tree
1666 function eltP(tag, content, className, style) {
1667 var e = elt(tag, content, className, style);
1668 e.setAttribute("role", "presentation");
1669 return e
1670 }
1671
1672 var range;
1673 if (document.createRange) { range = function(node, start, end, endNode) {
1674 var r = document.createRange();
1675 r.setEnd(endNode || node, end);
1676 r.setStart(node, start);
1677 return r
1678 }; }
1679 else { range = function(node, start, end) {
1680 var r = document.body.createTextRange();
1681 try { r.moveToElementText(node.parentNode); }
1682 catch(e) { return r }
1683 r.collapse(true);
1684 r.moveEnd("character", end);
1685 r.moveStart("character", start);
1686 return r
1687 }; }
1688
1689 function contains(parent, child) {
1690 if (child.nodeType == 3) // Android browser always returns false when child is a textnode
1691 { child = child.parentNode; }
1692 if (parent.contains)
1693 { return parent.contains(child) }
1694 do {
1695 if (child.nodeType == 11) { child = child.host; }
1696 if (child == parent) { return true }
1697 } while (child = child.parentNode)
1698 }
1699
1700 function activeElt() {
1701 // IE and Edge may throw an "Unspecified Error" when accessing document.activeElement.
1702 // IE < 10 will throw when accessed while the page is loading or in an iframe.
1703 // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable.
1704 var activeElement;
1705 try {
1706 activeElement = document.activeElement;
1707 } catch(e) {
1708 activeElement = document.body || null;
1709 }
1710 while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement)
1711 { activeElement = activeElement.shadowRoot.activeElement; }
1712 return activeElement
1713 }
1714
1715 function addClass(node, cls) {
1716 var current = node.className;
1717 if (!classTest(cls).test(current)) { node.className += (current ? " " : "") + cls; }
1718 }
1719 function joinClasses(a, b) {
1720 var as = a.split(" ");
1721 for (var i = 0; i < as.length; i++)
1722 { if (as[i] && !classTest(as[i]).test(b)) { b += " " + as[i]; } }
1723 return b
1724 }
1725
1726 var selectInput = function(node) { node.select(); };
1727 if (ios) // Mobile Safari apparently has a bug where select() is broken.
1728 { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; }; }
1729 else if (ie) // Suppress mysterious IE10 errors
1730 { selectInput = function(node) { try { node.select(); } catch(_e) {} }; }
1731
1732 function bind(f) {
1733 var args = Array.prototype.slice.call(arguments, 1);
1734 return function(){return f.apply(null, args)}
1735 }
1736
1737 function copyObj(obj, target, overwrite) {
1738 if (!target) { target = {}; }
1739 for (var prop in obj)
1740 { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
1741 { target[prop] = obj[prop]; } }
1742 return target
1743 }
1744
1745 // Counts the column offset in a string, taking tabs into account.
1746 // Used mostly to find indentation.
1747 function countColumn(string, end, tabSize, startIndex, startValue) {
1748 if (end == null) {
1749 end = string.search(/[^\s\u00a0]/);
1750 if (end == -1) { end = string.length; }
1751 }
1752 for (var i = startIndex || 0, n = startValue || 0;;) {
1753 var nextTab = string.indexOf("\t", i);
1754 if (nextTab < 0 || nextTab >= end)
1755 { return n + (end - i) }
1756 n += nextTab - i;
1757 n += tabSize - (n % tabSize);
1758 i = nextTab + 1;
1759 }
1760 }
1761
1762 var Delayed = function() {this.id = null;};
1763 Delayed.prototype.set = function (ms, f) {
1764 clearTimeout(this.id);
1765 this.id = setTimeout(f, ms);
1766 };
1767
1768 function indexOf(array, elt) {
1769 for (var i = 0; i < array.length; ++i)
1770 { if (array[i] == elt) { return i } }
1771 return -1
1772 }
1773
1774 // Number of pixels added to scroller and sizer to hide scrollbar
1775 var scrollerGap = 30;
1776
1777 // Returned or thrown by various protocols to signal 'I'm not
1778 // handling this'.
1779 var Pass = {toString: function(){return "CodeMirror.Pass"}};
1780
1781 // Reused option objects for setSelection & friends
1782 var sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"};
1783
1784 // The inverse of countColumn -- find the offset that corresponds to
1785 // a particular column.
1786 function findColumn(string, goal, tabSize) {
1787 for (var pos = 0, col = 0;;) {
1788 var nextTab = string.indexOf("\t", pos);
1789 if (nextTab == -1) { nextTab = string.length; }
1790 var skipped = nextTab - pos;
1791 if (nextTab == string.length || col + skipped >= goal)
1792 { return pos + Math.min(skipped, goal - col) }
1793 col += nextTab - pos;
1794 col += tabSize - (col % tabSize);
1795 pos = nextTab + 1;
1796 if (col >= goal) { return pos }
1797 }
1798 }
1799
1800 var spaceStrs = [""];
1801 function spaceStr(n) {
1802 while (spaceStrs.length <= n)
1803 { spaceStrs.push(lst(spaceStrs) + " "); }
1804 return spaceStrs[n]
1805 }
1806
1807 function lst(arr) { return arr[arr.length-1] }
1808
1809 function map(array, f) {
1810 var out = [];
1811 for (var i = 0; i < array.length; i++) { out[i] = f(array[i], i); }
1812 return out
1813 }
1814
1815 function insertSorted(array, value, score) {
1816 var pos = 0, priority = score(value);
1817 while (pos < array.length && score(array[pos]) <= priority) { pos++; }
1818 array.splice(pos, 0, value);
1819 }
1820
1821 function nothing() {}
1822
1823 function createObj(base, props) {
1824 var inst;
1825 if (Object.create) {
1826 inst = Object.create(base);
1827 } else {
1828 nothing.prototype = base;
1829 inst = new nothing();
1830 }
1831 if (props) { copyObj(props, inst); }
1832 return inst
1833 }
1834
1835 var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
1836 function isWordCharBasic(ch) {
1837 return /\w/.test(ch) || ch > "\x80" &&
1838 (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch))
1839 }
1840 function isWordChar(ch, helper) {
1841 if (!helper) { return isWordCharBasic(ch) }
1842 if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) { return true }
1843 return helper.test(ch)
1844 }
1845
1846 function isEmpty(obj) {
1847 for (var n in obj) { if (obj.hasOwnProperty(n) && obj[n]) { return false } }
1848 return true
1849 }
1850
1851 // Extending unicode characters. A series of a non-extending char +
1852 // any number of extending chars is treated as a single unit as far
1853 // as editing and measuring is concerned. This is not fully correct,
1854 // since some scripts/fonts/browsers also treat other configurations
1855 // of code points as a group.
1856 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]/;
1857 function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) }
1858
1859 // Returns a number from the range [`0`; `str.length`] unless `pos` is outside that range.
1860 function skipExtendingChars(str, pos, dir) {
1861 while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(pos))) { pos += dir; }
1862 return pos
1863 }
1864
1865 // Returns the value from the range [`from`; `to`] that satisfies
1866 // `pred` and is closest to `from`. Assumes that at least `to`
1867 // satisfies `pred`. Supports `from` being greater than `to`.
1868 function findFirst(pred, from, to) {
1869 // At any point we are certain `to` satisfies `pred`, don't know
1870 // whether `from` does.
1871 var dir = from > to ? -1 : 1;
1872 for (;;) {
1873 if (from == to) { return from }
1874 var midF = (from + to) / 2, mid = dir < 0 ? Math.ceil(midF) : Math.floor(midF);
1875 if (mid == from) { return pred(mid) ? from : to }
1876 if (pred(mid)) { to = mid; }
1877 else { from = mid + dir; }
1878 }
1879 }
1880
1881 // BIDI HELPERS
1882
1883 function iterateBidiSections(order, from, to, f) {
1884 if (!order) { return f(from, to, "ltr", 0) }
1885 var found = false;
1886 for (var i = 0; i < order.length; ++i) {
1887 var part = order[i];
1888 if (part.from < to && part.to > from || from == to && part.to == from) {
1889 f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr", i);
1890 found = true;
1891 }
1892 }
1893 if (!found) { f(from, to, "ltr"); }
1894 }
1895
1896 var bidiOther = null;
1897 function getBidiPartAt(order, ch, sticky) {
1898 var found;
1899 bidiOther = null;
1900 for (var i = 0; i < order.length; ++i) {
1901 var cur = order[i];
1902 if (cur.from < ch && cur.to > ch) { return i }
1903 if (cur.to == ch) {
1904 if (cur.from != cur.to && sticky == "before") { found = i; }
1905 else { bidiOther = i; }
1906 }
1907 if (cur.from == ch) {
1908 if (cur.from != cur.to && sticky != "before") { found = i; }
1909 else { bidiOther = i; }
1910 }
1911 }
1912 return found != null ? found : bidiOther
1913 }
1914
1915 // Bidirectional ordering algorithm
1916 // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
1917 // that this (partially) implements.
1918
1919 // One-char codes used for character types:
1920 // L (L): Left-to-Right
1921 // R (R): Right-to-Left
1922 // r (AL): Right-to-Left Arabic
1923 // 1 (EN): European Number
1924 // + (ES): European Number Separator
1925 // % (ET): European Number Terminator
1926 // n (AN): Arabic Number
1927 // , (CS): Common Number Separator
1928 // m (NSM): Non-Spacing Mark
1929 // b (BN): Boundary Neutral
1930 // s (B): Paragraph Separator
1931 // t (S): Segment Separator
1932 // w (WS): Whitespace
1933 // N (ON): Other Neutrals
1934
1935 // Returns null if characters are ordered as they appear
1936 // (left-to-right), or an array of sections ({from, to, level}
1937 // objects) in the order in which they occur visually.
1938 var bidiOrdering = (function() {
1939 // Character types for codepoints 0 to 0xff
1940 var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN";
1941 // Character types for codepoints 0x600 to 0x6f9
1942 var arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111";
1943 function charType(code) {
1944 if (code <= 0xf7) { return lowTypes.charAt(code) }
1945 else if (0x590 <= code && code <= 0x5f4) { return "R" }
1946 else if (0x600 <= code && code <= 0x6f9) { return arabicTypes.charAt(code - 0x600) }
1947 else if (0x6ee <= code && code <= 0x8ac) { return "r" }
1948 else if (0x2000 <= code && code <= 0x200b) { return "w" }
1949 else if (code == 0x200c) { return "b" }
1950 else { return "L" }
1951 }
1952
1953 var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
1954 var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
1955
1956 function BidiSpan(level, from, to) {
1957 this.level = level;
1958 this.from = from; this.to = to;
1959 }
1960
1961 return function(str, direction) {
1962 var outerType = direction == "ltr" ? "L" : "R";
1963
1964 if (str.length == 0 || direction == "ltr" && !bidiRE.test(str)) { return false }
1965 var len = str.length, types = [];
1966 for (var i = 0; i < len; ++i)
1967 { types.push(charType(str.charCodeAt(i))); }
1968
1969 // W1. Examine each non-spacing mark (NSM) in the level run, and
1970 // change the type of the NSM to the type of the previous
1971 // character. If the NSM is at the start of the level run, it will
1972 // get the type of sor.
1973 for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) {
1974 var type = types[i$1];
1975 if (type == "m") { types[i$1] = prev; }
1976 else { prev = type; }
1977 }
1978
1979 // W2. Search backwards from each instance of a European number
1980 // until the first strong type (R, L, AL, or sor) is found. If an
1981 // AL is found, change the type of the European number to Arabic
1982 // number.
1983 // W3. Change all ALs to R.
1984 for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) {
1985 var type$1 = types[i$2];
1986 if (type$1 == "1" && cur == "r") { types[i$2] = "n"; }
1987 else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == "r") { types[i$2] = "R"; } }
1988 }
1989
1990 // W4. A single European separator between two European numbers
1991 // changes to a European number. A single common separator between
1992 // two numbers of the same type changes to that type.
1993 for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) {
1994 var type$2 = types[i$3];
1995 if (type$2 == "+" && prev$1 == "1" && types[i$3+1] == "1") { types[i$3] = "1"; }
1996 else if (type$2 == "," && prev$1 == types[i$3+1] &&
1997 (prev$1 == "1" || prev$1 == "n")) { types[i$3] = prev$1; }
1998 prev$1 = type$2;
1999 }
2000
2001 // W5. A sequence of European terminators adjacent to European
2002 // numbers changes to all European numbers.
2003 // W6. Otherwise, separators and terminators change to Other
2004 // Neutral.
2005 for (var i$4 = 0; i$4 < len; ++i$4) {
2006 var type$3 = types[i$4];
2007 if (type$3 == ",") { types[i$4] = "N"; }
2008 else if (type$3 == "%") {
2009 var end = (void 0);
2010 for (end = i$4 + 1; end < len && types[end] == "%"; ++end) {}
2011 var replace = (i$4 && types[i$4-1] == "!") || (end < len && types[end] == "1") ? "1" : "N";
2012 for (var j = i$4; j < end; ++j) { types[j] = replace; }
2013 i$4 = end - 1;
2014 }
2015 }
2016
2017 // W7. Search backwards from each instance of a European number
2018 // until the first strong type (R, L, or sor) is found. If an L is
2019 // found, then change the type of the European number to L.
2020 for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) {
2021 var type$4 = types[i$5];
2022 if (cur$1 == "L" && type$4 == "1") { types[i$5] = "L"; }
2023 else if (isStrong.test(type$4)) { cur$1 = type$4; }
2024 }
2025
2026 // N1. A sequence of neutrals takes the direction of the
2027 // surrounding strong text if the text on both sides has the same
2028 // direction. European and Arabic numbers act as if they were R in
2029 // terms of their influence on neutrals. Start-of-level-run (sor)
2030 // and end-of-level-run (eor) are used at level run boundaries.
2031 // N2. Any remaining neutrals take the embedding direction.
2032 for (var i$6 = 0; i$6 < len; ++i$6) {
2033 if (isNeutral.test(types[i$6])) {
2034 var end$1 = (void 0);
2035 for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {}
2036 var before = (i$6 ? types[i$6-1] : outerType) == "L";
2037 var after = (end$1 < len ? types[end$1] : outerType) == "L";
2038 var replace$1 = before == after ? (before ? "L" : "R") : outerType;
2039 for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1; }
2040 i$6 = end$1 - 1;
2041 }
2042 }
2043
2044 // Here we depart from the documented algorithm, in order to avoid
2045 // building up an actual levels array. Since there are only three
2046 // levels (0, 1, 2) in an implementation that doesn't take
2047 // explicit embedding into account, we can build up the order on
2048 // the fly, without following the level-based algorithm.
2049 var order = [], m;
2050 for (var i$7 = 0; i$7 < len;) {
2051 if (countsAsLeft.test(types[i$7])) {
2052 var start = i$7;
2053 for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {}
2054 order.push(new BidiSpan(0, start, i$7));
2055 } else {
2056 var pos = i$7, at = order.length;
2057 for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {}
2058 for (var j$2 = pos; j$2 < i$7;) {
2059 if (countsAsNum.test(types[j$2])) {
2060 if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)); }
2061 var nstart = j$2;
2062 for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {}
2063 order.splice(at, 0, new BidiSpan(2, nstart, j$2));
2064 pos = j$2;
2065 } else { ++j$2; }
2066 }
2067 if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)); }
2068 }
2069 }
2070 if (direction == "ltr") {
2071 if (order[0].level == 1 && (m = str.match(/^\s+/))) {
2072 order[0].from = m[0].length;
2073 order.unshift(new BidiSpan(0, 0, m[0].length));
2074 }
2075 if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
2076 lst(order).to -= m[0].length;
2077 order.push(new BidiSpan(0, len - m[0].length, len));
2078 }
2079 }
2080
2081 return direction == "rtl" ? order.reverse() : order
2082 }
2083 })();
2084
2085 // Get the bidi ordering for the given line (and cache it). Returns
2086 // false for lines that are fully left-to-right, and an array of
2087 // BidiSpan objects otherwise.
2088 function getOrder(line, direction) {
2089 var order = line.order;
2090 if (order == null) { order = line.order = bidiOrdering(line.text, direction); }
2091 return order
2092 }
2093
2094 // EVENT HANDLING
2095
2096 // Lightweight event framework. on/off also work on DOM nodes,
2097 // registering native DOM handlers.
2098
2099 var noHandlers = [];
2100
2101 var on = function(emitter, type, f) {
2102 if (emitter.addEventListener) {
2103 emitter.addEventListener(type, f, false);
2104 } else if (emitter.attachEvent) {
2105 emitter.attachEvent("on" + type, f);
2106 } else {
2107 var map$$1 = emitter._handlers || (emitter._handlers = {});
2108 map$$1[type] = (map$$1[type] || noHandlers).concat(f);
2109 }
2110 };
2111
2112 function getHandlers(emitter, type) {
2113 return emitter._handlers && emitter._handlers[type] || noHandlers
2114 }
2115
2116 function off(emitter, type, f) {
2117 if (emitter.removeEventListener) {
2118 emitter.removeEventListener(type, f, false);
2119 } else if (emitter.detachEvent) {
2120 emitter.detachEvent("on" + type, f);
2121 } else {
2122 var map$$1 = emitter._handlers, arr = map$$1 && map$$1[type];
2123 if (arr) {
2124 var index = indexOf(arr, f);
2125 if (index > -1)
2126 { map$$1[type] = arr.slice(0, index).concat(arr.slice(index + 1)); }
2127 }
2128 }
2129 }
2130
2131 function signal(emitter, type /*, values...*/) {
2132 var handlers = getHandlers(emitter, type);
2133 if (!handlers.length) { return }
2134 var args = Array.prototype.slice.call(arguments, 2);
2135 for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args); }
2136 }
2137
2138 // The DOM events that CodeMirror handles can be overridden by
2139 // registering a (non-DOM) handler on the editor for the event name,
2140 // and preventDefault-ing the event in that handler.
2141 function signalDOMEvent(cm, e, override) {
2142 if (typeof e == "string")
2143 { e = {type: e, preventDefault: function() { this.defaultPrevented = true; }}; }
2144 signal(cm, override || e.type, cm, e);
2145 return e_defaultPrevented(e) || e.codemirrorIgnore
2146 }
2147
2148 function signalCursorActivity(cm) {
2149 var arr = cm._handlers && cm._handlers.cursorActivity;
2150 if (!arr) { return }
2151 var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []);
2152 for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1)
2153 { set.push(arr[i]); } }
2154 }
2155
2156 function hasHandler(emitter, type) {
2157 return getHandlers(emitter, type).length > 0
2158 }
2159
2160 // Add on and off methods to a constructor's prototype, to make
2161 // registering events on such objects more convenient.
2162 function eventMixin(ctor) {
2163 ctor.prototype.on = function(type, f) {on(this, type, f);};
2164 ctor.prototype.off = function(type, f) {off(this, type, f);};
2165 }
2166
2167 // Due to the fact that we still support jurassic IE versions, some
2168 // compatibility wrappers are needed.
2169
2170 function e_preventDefault(e) {
2171 if (e.preventDefault) { e.preventDefault(); }
2172 else { e.returnValue = false; }
2173 }
2174 function e_stopPropagation(e) {
2175 if (e.stopPropagation) { e.stopPropagation(); }
2176 else { e.cancelBubble = true; }
2177 }
2178 function e_defaultPrevented(e) {
2179 return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false
2180 }
2181 function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
2182
2183 function e_target(e) {return e.target || e.srcElement}
2184 function e_button(e) {
2185 var b = e.which;
2186 if (b == null) {
2187 if (e.button & 1) { b = 1; }
2188 else if (e.button & 2) { b = 3; }
2189 else if (e.button & 4) { b = 2; }
2190 }
2191 if (mac && e.ctrlKey && b == 1) { b = 3; }
2192 return b
2193 }
2194
2195 // Detect drag-and-drop
2196 var dragAndDrop = function() {
2197 // There is *some* kind of drag-and-drop support in IE6-8, but I
2198 // couldn't get it to work yet.
2199 if (ie && ie_version < 9) { return false }
2200 var div = elt('div');
2201 return "draggable" in div || "dragDrop" in div
2202 }();
2203
2204 var zwspSupported;
2205 function zeroWidthElement(measure) {
2206 if (zwspSupported == null) {
2207 var test = elt("span", "\u200b");
2208 removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]));
2209 if (measure.firstChild.offsetHeight != 0)
2210 { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8); }
2211 }
2212 var node = zwspSupported ? elt("span", "\u200b") :
2213 elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
2214 node.setAttribute("cm-text", "");
2215 return node
2216 }
2217
2218 // Feature-detect IE's crummy client rect reporting for bidi text
2219 var badBidiRects;
2220 function hasBadBidiRects(measure) {
2221 if (badBidiRects != null) { return badBidiRects }
2222 var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA"));
2223 var r0 = range(txt, 0, 1).getBoundingClientRect();
2224 var r1 = range(txt, 1, 2).getBoundingClientRect();
2225 removeChildren(measure);
2226 if (!r0 || r0.left == r0.right) { return false } // Safari returns null in some cases (#2780)
2227 return badBidiRects = (r1.right - r0.right < 3)
2228 }
2229
2230 // See if "".split is the broken IE version, if so, provide an
2231 // alternative way to split lines.
2232 var splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function (string) {
2233 var pos = 0, result = [], l = string.length;
2234 while (pos <= l) {
2235 var nl = string.indexOf("\n", pos);
2236 if (nl == -1) { nl = string.length; }
2237 var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
2238 var rt = line.indexOf("\r");
2239 if (rt != -1) {
2240 result.push(line.slice(0, rt));
2241 pos += rt + 1;
2242 } else {
2243 result.push(line);
2244 pos = nl + 1;
2245 }
2246 }
2247 return result
2248 } : function (string) { return string.split(/\r\n?|\n/); };
2249
2250 var hasSelection = window.getSelection ? function (te) {
2251 try { return te.selectionStart != te.selectionEnd }
2252 catch(e) { return false }
2253 } : function (te) {
2254 var range$$1;
2255 try {range$$1 = te.ownerDocument.selection.createRange();}
2256 catch(e) {}
2257 if (!range$$1 || range$$1.parentElement() != te) { return false }
2258 return range$$1.compareEndPoints("StartToEnd", range$$1) != 0
2259 };
2260
2261 var hasCopyEvent = (function () {
2262 var e = elt("div");
2263 if ("oncopy" in e) { return true }
2264 e.setAttribute("oncopy", "return;");
2265 return typeof e.oncopy == "function"
2266 })();
2267
2268 var badZoomedRects = null;
2269 function hasBadZoomedRects(measure) {
2270 if (badZoomedRects != null) { return badZoomedRects }
2271 var node = removeChildrenAndAdd(measure, elt("span", "x"));
2272 var normal = node.getBoundingClientRect();
2273 var fromRange = range(node, 0, 1).getBoundingClientRect();
2274 return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1
2275 }
2276
2277 // Known modes, by name and by MIME
2278 var modes = {}, mimeModes = {};
2279
2280 // Extra arguments are stored as the mode's dependencies, which is
2281 // used by (legacy) mechanisms like loadmode.js to automatically
2282 // load a mode. (Preferred mechanism is the require/define calls.)
2283 function defineMode(name, mode) {
2284 if (arguments.length > 2)
2285 { mode.dependencies = Array.prototype.slice.call(arguments, 2); }
2286 modes[name] = mode;
2287 }
2288
2289 function defineMIME(mime, spec) {
2290 mimeModes[mime] = spec;
2291 }
2292
2293 // Given a MIME type, a {name, ...options} config object, or a name
2294 // string, return a mode config object.
2295 function resolveMode(spec) {
2296 if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
2297 spec = mimeModes[spec];
2298 } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
2299 var found = mimeModes[spec.name];
2300 if (typeof found == "string") { found = {name: found}; }
2301 spec = createObj(found, spec);
2302 spec.name = found.name;
2303 } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
2304 return resolveMode("application/xml")
2305 } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) {
2306 return resolveMode("application/json")
2307 }
2308 if (typeof spec == "string") { return {name: spec} }
2309 else { return spec || {name: "null"} }
2310 }
2311
2312 // Given a mode spec (anything that resolveMode accepts), find and
2313 // initialize an actual mode object.
2314 function getMode(options, spec) {
2315 spec = resolveMode(spec);
2316 var mfactory = modes[spec.name];
2317 if (!mfactory) { return getMode(options, "text/plain") }
2318 var modeObj = mfactory(options, spec);
2319 if (modeExtensions.hasOwnProperty(spec.name)) {
2320 var exts = modeExtensions[spec.name];
2321 for (var prop in exts) {
2322 if (!exts.hasOwnProperty(prop)) { continue }
2323 if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop]; }
2324 modeObj[prop] = exts[prop];
2325 }
2326 }
2327 modeObj.name = spec.name;
2328 if (spec.helperType) { modeObj.helperType = spec.helperType; }
2329 if (spec.modeProps) { for (var prop$1 in spec.modeProps)
2330 { modeObj[prop$1] = spec.modeProps[prop$1]; } }
2331
2332 return modeObj
2333 }
2334
2335 // This can be used to attach properties to mode objects from
2336 // outside the actual mode definition.
2337 var modeExtensions = {};
2338 function extendMode(mode, properties) {
2339 var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
2340 copyObj(properties, exts);
2341 }
2342
2343 function copyState(mode, state) {
2344 if (state === true) { return state }
2345 if (mode.copyState) { return mode.copyState(state) }
2346 var nstate = {};
2347 for (var n in state) {
2348 var val = state[n];
2349 if (val instanceof Array) { val = val.concat([]); }
2350 nstate[n] = val;
2351 }
2352 return nstate
2353 }
2354
2355 // Given a mode and a state (for that mode), find the inner mode and
2356 // state at the position that the state refers to.
2357 function innerMode(mode, state) {
2358 var info;
2359 while (mode.innerMode) {
2360 info = mode.innerMode(state);
2361 if (!info || info.mode == mode) { break }
2362 state = info.state;
2363 mode = info.mode;
2364 }
2365 return info || {mode: mode, state: state}
2366 }
2367
2368 function startState(mode, a1, a2) {
2369 return mode.startState ? mode.startState(a1, a2) : true
2370 }
2371
2372 // STRING STREAM
2373
2374 // Fed to the mode parsers, provides helper functions to make
2375 // parsers more succinct.
2376
2377 var StringStream = function(string, tabSize, lineOracle) {
2378 this.pos = this.start = 0;
2379 this.string = string;
2380 this.tabSize = tabSize || 8;
2381 this.lastColumnPos = this.lastColumnValue = 0;
2382 this.lineStart = 0;
2383 this.lineOracle = lineOracle;
2384 };
2385
2386 StringStream.prototype.eol = function () {return this.pos >= this.string.length};
2387 StringStream.prototype.sol = function () {return this.pos == this.lineStart};
2388 StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined};
2389 StringStream.prototype.next = function () {
2390 if (this.pos < this.string.length)
2391 { return this.string.charAt(this.pos++) }
2392 };
2393 StringStream.prototype.eat = function (match) {
2394 var ch = this.string.charAt(this.pos);
2395 var ok;
2396 if (typeof match == "string") { ok = ch == match; }
2397 else { ok = ch && (match.test ? match.test(ch) : match(ch)); }
2398 if (ok) {++this.pos; return ch}
2399 };
2400 StringStream.prototype.eatWhile = function (match) {
2401 var start = this.pos;
2402 while (this.eat(match)){}
2403 return this.pos > start
2404 };
2405 StringStream.prototype.eatSpace = function () {
2406 var this$1 = this;
2407
2408 var start = this.pos;
2409 while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this$1.pos; }
2410 return this.pos > start
2411 };
2412 StringStream.prototype.skipToEnd = function () {this.pos = this.string.length;};
2413 StringStream.prototype.skipTo = function (ch) {
2414 var found = this.string.indexOf(ch, this.pos);
2415 if (found > -1) {this.pos = found; return true}
2416 };
2417 StringStream.prototype.backUp = function (n) {this.pos -= n;};
2418 StringStream.prototype.column = function () {
2419 if (this.lastColumnPos < this.start) {
2420 this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
2421 this.lastColumnPos = this.start;
2422 }
2423 return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
2424 };
2425 StringStream.prototype.indentation = function () {
2426 return countColumn(this.string, null, this.tabSize) -
2427 (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
2428 };
2429 StringStream.prototype.match = function (pattern, consume, caseInsensitive) {
2430 if (typeof pattern == "string") {
2431 var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; };
2432 var substr = this.string.substr(this.pos, pattern.length);
2433 if (cased(substr) == cased(pattern)) {
2434 if (consume !== false) { this.pos += pattern.length; }
2435 return true
2436 }
2437 } else {
2438 var match = this.string.slice(this.pos).match(pattern);
2439 if (match && match.index > 0) { return null }
2440 if (match && consume !== false) { this.pos += match[0].length; }
2441 return match
2442 }
2443 };
2444 StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)};
2445 StringStream.prototype.hideFirstChars = function (n, inner) {
2446 this.lineStart += n;
2447 try { return inner() }
2448 finally { this.lineStart -= n; }
2449 };
2450 StringStream.prototype.lookAhead = function (n) {
2451 var oracle = this.lineOracle;
2452 return oracle && oracle.lookAhead(n)
2453 };
2454 StringStream.prototype.baseToken = function () {
2455 var oracle = this.lineOracle;
2456 return oracle && oracle.baseToken(this.pos)
2457 };
2458
2459 // Find the line object corresponding to the given line number.
2460 function getLine(doc, n) {
2461 n -= doc.first;
2462 if (n < 0 || n >= doc.size) { throw new Error("There is no line " + (n + doc.first) + " in the document.") }
2463 var chunk = doc;
2464 while (!chunk.lines) {
2465 for (var i = 0;; ++i) {
2466 var child = chunk.children[i], sz = child.chunkSize();
2467 if (n < sz) { chunk = child; break }
2468 n -= sz;
2469 }
2470 }
2471 return chunk.lines[n]
2472 }
2473
2474 // Get the part of a document between two positions, as an array of
2475 // strings.
2476 function getBetween(doc, start, end) {
2477 var out = [], n = start.line;
2478 doc.iter(start.line, end.line + 1, function (line) {
2479 var text = line.text;
2480 if (n == end.line) { text = text.slice(0, end.ch); }
2481 if (n == start.line) { text = text.slice(start.ch); }
2482 out.push(text);
2483 ++n;
2484 });
2485 return out
2486 }
2487 // Get the lines between from and to, as array of strings.
2488 function getLines(doc, from, to) {
2489 var out = [];
2490 doc.iter(from, to, function (line) { out.push(line.text); }); // iter aborts when callback returns truthy value
2491 return out
2492 }
2493
2494 // Update the height of a line, propagating the height change
2495 // upwards to parent nodes.
2496 function updateLineHeight(line, height) {
2497 var diff = height - line.height;
2498 if (diff) { for (var n = line; n; n = n.parent) { n.height += diff; } }
2499 }
2500
2501 // Given a line object, find its line number by walking up through
2502 // its parent links.
2503 function lineNo(line) {
2504 if (line.parent == null) { return null }
2505 var cur = line.parent, no = indexOf(cur.lines, line);
2506 for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
2507 for (var i = 0;; ++i) {
2508 if (chunk.children[i] == cur) { break }
2509 no += chunk.children[i].chunkSize();
2510 }
2511 }
2512 return no + cur.first
2513 }
2514
2515 // Find the line at the given vertical position, using the height
2516 // information in the document tree.
2517 function lineAtHeight(chunk, h) {
2518 var n = chunk.first;
2519 outer: do {
2520 for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) {
2521 var child = chunk.children[i$1], ch = child.height;
2522 if (h < ch) { chunk = child; continue outer }
2523 h -= ch;
2524 n += child.chunkSize();
2525 }
2526 return n
2527 } while (!chunk.lines)
2528 var i = 0;
2529 for (; i < chunk.lines.length; ++i) {
2530 var line = chunk.lines[i], lh = line.height;
2531 if (h < lh) { break }
2532 h -= lh;
2533 }
2534 return n + i
2535 }
2536
2537 function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size}
2538
2539 function lineNumberFor(options, i) {
2540 return String(options.lineNumberFormatter(i + options.firstLineNumber))
2541 }
2542
2543 // A Pos instance represents a position within the text.
2544 function Pos(line, ch, sticky) {
2545 if ( sticky === void 0 ) sticky = null;
2546
2547 if (!(this instanceof Pos)) { return new Pos(line, ch, sticky) }
2548 this.line = line;
2549 this.ch = ch;
2550 this.sticky = sticky;
2551 }
2552
2553 // Compare two positions, return 0 if they are the same, a negative
2554 // number when a is less, and a positive number otherwise.
2555 function cmp(a, b) { return a.line - b.line || a.ch - b.ch }
2556
2557 function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 }
2558
2559 function copyPos(x) {return Pos(x.line, x.ch)}
2560 function maxPos(a, b) { return cmp(a, b) < 0 ? b : a }
2561 function minPos(a, b) { return cmp(a, b) < 0 ? a : b }
2562
2563 // Most of the external API clips given positions to make sure they
2564 // actually exist within the document.
2565 function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))}
2566 function clipPos(doc, pos) {
2567 if (pos.line < doc.first) { return Pos(doc.first, 0) }
2568 var last = doc.first + doc.size - 1;
2569 if (pos.line > last) { return Pos(last, getLine(doc, last).text.length) }
2570 return clipToLen(pos, getLine(doc, pos.line).text.length)
2571 }
2572 function clipToLen(pos, linelen) {
2573 var ch = pos.ch;
2574 if (ch == null || ch > linelen) { return Pos(pos.line, linelen) }
2575 else if (ch < 0) { return Pos(pos.line, 0) }
2576 else { return pos }
2577 }
2578 function clipPosArray(doc, array) {
2579 var out = [];
2580 for (var i = 0; i < array.length; i++) { out[i] = clipPos(doc, array[i]); }
2581 return out
2582 }
2583
2584 var SavedContext = function(state, lookAhead) {
2585 this.state = state;
2586 this.lookAhead = lookAhead;
2587 };
2588
2589 var Context = function(doc, state, line, lookAhead) {
2590 this.state = state;
2591 this.doc = doc;
2592 this.line = line;
2593 this.maxLookAhead = lookAhead || 0;
2594 this.baseTokens = null;
2595 this.baseTokenPos = 1;
2596 };
2597
2598 Context.prototype.lookAhead = function (n) {
2599 var line = this.doc.getLine(this.line + n);
2600 if (line != null && n > this.maxLookAhead) { this.maxLookAhead = n; }
2601 return line
2602 };
2603
2604 Context.prototype.baseToken = function (n) {
2605 var this$1 = this;
2606
2607 if (!this.baseTokens) { return null }
2608 while (this.baseTokens[this.baseTokenPos] <= n)
2609 { this$1.baseTokenPos += 2; }
2610 var type = this.baseTokens[this.baseTokenPos + 1];
2611 return {type: type && type.replace(/( |^)overlay .*/, ""),
2612 size: this.baseTokens[this.baseTokenPos] - n}
2613 };
2614
2615 Context.prototype.nextLine = function () {
2616 this.line++;
2617 if (this.maxLookAhead > 0) { this.maxLookAhead--; }
2618 };
2619
2620 Context.fromSaved = function (doc, saved, line) {
2621 if (saved instanceof SavedContext)
2622 { return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead) }
2623 else
2624 { return new Context(doc, copyState(doc.mode, saved), line) }
2625 };
2626
2627 Context.prototype.save = function (copy) {
2628 var state = copy !== false ? copyState(this.doc.mode, this.state) : this.state;
2629 return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state
2630 };
2631
2632
2633 // Compute a style array (an array starting with a mode generation
2634 // -- for invalidation -- followed by pairs of end positions and
2635 // style strings), which is used to highlight the tokens on the
2636 // line.
2637 function highlightLine(cm, line, context, forceToEnd) {
2638 // A styles array always starts with a number identifying the
2639 // mode/overlays that it is based on (for easy invalidation).
2640 var st = [cm.state.modeGen], lineClasses = {};
2641 // Compute the base array of styles
2642 runMode(cm, line.text, cm.doc.mode, context, function (end, style) { return st.push(end, style); },
2643 lineClasses, forceToEnd);
2644 var state = context.state;
2645
2646 // Run overlays, adjust style array.
2647 var loop = function ( o ) {
2648 context.baseTokens = st;
2649 var overlay = cm.state.overlays[o], i = 1, at = 0;
2650 context.state = true;
2651 runMode(cm, line.text, overlay.mode, context, function (end, style) {
2652 var start = i;
2653 // Ensure there's a token end at the current position, and that i points at it
2654 while (at < end) {
2655 var i_end = st[i];
2656 if (i_end > end)
2657 { st.splice(i, 1, end, st[i+1], i_end); }
2658 i += 2;
2659 at = Math.min(end, i_end);
2660 }
2661 if (!style) { return }
2662 if (overlay.opaque) {
2663 st.splice(start, i - start, end, "overlay " + style);
2664 i = start + 2;
2665 } else {
2666 for (; start < i; start += 2) {
2667 var cur = st[start+1];
2668 st[start+1] = (cur ? cur + " " : "") + "overlay " + style;
2669 }
2670 }
2671 }, lineClasses);
2672 context.state = state;
2673 context.baseTokens = null;
2674 context.baseTokenPos = 1;
2675 };
2676
2677 for (var o = 0; o < cm.state.overlays.length; ++o) loop( o );
2678
2679 return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null}
2680 }
2681
2682 function getLineStyles(cm, line, updateFrontier) {
2683 if (!line.styles || line.styles[0] != cm.state.modeGen) {
2684 var context = getContextBefore(cm, lineNo(line));
2685 var resetState = line.text.length > cm.options.maxHighlightLength && copyState(cm.doc.mode, context.state);
2686 var result = highlightLine(cm, line, context);
2687 if (resetState) { context.state = resetState; }
2688 line.stateAfter = context.save(!resetState);
2689 line.styles = result.styles;
2690 if (result.classes) { line.styleClasses = result.classes; }
2691 else if (line.styleClasses) { line.styleClasses = null; }
2692 if (updateFrontier === cm.doc.highlightFrontier)
2693 { cm.doc.modeFrontier = Math.max(cm.doc.modeFrontier, ++cm.doc.highlightFrontier); }
2694 }
2695 return line.styles
2696 }
2697
2698 function getContextBefore(cm, n, precise) {
2699 var doc = cm.doc, display = cm.display;
2700 if (!doc.mode.startState) { return new Context(doc, true, n) }
2701 var start = findStartLine(cm, n, precise);
2702 var saved = start > doc.first && getLine(doc, start - 1).stateAfter;
2703 var context = saved ? Context.fromSaved(doc, saved, start) : new Context(doc, startState(doc.mode), start);
2704
2705 doc.iter(start, n, function (line) {
2706 processLine(cm, line.text, context);
2707 var pos = context.line;
2708 line.stateAfter = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo ? context.save() : null;
2709 context.nextLine();
2710 });
2711 if (precise) { doc.modeFrontier = context.line; }
2712 return context
2713 }
2714
2715 // Lightweight form of highlight -- proceed over this line and
2716 // update state, but don't save a style array. Used for lines that
2717 // aren't currently visible.
2718 function processLine(cm, text, context, startAt) {
2719 var mode = cm.doc.mode;
2720 var stream = new StringStream(text, cm.options.tabSize, context);
2721 stream.start = stream.pos = startAt || 0;
2722 if (text == "") { callBlankLine(mode, context.state); }
2723 while (!stream.eol()) {
2724 readToken(mode, stream, context.state);
2725 stream.start = stream.pos;
2726 }
2727 }
2728
2729 function callBlankLine(mode, state) {
2730 if (mode.blankLine) { return mode.blankLine(state) }
2731 if (!mode.innerMode) { return }
2732 var inner = innerMode(mode, state);
2733 if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) }
2734 }
2735
2736 function readToken(mode, stream, state, inner) {
2737 for (var i = 0; i < 10; i++) {
2738 if (inner) { inner[0] = innerMode(mode, state).mode; }
2739 var style = mode.token(stream, state);
2740 if (stream.pos > stream.start) { return style }
2741 }
2742 throw new Error("Mode " + mode.name + " failed to advance stream.")
2743 }
2744
2745 var Token = function(stream, type, state) {
2746 this.start = stream.start; this.end = stream.pos;
2747 this.string = stream.current();
2748 this.type = type || null;
2749 this.state = state;
2750 };
2751
2752 // Utility for getTokenAt and getLineTokens
2753 function takeToken(cm, pos, precise, asArray) {
2754 var doc = cm.doc, mode = doc.mode, style;
2755 pos = clipPos(doc, pos);
2756 var line = getLine(doc, pos.line), context = getContextBefore(cm, pos.line, precise);
2757 var stream = new StringStream(line.text, cm.options.tabSize, context), tokens;
2758 if (asArray) { tokens = []; }
2759 while ((asArray || stream.pos < pos.ch) && !stream.eol()) {
2760 stream.start = stream.pos;
2761 style = readToken(mode, stream, context.state);
2762 if (asArray) { tokens.push(new Token(stream, style, copyState(doc.mode, context.state))); }
2763 }
2764 return asArray ? tokens : new Token(stream, style, context.state)
2765 }
2766
2767 function extractLineClasses(type, output) {
2768 if (type) { for (;;) {
2769 var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/);
2770 if (!lineClass) { break }
2771 type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length);
2772 var prop = lineClass[1] ? "bgClass" : "textClass";
2773 if (output[prop] == null)
2774 { output[prop] = lineClass[2]; }
2775 else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop]))
2776 { output[prop] += " " + lineClass[2]; }
2777 } }
2778 return type
2779 }
2780
2781 // Run the given mode's parser over a line, calling f for each token.
2782 function runMode(cm, text, mode, context, f, lineClasses, forceToEnd) {
2783 var flattenSpans = mode.flattenSpans;
2784 if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans; }
2785 var curStart = 0, curStyle = null;
2786 var stream = new StringStream(text, cm.options.tabSize, context), style;
2787 var inner = cm.options.addModeClass && [null];
2788 if (text == "") { extractLineClasses(callBlankLine(mode, context.state), lineClasses); }
2789 while (!stream.eol()) {
2790 if (stream.pos > cm.options.maxHighlightLength) {
2791 flattenSpans = false;
2792 if (forceToEnd) { processLine(cm, text, context, stream.pos); }
2793 stream.pos = text.length;
2794 style = null;
2795 } else {
2796 style = extractLineClasses(readToken(mode, stream, context.state, inner), lineClasses);
2797 }
2798 if (inner) {
2799 var mName = inner[0].name;
2800 if (mName) { style = "m-" + (style ? mName + " " + style : mName); }
2801 }
2802 if (!flattenSpans || curStyle != style) {
2803 while (curStart < stream.start) {
2804 curStart = Math.min(stream.start, curStart + 5000);
2805 f(curStart, curStyle);
2806 }
2807 curStyle = style;
2808 }
2809 stream.start = stream.pos;
2810 }
2811 while (curStart < stream.pos) {
2812 // Webkit seems to refuse to render text nodes longer than 57444
2813 // characters, and returns inaccurate measurements in nodes
2814 // starting around 5000 chars.
2815 var pos = Math.min(stream.pos, curStart + 5000);
2816 f(pos, curStyle);
2817 curStart = pos;
2818 }
2819 }
2820
2821 // Finds the line to start with when starting a parse. Tries to
2822 // find a line with a stateAfter, so that it can start with a
2823 // valid state. If that fails, it returns the line with the
2824 // smallest indentation, which tends to need the least context to
2825 // parse correctly.
2826 function findStartLine(cm, n, precise) {
2827 var minindent, minline, doc = cm.doc;
2828 var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100);
2829 for (var search = n; search > lim; --search) {
2830 if (search <= doc.first) { return doc.first }
2831 var line = getLine(doc, search - 1), after = line.stateAfter;
2832 if (after && (!precise || search + (after instanceof SavedContext ? after.lookAhead : 0) <= doc.modeFrontier))
2833 { return search }
2834 var indented = countColumn(line.text, null, cm.options.tabSize);
2835 if (minline == null || minindent > indented) {
2836 minline = search - 1;
2837 minindent = indented;
2838 }
2839 }
2840 return minline
2841 }
2842
2843 function retreatFrontier(doc, n) {
2844 doc.modeFrontier = Math.min(doc.modeFrontier, n);
2845 if (doc.highlightFrontier < n - 10) { return }
2846 var start = doc.first;
2847 for (var line = n - 1; line > start; line--) {
2848 var saved = getLine(doc, line).stateAfter;
2849 // change is on 3
2850 // state on line 1 looked ahead 2 -- so saw 3
2851 // test 1 + 2 < 3 should cover this
2852 if (saved && (!(saved instanceof SavedContext) || line + saved.lookAhead < n)) {
2853 start = line + 1;
2854 break
2855 }
2856 }
2857 doc.highlightFrontier = Math.min(doc.highlightFrontier, start);
2858 }
2859
2860 // Optimize some code when these features are not used.
2861 var sawReadOnlySpans = false, sawCollapsedSpans = false;
2862
2863 function seeReadOnlySpans() {
2864 sawReadOnlySpans = true;
2865 }
2866
2867 function seeCollapsedSpans() {
2868 sawCollapsedSpans = true;
2869 }
2870
2871 // TEXTMARKER SPANS
2872
2873 function MarkedSpan(marker, from, to) {
2874 this.marker = marker;
2875 this.from = from; this.to = to;
2876 }
2877
2878 // Search an array of spans for a span matching the given marker.
2879 function getMarkedSpanFor(spans, marker) {
2880 if (spans) { for (var i = 0; i < spans.length; ++i) {
2881 var span = spans[i];
2882 if (span.marker == marker) { return span }
2883 } }
2884 }
2885 // Remove a span from an array, returning undefined if no spans are
2886 // left (we don't store arrays for lines without spans).
2887 function removeMarkedSpan(spans, span) {
2888 var r;
2889 for (var i = 0; i < spans.length; ++i)
2890 { if (spans[i] != span) { (r || (r = [])).push(spans[i]); } }
2891 return r
2892 }
2893 // Add a span to a line.
2894 function addMarkedSpan(line, span) {
2895 line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
2896 span.marker.attachLine(line);
2897 }
2898
2899 // Used for the algorithm that adjusts markers for a change in the
2900 // document. These functions cut an array of spans at a given
2901 // character position, returning an array of remaining chunks (or
2902 // undefined if nothing remains).
2903 function markedSpansBefore(old, startCh, isInsert) {
2904 var nw;
2905 if (old) { for (var i = 0; i < old.length; ++i) {
2906 var span = old[i], marker = span.marker;
2907 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
2908 if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) {
2909 var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh)
2910 ;(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to));
2911 }
2912 } }
2913 return nw
2914 }
2915 function markedSpansAfter(old, endCh, isInsert) {
2916 var nw;
2917 if (old) { for (var i = 0; i < old.length; ++i) {
2918 var span = old[i], marker = span.marker;
2919 var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
2920 if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) {
2921 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh)
2922 ;(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh,
2923 span.to == null ? null : span.to - endCh));
2924 }
2925 } }
2926 return nw
2927 }
2928
2929 // Given a change object, compute the new set of marker spans that
2930 // cover the line in which the change took place. Removes spans
2931 // entirely within the change, reconnects spans belonging to the
2932 // same marker that appear on both sides of the change, and cuts off
2933 // spans partially within the change. Returns an array of span
2934 // arrays with one element for each line in (after) the change.
2935 function stretchSpansOverChange(doc, change) {
2936 if (change.full) { return null }
2937 var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans;
2938 var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans;
2939 if (!oldFirst && !oldLast) { return null }
2940
2941 var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0;
2942 // Get the spans that 'stick out' on both sides
2943 var first = markedSpansBefore(oldFirst, startCh, isInsert);
2944 var last = markedSpansAfter(oldLast, endCh, isInsert);
2945
2946 // Next, merge those two ends
2947 var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0);
2948 if (first) {
2949 // Fix up .to properties of first
2950 for (var i = 0; i < first.length; ++i) {
2951 var span = first[i];
2952 if (span.to == null) {
2953 var found = getMarkedSpanFor(last, span.marker);
2954 if (!found) { span.to = startCh; }
2955 else if (sameLine) { span.to = found.to == null ? null : found.to + offset; }
2956 }
2957 }
2958 }
2959 if (last) {
2960 // Fix up .from in last (or move them into first in case of sameLine)
2961 for (var i$1 = 0; i$1 < last.length; ++i$1) {
2962 var span$1 = last[i$1];
2963 if (span$1.to != null) { span$1.to += offset; }
2964 if (span$1.from == null) {
2965 var found$1 = getMarkedSpanFor(first, span$1.marker);
2966 if (!found$1) {
2967 span$1.from = offset;
2968 if (sameLine) { (first || (first = [])).push(span$1); }
2969 }
2970 } else {
2971 span$1.from += offset;
2972 if (sameLine) { (first || (first = [])).push(span$1); }
2973 }
2974 }
2975 }
2976 // Make sure we didn't create any zero-length spans
2977 if (first) { first = clearEmptySpans(first); }
2978 if (last && last != first) { last = clearEmptySpans(last); }
2979
2980 var newMarkers = [first];
2981 if (!sameLine) {
2982 // Fill gap with whole-line-spans
2983 var gap = change.text.length - 2, gapMarkers;
2984 if (gap > 0 && first)
2985 { for (var i$2 = 0; i$2 < first.length; ++i$2)
2986 { if (first[i$2].to == null)
2987 { (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i$2].marker, null, null)); } } }
2988 for (var i$3 = 0; i$3 < gap; ++i$3)
2989 { newMarkers.push(gapMarkers); }
2990 newMarkers.push(last);
2991 }
2992 return newMarkers
2993 }
2994
2995 // Remove spans that are empty and don't have a clearWhenEmpty
2996 // option of false.
2997 function clearEmptySpans(spans) {
2998 for (var i = 0; i < spans.length; ++i) {
2999 var span = spans[i];
3000 if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)
3001 { spans.splice(i--, 1); }
3002 }
3003 if (!spans.length) { return null }
3004 return spans
3005 }
3006
3007 // Used to 'clip' out readOnly ranges when making a change.
3008 function removeReadOnlyRanges(doc, from, to) {
3009 var markers = null;
3010 doc.iter(from.line, to.line + 1, function (line) {
3011 if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
3012 var mark = line.markedSpans[i].marker;
3013 if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
3014 { (markers || (markers = [])).push(mark); }
3015 } }
3016 });
3017 if (!markers) { return null }
3018 var parts = [{from: from, to: to}];
3019 for (var i = 0; i < markers.length; ++i) {
3020 var mk = markers[i], m = mk.find(0);
3021 for (var j = 0; j < parts.length; ++j) {
3022 var p = parts[j];
3023 if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) { continue }
3024 var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to);
3025 if (dfrom < 0 || !mk.inclusiveLeft && !dfrom)
3026 { newParts.push({from: p.from, to: m.from}); }
3027 if (dto > 0 || !mk.inclusiveRight && !dto)
3028 { newParts.push({from: m.to, to: p.to}); }
3029 parts.splice.apply(parts, newParts);
3030 j += newParts.length - 3;
3031 }
3032 }
3033 return parts
3034 }
3035
3036 // Connect or disconnect spans from a line.
3037 function detachMarkedSpans(line) {
3038 var spans = line.markedSpans;
3039 if (!spans) { return }
3040 for (var i = 0; i < spans.length; ++i)
3041 { spans[i].marker.detachLine(line); }
3042 line.markedSpans = null;
3043 }
3044 function attachMarkedSpans(line, spans) {
3045 if (!spans) { return }
3046 for (var i = 0; i < spans.length; ++i)
3047 { spans[i].marker.attachLine(line); }
3048 line.markedSpans = spans;
3049 }
3050
3051 // Helpers used when computing which overlapping collapsed span
3052 // counts as the larger one.
3053 function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 }
3054 function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 }
3055
3056 // Returns a number indicating which of two overlapping collapsed
3057 // spans is larger (and thus includes the other). Falls back to
3058 // comparing ids when the spans cover exactly the same range.
3059 function compareCollapsedMarkers(a, b) {
3060 var lenDiff = a.lines.length - b.lines.length;
3061 if (lenDiff != 0) { return lenDiff }
3062 var aPos = a.find(), bPos = b.find();
3063 var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b);
3064 if (fromCmp) { return -fromCmp }
3065 var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b);
3066 if (toCmp) { return toCmp }
3067 return b.id - a.id
3068 }
3069
3070 // Find out whether a line ends or starts in a collapsed span. If
3071 // so, return the marker for that span.
3072 function collapsedSpanAtSide(line, start) {
3073 var sps = sawCollapsedSpans && line.markedSpans, found;
3074 if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
3075 sp = sps[i];
3076 if (sp.marker.collapsed && (start ? sp.from : sp.to) == null &&
3077 (!found || compareCollapsedMarkers(found, sp.marker) < 0))
3078 { found = sp.marker; }
3079 } }
3080 return found
3081 }
3082 function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) }
3083 function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) }
3084
3085 function collapsedSpanAround(line, ch) {
3086 var sps = sawCollapsedSpans && line.markedSpans, found;
3087 if (sps) { for (var i = 0; i < sps.length; ++i) {
3088 var sp = sps[i];
3089 if (sp.marker.collapsed && (sp.from == null || sp.from < ch) && (sp.to == null || sp.to > ch) &&
3090 (!found || compareCollapsedMarkers(found, sp.marker) < 0)) { found = sp.marker; }
3091 } }
3092 return found
3093 }
3094
3095 // Test whether there exists a collapsed span that partially
3096 // overlaps (covers the start or end, but not both) of a new span.
3097 // Such overlap is not allowed.
3098 function conflictingCollapsedRange(doc, lineNo$$1, from, to, marker) {
3099 var line = getLine(doc, lineNo$$1);
3100 var sps = sawCollapsedSpans && line.markedSpans;
3101 if (sps) { for (var i = 0; i < sps.length; ++i) {
3102 var sp = sps[i];
3103 if (!sp.marker.collapsed) { continue }
3104 var found = sp.marker.find(0);
3105 var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker);
3106 var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker);
3107 if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue }
3108 if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) ||
3109 fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0))
3110 { return true }
3111 } }
3112 }
3113
3114 // A visual line is a line as drawn on the screen. Folding, for
3115 // example, can cause multiple logical lines to appear on the same
3116 // visual line. This finds the start of the visual line that the
3117 // given line is part of (usually that is the line itself).
3118 function visualLine(line) {
3119 var merged;
3120 while (merged = collapsedSpanAtStart(line))
3121 { line = merged.find(-1, true).line; }
3122 return line
3123 }
3124
3125 function visualLineEnd(line) {
3126 var merged;
3127 while (merged = collapsedSpanAtEnd(line))
3128 { line = merged.find(1, true).line; }
3129 return line
3130 }
3131
3132 // Returns an array of logical lines that continue the visual line
3133 // started by the argument, or undefined if there are no such lines.
3134 function visualLineContinued(line) {
3135 var merged, lines;
3136 while (merged = collapsedSpanAtEnd(line)) {
3137 line = merged.find(1, true).line
3138 ;(lines || (lines = [])).push(line);
3139 }
3140 return lines
3141 }
3142
3143 // Get the line number of the start of the visual line that the
3144 // given line number is part of.
3145 function visualLineNo(doc, lineN) {
3146 var line = getLine(doc, lineN), vis = visualLine(line);
3147 if (line == vis) { return lineN }
3148 return lineNo(vis)
3149 }
3150
3151 // Get the line number of the start of the next visual line after
3152 // the given line.
3153 function visualLineEndNo(doc, lineN) {
3154 if (lineN > doc.lastLine()) { return lineN }
3155 var line = getLine(doc, lineN), merged;
3156 if (!lineIsHidden(doc, line)) { return lineN }
3157 while (merged = collapsedSpanAtEnd(line))
3158 { line = merged.find(1, true).line; }
3159 return lineNo(line) + 1
3160 }
3161
3162 // Compute whether a line is hidden. Lines count as hidden when they
3163 // are part of a visual line that starts with another line, or when
3164 // they are entirely covered by collapsed, non-widget span.
3165 function lineIsHidden(doc, line) {
3166 var sps = sawCollapsedSpans && line.markedSpans;
3167 if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
3168 sp = sps[i];
3169 if (!sp.marker.collapsed) { continue }
3170 if (sp.from == null) { return true }
3171 if (sp.marker.widgetNode) { continue }
3172 if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
3173 { return true }
3174 } }
3175 }
3176 function lineIsHiddenInner(doc, line, span) {
3177 if (span.to == null) {
3178 var end = span.marker.find(1, true);
3179 return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker))
3180 }
3181 if (span.marker.inclusiveRight && span.to == line.text.length)
3182 { return true }
3183 for (var sp = (void 0), i = 0; i < line.markedSpans.length; ++i) {
3184 sp = line.markedSpans[i];
3185 if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to &&
3186 (sp.to == null || sp.to != span.from) &&
3187 (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
3188 lineIsHiddenInner(doc, line, sp)) { return true }
3189 }
3190 }
3191
3192 // Find the height above the given line.
3193 function heightAtLine(lineObj) {
3194 lineObj = visualLine(lineObj);
3195
3196 var h = 0, chunk = lineObj.parent;
3197 for (var i = 0; i < chunk.lines.length; ++i) {
3198 var line = chunk.lines[i];
3199 if (line == lineObj) { break }
3200 else { h += line.height; }
3201 }
3202 for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
3203 for (var i$1 = 0; i$1 < p.children.length; ++i$1) {
3204 var cur = p.children[i$1];
3205 if (cur == chunk) { break }
3206 else { h += cur.height; }
3207 }
3208 }
3209 return h
3210 }
3211
3212 // Compute the character length of a line, taking into account
3213 // collapsed ranges (see markText) that might hide parts, and join
3214 // other lines onto it.
3215 function lineLength(line) {
3216 if (line.height == 0) { return 0 }
3217 var len = line.text.length, merged, cur = line;
3218 while (merged = collapsedSpanAtStart(cur)) {
3219 var found = merged.find(0, true);
3220 cur = found.from.line;
3221 len += found.from.ch - found.to.ch;
3222 }
3223 cur = line;
3224 while (merged = collapsedSpanAtEnd(cur)) {
3225 var found$1 = merged.find(0, true);
3226 len -= cur.text.length - found$1.from.ch;
3227 cur = found$1.to.line;
3228 len += cur.text.length - found$1.to.ch;
3229 }
3230 return len
3231 }
3232
3233 // Find the longest line in the document.
3234 function findMaxLine(cm) {
3235 var d = cm.display, doc = cm.doc;
3236 d.maxLine = getLine(doc, doc.first);
3237 d.maxLineLength = lineLength(d.maxLine);
3238 d.maxLineChanged = true;
3239 doc.iter(function (line) {
3240 var len = lineLength(line);
3241 if (len > d.maxLineLength) {
3242 d.maxLineLength = len;
3243 d.maxLine = line;
3244 }
3245 });
3246 }
3247
3248 // LINE DATA STRUCTURE
3249
3250 // Line objects. These hold state related to a line, including
3251 // highlighting info (the styles array).
3252 var Line = function(text, markedSpans, estimateHeight) {
3253 this.text = text;
3254 attachMarkedSpans(this, markedSpans);
3255 this.height = estimateHeight ? estimateHeight(this) : 1;
3256 };
3257
3258 Line.prototype.lineNo = function () { return lineNo(this) };
3259 eventMixin(Line);
3260
3261 // Change the content (text, markers) of a line. Automatically
3262 // invalidates cached information and tries to re-estimate the
3263 // line's height.
3264 function updateLine(line, text, markedSpans, estimateHeight) {
3265 line.text = text;
3266 if (line.stateAfter) { line.stateAfter = null; }
3267 if (line.styles) { line.styles = null; }
3268 if (line.order != null) { line.order = null; }
3269 detachMarkedSpans(line);
3270 attachMarkedSpans(line, markedSpans);
3271 var estHeight = estimateHeight ? estimateHeight(line) : 1;
3272 if (estHeight != line.height) { updateLineHeight(line, estHeight); }
3273 }
3274
3275 // Detach a line from the document tree and its markers.
3276 function cleanUpLine(line) {
3277 line.parent = null;
3278 detachMarkedSpans(line);
3279 }
3280
3281 // Convert a style as returned by a mode (either null, or a string
3282 // containing one or more styles) to a CSS style. This is cached,
3283 // and also looks for line-wide styles.
3284 var styleToClassCache = {}, styleToClassCacheWithMode = {};
3285 function interpretTokenStyle(style, options) {
3286 if (!style || /^\s*$/.test(style)) { return null }
3287 var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache;
3288 return cache[style] ||
3289 (cache[style] = style.replace(/\S+/g, "cm-$&"))
3290 }
3291
3292 // Render the DOM representation of the text of a line. Also builds
3293 // up a 'line map', which points at the DOM nodes that represent
3294 // specific stretches of text, and is used by the measuring code.
3295 // The returned object contains the DOM node, this map, and
3296 // information about line-wide styles that were set by the mode.
3297 function buildLineContent(cm, lineView) {
3298 // The padding-right forces the element to have a 'border', which
3299 // is needed on Webkit to be able to get line-level bounding
3300 // rectangles for it (in measureChar).
3301 var content = eltP("span", null, null, webkit ? "padding-right: .1px" : null);
3302 var builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: content,
3303 col: 0, pos: 0, cm: cm,
3304 trailingSpace: false,
3305 splitSpaces: cm.getOption("lineWrapping")};
3306 lineView.measure = {};
3307
3308 // Iterate over the logical lines that make up this visual line.
3309 for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {
3310 var line = i ? lineView.rest[i - 1] : lineView.line, order = (void 0);
3311 builder.pos = 0;
3312 builder.addToken = buildToken;
3313 // Optionally wire in some hacks into the token-rendering
3314 // algorithm, to deal with browser quirks.
3315 if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction)))
3316 { builder.addToken = buildTokenBadBidi(builder.addToken, order); }
3317 builder.map = [];
3318 var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line);
3319 insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate));
3320 if (line.styleClasses) {
3321 if (line.styleClasses.bgClass)
3322 { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || ""); }
3323 if (line.styleClasses.textClass)
3324 { builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || ""); }
3325 }
3326
3327 // Ensure at least a single node is present, for measuring.
3328 if (builder.map.length == 0)
3329 { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))); }
3330
3331 // Store the map and a cache object for the current logical line
3332 if (i == 0) {
3333 lineView.measure.map = builder.map;
3334 lineView.measure.cache = {};
3335 } else {
3336 (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map)
3337 ;(lineView.measure.caches || (lineView.measure.caches = [])).push({});
3338 }
3339 }
3340
3341 // See issue #2901
3342 if (webkit) {
3343 var last = builder.content.lastChild;
3344 if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab")))
3345 { builder.content.className = "cm-tab-wrap-hack"; }
3346 }
3347
3348 signal(cm, "renderLine", cm, lineView.line, builder.pre);
3349 if (builder.pre.className)
3350 { builder.textClass = joinClasses(builder.pre.className, builder.textClass || ""); }
3351
3352 return builder
3353 }
3354
3355 function defaultSpecialCharPlaceholder(ch) {
3356 var token = elt("span", "\u2022", "cm-invalidchar");
3357 token.title = "\\u" + ch.charCodeAt(0).toString(16);
3358 token.setAttribute("aria-label", token.title);
3359 return token
3360 }
3361
3362 // Build up the DOM representation for a single token, and add it to
3363 // the line map. Takes care to render special characters separately.
3364 function buildToken(builder, text, style, startStyle, endStyle, css, attributes) {
3365 if (!text) { return }
3366 var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text;
3367 var special = builder.cm.state.specialChars, mustWrap = false;
3368 var content;
3369 if (!special.test(text)) {
3370 builder.col += text.length;
3371 content = document.createTextNode(displayText);
3372 builder.map.push(builder.pos, builder.pos + text.length, content);
3373 if (ie && ie_version < 9) { mustWrap = true; }
3374 builder.pos += text.length;
3375 } else {
3376 content = document.createDocumentFragment();
3377 var pos = 0;
3378 while (true) {
3379 special.lastIndex = pos;
3380 var m = special.exec(text);
3381 var skipped = m ? m.index - pos : text.length - pos;
3382 if (skipped) {
3383 var txt = document.createTextNode(displayText.slice(pos, pos + skipped));
3384 if (ie && ie_version < 9) { content.appendChild(elt("span", [txt])); }
3385 else { content.appendChild(txt); }
3386 builder.map.push(builder.pos, builder.pos + skipped, txt);
3387 builder.col += skipped;
3388 builder.pos += skipped;
3389 }
3390 if (!m) { break }
3391 pos += skipped + 1;
3392 var txt$1 = (void 0);
3393 if (m[0] == "\t") {
3394 var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
3395 txt$1 = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
3396 txt$1.setAttribute("role", "presentation");
3397 txt$1.setAttribute("cm-text", "\t");
3398 builder.col += tabWidth;
3399 } else if (m[0] == "\r" || m[0] == "\n") {
3400 txt$1 = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar"));
3401 txt$1.setAttribute("cm-text", m[0]);
3402 builder.col += 1;
3403 } else {
3404 txt$1 = builder.cm.options.specialCharPlaceholder(m[0]);
3405 txt$1.setAttribute("cm-text", m[0]);
3406 if (ie && ie_version < 9) { content.appendChild(elt("span", [txt$1])); }
3407 else { content.appendChild(txt$1); }
3408 builder.col += 1;
3409 }
3410 builder.map.push(builder.pos, builder.pos + 1, txt$1);
3411 builder.pos++;
3412 }
3413 }
3414 builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32;
3415 if (style || startStyle || endStyle || mustWrap || css) {
3416 var fullStyle = style || "";
3417 if (startStyle) { fullStyle += startStyle; }
3418 if (endStyle) { fullStyle += endStyle; }
3419 var token = elt("span", [content], fullStyle, css);
3420 if (attributes) {
3421 for (var attr in attributes) { if (attributes.hasOwnProperty(attr) && attr != "style" && attr != "class")
3422 { token.setAttribute(attr, attributes[attr]); } }
3423 }
3424 return builder.content.appendChild(token)
3425 }
3426 builder.content.appendChild(content);
3427 }
3428
3429 // Change some spaces to NBSP to prevent the browser from collapsing
3430 // trailing spaces at the end of a line when rendering text (issue #1362).
3431 function splitSpaces(text, trailingBefore) {
3432 if (text.length > 1 && !/ /.test(text)) { return text }
3433 var spaceBefore = trailingBefore, result = "";
3434 for (var i = 0; i < text.length; i++) {
3435 var ch = text.charAt(i);
3436 if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32))
3437 { ch = "\u00a0"; }
3438 result += ch;
3439 spaceBefore = ch == " ";
3440 }
3441 return result
3442 }
3443
3444 // Work around nonsense dimensions being reported for stretches of
3445 // right-to-left text.
3446 function buildTokenBadBidi(inner, order) {
3447 return function (builder, text, style, startStyle, endStyle, css, attributes) {
3448 style = style ? style + " cm-force-border" : "cm-force-border";
3449 var start = builder.pos, end = start + text.length;
3450 for (;;) {
3451 // Find the part that overlaps with the start of this text
3452 var part = (void 0);
3453 for (var i = 0; i < order.length; i++) {
3454 part = order[i];
3455 if (part.to > start && part.from <= start) { break }
3456 }
3457 if (part.to >= end) { return inner(builder, text, style, startStyle, endStyle, css, attributes) }
3458 inner(builder, text.slice(0, part.to - start), style, startStyle, null, css, attributes);
3459 startStyle = null;
3460 text = text.slice(part.to - start);
3461 start = part.to;
3462 }
3463 }
3464 }
3465
3466 function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
3467 var widget = !ignoreWidget && marker.widgetNode;
3468 if (widget) { builder.map.push(builder.pos, builder.pos + size, widget); }
3469 if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {
3470 if (!widget)
3471 { widget = builder.content.appendChild(document.createElement("span")); }
3472 widget.setAttribute("cm-marker", marker.id);
3473 }
3474 if (widget) {
3475 builder.cm.display.input.setUneditable(widget);
3476 builder.content.appendChild(widget);
3477 }
3478 builder.pos += size;
3479 builder.trailingSpace = false;
3480 }
3481
3482 // Outputs a number of spans to make up a line, taking highlighting
3483 // and marked text into account.
3484 function insertLineContent(line, builder, styles) {
3485 var spans = line.markedSpans, allText = line.text, at = 0;
3486 if (!spans) {
3487 for (var i$1 = 1; i$1 < styles.length; i$1+=2)
3488 { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpretTokenStyle(styles[i$1+1], builder.cm.options)); }
3489 return
3490 }
3491
3492 var len = allText.length, pos = 0, i = 1, text = "", style, css;
3493 var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed, attributes;
3494 for (;;) {
3495 if (nextChange == pos) { // Update current marker set
3496 spanStyle = spanEndStyle = spanStartStyle = css = "";
3497 attributes = null;
3498 collapsed = null; nextChange = Infinity;
3499 var foundBookmarks = [], endStyles = (void 0);
3500 for (var j = 0; j < spans.length; ++j) {
3501 var sp = spans[j], m = sp.marker;
3502 if (m.type == "bookmark" && sp.from == pos && m.widgetNode) {
3503 foundBookmarks.push(m);
3504 } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) {
3505 if (sp.to != null && sp.to != pos && nextChange > sp.to) {
3506 nextChange = sp.to;
3507 spanEndStyle = "";
3508 }
3509 if (m.className) { spanStyle += " " + m.className; }
3510 if (m.css) { css = (css ? css + ";" : "") + m.css; }
3511 if (m.startStyle && sp.from == pos) { spanStartStyle += " " + m.startStyle; }
3512 if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [])).push(m.endStyle, sp.to); }
3513 // support for the old title property
3514 // https://github.com/codemirror/CodeMirror/pull/5673
3515 if (m.title) { (attributes || (attributes = {})).title = m.title; }
3516 if (m.attributes) {
3517 for (var attr in m.attributes)
3518 { (attributes || (attributes = {}))[attr] = m.attributes[attr]; }
3519 }
3520 if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))
3521 { collapsed = sp; }
3522 } else if (sp.from > pos && nextChange > sp.from) {
3523 nextChange = sp.from;
3524 }
3525 }
3526 if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2)
3527 { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += " " + endStyles[j$1]; } } }
3528
3529 if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2)
3530 { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]); } }
3531 if (collapsed && (collapsed.from || 0) == pos) {
3532 buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos,
3533 collapsed.marker, collapsed.from == null);
3534 if (collapsed.to == null) { return }
3535 if (collapsed.to == pos) { collapsed = false; }
3536 }
3537 }
3538 if (pos >= len) { break }
3539
3540 var upto = Math.min(len, nextChange);
3541 while (true) {
3542 if (text) {
3543 var end = pos + text.length;
3544 if (!collapsed) {
3545 var tokenText = end > upto ? text.slice(0, upto - pos) : text;
3546 builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
3547 spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", css, attributes);
3548 }
3549 if (end >= upto) {text = text.slice(upto - pos); pos = upto; break}
3550 pos = end;
3551 spanStartStyle = "";
3552 }
3553 text = allText.slice(at, at = styles[i++]);
3554 style = interpretTokenStyle(styles[i++], builder.cm.options);
3555 }
3556 }
3557 }
3558
3559
3560 // These objects are used to represent the visible (currently drawn)
3561 // part of the document. A LineView may correspond to multiple
3562 // logical lines, if those are connected by collapsed ranges.
3563 function LineView(doc, line, lineN) {
3564 // The starting line
3565 this.line = line;
3566 // Continuing lines, if any
3567 this.rest = visualLineContinued(line);
3568 // Number of logical lines in this visual line
3569 this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1;
3570 this.node = this.text = null;
3571 this.hidden = lineIsHidden(doc, line);
3572 }
3573
3574 // Create a range of LineView objects for the given lines.
3575 function buildViewArray(cm, from, to) {
3576 var array = [], nextPos;
3577 for (var pos = from; pos < to; pos = nextPos) {
3578 var view = new LineView(cm.doc, getLine(cm.doc, pos), pos);
3579 nextPos = pos + view.size;
3580 array.push(view);
3581 }
3582 return array
3583 }
3584
3585 var operationGroup = null;
3586
3587 function pushOperation(op) {
3588 if (operationGroup) {
3589 operationGroup.ops.push(op);
3590 } else {
3591 op.ownsGroup = operationGroup = {
3592 ops: [op],
3593 delayedCallbacks: []
3594 };
3595 }
3596 }
3597
3598 function fireCallbacksForOps(group) {
3599 // Calls delayed callbacks and cursorActivity handlers until no
3600 // new ones appear
3601 var callbacks = group.delayedCallbacks, i = 0;
3602 do {
3603 for (; i < callbacks.length; i++)
3604 { callbacks[i].call(null); }
3605 for (var j = 0; j < group.ops.length; j++) {
3606 var op = group.ops[j];
3607 if (op.cursorActivityHandlers)
3608 { while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
3609 { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm); } }
3610 }
3611 } while (i < callbacks.length)
3612 }
3613
3614 function finishOperation(op, endCb) {
3615 var group = op.ownsGroup;
3616 if (!group) { return }
3617
3618 try { fireCallbacksForOps(group); }
3619 finally {
3620 operationGroup = null;
3621 endCb(group);
3622 }
3623 }
3624
3625 var orphanDelayedCallbacks = null;
3626
3627 // Often, we want to signal events at a point where we are in the
3628 // middle of some work, but don't want the handler to start calling
3629 // other methods on the editor, which might be in an inconsistent
3630 // state or simply not expect any other events to happen.
3631 // signalLater looks whether there are any handlers, and schedules
3632 // them to be executed when the last operation ends, or, if no
3633 // operation is active, when a timeout fires.
3634 function signalLater(emitter, type /*, values...*/) {
3635 var arr = getHandlers(emitter, type);
3636 if (!arr.length) { return }
3637 var args = Array.prototype.slice.call(arguments, 2), list;
3638 if (operationGroup) {
3639 list = operationGroup.delayedCallbacks;
3640 } else if (orphanDelayedCallbacks) {
3641 list = orphanDelayedCallbacks;
3642 } else {
3643 list = orphanDelayedCallbacks = [];
3644 setTimeout(fireOrphanDelayed, 0);
3645 }
3646 var loop = function ( i ) {
3647 list.push(function () { return arr[i].apply(null, args); });
3648 };
3649
3650 for (var i = 0; i < arr.length; ++i)
3651 loop( i );
3652 }
3653
3654 function fireOrphanDelayed() {
3655 var delayed = orphanDelayedCallbacks;
3656 orphanDelayedCallbacks = null;
3657 for (var i = 0; i < delayed.length; ++i) { delayed[i](); }
3658 }
3659
3660 // When an aspect of a line changes, a string is added to
3661 // lineView.changes. This updates the relevant part of the line's
3662 // DOM structure.
3663 function updateLineForChanges(cm, lineView, lineN, dims) {
3664 for (var j = 0; j < lineView.changes.length; j++) {
3665 var type = lineView.changes[j];
3666 if (type == "text") { updateLineText(cm, lineView); }
3667 else if (type == "gutter") { updateLineGutter(cm, lineView, lineN, dims); }
3668 else if (type == "class") { updateLineClasses(cm, lineView); }
3669 else if (type == "widget") { updateLineWidgets(cm, lineView, dims); }
3670 }
3671 lineView.changes = null;
3672 }
3673
3674 // Lines with gutter elements, widgets or a background class need to
3675 // be wrapped, and have the extra elements added to the wrapper div
3676 function ensureLineWrapped(lineView) {
3677 if (lineView.node == lineView.text) {
3678 lineView.node = elt("div", null, null, "position: relative");
3679 if (lineView.text.parentNode)
3680 { lineView.text.parentNode.replaceChild(lineView.node, lineView.text); }
3681 lineView.node.appendChild(lineView.text);
3682 if (ie && ie_version < 8) { lineView.node.style.zIndex = 2; }
3683 }
3684 return lineView.node
3685 }
3686
3687 function updateLineBackground(cm, lineView) {
3688 var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass;
3689 if (cls) { cls += " CodeMirror-linebackground"; }
3690 if (lineView.background) {
3691 if (cls) { lineView.background.className = cls; }
3692 else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; }
3693 } else if (cls) {
3694 var wrap = ensureLineWrapped(lineView);
3695 lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild);
3696 cm.display.input.setUneditable(lineView.background);
3697 }
3698 }
3699
3700 // Wrapper around buildLineContent which will reuse the structure
3701 // in display.externalMeasured when possible.
3702 function getLineContent(cm, lineView) {
3703 var ext = cm.display.externalMeasured;
3704 if (ext && ext.line == lineView.line) {
3705 cm.display.externalMeasured = null;
3706 lineView.measure = ext.measure;
3707 return ext.built
3708 }
3709 return buildLineContent(cm, lineView)
3710 }
3711
3712 // Redraw the line's text. Interacts with the background and text
3713 // classes because the mode may output tokens that influence these
3714 // classes.
3715 function updateLineText(cm, lineView) {
3716 var cls = lineView.text.className;
3717 var built = getLineContent(cm, lineView);
3718 if (lineView.text == lineView.node) { lineView.node = built.pre; }
3719 lineView.text.parentNode.replaceChild(built.pre, lineView.text);
3720 lineView.text = built.pre;
3721 if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {
3722 lineView.bgClass = built.bgClass;
3723 lineView.textClass = built.textClass;
3724 updateLineClasses(cm, lineView);
3725 } else if (cls) {
3726 lineView.text.className = cls;
3727 }
3728 }
3729
3730 function updateLineClasses(cm, lineView) {
3731 updateLineBackground(cm, lineView);
3732 if (lineView.line.wrapClass)
3733 { ensureLineWrapped(lineView).className = lineView.line.wrapClass; }
3734 else if (lineView.node != lineView.text)
3735 { lineView.node.className = ""; }
3736 var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass;
3737 lineView.text.className = textClass || "";
3738 }
3739
3740 function updateLineGutter(cm, lineView, lineN, dims) {
3741 if (lineView.gutter) {
3742 lineView.node.removeChild(lineView.gutter);
3743 lineView.gutter = null;
3744 }
3745 if (lineView.gutterBackground) {
3746 lineView.node.removeChild(lineView.gutterBackground);
3747 lineView.gutterBackground = null;
3748 }
3749 if (lineView.line.gutterClass) {
3750 var wrap = ensureLineWrapped(lineView);
3751 lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass,
3752 ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px; width: " + (dims.gutterTotalWidth) + "px"));
3753 cm.display.input.setUneditable(lineView.gutterBackground);
3754 wrap.insertBefore(lineView.gutterBackground, lineView.text);
3755 }
3756 var markers = lineView.line.gutterMarkers;
3757 if (cm.options.lineNumbers || markers) {
3758 var wrap$1 = ensureLineWrapped(lineView);
3759 var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"));
3760 cm.display.input.setUneditable(gutterWrap);
3761 wrap$1.insertBefore(gutterWrap, lineView.text);
3762 if (lineView.line.gutterClass)
3763 { gutterWrap.className += " " + lineView.line.gutterClass; }
3764 if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
3765 { lineView.lineNumber = gutterWrap.appendChild(
3766 elt("div", lineNumberFor(cm.options, lineN),
3767 "CodeMirror-linenumber CodeMirror-gutter-elt",
3768 ("left: " + (dims.gutterLeft["CodeMirror-linenumbers"]) + "px; width: " + (cm.display.lineNumInnerWidth) + "px"))); }
3769 if (markers) { for (var k = 0; k < cm.display.gutterSpecs.length; ++k) {
3770 var id = cm.display.gutterSpecs[k].className, found = markers.hasOwnProperty(id) && markers[id];
3771 if (found)
3772 { gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt",
3773 ("left: " + (dims.gutterLeft[id]) + "px; width: " + (dims.gutterWidth[id]) + "px"))); }
3774 } }
3775 }
3776 }
3777
3778 function updateLineWidgets(cm, lineView, dims) {
3779 if (lineView.alignable) { lineView.alignable = null; }
3780 for (var node = lineView.node.firstChild, next = (void 0); node; node = next) {
3781 next = node.nextSibling;
3782 if (node.className == "CodeMirror-linewidget")
3783 { lineView.node.removeChild(node); }
3784 }
3785 insertLineWidgets(cm, lineView, dims);
3786 }
3787
3788 // Build a line's DOM representation from scratch
3789 function buildLineElement(cm, lineView, lineN, dims) {
3790 var built = getLineContent(cm, lineView);
3791 lineView.text = lineView.node = built.pre;
3792 if (built.bgClass) { lineView.bgClass = built.bgClass; }
3793 if (built.textClass) { lineView.textClass = built.textClass; }
3794
3795 updateLineClasses(cm, lineView);
3796 updateLineGutter(cm, lineView, lineN, dims);
3797 insertLineWidgets(cm, lineView, dims);
3798 return lineView.node
3799 }
3800
3801 // A lineView may contain multiple logical lines (when merged by
3802 // collapsed spans). The widgets for all of them need to be drawn.
3803 function insertLineWidgets(cm, lineView, dims) {
3804 insertLineWidgetsFor(cm, lineView.line, lineView, dims, true);
3805 if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
3806 { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false); } }
3807 }
3808
3809 function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {
3810 if (!line.widgets) { return }
3811 var wrap = ensureLineWrapped(lineView);
3812 for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
3813 var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
3814 if (!widget.handleMouseEvents) { node.setAttribute("cm-ignore-events", "true"); }
3815 positionLineWidget(widget, node, lineView, dims);
3816 cm.display.input.setUneditable(node);
3817 if (allowAbove && widget.above)
3818 { wrap.insertBefore(node, lineView.gutter || lineView.text); }
3819 else
3820 { wrap.appendChild(node); }
3821 signalLater(widget, "redraw");
3822 }
3823 }
3824
3825 function positionLineWidget(widget, node, lineView, dims) {
3826 if (widget.noHScroll) {
3827 (lineView.alignable || (lineView.alignable = [])).push(node);
3828 var width = dims.wrapperWidth;
3829 node.style.left = dims.fixedPos + "px";
3830 if (!widget.coverGutter) {
3831 width -= dims.gutterTotalWidth;
3832 node.style.paddingLeft = dims.gutterTotalWidth + "px";
3833 }
3834 node.style.width = width + "px";
3835 }
3836 if (widget.coverGutter) {
3837 node.style.zIndex = 5;
3838 node.style.position = "relative";
3839 if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + "px"; }
3840 }
3841 }
3842
3843 function widgetHeight(widget) {
3844 if (widget.height != null) { return widget.height }
3845 var cm = widget.doc.cm;
3846 if (!cm) { return 0 }
3847 if (!contains(document.body, widget.node)) {
3848 var parentStyle = "position: relative;";
3849 if (widget.coverGutter)
3850 { parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;"; }
3851 if (widget.noHScroll)
3852 { parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;"; }
3853 removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle));
3854 }
3855 return widget.height = widget.node.parentNode.offsetHeight
3856 }
3857
3858 // Return true when the given mouse event happened in a widget
3859 function eventInWidget(display, e) {
3860 for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
3861 if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") ||
3862 (n.parentNode == display.sizer && n != display.mover))
3863 { return true }
3864 }
3865 }
3866
3867 // POSITION MEASUREMENT
3868
3869 function paddingTop(display) {return display.lineSpace.offsetTop}
3870 function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight}
3871 function paddingH(display) {
3872 if (display.cachedPaddingH) { return display.cachedPaddingH }
3873 var e = removeChildrenAndAdd(display.measure, elt("pre", "x"));
3874 var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle;
3875 var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)};
3876 if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data; }
3877 return data
3878 }
3879
3880 function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth }
3881 function displayWidth(cm) {
3882 return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth
3883 }
3884 function displayHeight(cm) {
3885 return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight
3886 }
3887
3888 // Ensure the lineView.wrapping.heights array is populated. This is
3889 // an array of bottom offsets for the lines that make up a drawn
3890 // line. When lineWrapping is on, there might be more than one
3891 // height.
3892 function ensureLineHeights(cm, lineView, rect) {
3893 var wrapping = cm.options.lineWrapping;
3894 var curWidth = wrapping && displayWidth(cm);
3895 if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {
3896 var heights = lineView.measure.heights = [];
3897 if (wrapping) {
3898 lineView.measure.width = curWidth;
3899 var rects = lineView.text.firstChild.getClientRects();
3900 for (var i = 0; i < rects.length - 1; i++) {
3901 var cur = rects[i], next = rects[i + 1];
3902 if (Math.abs(cur.bottom - next.bottom) > 2)
3903 { heights.push((cur.bottom + next.top) / 2 - rect.top); }
3904 }
3905 }
3906 heights.push(rect.bottom - rect.top);
3907 }
3908 }
3909
3910 // Find a line map (mapping character offsets to text nodes) and a
3911 // measurement cache for the given line number. (A line view might
3912 // contain multiple lines when collapsed ranges are present.)
3913 function mapFromLineView(lineView, line, lineN) {
3914 if (lineView.line == line)
3915 { return {map: lineView.measure.map, cache: lineView.measure.cache} }
3916 for (var i = 0; i < lineView.rest.length; i++)
3917 { if (lineView.rest[i] == line)
3918 { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } }
3919 for (var i$1 = 0; i$1 < lineView.rest.length; i$1++)
3920 { if (lineNo(lineView.rest[i$1]) > lineN)
3921 { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } }
3922 }
3923
3924 // Render a line into the hidden node display.externalMeasured. Used
3925 // when measurement is needed for a line that's not in the viewport.
3926 function updateExternalMeasurement(cm, line) {
3927 line = visualLine(line);
3928 var lineN = lineNo(line);
3929 var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN);
3930 view.lineN = lineN;
3931 var built = view.built = buildLineContent(cm, view);
3932 view.text = built.pre;
3933 removeChildrenAndAdd(cm.display.lineMeasure, built.pre);
3934 return view
3935 }
3936
3937 // Get a {top, bottom, left, right} box (in line-local coordinates)
3938 // for a given character.
3939 function measureChar(cm, line, ch, bias) {
3940 return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias)
3941 }
3942
3943 // Find a line view that corresponds to the given line number.
3944 function findViewForLine(cm, lineN) {
3945 if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo)
3946 { return cm.display.view[findViewIndex(cm, lineN)] }
3947 var ext = cm.display.externalMeasured;
3948 if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size)
3949 { return ext }
3950 }
3951
3952 // Measurement can be split in two steps, the set-up work that
3953 // applies to the whole line, and the measurement of the actual
3954 // character. Functions like coordsChar, that need to do a lot of
3955 // measurements in a row, can thus ensure that the set-up work is
3956 // only done once.
3957 function prepareMeasureForLine(cm, line) {
3958 var lineN = lineNo(line);
3959 var view = findViewForLine(cm, lineN);
3960 if (view && !view.text) {
3961 view = null;
3962 } else if (view && view.changes) {
3963 updateLineForChanges(cm, view, lineN, getDimensions(cm));
3964 cm.curOp.forceUpdate = true;
3965 }
3966 if (!view)
3967 { view = updateExternalMeasurement(cm, line); }
3968
3969 var info = mapFromLineView(view, line, lineN);
3970 return {
3971 line: line, view: view, rect: null,
3972 map: info.map, cache: info.cache, before: info.before,
3973 hasHeights: false
3974 }
3975 }
3976
3977 // Given a prepared measurement object, measures the position of an
3978 // actual character (or fetches it from the cache).
3979 function measureCharPrepared(cm, prepared, ch, bias, varHeight) {
3980 if (prepared.before) { ch = -1; }
3981 var key = ch + (bias || ""), found;
3982 if (prepared.cache.hasOwnProperty(key)) {
3983 found = prepared.cache[key];
3984 } else {
3985 if (!prepared.rect)
3986 { prepared.rect = prepared.view.text.getBoundingClientRect(); }
3987 if (!prepared.hasHeights) {
3988 ensureLineHeights(cm, prepared.view, prepared.rect);
3989 prepared.hasHeights = true;
3990 }
3991 found = measureCharInner(cm, prepared, ch, bias);
3992 if (!found.bogus) { prepared.cache[key] = found; }
3993 }
3994 return {left: found.left, right: found.right,
3995 top: varHeight ? found.rtop : found.top,
3996 bottom: varHeight ? found.rbottom : found.bottom}
3997 }
3998
3999 var nullRect = {left: 0, right: 0, top: 0, bottom: 0};
4000
4001 function nodeAndOffsetInLineMap(map$$1, ch, bias) {
4002 var node, start, end, collapse, mStart, mEnd;
4003 // First, search the line map for the text node corresponding to,
4004 // or closest to, the target character.
4005 for (var i = 0; i < map$$1.length; i += 3) {
4006 mStart = map$$1[i];
4007 mEnd = map$$1[i + 1];
4008 if (ch < mStart) {
4009 start = 0; end = 1;
4010 collapse = "left";
4011 } else if (ch < mEnd) {
4012 start = ch - mStart;
4013 end = start + 1;
4014 } else if (i == map$$1.length - 3 || ch == mEnd && map$$1[i + 3] > ch) {
4015 end = mEnd - mStart;
4016 start = end - 1;
4017 if (ch >= mEnd) { collapse = "right"; }
4018 }
4019 if (start != null) {
4020 node = map$$1[i + 2];
4021 if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right"))
4022 { collapse = bias; }
4023 if (bias == "left" && start == 0)
4024 { while (i && map$$1[i - 2] == map$$1[i - 3] && map$$1[i - 1].insertLeft) {
4025 node = map$$1[(i -= 3) + 2];
4026 collapse = "left";
4027 } }
4028 if (bias == "right" && start == mEnd - mStart)
4029 { while (i < map$$1.length - 3 && map$$1[i + 3] == map$$1[i + 4] && !map$$1[i + 5].insertLeft) {
4030 node = map$$1[(i += 3) + 2];
4031 collapse = "right";
4032 } }
4033 break
4034 }
4035 }
4036 return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd}
4037 }
4038
4039 function getUsefulRect(rects, bias) {
4040 var rect = nullRect;
4041 if (bias == "left") { for (var i = 0; i < rects.length; i++) {
4042 if ((rect = rects[i]).left != rect.right) { break }
4043 } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) {
4044 if ((rect = rects[i$1]).left != rect.right) { break }
4045 } }
4046 return rect
4047 }
4048
4049 function measureCharInner(cm, prepared, ch, bias) {
4050 var place = nodeAndOffsetInLineMap(prepared.map, ch, bias);
4051 var node = place.node, start = place.start, end = place.end, collapse = place.collapse;
4052
4053 var rect;
4054 if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.
4055 for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonsense rectangles are returned
4056 while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start; }
4057 while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) { ++end; }
4058 if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart)
4059 { rect = node.parentNode.getBoundingClientRect(); }
4060 else
4061 { rect = getUsefulRect(range(node, start, end).getClientRects(), bias); }
4062 if (rect.left || rect.right || start == 0) { break }
4063 end = start;
4064 start = start - 1;
4065 collapse = "right";
4066 }
4067 if (ie && ie_version < 11) { rect = maybeUpdateRectForZooming(cm.display.measure, rect); }
4068 } else { // If it is a widget, simply get the box for the whole widget.
4069 if (start > 0) { collapse = bias = "right"; }
4070 var rects;
4071 if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)
4072 { rect = rects[bias == "right" ? rects.length - 1 : 0]; }
4073 else
4074 { rect = node.getBoundingClientRect(); }
4075 }
4076 if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {
4077 var rSpan = node.parentNode.getClientRects()[0];
4078 if (rSpan)
4079 { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom}; }
4080 else
4081 { rect = nullRect; }
4082 }
4083
4084 var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top;
4085 var mid = (rtop + rbot) / 2;
4086 var heights = prepared.view.measure.heights;
4087 var i = 0;
4088 for (; i < heights.length - 1; i++)
4089 { if (mid < heights[i]) { break } }
4090 var top = i ? heights[i - 1] : 0, bot = heights[i];
4091 var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left,
4092 right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left,
4093 top: top, bottom: bot};
4094 if (!rect.left && !rect.right) { result.bogus = true; }
4095 if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; }
4096
4097 return result
4098 }
4099
4100 // Work around problem with bounding client rects on ranges being
4101 // returned incorrectly when zoomed on IE10 and below.
4102 function maybeUpdateRectForZooming(measure, rect) {
4103 if (!window.screen || screen.logicalXDPI == null ||
4104 screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))
4105 { return rect }
4106 var scaleX = screen.logicalXDPI / screen.deviceXDPI;
4107 var scaleY = screen.logicalYDPI / screen.deviceYDPI;
4108 return {left: rect.left * scaleX, right: rect.right * scaleX,
4109 top: rect.top * scaleY, bottom: rect.bottom * scaleY}
4110 }
4111
4112 function clearLineMeasurementCacheFor(lineView) {
4113 if (lineView.measure) {
4114 lineView.measure.cache = {};
4115 lineView.measure.heights = null;
4116 if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
4117 { lineView.measure.caches[i] = {}; } }
4118 }
4119 }
4120
4121 function clearLineMeasurementCache(cm) {
4122 cm.display.externalMeasure = null;
4123 removeChildren(cm.display.lineMeasure);
4124 for (var i = 0; i < cm.display.view.length; i++)
4125 { clearLineMeasurementCacheFor(cm.display.view[i]); }
4126 }
4127
4128 function clearCaches(cm) {
4129 clearLineMeasurementCache(cm);
4130 cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null;
4131 if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true; }
4132 cm.display.lineNumChars = null;
4133 }
4134
4135 function pageScrollX() {
4136 // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=489206
4137 // which causes page_Offset and bounding client rects to use
4138 // different reference viewports and invalidate our calculations.
4139 if (chrome && android) { return -(document.body.getBoundingClientRect().left - parseInt(getComputedStyle(document.body).marginLeft)) }
4140 return window.pageXOffset || (document.documentElement || document.body).scrollLeft
4141 }
4142 function pageScrollY() {
4143 if (chrome && android) { return -(document.body.getBoundingClientRect().top - parseInt(getComputedStyle(document.body).marginTop)) }
4144 return window.pageYOffset || (document.documentElement || document.body).scrollTop
4145 }
4146
4147 function widgetTopHeight(lineObj) {
4148 var height = 0;
4149 if (lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above)
4150 { height += widgetHeight(lineObj.widgets[i]); } } }
4151 return height
4152 }
4153
4154 // Converts a {top, bottom, left, right} box from line-local
4155 // coordinates into another coordinate system. Context may be one of
4156 // "line", "div" (display.lineDiv), "local"./null (editor), "window",
4157 // or "page".
4158 function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) {
4159 if (!includeWidgets) {
4160 var height = widgetTopHeight(lineObj);
4161 rect.top += height; rect.bottom += height;
4162 }
4163 if (context == "line") { return rect }
4164 if (!context) { context = "local"; }
4165 var yOff = heightAtLine(lineObj);
4166 if (context == "local") { yOff += paddingTop(cm.display); }
4167 else { yOff -= cm.display.viewOffset; }
4168 if (context == "page" || context == "window") {
4169 var lOff = cm.display.lineSpace.getBoundingClientRect();
4170 yOff += lOff.top + (context == "window" ? 0 : pageScrollY());
4171 var xOff = lOff.left + (context == "window" ? 0 : pageScrollX());
4172 rect.left += xOff; rect.right += xOff;
4173 }
4174 rect.top += yOff; rect.bottom += yOff;
4175 return rect
4176 }
4177
4178 // Coverts a box from "div" coords to another coordinate system.
4179 // Context may be "window", "page", "div", or "local"./null.
4180 function fromCoordSystem(cm, coords, context) {
4181 if (context == "div") { return coords }
4182 var left = coords.left, top = coords.top;
4183 // First move into "page" coordinate system
4184 if (context == "page") {
4185 left -= pageScrollX();
4186 top -= pageScrollY();
4187 } else if (context == "local" || !context) {
4188 var localBox = cm.display.sizer.getBoundingClientRect();
4189 left += localBox.left;
4190 top += localBox.top;
4191 }
4192
4193 var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect();
4194 return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top}
4195 }
4196
4197 function charCoords(cm, pos, context, lineObj, bias) {
4198 if (!lineObj) { lineObj = getLine(cm.doc, pos.line); }
4199 return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context)
4200 }
4201
4202 // Returns a box for a given cursor position, which may have an
4203 // 'other' property containing the position of the secondary cursor
4204 // on a bidi boundary.
4205 // A cursor Pos(line, char, "before") is on the same visual line as `char - 1`
4206 // and after `char - 1` in writing order of `char - 1`
4207 // A cursor Pos(line, char, "after") is on the same visual line as `char`
4208 // and before `char` in writing order of `char`
4209 // Examples (upper-case letters are RTL, lower-case are LTR):
4210 // Pos(0, 1, ...)
4211 // before after
4212 // ab a|b a|b
4213 // aB a|B aB|
4214 // Ab |Ab A|b
4215 // AB B|A B|A
4216 // Every position after the last character on a line is considered to stick
4217 // to the last character on the line.
4218 function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
4219 lineObj = lineObj || getLine(cm.doc, pos.line);
4220 if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); }
4221 function get(ch, right) {
4222 var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight);
4223 if (right) { m.left = m.right; } else { m.right = m.left; }
4224 return intoCoordSystem(cm, lineObj, m, context)
4225 }
4226 var order = getOrder(lineObj, cm.doc.direction), ch = pos.ch, sticky = pos.sticky;
4227 if (ch >= lineObj.text.length) {
4228 ch = lineObj.text.length;
4229 sticky = "before";
4230 } else if (ch <= 0) {
4231 ch = 0;
4232 sticky = "after";
4233 }
4234 if (!order) { return get(sticky == "before" ? ch - 1 : ch, sticky == "before") }
4235
4236 function getBidi(ch, partPos, invert) {
4237 var part = order[partPos], right = part.level == 1;
4238 return get(invert ? ch - 1 : ch, right != invert)
4239 }
4240 var partPos = getBidiPartAt(order, ch, sticky);
4241 var other = bidiOther;
4242 var val = getBidi(ch, partPos, sticky == "before");
4243 if (other != null) { val.other = getBidi(ch, other, sticky != "before"); }
4244 return val
4245 }
4246
4247 // Used to cheaply estimate the coordinates for a position. Used for
4248 // intermediate scroll updates.
4249 function estimateCoords(cm, pos) {
4250 var left = 0;
4251 pos = clipPos(cm.doc, pos);
4252 if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch; }
4253 var lineObj = getLine(cm.doc, pos.line);
4254 var top = heightAtLine(lineObj) + paddingTop(cm.display);
4255 return {left: left, right: left, top: top, bottom: top + lineObj.height}
4256 }
4257
4258 // Positions returned by coordsChar contain some extra information.
4259 // xRel is the relative x position of the input coordinates compared
4260 // to the found position (so xRel > 0 means the coordinates are to
4261 // the right of the character position, for example). When outside
4262 // is true, that means the coordinates lie outside the line's
4263 // vertical range.
4264 function PosWithInfo(line, ch, sticky, outside, xRel) {
4265 var pos = Pos(line, ch, sticky);
4266 pos.xRel = xRel;
4267 if (outside) { pos.outside = true; }
4268 return pos
4269 }
4270
4271 // Compute the character position closest to the given coordinates.
4272 // Input must be lineSpace-local ("div" coordinate system).
4273 function coordsChar(cm, x, y) {
4274 var doc = cm.doc;
4275 y += cm.display.viewOffset;
4276 if (y < 0) { return PosWithInfo(doc.first, 0, null, true, -1) }
4277 var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
4278 if (lineN > last)
4279 { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, true, 1) }
4280 if (x < 0) { x = 0; }
4281
4282 var lineObj = getLine(doc, lineN);
4283 for (;;) {
4284 var found = coordsCharInner(cm, lineObj, lineN, x, y);
4285 var collapsed = collapsedSpanAround(lineObj, found.ch + (found.xRel > 0 ? 1 : 0));
4286 if (!collapsed) { return found }
4287 var rangeEnd = collapsed.find(1);
4288 if (rangeEnd.line == lineN) { return rangeEnd }
4289 lineObj = getLine(doc, lineN = rangeEnd.line);
4290 }
4291 }
4292
4293 function wrappedLineExtent(cm, lineObj, preparedMeasure, y) {
4294 y -= widgetTopHeight(lineObj);
4295 var end = lineObj.text.length;
4296 var begin = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch - 1).bottom <= y; }, end, 0);
4297 end = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch).top > y; }, begin, end);
4298 return {begin: begin, end: end}
4299 }
4300
4301 function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) {
4302 if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); }
4303 var targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), "line").top;
4304 return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop)
4305 }
4306
4307 // Returns true if the given side of a box is after the given
4308 // coordinates, in top-to-bottom, left-to-right order.
4309 function boxIsAfter(box, x, y, left) {
4310 return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x
4311 }
4312
4313 function coordsCharInner(cm, lineObj, lineNo$$1, x, y) {
4314 // Move y into line-local coordinate space
4315 y -= heightAtLine(lineObj);
4316 var preparedMeasure = prepareMeasureForLine(cm, lineObj);
4317 // When directly calling `measureCharPrepared`, we have to adjust
4318 // for the widgets at this line.
4319 var widgetHeight$$1 = widgetTopHeight(lineObj);
4320 var begin = 0, end = lineObj.text.length, ltr = true;
4321
4322 var order = getOrder(lineObj, cm.doc.direction);
4323 // If the line isn't plain left-to-right text, first figure out
4324 // which bidi section the coordinates fall into.
4325 if (order) {
4326 var part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart)
4327 (cm, lineObj, lineNo$$1, preparedMeasure, order, x, y);
4328 ltr = part.level != 1;
4329 // The awkward -1 offsets are needed because findFirst (called
4330 // on these below) will treat its first bound as inclusive,
4331 // second as exclusive, but we want to actually address the
4332 // characters in the part's range
4333 begin = ltr ? part.from : part.to - 1;
4334 end = ltr ? part.to : part.from - 1;
4335 }
4336
4337 // A binary search to find the first character whose bounding box
4338 // starts after the coordinates. If we run across any whose box wrap
4339 // the coordinates, store that.
4340 var chAround = null, boxAround = null;
4341 var ch = findFirst(function (ch) {
4342 var box = measureCharPrepared(cm, preparedMeasure, ch);
4343 box.top += widgetHeight$$1; box.bottom += widgetHeight$$1;
4344 if (!boxIsAfter(box, x, y, false)) { return false }
4345 if (box.top <= y && box.left <= x) {
4346 chAround = ch;
4347 boxAround = box;
4348 }
4349 return true
4350 }, begin, end);
4351
4352 var baseX, sticky, outside = false;
4353 // If a box around the coordinates was found, use that
4354 if (boxAround) {
4355 // Distinguish coordinates nearer to the left or right side of the box
4356 var atLeft = x - boxAround.left < boxAround.right - x, atStart = atLeft == ltr;
4357 ch = chAround + (atStart ? 0 : 1);
4358 sticky = atStart ? "after" : "before";
4359 baseX = atLeft ? boxAround.left : boxAround.right;
4360 } else {
4361 // (Adjust for extended bound, if necessary.)
4362 if (!ltr && (ch == end || ch == begin)) { ch++; }
4363 // To determine which side to associate with, get the box to the
4364 // left of the character and compare it's vertical position to the
4365 // coordinates
4366 sticky = ch == 0 ? "after" : ch == lineObj.text.length ? "before" :
4367 (measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight$$1 <= y) == ltr ?
4368 "after" : "before";
4369 // Now get accurate coordinates for this place, in order to get a
4370 // base X position
4371 var coords = cursorCoords(cm, Pos(lineNo$$1, ch, sticky), "line", lineObj, preparedMeasure);
4372 baseX = coords.left;
4373 outside = y < coords.top || y >= coords.bottom;
4374 }
4375
4376 ch = skipExtendingChars(lineObj.text, ch, 1);
4377 return PosWithInfo(lineNo$$1, ch, sticky, outside, x - baseX)
4378 }
4379
4380 function coordsBidiPart(cm, lineObj, lineNo$$1, preparedMeasure, order, x, y) {
4381 // Bidi parts are sorted left-to-right, and in a non-line-wrapping
4382 // situation, we can take this ordering to correspond to the visual
4383 // ordering. This finds the first part whose end is after the given
4384 // coordinates.
4385 var index = findFirst(function (i) {
4386 var part = order[i], ltr = part.level != 1;
4387 return boxIsAfter(cursorCoords(cm, Pos(lineNo$$1, ltr ? part.to : part.from, ltr ? "before" : "after"),
4388 "line", lineObj, preparedMeasure), x, y, true)
4389 }, 0, order.length - 1);
4390 var part = order[index];
4391 // If this isn't the first part, the part's start is also after
4392 // the coordinates, and the coordinates aren't on the same line as
4393 // that start, move one part back.
4394 if (index > 0) {
4395 var ltr = part.level != 1;
4396 var start = cursorCoords(cm, Pos(lineNo$$1, ltr ? part.from : part.to, ltr ? "after" : "before"),
4397 "line", lineObj, preparedMeasure);
4398 if (boxIsAfter(start, x, y, true) && start.top > y)
4399 { part = order[index - 1]; }
4400 }
4401 return part
4402 }
4403
4404 function coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x, y) {
4405 // In a wrapped line, rtl text on wrapping boundaries can do things
4406 // that don't correspond to the ordering in our `order` array at
4407 // all, so a binary search doesn't work, and we want to return a
4408 // part that only spans one line so that the binary search in
4409 // coordsCharInner is safe. As such, we first find the extent of the
4410 // wrapped line, and then do a flat search in which we discard any
4411 // spans that aren't on the line.
4412 var ref = wrappedLineExtent(cm, lineObj, preparedMeasure, y);
4413 var begin = ref.begin;
4414 var end = ref.end;
4415 if (/\s/.test(lineObj.text.charAt(end - 1))) { end--; }
4416 var part = null, closestDist = null;
4417 for (var i = 0; i < order.length; i++) {
4418 var p = order[i];
4419 if (p.from >= end || p.to <= begin) { continue }
4420 var ltr = p.level != 1;
4421 var endX = measureCharPrepared(cm, preparedMeasure, ltr ? Math.min(end, p.to) - 1 : Math.max(begin, p.from)).right;
4422 // Weigh against spans ending before this, so that they are only
4423 // picked if nothing ends after
4424 var dist = endX < x ? x - endX + 1e9 : endX - x;
4425 if (!part || closestDist > dist) {
4426 part = p;
4427 closestDist = dist;
4428 }
4429 }
4430 if (!part) { part = order[order.length - 1]; }
4431 // Clip the part to the wrapped line.
4432 if (part.from < begin) { part = {from: begin, to: part.to, level: part.level}; }
4433 if (part.to > end) { part = {from: part.from, to: end, level: part.level}; }
4434 return part
4435 }
4436
4437 var measureText;
4438 // Compute the default text height.
4439 function textHeight(display) {
4440 if (display.cachedTextHeight != null) { return display.cachedTextHeight }
4441 if (measureText == null) {
4442 measureText = elt("pre");
4443 // Measure a bunch of lines, for browsers that compute
4444 // fractional heights.
4445 for (var i = 0; i < 49; ++i) {
4446 measureText.appendChild(document.createTextNode("x"));
4447 measureText.appendChild(elt("br"));
4448 }
4449 measureText.appendChild(document.createTextNode("x"));
4450 }
4451 removeChildrenAndAdd(display.measure, measureText);
4452 var height = measureText.offsetHeight / 50;
4453 if (height > 3) { display.cachedTextHeight = height; }
4454 removeChildren(display.measure);
4455 return height || 1
4456 }
4457
4458 // Compute the default character width.
4459 function charWidth(display) {
4460 if (display.cachedCharWidth != null) { return display.cachedCharWidth }
4461 var anchor = elt("span", "xxxxxxxxxx");
4462 var pre = elt("pre", [anchor]);
4463 removeChildrenAndAdd(display.measure, pre);
4464 var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10;
4465 if (width > 2) { display.cachedCharWidth = width; }
4466 return width || 10
4467 }
4468
4469 // Do a bulk-read of the DOM positions and sizes needed to draw the
4470 // view, so that we don't interleave reading and writing to the DOM.
4471 function getDimensions(cm) {
4472 var d = cm.display, left = {}, width = {};
4473 var gutterLeft = d.gutters.clientLeft;
4474 for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
4475 var id = cm.display.gutterSpecs[i].className;
4476 left[id] = n.offsetLeft + n.clientLeft + gutterLeft;
4477 width[id] = n.clientWidth;
4478 }
4479 return {fixedPos: compensateForHScroll(d),
4480 gutterTotalWidth: d.gutters.offsetWidth,
4481 gutterLeft: left,
4482 gutterWidth: width,
4483 wrapperWidth: d.wrapper.clientWidth}
4484 }
4485
4486 // Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
4487 // but using getBoundingClientRect to get a sub-pixel-accurate
4488 // result.
4489 function compensateForHScroll(display) {
4490 return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left
4491 }
4492
4493 // Returns a function that estimates the height of a line, to use as
4494 // first approximation until the line becomes visible (and is thus
4495 // properly measurable).
4496 function estimateHeight(cm) {
4497 var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
4498 var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
4499 return function (line) {
4500 if (lineIsHidden(cm.doc, line)) { return 0 }
4501
4502 var widgetsHeight = 0;
4503 if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) {
4504 if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height; }
4505 } }
4506
4507 if (wrapping)
4508 { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th }
4509 else
4510 { return widgetsHeight + th }
4511 }
4512 }
4513
4514 function estimateLineHeights(cm) {
4515 var doc = cm.doc, est = estimateHeight(cm);
4516 doc.iter(function (line) {
4517 var estHeight = est(line);
4518 if (estHeight != line.height) { updateLineHeight(line, estHeight); }
4519 });
4520 }
4521
4522 // Given a mouse event, find the corresponding position. If liberal
4523 // is false, it checks whether a gutter or scrollbar was clicked,
4524 // and returns null if it was. forRect is used by rectangular
4525 // selections, and tries to estimate a character position even for
4526 // coordinates beyond the right of the text.
4527 function posFromMouse(cm, e, liberal, forRect) {
4528 var display = cm.display;
4529 if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") { return null }
4530
4531 var x, y, space = display.lineSpace.getBoundingClientRect();
4532 // Fails unpredictably on IE[67] when mouse is dragged around quickly.
4533 try { x = e.clientX - space.left; y = e.clientY - space.top; }
4534 catch (e) { return null }
4535 var coords = coordsChar(cm, x, y), line;
4536 if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) {
4537 var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length;
4538 coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff));
4539 }
4540 return coords
4541 }
4542
4543 // Find the view element corresponding to a given line. Return null
4544 // when the line isn't visible.
4545 function findViewIndex(cm, n) {
4546 if (n >= cm.display.viewTo) { return null }
4547 n -= cm.display.viewFrom;
4548 if (n < 0) { return null }
4549 var view = cm.display.view;
4550 for (var i = 0; i < view.length; i++) {
4551 n -= view[i].size;
4552 if (n < 0) { return i }
4553 }
4554 }
4555
4556 // Updates the display.view data structure for a given change to the
4557 // document. From and to are in pre-change coordinates. Lendiff is
4558 // the amount of lines added or subtracted by the change. This is
4559 // used for changes that span multiple lines, or change the way
4560 // lines are divided into visual lines. regLineChange (below)
4561 // registers single-line changes.
4562 function regChange(cm, from, to, lendiff) {
4563 if (from == null) { from = cm.doc.first; }
4564 if (to == null) { to = cm.doc.first + cm.doc.size; }
4565 if (!lendiff) { lendiff = 0; }
4566
4567 var display = cm.display;
4568 if (lendiff && to < display.viewTo &&
4569 (display.updateLineNumbers == null || display.updateLineNumbers > from))
4570 { display.updateLineNumbers = from; }
4571
4572 cm.curOp.viewChanged = true;
4573
4574 if (from >= display.viewTo) { // Change after
4575 if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)
4576 { resetView(cm); }
4577 } else if (to <= display.viewFrom) { // Change before
4578 if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {
4579 resetView(cm);
4580 } else {
4581 display.viewFrom += lendiff;
4582 display.viewTo += lendiff;
4583 }
4584 } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap
4585 resetView(cm);
4586 } else if (from <= display.viewFrom) { // Top overlap
4587 var cut = viewCuttingPoint(cm, to, to + lendiff, 1);
4588 if (cut) {
4589 display.view = display.view.slice(cut.index);
4590 display.viewFrom = cut.lineN;
4591 display.viewTo += lendiff;
4592 } else {
4593 resetView(cm);
4594 }
4595 } else if (to >= display.viewTo) { // Bottom overlap
4596 var cut$1 = viewCuttingPoint(cm, from, from, -1);
4597 if (cut$1) {
4598 display.view = display.view.slice(0, cut$1.index);
4599 display.viewTo = cut$1.lineN;
4600 } else {
4601 resetView(cm);
4602 }
4603 } else { // Gap in the middle
4604 var cutTop = viewCuttingPoint(cm, from, from, -1);
4605 var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1);
4606 if (cutTop && cutBot) {
4607 display.view = display.view.slice(0, cutTop.index)
4608 .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))
4609 .concat(display.view.slice(cutBot.index));
4610 display.viewTo += lendiff;
4611 } else {
4612 resetView(cm);
4613 }
4614 }
4615
4616 var ext = display.externalMeasured;
4617 if (ext) {
4618 if (to < ext.lineN)
4619 { ext.lineN += lendiff; }
4620 else if (from < ext.lineN + ext.size)
4621 { display.externalMeasured = null; }
4622 }
4623 }
4624
4625 // Register a change to a single line. Type must be one of "text",
4626 // "gutter", "class", "widget"
4627 function regLineChange(cm, line, type) {
4628 cm.curOp.viewChanged = true;
4629 var display = cm.display, ext = cm.display.externalMeasured;
4630 if (ext && line >= ext.lineN && line < ext.lineN + ext.size)
4631 { display.externalMeasured = null; }
4632
4633 if (line < display.viewFrom || line >= display.viewTo) { return }
4634 var lineView = display.view[findViewIndex(cm, line)];
4635 if (lineView.node == null) { return }
4636 var arr = lineView.changes || (lineView.changes = []);
4637 if (indexOf(arr, type) == -1) { arr.push(type); }
4638 }
4639
4640 // Clear the view.
4641 function resetView(cm) {
4642 cm.display.viewFrom = cm.display.viewTo = cm.doc.first;
4643 cm.display.view = [];
4644 cm.display.viewOffset = 0;
4645 }
4646
4647 function viewCuttingPoint(cm, oldN, newN, dir) {
4648 var index = findViewIndex(cm, oldN), diff, view = cm.display.view;
4649 if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)
4650 { return {index: index, lineN: newN} }
4651 var n = cm.display.viewFrom;
4652 for (var i = 0; i < index; i++)
4653 { n += view[i].size; }
4654 if (n != oldN) {
4655 if (dir > 0) {
4656 if (index == view.length - 1) { return null }
4657 diff = (n + view[index].size) - oldN;
4658 index++;
4659 } else {
4660 diff = n - oldN;
4661 }
4662 oldN += diff; newN += diff;
4663 }
4664 while (visualLineNo(cm.doc, newN) != newN) {
4665 if (index == (dir < 0 ? 0 : view.length - 1)) { return null }
4666 newN += dir * view[index - (dir < 0 ? 1 : 0)].size;
4667 index += dir;
4668 }
4669 return {index: index, lineN: newN}
4670 }
4671
4672 // Force the view to cover a given range, adding empty view element
4673 // or clipping off existing ones as needed.
4674 function adjustView(cm, from, to) {
4675 var display = cm.display, view = display.view;
4676 if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {
4677 display.view = buildViewArray(cm, from, to);
4678 display.viewFrom = from;
4679 } else {
4680 if (display.viewFrom > from)
4681 { display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view); }
4682 else if (display.viewFrom < from)
4683 { display.view = display.view.slice(findViewIndex(cm, from)); }
4684 display.viewFrom = from;
4685 if (display.viewTo < to)
4686 { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)); }
4687 else if (display.viewTo > to)
4688 { display.view = display.view.slice(0, findViewIndex(cm, to)); }
4689 }
4690 display.viewTo = to;
4691 }
4692
4693 // Count the number of lines in the view whose DOM representation is
4694 // out of date (or nonexistent).
4695 function countDirtyView(cm) {
4696 var view = cm.display.view, dirty = 0;
4697 for (var i = 0; i < view.length; i++) {
4698 var lineView = view[i];
4699 if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty; }
4700 }
4701 return dirty
4702 }
4703
4704 function updateSelection(cm) {
4705 cm.display.input.showSelection(cm.display.input.prepareSelection());
4706 }
4707
4708 function prepareSelection(cm, primary) {
4709 if ( primary === void 0 ) primary = true;
4710
4711 var doc = cm.doc, result = {};
4712 var curFragment = result.cursors = document.createDocumentFragment();
4713 var selFragment = result.selection = document.createDocumentFragment();
4714
4715 for (var i = 0; i < doc.sel.ranges.length; i++) {
4716 if (!primary && i == doc.sel.primIndex) { continue }
4717 var range$$1 = doc.sel.ranges[i];
4718 if (range$$1.from().line >= cm.display.viewTo || range$$1.to().line < cm.display.viewFrom) { continue }
4719 var collapsed = range$$1.empty();
4720 if (collapsed || cm.options.showCursorWhenSelecting)
4721 { drawSelectionCursor(cm, range$$1.head, curFragment); }
4722 if (!collapsed)
4723 { drawSelectionRange(cm, range$$1, selFragment); }
4724 }
4725 return result
4726 }
4727
4728 // Draws a cursor for the given range
4729 function drawSelectionCursor(cm, head, output) {
4730 var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine);
4731
4732 var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor"));
4733 cursor.style.left = pos.left + "px";
4734 cursor.style.top = pos.top + "px";
4735 cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
4736
4737 if (pos.other) {
4738 // Secondary cursor, shown when on a 'jump' in bi-directional text
4739 var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"));
4740 otherCursor.style.display = "";
4741 otherCursor.style.left = pos.other.left + "px";
4742 otherCursor.style.top = pos.other.top + "px";
4743 otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
4744 }
4745 }
4746
4747 function cmpCoords(a, b) { return a.top - b.top || a.left - b.left }
4748
4749 // Draws the given range as a highlighted selection
4750 function drawSelectionRange(cm, range$$1, output) {
4751 var display = cm.display, doc = cm.doc;
4752 var fragment = document.createDocumentFragment();
4753 var padding = paddingH(cm.display), leftSide = padding.left;
4754 var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right;
4755 var docLTR = doc.direction == "ltr";
4756
4757 function add(left, top, width, bottom) {
4758 if (top < 0) { top = 0; }
4759 top = Math.round(top);
4760 bottom = Math.round(bottom);
4761 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")));
4762 }
4763
4764 function drawForLine(line, fromArg, toArg) {
4765 var lineObj = getLine(doc, line);
4766 var lineLen = lineObj.text.length;
4767 var start, end;
4768 function coords(ch, bias) {
4769 return charCoords(cm, Pos(line, ch), "div", lineObj, bias)
4770 }
4771
4772 function wrapX(pos, dir, side) {
4773 var extent = wrappedLineExtentChar(cm, lineObj, null, pos);
4774 var prop = (dir == "ltr") == (side == "after") ? "left" : "right";
4775 var ch = side == "after" ? extent.begin : extent.end - (/\s/.test(lineObj.text.charAt(extent.end - 1)) ? 2 : 1);
4776 return coords(ch, prop)[prop]
4777 }
4778
4779 var order = getOrder(lineObj, doc.direction);
4780 iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir, i) {
4781 var ltr = dir == "ltr";
4782 var fromPos = coords(from, ltr ? "left" : "right");
4783 var toPos = coords(to - 1, ltr ? "right" : "left");
4784
4785 var openStart = fromArg == null && from == 0, openEnd = toArg == null && to == lineLen;
4786 var first = i == 0, last = !order || i == order.length - 1;
4787 if (toPos.top - fromPos.top <= 3) { // Single line
4788 var openLeft = (docLTR ? openStart : openEnd) && first;
4789 var openRight = (docLTR ? openEnd : openStart) && last;
4790 var left = openLeft ? leftSide : (ltr ? fromPos : toPos).left;
4791 var right = openRight ? rightSide : (ltr ? toPos : fromPos).right;
4792 add(left, fromPos.top, right - left, fromPos.bottom);
4793 } else { // Multiple lines
4794 var topLeft, topRight, botLeft, botRight;
4795 if (ltr) {
4796 topLeft = docLTR && openStart && first ? leftSide : fromPos.left;
4797 topRight = docLTR ? rightSide : wrapX(from, dir, "before");
4798 botLeft = docLTR ? leftSide : wrapX(to, dir, "after");
4799 botRight = docLTR && openEnd && last ? rightSide : toPos.right;
4800 } else {
4801 topLeft = !docLTR ? leftSide : wrapX(from, dir, "before");
4802 topRight = !docLTR && openStart && first ? rightSide : fromPos.right;
4803 botLeft = !docLTR && openEnd && last ? leftSide : toPos.left;
4804 botRight = !docLTR ? rightSide : wrapX(to, dir, "after");
4805 }
4806 add(topLeft, fromPos.top, topRight - topLeft, fromPos.bottom);
4807 if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top); }
4808 add(botLeft, toPos.top, botRight - botLeft, toPos.bottom);
4809 }
4810
4811 if (!start || cmpCoords(fromPos, start) < 0) { start = fromPos; }
4812 if (cmpCoords(toPos, start) < 0) { start = toPos; }
4813 if (!end || cmpCoords(fromPos, end) < 0) { end = fromPos; }
4814 if (cmpCoords(toPos, end) < 0) { end = toPos; }
4815 });
4816 return {start: start, end: end}
4817 }
4818
4819 var sFrom = range$$1.from(), sTo = range$$1.to();
4820 if (sFrom.line == sTo.line) {
4821 drawForLine(sFrom.line, sFrom.ch, sTo.ch);
4822 } else {
4823 var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line);
4824 var singleVLine = visualLine(fromLine) == visualLine(toLine);
4825 var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end;
4826 var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start;
4827 if (singleVLine) {
4828 if (leftEnd.top < rightStart.top - 2) {
4829 add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);
4830 add(leftSide, rightStart.top, rightStart.left, rightStart.bottom);
4831 } else {
4832 add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);
4833 }
4834 }
4835 if (leftEnd.bottom < rightStart.top)
4836 { add(leftSide, leftEnd.bottom, null, rightStart.top); }
4837 }
4838
4839 output.appendChild(fragment);
4840 }
4841
4842 // Cursor-blinking
4843 function restartBlink(cm) {
4844 if (!cm.state.focused) { return }
4845 var display = cm.display;
4846 clearInterval(display.blinker);
4847 var on = true;
4848 display.cursorDiv.style.visibility = "";
4849 if (cm.options.cursorBlinkRate > 0)
4850 { display.blinker = setInterval(function () { return display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; },
4851 cm.options.cursorBlinkRate); }
4852 else if (cm.options.cursorBlinkRate < 0)
4853 { display.cursorDiv.style.visibility = "hidden"; }
4854 }
4855
4856 function ensureFocus(cm) {
4857 if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); }
4858 }
4859
4860 function delayBlurEvent(cm) {
4861 cm.state.delayingBlurEvent = true;
4862 setTimeout(function () { if (cm.state.delayingBlurEvent) {
4863 cm.state.delayingBlurEvent = false;
4864 onBlur(cm);
4865 } }, 100);
4866 }
4867
4868 function onFocus(cm, e) {
4869 if (cm.state.delayingBlurEvent) { cm.state.delayingBlurEvent = false; }
4870
4871 if (cm.options.readOnly == "nocursor") { return }
4872 if (!cm.state.focused) {
4873 signal(cm, "focus", cm, e);
4874 cm.state.focused = true;
4875 addClass(cm.display.wrapper, "CodeMirror-focused");
4876 // This test prevents this from firing when a context
4877 // menu is closed (since the input reset would kill the
4878 // select-all detection hack)
4879 if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {
4880 cm.display.input.reset();
4881 if (webkit) { setTimeout(function () { return cm.display.input.reset(true); }, 20); } // Issue #1730
4882 }
4883 cm.display.input.receivedFocus();
4884 }
4885 restartBlink(cm);
4886 }
4887 function onBlur(cm, e) {
4888 if (cm.state.delayingBlurEvent) { return }
4889
4890 if (cm.state.focused) {
4891 signal(cm, "blur", cm, e);
4892 cm.state.focused = false;
4893 rmClass(cm.display.wrapper, "CodeMirror-focused");
4894 }
4895 clearInterval(cm.display.blinker);
4896 setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false; } }, 150);
4897 }
4898
4899 // Read the actual heights of the rendered lines, and update their
4900 // stored heights to match.
4901 function updateHeightsInViewport(cm) {
4902 var display = cm.display;
4903 var prevBottom = display.lineDiv.offsetTop;
4904 for (var i = 0; i < display.view.length; i++) {
4905 var cur = display.view[i], wrapping = cm.options.lineWrapping;
4906 var height = (void 0), width = 0;
4907 if (cur.hidden) { continue }
4908 if (ie && ie_version < 8) {
4909 var bot = cur.node.offsetTop + cur.node.offsetHeight;
4910 height = bot - prevBottom;
4911 prevBottom = bot;
4912 } else {
4913 var box = cur.node.getBoundingClientRect();
4914 height = box.bottom - box.top;
4915 // Check that lines don't extend past the right of the current
4916 // editor width
4917 if (!wrapping && cur.text.firstChild)
4918 { width = cur.text.firstChild.getBoundingClientRect().right - box.left - 1; }
4919 }
4920 var diff = cur.line.height - height;
4921 if (diff > .005 || diff < -.005) {
4922 updateLineHeight(cur.line, height);
4923 updateWidgetHeight(cur.line);
4924 if (cur.rest) { for (var j = 0; j < cur.rest.length; j++)
4925 { updateWidgetHeight(cur.rest[j]); } }
4926 }
4927 if (width > cm.display.sizerWidth) {
4928 var chWidth = Math.ceil(width / charWidth(cm.display));
4929 if (chWidth > cm.display.maxLineLength) {
4930 cm.display.maxLineLength = chWidth;
4931 cm.display.maxLine = cur.line;
4932 cm.display.maxLineChanged = true;
4933 }
4934 }
4935 }
4936 }
4937
4938 // Read and store the height of line widgets associated with the
4939 // given line.
4940 function updateWidgetHeight(line) {
4941 if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i) {
4942 var w = line.widgets[i], parent = w.node.parentNode;
4943 if (parent) { w.height = parent.offsetHeight; }
4944 } }
4945 }
4946
4947 // Compute the lines that are visible in a given viewport (defaults
4948 // the the current scroll position). viewport may contain top,
4949 // height, and ensure (see op.scrollToPos) properties.
4950 function visibleLines(display, doc, viewport) {
4951 var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop;
4952 top = Math.floor(top - paddingTop(display));
4953 var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight;
4954
4955 var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom);
4956 // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
4957 // forces those lines into the viewport (if possible).
4958 if (viewport && viewport.ensure) {
4959 var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line;
4960 if (ensureFrom < from) {
4961 from = ensureFrom;
4962 to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight);
4963 } else if (Math.min(ensureTo, doc.lastLine()) >= to) {
4964 from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight);
4965 to = ensureTo;
4966 }
4967 }
4968 return {from: from, to: Math.max(to, from + 1)}
4969 }
4970
4971 // SCROLLING THINGS INTO VIEW
4972
4973 // If an editor sits on the top or bottom of the window, partially
4974 // scrolled out of view, this ensures that the cursor is visible.
4975 function maybeScrollWindow(cm, rect) {
4976 if (signalDOMEvent(cm, "scrollCursorIntoView")) { return }
4977
4978 var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;
4979 if (rect.top + box.top < 0) { doScroll = true; }
4980 else if (rect.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) { doScroll = false; }
4981 if (doScroll != null && !phantom) {
4982 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;"));
4983 cm.display.lineSpace.appendChild(scrollNode);
4984 scrollNode.scrollIntoView(doScroll);
4985 cm.display.lineSpace.removeChild(scrollNode);
4986 }
4987 }
4988
4989 // Scroll a given position into view (immediately), verifying that
4990 // it actually became visible (as line heights are accurately
4991 // measured, the position of something may 'drift' during drawing).
4992 function scrollPosIntoView(cm, pos, end, margin) {
4993 if (margin == null) { margin = 0; }
4994 var rect;
4995 if (!cm.options.lineWrapping && pos == end) {
4996 // Set pos and end to the cursor positions around the character pos sticks to
4997 // If pos.sticky == "before", that is around pos.ch - 1, otherwise around pos.ch
4998 // If pos == Pos(_, 0, "before"), pos and end are unchanged
4999 pos = pos.ch ? Pos(pos.line, pos.sticky == "before" ? pos.ch - 1 : pos.ch, "after") : pos;
5000 end = pos.sticky == "before" ? Pos(pos.line, pos.ch + 1, "before") : pos;
5001 }
5002 for (var limit = 0; limit < 5; limit++) {
5003 var changed = false;
5004 var coords = cursorCoords(cm, pos);
5005 var endCoords = !end || end == pos ? coords : cursorCoords(cm, end);
5006 rect = {left: Math.min(coords.left, endCoords.left),
5007 top: Math.min(coords.top, endCoords.top) - margin,
5008 right: Math.max(coords.left, endCoords.left),
5009 bottom: Math.max(coords.bottom, endCoords.bottom) + margin};
5010 var scrollPos = calculateScrollPos(cm, rect);
5011 var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
5012 if (scrollPos.scrollTop != null) {
5013 updateScrollTop(cm, scrollPos.scrollTop);
5014 if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true; }
5015 }
5016 if (scrollPos.scrollLeft != null) {
5017 setScrollLeft(cm, scrollPos.scrollLeft);
5018 if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true; }
5019 }
5020 if (!changed) { break }
5021 }
5022 return rect
5023 }
5024
5025 // Scroll a given set of coordinates into view (immediately).
5026 function scrollIntoView(cm, rect) {
5027 var scrollPos = calculateScrollPos(cm, rect);
5028 if (scrollPos.scrollTop != null) { updateScrollTop(cm, scrollPos.scrollTop); }
5029 if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft); }
5030 }
5031
5032 // Calculate a new scroll position needed to scroll the given
5033 // rectangle into view. Returns an object with scrollTop and
5034 // scrollLeft properties. When these are undefined, the
5035 // vertical/horizontal position does not need to be adjusted.
5036 function calculateScrollPos(cm, rect) {
5037 var display = cm.display, snapMargin = textHeight(cm.display);
5038 if (rect.top < 0) { rect.top = 0; }
5039 var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop;
5040 var screen = displayHeight(cm), result = {};
5041 if (rect.bottom - rect.top > screen) { rect.bottom = rect.top + screen; }
5042 var docBottom = cm.doc.height + paddingVert(display);
5043 var atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMargin;
5044 if (rect.top < screentop) {
5045 result.scrollTop = atTop ? 0 : rect.top;
5046 } else if (rect.bottom > screentop + screen) {
5047 var newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen);
5048 if (newTop != screentop) { result.scrollTop = newTop; }
5049 }
5050
5051 var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft;
5052 var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0);
5053 var tooWide = rect.right - rect.left > screenw;
5054 if (tooWide) { rect.right = rect.left + screenw; }
5055 if (rect.left < 10)
5056 { result.scrollLeft = 0; }
5057 else if (rect.left < screenleft)
5058 { result.scrollLeft = Math.max(0, rect.left - (tooWide ? 0 : 10)); }
5059 else if (rect.right > screenw + screenleft - 3)
5060 { result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw; }
5061 return result
5062 }
5063
5064 // Store a relative adjustment to the scroll position in the current
5065 // operation (to be applied when the operation finishes).
5066 function addToScrollTop(cm, top) {
5067 if (top == null) { return }
5068 resolveScrollToPos(cm);
5069 cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top;
5070 }
5071
5072 // Make sure that at the end of the operation the current cursor is
5073 // shown.
5074 function ensureCursorVisible(cm) {
5075 resolveScrollToPos(cm);
5076 var cur = cm.getCursor();
5077 cm.curOp.scrollToPos = {from: cur, to: cur, margin: cm.options.cursorScrollMargin};
5078 }
5079
5080 function scrollToCoords(cm, x, y) {
5081 if (x != null || y != null) { resolveScrollToPos(cm); }
5082 if (x != null) { cm.curOp.scrollLeft = x; }
5083 if (y != null) { cm.curOp.scrollTop = y; }
5084 }
5085
5086 function scrollToRange(cm, range$$1) {
5087 resolveScrollToPos(cm);
5088 cm.curOp.scrollToPos = range$$1;
5089 }
5090
5091 // When an operation has its scrollToPos property set, and another
5092 // scroll action is applied before the end of the operation, this
5093 // 'simulates' scrolling that position into view in a cheap way, so
5094 // that the effect of intermediate scroll commands is not ignored.
5095 function resolveScrollToPos(cm) {
5096 var range$$1 = cm.curOp.scrollToPos;
5097 if (range$$1) {
5098 cm.curOp.scrollToPos = null;
5099 var from = estimateCoords(cm, range$$1.from), to = estimateCoords(cm, range$$1.to);
5100 scrollToCoordsRange(cm, from, to, range$$1.margin);
5101 }
5102 }
5103
5104 function scrollToCoordsRange(cm, from, to, margin) {
5105 var sPos = calculateScrollPos(cm, {
5106 left: Math.min(from.left, to.left),
5107 top: Math.min(from.top, to.top) - margin,
5108 right: Math.max(from.right, to.right),
5109 bottom: Math.max(from.bottom, to.bottom) + margin
5110 });
5111 scrollToCoords(cm, sPos.scrollLeft, sPos.scrollTop);
5112 }
5113
5114 // Sync the scrollable area and scrollbars, ensure the viewport
5115 // covers the visible area.
5116 function updateScrollTop(cm, val) {
5117 if (Math.abs(cm.doc.scrollTop - val) < 2) { return }
5118 if (!gecko) { updateDisplaySimple(cm, {top: val}); }
5119 setScrollTop(cm, val, true);
5120 if (gecko) { updateDisplaySimple(cm); }
5121 startWorker(cm, 100);
5122 }
5123
5124 function setScrollTop(cm, val, forceScroll) {
5125 val = Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val);
5126 if (cm.display.scroller.scrollTop == val && !forceScroll) { return }
5127 cm.doc.scrollTop = val;
5128 cm.display.scrollbars.setScrollTop(val);
5129 if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = val; }
5130 }
5131
5132 // Sync scroller and scrollbar, ensure the gutter elements are
5133 // aligned.
5134 function setScrollLeft(cm, val, isScroller, forceScroll) {
5135 val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth);
5136 if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) { return }
5137 cm.doc.scrollLeft = val;
5138 alignHorizontally(cm);
5139 if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val; }
5140 cm.display.scrollbars.setScrollLeft(val);
5141 }
5142
5143 // SCROLLBARS
5144
5145 // Prepare DOM reads needed to update the scrollbars. Done in one
5146 // shot to minimize update/measure roundtrips.
5147 function measureForScrollbars(cm) {
5148 var d = cm.display, gutterW = d.gutters.offsetWidth;
5149 var docH = Math.round(cm.doc.height + paddingVert(cm.display));
5150 return {
5151 clientHeight: d.scroller.clientHeight,
5152 viewHeight: d.wrapper.clientHeight,
5153 scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,
5154 viewWidth: d.wrapper.clientWidth,
5155 barLeft: cm.options.fixedGutter ? gutterW : 0,
5156 docHeight: docH,
5157 scrollHeight: docH + scrollGap(cm) + d.barHeight,
5158 nativeBarWidth: d.nativeBarWidth,
5159 gutterWidth: gutterW
5160 }
5161 }
5162
5163 var NativeScrollbars = function(place, scroll, cm) {
5164 this.cm = cm;
5165 var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar");
5166 var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar");
5167 vert.tabIndex = horiz.tabIndex = -1;
5168 place(vert); place(horiz);
5169
5170 on(vert, "scroll", function () {
5171 if (vert.clientHeight) { scroll(vert.scrollTop, "vertical"); }
5172 });
5173 on(horiz, "scroll", function () {
5174 if (horiz.clientWidth) { scroll(horiz.scrollLeft, "horizontal"); }
5175 });
5176
5177 this.checkedZeroWidth = false;
5178 // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
5179 if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = "18px"; }
5180 };
5181
5182 NativeScrollbars.prototype.update = function (measure) {
5183 var needsH = measure.scrollWidth > measure.clientWidth + 1;
5184 var needsV = measure.scrollHeight > measure.clientHeight + 1;
5185 var sWidth = measure.nativeBarWidth;
5186
5187 if (needsV) {
5188 this.vert.style.display = "block";
5189 this.vert.style.bottom = needsH ? sWidth + "px" : "0";
5190 var totalHeight = measure.viewHeight - (needsH ? sWidth : 0);
5191 // A bug in IE8 can cause this value to be negative, so guard it.
5192 this.vert.firstChild.style.height =
5193 Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px";
5194 } else {
5195 this.vert.style.display = "";
5196 this.vert.firstChild.style.height = "0";
5197 }
5198
5199 if (needsH) {
5200 this.horiz.style.display = "block";
5201 this.horiz.style.right = needsV ? sWidth + "px" : "0";
5202 this.horiz.style.left = measure.barLeft + "px";
5203 var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0);
5204 this.horiz.firstChild.style.width =
5205 Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + "px";
5206 } else {
5207 this.horiz.style.display = "";
5208 this.horiz.firstChild.style.width = "0";
5209 }
5210
5211 if (!this.checkedZeroWidth && measure.clientHeight > 0) {
5212 if (sWidth == 0) { this.zeroWidthHack(); }
5213 this.checkedZeroWidth = true;
5214 }
5215
5216 return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0}
5217 };
5218
5219 NativeScrollbars.prototype.setScrollLeft = function (pos) {
5220 if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos; }
5221 if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz, "horiz"); }
5222 };
5223
5224 NativeScrollbars.prototype.setScrollTop = function (pos) {
5225 if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos; }
5226 if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert, "vert"); }
5227 };
5228
5229 NativeScrollbars.prototype.zeroWidthHack = function () {
5230 var w = mac && !mac_geMountainLion ? "12px" : "18px";
5231 this.horiz.style.height = this.vert.style.width = w;
5232 this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none";
5233 this.disableHoriz = new Delayed;
5234 this.disableVert = new Delayed;
5235 };
5236
5237 NativeScrollbars.prototype.enableZeroWidthBar = function (bar, delay, type) {
5238 bar.style.pointerEvents = "auto";
5239 function maybeDisable() {
5240 // To find out whether the scrollbar is still visible, we
5241 // check whether the element under the pixel in the bottom
5242 // right corner of the scrollbar box is the scrollbar box
5243 // itself (when the bar is still visible) or its filler child
5244 // (when the bar is hidden). If it is still visible, we keep
5245 // it enabled, if it's hidden, we disable pointer events.
5246 var box = bar.getBoundingClientRect();
5247 var elt$$1 = type == "vert" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2)
5248 : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1);
5249 if (elt$$1 != bar) { bar.style.pointerEvents = "none"; }
5250 else { delay.set(1000, maybeDisable); }
5251 }
5252 delay.set(1000, maybeDisable);
5253 };
5254
5255 NativeScrollbars.prototype.clear = function () {
5256 var parent = this.horiz.parentNode;
5257 parent.removeChild(this.horiz);
5258 parent.removeChild(this.vert);
5259 };
5260
5261 var NullScrollbars = function () {};
5262
5263 NullScrollbars.prototype.update = function () { return {bottom: 0, right: 0} };
5264 NullScrollbars.prototype.setScrollLeft = function () {};
5265 NullScrollbars.prototype.setScrollTop = function () {};
5266 NullScrollbars.prototype.clear = function () {};
5267
5268 function updateScrollbars(cm, measure) {
5269 if (!measure) { measure = measureForScrollbars(cm); }
5270 var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight;
5271 updateScrollbarsInner(cm, measure);
5272 for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) {
5273 if (startWidth != cm.display.barWidth && cm.options.lineWrapping)
5274 { updateHeightsInViewport(cm); }
5275 updateScrollbarsInner(cm, measureForScrollbars(cm));
5276 startWidth = cm.display.barWidth; startHeight = cm.display.barHeight;
5277 }
5278 }
5279
5280 // Re-synchronize the fake scrollbars with the actual size of the
5281 // content.
5282 function updateScrollbarsInner(cm, measure) {
5283 var d = cm.display;
5284 var sizes = d.scrollbars.update(measure);
5285
5286 d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px";
5287 d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px";
5288 d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent";
5289
5290 if (sizes.right && sizes.bottom) {
5291 d.scrollbarFiller.style.display = "block";
5292 d.scrollbarFiller.style.height = sizes.bottom + "px";
5293 d.scrollbarFiller.style.width = sizes.right + "px";
5294 } else { d.scrollbarFiller.style.display = ""; }
5295 if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
5296 d.gutterFiller.style.display = "block";
5297 d.gutterFiller.style.height = sizes.bottom + "px";
5298 d.gutterFiller.style.width = measure.gutterWidth + "px";
5299 } else { d.gutterFiller.style.display = ""; }
5300 }
5301
5302 var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars};
5303
5304 function initScrollbars(cm) {
5305 if (cm.display.scrollbars) {
5306 cm.display.scrollbars.clear();
5307 if (cm.display.scrollbars.addClass)
5308 { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass); }
5309 }
5310
5311 cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) {
5312 cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller);
5313 // Prevent clicks in the scrollbars from killing focus
5314 on(node, "mousedown", function () {
5315 if (cm.state.focused) { setTimeout(function () { return cm.display.input.focus(); }, 0); }
5316 });
5317 node.setAttribute("cm-not-content", "true");
5318 }, function (pos, axis) {
5319 if (axis == "horizontal") { setScrollLeft(cm, pos); }
5320 else { updateScrollTop(cm, pos); }
5321 }, cm);
5322 if (cm.display.scrollbars.addClass)
5323 { addClass(cm.display.wrapper, cm.display.scrollbars.addClass); }
5324 }
5325
5326 // Operations are used to wrap a series of changes to the editor
5327 // state in such a way that each change won't have to update the
5328 // cursor and display (which would be awkward, slow, and
5329 // error-prone). Instead, display updates are batched and then all
5330 // combined and executed at once.
5331
5332 var nextOpId = 0;
5333 // Start a new operation.
5334 function startOperation(cm) {
5335 cm.curOp = {
5336 cm: cm,
5337 viewChanged: false, // Flag that indicates that lines might need to be redrawn
5338 startHeight: cm.doc.height, // Used to detect need to update scrollbar
5339 forceUpdate: false, // Used to force a redraw
5340 updateInput: 0, // Whether to reset the input textarea
5341 typing: false, // Whether this reset should be careful to leave existing text (for compositing)
5342 changeObjs: null, // Accumulated changes, for firing change events
5343 cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on
5344 cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
5345 selectionChanged: false, // Whether the selection needs to be redrawn
5346 updateMaxLine: false, // Set when the widest line needs to be determined anew
5347 scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
5348 scrollToPos: null, // Used to scroll to a specific position
5349 focus: false,
5350 id: ++nextOpId // Unique ID
5351 };
5352 pushOperation(cm.curOp);
5353 }
5354
5355 // Finish an operation, updating the display and signalling delayed events
5356 function endOperation(cm) {
5357 var op = cm.curOp;
5358 if (op) { finishOperation(op, function (group) {
5359 for (var i = 0; i < group.ops.length; i++)
5360 { group.ops[i].cm.curOp = null; }
5361 endOperations(group);
5362 }); }
5363 }
5364
5365 // The DOM updates done when an operation finishes are batched so
5366 // that the minimum number of relayouts are required.
5367 function endOperations(group) {
5368 var ops = group.ops;
5369 for (var i = 0; i < ops.length; i++) // Read DOM
5370 { endOperation_R1(ops[i]); }
5371 for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe)
5372 { endOperation_W1(ops[i$1]); }
5373 for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM
5374 { endOperation_R2(ops[i$2]); }
5375 for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe)
5376 { endOperation_W2(ops[i$3]); }
5377 for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM
5378 { endOperation_finish(ops[i$4]); }
5379 }
5380
5381 function endOperation_R1(op) {
5382 var cm = op.cm, display = cm.display;
5383 maybeClipScrollbars(cm);
5384 if (op.updateMaxLine) { findMaxLine(cm); }
5385
5386 op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||
5387 op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
5388 op.scrollToPos.to.line >= display.viewTo) ||
5389 display.maxLineChanged && cm.options.lineWrapping;
5390 op.update = op.mustUpdate &&
5391 new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate);
5392 }
5393
5394 function endOperation_W1(op) {
5395 op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update);
5396 }
5397
5398 function endOperation_R2(op) {
5399 var cm = op.cm, display = cm.display;
5400 if (op.updatedDisplay) { updateHeightsInViewport(cm); }
5401
5402 op.barMeasure = measureForScrollbars(cm);
5403
5404 // If the max line changed since it was last measured, measure it,
5405 // and ensure the document's width matches it.
5406 // updateDisplay_W2 will use these properties to do the actual resizing
5407 if (display.maxLineChanged && !cm.options.lineWrapping) {
5408 op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3;
5409 cm.display.sizerWidth = op.adjustWidthTo;
5410 op.barMeasure.scrollWidth =
5411 Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth);
5412 op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm));
5413 }
5414
5415 if (op.updatedDisplay || op.selectionChanged)
5416 { op.preparedSelection = display.input.prepareSelection(); }
5417 }
5418
5419 function endOperation_W2(op) {
5420 var cm = op.cm;
5421
5422 if (op.adjustWidthTo != null) {
5423 cm.display.sizer.style.minWidth = op.adjustWidthTo + "px";
5424 if (op.maxScrollLeft < cm.doc.scrollLeft)
5425 { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true); }
5426 cm.display.maxLineChanged = false;
5427 }
5428
5429 var takeFocus = op.focus && op.focus == activeElt();
5430 if (op.preparedSelection)
5431 { cm.display.input.showSelection(op.preparedSelection, takeFocus); }
5432 if (op.updatedDisplay || op.startHeight != cm.doc.height)
5433 { updateScrollbars(cm, op.barMeasure); }
5434 if (op.updatedDisplay)
5435 { setDocumentHeight(cm, op.barMeasure); }
5436
5437 if (op.selectionChanged) { restartBlink(cm); }
5438
5439 if (cm.state.focused && op.updateInput)
5440 { cm.display.input.reset(op.typing); }
5441 if (takeFocus) { ensureFocus(op.cm); }
5442 }
5443
5444 function endOperation_finish(op) {
5445 var cm = op.cm, display = cm.display, doc = cm.doc;
5446
5447 if (op.updatedDisplay) { postUpdateDisplay(cm, op.update); }
5448
5449 // Abort mouse wheel delta measurement, when scrolling explicitly
5450 if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))
5451 { display.wheelStartX = display.wheelStartY = null; }
5452
5453 // Propagate the scroll position to the actual DOM scroller
5454 if (op.scrollTop != null) { setScrollTop(cm, op.scrollTop, op.forceScroll); }
5455
5456 if (op.scrollLeft != null) { setScrollLeft(cm, op.scrollLeft, true, true); }
5457 // If we need to scroll a specific position into view, do so.
5458 if (op.scrollToPos) {
5459 var rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
5460 clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin);
5461 maybeScrollWindow(cm, rect);
5462 }
5463
5464 // Fire events for markers that are hidden/unidden by editing or
5465 // undoing
5466 var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
5467 if (hidden) { for (var i = 0; i < hidden.length; ++i)
5468 { if (!hidden[i].lines.length) { signal(hidden[i], "hide"); } } }
5469 if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1)
5470 { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], "unhide"); } } }
5471
5472 if (display.wrapper.offsetHeight)
5473 { doc.scrollTop = cm.display.scroller.scrollTop; }
5474
5475 // Fire change events, and delayed event handlers
5476 if (op.changeObjs)
5477 { signal(cm, "changes", cm, op.changeObjs); }
5478 if (op.update)
5479 { op.update.finish(); }
5480 }
5481
5482 // Run the given function in an operation
5483 function runInOp(cm, f) {
5484 if (cm.curOp) { return f() }
5485 startOperation(cm);
5486 try { return f() }
5487 finally { endOperation(cm); }
5488 }
5489 // Wraps a function in an operation. Returns the wrapped function.
5490 function operation(cm, f) {
5491 return function() {
5492 if (cm.curOp) { return f.apply(cm, arguments) }
5493 startOperation(cm);
5494 try { return f.apply(cm, arguments) }
5495 finally { endOperation(cm); }
5496 }
5497 }
5498 // Used to add methods to editor and doc instances, wrapping them in
5499 // operations.
5500 function methodOp(f) {
5501 return function() {
5502 if (this.curOp) { return f.apply(this, arguments) }
5503 startOperation(this);
5504 try { return f.apply(this, arguments) }
5505 finally { endOperation(this); }
5506 }
5507 }
5508 function docMethodOp(f) {
5509 return function() {
5510 var cm = this.cm;
5511 if (!cm || cm.curOp) { return f.apply(this, arguments) }
5512 startOperation(cm);
5513 try { return f.apply(this, arguments) }
5514 finally { endOperation(cm); }
5515 }
5516 }
5517
5518 // HIGHLIGHT WORKER
5519
5520 function startWorker(cm, time) {
5521 if (cm.doc.highlightFrontier < cm.display.viewTo)
5522 { cm.state.highlight.set(time, bind(highlightWorker, cm)); }
5523 }
5524
5525 function highlightWorker(cm) {
5526 var doc = cm.doc;
5527 if (doc.highlightFrontier >= cm.display.viewTo) { return }
5528 var end = +new Date + cm.options.workTime;
5529 var context = getContextBefore(cm, doc.highlightFrontier);
5530 var changedLines = [];
5531
5532 doc.iter(context.line, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function (line) {
5533 if (context.line >= cm.display.viewFrom) { // Visible
5534 var oldStyles = line.styles;
5535 var resetState = line.text.length > cm.options.maxHighlightLength ? copyState(doc.mode, context.state) : null;
5536 var highlighted = highlightLine(cm, line, context, true);
5537 if (resetState) { context.state = resetState; }
5538 line.styles = highlighted.styles;
5539 var oldCls = line.styleClasses, newCls = highlighted.classes;
5540 if (newCls) { line.styleClasses = newCls; }
5541 else if (oldCls) { line.styleClasses = null; }
5542 var ischange = !oldStyles || oldStyles.length != line.styles.length ||
5543 oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass);
5544 for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldStyles[i] != line.styles[i]; }
5545 if (ischange) { changedLines.push(context.line); }
5546 line.stateAfter = context.save();
5547 context.nextLine();
5548 } else {
5549 if (line.text.length <= cm.options.maxHighlightLength)
5550 { processLine(cm, line.text, context); }
5551 line.stateAfter = context.line % 5 == 0 ? context.save() : null;
5552 context.nextLine();
5553 }
5554 if (+new Date > end) {
5555 startWorker(cm, cm.options.workDelay);
5556 return true
5557 }
5558 });
5559 doc.highlightFrontier = context.line;
5560 doc.modeFrontier = Math.max(doc.modeFrontier, context.line);
5561 if (changedLines.length) { runInOp(cm, function () {
5562 for (var i = 0; i < changedLines.length; i++)
5563 { regLineChange(cm, changedLines[i], "text"); }
5564 }); }
5565 }
5566
5567 // DISPLAY DRAWING
5568
5569 var DisplayUpdate = function(cm, viewport, force) {
5570 var display = cm.display;
5571
5572 this.viewport = viewport;
5573 // Store some values that we'll need later (but don't want to force a relayout for)
5574 this.visible = visibleLines(display, cm.doc, viewport);
5575 this.editorIsHidden = !display.wrapper.offsetWidth;
5576 this.wrapperHeight = display.wrapper.clientHeight;
5577 this.wrapperWidth = display.wrapper.clientWidth;
5578 this.oldDisplayWidth = displayWidth(cm);
5579 this.force = force;
5580 this.dims = getDimensions(cm);
5581 this.events = [];
5582 };
5583
5584 DisplayUpdate.prototype.signal = function (emitter, type) {
5585 if (hasHandler(emitter, type))
5586 { this.events.push(arguments); }
5587 };
5588 DisplayUpdate.prototype.finish = function () {
5589 var this$1 = this;
5590
5591 for (var i = 0; i < this.events.length; i++)
5592 { signal.apply(null, this$1.events[i]); }
5593 };
5594
5595 function maybeClipScrollbars(cm) {
5596 var display = cm.display;
5597 if (!display.scrollbarsClipped && display.scroller.offsetWidth) {
5598 display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth;
5599 display.heightForcer.style.height = scrollGap(cm) + "px";
5600 display.sizer.style.marginBottom = -display.nativeBarWidth + "px";
5601 display.sizer.style.borderRightWidth = scrollGap(cm) + "px";
5602 display.scrollbarsClipped = true;
5603 }
5604 }
5605
5606 function selectionSnapshot(cm) {
5607 if (cm.hasFocus()) { return null }
5608 var active = activeElt();
5609 if (!active || !contains(cm.display.lineDiv, active)) { return null }
5610 var result = {activeElt: active};
5611 if (window.getSelection) {
5612 var sel = window.getSelection();
5613 if (sel.anchorNode && sel.extend && contains(cm.display.lineDiv, sel.anchorNode)) {
5614 result.anchorNode = sel.anchorNode;
5615 result.anchorOffset = sel.anchorOffset;
5616 result.focusNode = sel.focusNode;
5617 result.focusOffset = sel.focusOffset;
5618 }
5619 }
5620 return result
5621 }
5622
5623 function restoreSelection(snapshot) {
5624 if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt()) { return }
5625 snapshot.activeElt.focus();
5626 if (snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) {
5627 var sel = window.getSelection(), range$$1 = document.createRange();
5628 range$$1.setEnd(snapshot.anchorNode, snapshot.anchorOffset);
5629 range$$1.collapse(false);
5630 sel.removeAllRanges();
5631 sel.addRange(range$$1);
5632 sel.extend(snapshot.focusNode, snapshot.focusOffset);
5633 }
5634 }
5635
5636 // Does the actual updating of the line display. Bails out
5637 // (returning false) when there is nothing to be done and forced is
5638 // false.
5639 function updateDisplayIfNeeded(cm, update) {
5640 var display = cm.display, doc = cm.doc;
5641
5642 if (update.editorIsHidden) {
5643 resetView(cm);
5644 return false
5645 }
5646
5647 // Bail out if the visible area is already rendered and nothing changed.
5648 if (!update.force &&
5649 update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&
5650 (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&
5651 display.renderedView == display.view && countDirtyView(cm) == 0)
5652 { return false }
5653
5654 if (maybeUpdateLineNumberWidth(cm)) {
5655 resetView(cm);
5656 update.dims = getDimensions(cm);
5657 }
5658
5659 // Compute a suitable new viewport (from & to)
5660 var end = doc.first + doc.size;
5661 var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first);
5662 var to = Math.min(end, update.visible.to + cm.options.viewportMargin);
5663 if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max(doc.first, display.viewFrom); }
5664 if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, display.viewTo); }
5665 if (sawCollapsedSpans) {
5666 from = visualLineNo(cm.doc, from);
5667 to = visualLineEndNo(cm.doc, to);
5668 }
5669
5670 var different = from != display.viewFrom || to != display.viewTo ||
5671 display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth;
5672 adjustView(cm, from, to);
5673
5674 display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom));
5675 // Position the mover div to align with the current scroll position
5676 cm.display.mover.style.top = display.viewOffset + "px";
5677
5678 var toUpdate = countDirtyView(cm);
5679 if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view &&
5680 (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))
5681 { return false }
5682
5683 // For big changes, we hide the enclosing element during the
5684 // update, since that speeds up the operations on most browsers.
5685 var selSnapshot = selectionSnapshot(cm);
5686 if (toUpdate > 4) { display.lineDiv.style.display = "none"; }
5687 patchDisplay(cm, display.updateLineNumbers, update.dims);
5688 if (toUpdate > 4) { display.lineDiv.style.display = ""; }
5689 display.renderedView = display.view;
5690 // There might have been a widget with a focused element that got
5691 // hidden or updated, if so re-focus it.
5692 restoreSelection(selSnapshot);
5693
5694 // Prevent selection and cursors from interfering with the scroll
5695 // width and height.
5696 removeChildren(display.cursorDiv);
5697 removeChildren(display.selectionDiv);
5698 display.gutters.style.height = display.sizer.style.minHeight = 0;
5699
5700 if (different) {
5701 display.lastWrapHeight = update.wrapperHeight;
5702 display.lastWrapWidth = update.wrapperWidth;
5703 startWorker(cm, 400);
5704 }
5705
5706 display.updateLineNumbers = null;
5707
5708 return true
5709 }
5710
5711 function postUpdateDisplay(cm, update) {
5712 var viewport = update.viewport;
5713
5714 for (var first = true;; first = false) {
5715 if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) {
5716 // Clip forced viewport to actual scrollable area.
5717 if (viewport && viewport.top != null)
5718 { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)}; }
5719 // Updated line heights might result in the drawn area not
5720 // actually covering the viewport. Keep looping until it does.
5721 update.visible = visibleLines(cm.display, cm.doc, viewport);
5722 if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)
5723 { break }
5724 }
5725 if (!updateDisplayIfNeeded(cm, update)) { break }
5726 updateHeightsInViewport(cm);
5727 var barMeasure = measureForScrollbars(cm);
5728 updateSelection(cm);
5729 updateScrollbars(cm, barMeasure);
5730 setDocumentHeight(cm, barMeasure);
5731 update.force = false;
5732 }
5733
5734 update.signal(cm, "update", cm);
5735 if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {
5736 update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo);
5737 cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo;
5738 }
5739 }
5740
5741 function updateDisplaySimple(cm, viewport) {
5742 var update = new DisplayUpdate(cm, viewport);
5743 if (updateDisplayIfNeeded(cm, update)) {
5744 updateHeightsInViewport(cm);
5745 postUpdateDisplay(cm, update);
5746 var barMeasure = measureForScrollbars(cm);
5747 updateSelection(cm);
5748 updateScrollbars(cm, barMeasure);
5749 setDocumentHeight(cm, barMeasure);
5750 update.finish();
5751 }
5752 }
5753
5754 // Sync the actual display DOM structure with display.view, removing
5755 // nodes for lines that are no longer in view, and creating the ones
5756 // that are not there yet, and updating the ones that are out of
5757 // date.
5758 function patchDisplay(cm, updateNumbersFrom, dims) {
5759 var display = cm.display, lineNumbers = cm.options.lineNumbers;
5760 var container = display.lineDiv, cur = container.firstChild;
5761
5762 function rm(node) {
5763 var next = node.nextSibling;
5764 // Works around a throw-scroll bug in OS X Webkit
5765 if (webkit && mac && cm.display.currentWheelTarget == node)
5766 { node.style.display = "none"; }
5767 else
5768 { node.parentNode.removeChild(node); }
5769 return next
5770 }
5771
5772 var view = display.view, lineN = display.viewFrom;
5773 // Loop over the elements in the view, syncing cur (the DOM nodes
5774 // in display.lineDiv) with the view as we go.
5775 for (var i = 0; i < view.length; i++) {
5776 var lineView = view[i];
5777 if (lineView.hidden) ; else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet
5778 var node = buildLineElement(cm, lineView, lineN, dims);
5779 container.insertBefore(node, cur);
5780 } else { // Already drawn
5781 while (cur != lineView.node) { cur = rm(cur); }
5782 var updateNumber = lineNumbers && updateNumbersFrom != null &&
5783 updateNumbersFrom <= lineN && lineView.lineNumber;
5784 if (lineView.changes) {
5785 if (indexOf(lineView.changes, "gutter") > -1) { updateNumber = false; }
5786 updateLineForChanges(cm, lineView, lineN, dims);
5787 }
5788 if (updateNumber) {
5789 removeChildren(lineView.lineNumber);
5790 lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN)));
5791 }
5792 cur = lineView.node.nextSibling;
5793 }
5794 lineN += lineView.size;
5795 }
5796 while (cur) { cur = rm(cur); }
5797 }
5798
5799 function updateGutterSpace(display) {
5800 var width = display.gutters.offsetWidth;
5801 display.sizer.style.marginLeft = width + "px";
5802 }
5803
5804 function setDocumentHeight(cm, measure) {
5805 cm.display.sizer.style.minHeight = measure.docHeight + "px";
5806 cm.display.heightForcer.style.top = measure.docHeight + "px";
5807 cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px";
5808 }
5809
5810 // Re-align line numbers and gutter marks to compensate for
5811 // horizontal scrolling.
5812 function alignHorizontally(cm) {
5813 var display = cm.display, view = display.view;
5814 if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) { return }
5815 var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;
5816 var gutterW = display.gutters.offsetWidth, left = comp + "px";
5817 for (var i = 0; i < view.length; i++) { if (!view[i].hidden) {
5818 if (cm.options.fixedGutter) {
5819 if (view[i].gutter)
5820 { view[i].gutter.style.left = left; }
5821 if (view[i].gutterBackground)
5822 { view[i].gutterBackground.style.left = left; }
5823 }
5824 var align = view[i].alignable;
5825 if (align) { for (var j = 0; j < align.length; j++)
5826 { align[j].style.left = left; } }
5827 } }
5828 if (cm.options.fixedGutter)
5829 { display.gutters.style.left = (comp + gutterW) + "px"; }
5830 }
5831
5832 // Used to ensure that the line number gutter is still the right
5833 // size for the current document size. Returns true when an update
5834 // is needed.
5835 function maybeUpdateLineNumberWidth(cm) {
5836 if (!cm.options.lineNumbers) { return false }
5837 var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;
5838 if (last.length != display.lineNumChars) {
5839 var test = display.measure.appendChild(elt("div", [elt("div", last)],
5840 "CodeMirror-linenumber CodeMirror-gutter-elt"));
5841 var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
5842 display.lineGutter.style.width = "";
5843 display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1;
5844 display.lineNumWidth = display.lineNumInnerWidth + padding;
5845 display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
5846 display.lineGutter.style.width = display.lineNumWidth + "px";
5847 updateGutterSpace(cm.display);
5848 return true
5849 }
5850 return false
5851 }
5852
5853 function getGutters(gutters, lineNumbers) {
5854 var result = [], sawLineNumbers = false;
5855 for (var i = 0; i < gutters.length; i++) {
5856 var name = gutters[i], style = null;
5857 if (typeof name != "string") { style = name.style; name = name.className; }
5858 if (name == "CodeMirror-linenumbers") {
5859 if (!lineNumbers) { continue }
5860 else { sawLineNumbers = true; }
5861 }
5862 result.push({className: name, style: style});
5863 }
5864 if (lineNumbers && !sawLineNumbers) { result.push({className: "CodeMirror-linenumbers", style: null}); }
5865 return result
5866 }
5867
5868 // Rebuild the gutter elements, ensure the margin to the left of the
5869 // code matches their width.
5870 function renderGutters(display) {
5871 var gutters = display.gutters, specs = display.gutterSpecs;
5872 removeChildren(gutters);
5873 display.lineGutter = null;
5874 for (var i = 0; i < specs.length; ++i) {
5875 var ref = specs[i];
5876 var className = ref.className;
5877 var style = ref.style;
5878 var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + className));
5879 if (style) { gElt.style.cssText = style; }
5880 if (className == "CodeMirror-linenumbers") {
5881 display.lineGutter = gElt;
5882 gElt.style.width = (display.lineNumWidth || 1) + "px";
5883 }
5884 }
5885 gutters.style.display = specs.length ? "" : "none";
5886 updateGutterSpace(display);
5887 }
5888
5889 function updateGutters(cm) {
5890 renderGutters(cm.display);
5891 regChange(cm);
5892 alignHorizontally(cm);
5893 }
5894
5895 // The display handles the DOM integration, both for input reading
5896 // and content drawing. It holds references to DOM nodes and
5897 // display-related state.
5898
5899 function Display(place, doc, input, options) {
5900 var d = this;
5901 this.input = input;
5902
5903 // Covers bottom-right square when both scrollbars are present.
5904 d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
5905 d.scrollbarFiller.setAttribute("cm-not-content", "true");
5906 // Covers bottom of gutter when coverGutterNextToScrollbar is on
5907 // and h scrollbar is present.
5908 d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler");
5909 d.gutterFiller.setAttribute("cm-not-content", "true");
5910 // Will contain the actual code, positioned to cover the viewport.
5911 d.lineDiv = eltP("div", null, "CodeMirror-code");
5912 // Elements are added to these to represent selection and cursors.
5913 d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
5914 d.cursorDiv = elt("div", null, "CodeMirror-cursors");
5915 // A visibility: hidden element used to find the size of things.
5916 d.measure = elt("div", null, "CodeMirror-measure");
5917 // When lines outside of the viewport are measured, they are drawn in this.
5918 d.lineMeasure = elt("div", null, "CodeMirror-measure");
5919 // Wraps everything that needs to exist inside the vertically-padded coordinate system
5920 d.lineSpace = eltP("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],
5921 null, "position: relative; outline: none");
5922 var lines = eltP("div", [d.lineSpace], "CodeMirror-lines");
5923 // Moved around its parent to cover visible view.
5924 d.mover = elt("div", [lines], null, "position: relative");
5925 // Set to the height of the document, allowing scrolling.
5926 d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
5927 d.sizerWidth = null;
5928 // Behavior of elts with overflow: auto and padding is
5929 // inconsistent across browsers. This is used to ensure the
5930 // scrollable area is big enough.
5931 d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;");
5932 // Will contain the gutters, if any.
5933 d.gutters = elt("div", null, "CodeMirror-gutters");
5934 d.lineGutter = null;
5935 // Actual scrollable element.
5936 d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll");
5937 d.scroller.setAttribute("tabIndex", "-1");
5938 // The element in which the editor lives.
5939 d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror");
5940
5941 // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)
5942 if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
5943 if (!webkit && !(gecko && mobile)) { d.scroller.draggable = true; }
5944
5945 if (place) {
5946 if (place.appendChild) { place.appendChild(d.wrapper); }
5947 else { place(d.wrapper); }
5948 }
5949
5950 // Current rendered range (may be bigger than the view window).
5951 d.viewFrom = d.viewTo = doc.first;
5952 d.reportedViewFrom = d.reportedViewTo = doc.first;
5953 // Information about the rendered lines.
5954 d.view = [];
5955 d.renderedView = null;
5956 // Holds info about a single rendered line when it was rendered
5957 // for measurement, while not in view.
5958 d.externalMeasured = null;
5959 // Empty space (in pixels) above the view
5960 d.viewOffset = 0;
5961 d.lastWrapHeight = d.lastWrapWidth = 0;
5962 d.updateLineNumbers = null;
5963
5964 d.nativeBarWidth = d.barHeight = d.barWidth = 0;
5965 d.scrollbarsClipped = false;
5966
5967 // Used to only resize the line number gutter when necessary (when
5968 // the amount of lines crosses a boundary that makes its width change)
5969 d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
5970 // Set to true when a non-horizontal-scrolling line widget is
5971 // added. As an optimization, line widget aligning is skipped when
5972 // this is false.
5973 d.alignWidgets = false;
5974
5975 d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
5976
5977 // Tracks the maximum line length so that the horizontal scrollbar
5978 // can be kept static when scrolling.
5979 d.maxLine = null;
5980 d.maxLineLength = 0;
5981 d.maxLineChanged = false;
5982
5983 // Used for measuring wheel scrolling granularity
5984 d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
5985
5986 // True when shift is held down.
5987 d.shift = false;
5988
5989 // Used to track whether anything happened since the context menu
5990 // was opened.
5991 d.selForContextMenu = null;
5992
5993 d.activeTouch = null;
5994
5995 d.gutterSpecs = getGutters(options.gutters, options.lineNumbers);
5996 renderGutters(d);
5997
5998 input.init(d);
5999 }
6000
6001 // Since the delta values reported on mouse wheel events are
6002 // unstandardized between browsers and even browser versions, and
6003 // generally horribly unpredictable, this code starts by measuring
6004 // the scroll effect that the first few mouse wheel events have,
6005 // and, from that, detects the way it can convert deltas to pixel
6006 // offsets afterwards.
6007 //
6008 // The reason we want to know the amount a wheel event will scroll
6009 // is that it gives us a chance to update the display before the
6010 // actual scrolling happens, reducing flickering.
6011
6012 var wheelSamples = 0, wheelPixelsPerUnit = null;
6013 // Fill in a browser-detected starting value on browsers where we
6014 // know one. These don't have to be accurate -- the result of them
6015 // being wrong would just be a slight flicker on the first wheel
6016 // scroll (if it is large enough).
6017 if (ie) { wheelPixelsPerUnit = -.53; }
6018 else if (gecko) { wheelPixelsPerUnit = 15; }
6019 else if (chrome) { wheelPixelsPerUnit = -.7; }
6020 else if (safari) { wheelPixelsPerUnit = -1/3; }
6021
6022 function wheelEventDelta(e) {
6023 var dx = e.wheelDeltaX, dy = e.wheelDeltaY;
6024 if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail; }
6025 if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail; }
6026 else if (dy == null) { dy = e.wheelDelta; }
6027 return {x: dx, y: dy}
6028 }
6029 function wheelEventPixels(e) {
6030 var delta = wheelEventDelta(e);
6031 delta.x *= wheelPixelsPerUnit;
6032 delta.y *= wheelPixelsPerUnit;
6033 return delta
6034 }
6035
6036 function onScrollWheel(cm, e) {
6037 var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y;
6038
6039 var display = cm.display, scroll = display.scroller;
6040 // Quit if there's nothing to scroll here
6041 var canScrollX = scroll.scrollWidth > scroll.clientWidth;
6042 var canScrollY = scroll.scrollHeight > scroll.clientHeight;
6043 if (!(dx && canScrollX || dy && canScrollY)) { return }
6044
6045 // Webkit browsers on OS X abort momentum scrolls when the target
6046 // of the scroll event is removed from the scrollable element.
6047 // This hack (see related code in patchDisplay) makes sure the
6048 // element is kept around.
6049 if (dy && mac && webkit) {
6050 outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {
6051 for (var i = 0; i < view.length; i++) {
6052 if (view[i].node == cur) {
6053 cm.display.currentWheelTarget = cur;
6054 break outer
6055 }
6056 }
6057 }
6058 }
6059
6060 // On some browsers, horizontal scrolling will cause redraws to
6061 // happen before the gutter has been realigned, causing it to
6062 // wriggle around in a most unseemly way. When we have an
6063 // estimated pixels/delta value, we just handle horizontal
6064 // scrolling entirely here. It'll be slightly off from native, but
6065 // better than glitching out.
6066 if (dx && !gecko && !presto && wheelPixelsPerUnit != null) {
6067 if (dy && canScrollY)
6068 { updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * wheelPixelsPerUnit)); }
6069 setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * wheelPixelsPerUnit));
6070 // Only prevent default scrolling if vertical scrolling is
6071 // actually possible. Otherwise, it causes vertical scroll
6072 // jitter on OSX trackpads when deltaX is small and deltaY
6073 // is large (issue #3579)
6074 if (!dy || (dy && canScrollY))
6075 { e_preventDefault(e); }
6076 display.wheelStartX = null; // Abort measurement, if in progress
6077 return
6078 }
6079
6080 // 'Project' the visible viewport to cover the area that is being
6081 // scrolled into view (if we know enough to estimate it).
6082 if (dy && wheelPixelsPerUnit != null) {
6083 var pixels = dy * wheelPixelsPerUnit;
6084 var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
6085 if (pixels < 0) { top = Math.max(0, top + pixels - 50); }
6086 else { bot = Math.min(cm.doc.height, bot + pixels + 50); }
6087 updateDisplaySimple(cm, {top: top, bottom: bot});
6088 }
6089
6090 if (wheelSamples < 20) {
6091 if (display.wheelStartX == null) {
6092 display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;
6093 display.wheelDX = dx; display.wheelDY = dy;
6094 setTimeout(function () {
6095 if (display.wheelStartX == null) { return }
6096 var movedX = scroll.scrollLeft - display.wheelStartX;
6097 var movedY = scroll.scrollTop - display.wheelStartY;
6098 var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
6099 (movedX && display.wheelDX && movedX / display.wheelDX);
6100 display.wheelStartX = display.wheelStartY = null;
6101 if (!sample) { return }
6102 wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
6103 ++wheelSamples;
6104 }, 200);
6105 } else {
6106 display.wheelDX += dx; display.wheelDY += dy;
6107 }
6108 }
6109 }
6110
6111 // Selection objects are immutable. A new one is created every time
6112 // the selection changes. A selection is one or more non-overlapping
6113 // (and non-touching) ranges, sorted, and an integer that indicates
6114 // which one is the primary selection (the one that's scrolled into
6115 // view, that getCursor returns, etc).
6116 var Selection = function(ranges, primIndex) {
6117 this.ranges = ranges;
6118 this.primIndex = primIndex;
6119 };
6120
6121 Selection.prototype.primary = function () { return this.ranges[this.primIndex] };
6122
6123 Selection.prototype.equals = function (other) {
6124 var this$1 = this;
6125
6126 if (other == this) { return true }
6127 if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false }
6128 for (var i = 0; i < this.ranges.length; i++) {
6129 var here = this$1.ranges[i], there = other.ranges[i];
6130 if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) { return false }
6131 }
6132 return true
6133 };
6134
6135 Selection.prototype.deepCopy = function () {
6136 var this$1 = this;
6137
6138 var out = [];
6139 for (var i = 0; i < this.ranges.length; i++)
6140 { out[i] = new Range(copyPos(this$1.ranges[i].anchor), copyPos(this$1.ranges[i].head)); }
6141 return new Selection(out, this.primIndex)
6142 };
6143
6144 Selection.prototype.somethingSelected = function () {
6145 var this$1 = this;
6146
6147 for (var i = 0; i < this.ranges.length; i++)
6148 { if (!this$1.ranges[i].empty()) { return true } }
6149 return false
6150 };
6151
6152 Selection.prototype.contains = function (pos, end) {
6153 var this$1 = this;
6154
6155 if (!end) { end = pos; }
6156 for (var i = 0; i < this.ranges.length; i++) {
6157 var range = this$1.ranges[i];
6158 if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)
6159 { return i }
6160 }
6161 return -1
6162 };
6163
6164 var Range = function(anchor, head) {
6165 this.anchor = anchor; this.head = head;
6166 };
6167
6168 Range.prototype.from = function () { return minPos(this.anchor, this.head) };
6169 Range.prototype.to = function () { return maxPos(this.anchor, this.head) };
6170 Range.prototype.empty = function () { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch };
6171
6172 // Take an unsorted, potentially overlapping set of ranges, and
6173 // build a selection out of it. 'Consumes' ranges array (modifying
6174 // it).
6175 function normalizeSelection(cm, ranges, primIndex) {
6176 var mayTouch = cm && cm.options.selectionsMayTouch;
6177 var prim = ranges[primIndex];
6178 ranges.sort(function (a, b) { return cmp(a.from(), b.from()); });
6179 primIndex = indexOf(ranges, prim);
6180 for (var i = 1; i < ranges.length; i++) {
6181 var cur = ranges[i], prev = ranges[i - 1];
6182 var diff = cmp(prev.to(), cur.from());
6183 if (mayTouch && !cur.empty() ? diff > 0 : diff >= 0) {
6184 var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to());
6185 var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head;
6186 if (i <= primIndex) { --primIndex; }
6187 ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to));
6188 }
6189 }
6190 return new Selection(ranges, primIndex)
6191 }
6192
6193 function simpleSelection(anchor, head) {
6194 return new Selection([new Range(anchor, head || anchor)], 0)
6195 }
6196
6197 // Compute the position of the end of a change (its 'to' property
6198 // refers to the pre-change end).
6199 function changeEnd(change) {
6200 if (!change.text) { return change.to }
6201 return Pos(change.from.line + change.text.length - 1,
6202 lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0))
6203 }
6204
6205 // Adjust a position to refer to the post-change position of the
6206 // same text, or the end of the change if the change covers it.
6207 function adjustForChange(pos, change) {
6208 if (cmp(pos, change.from) < 0) { return pos }
6209 if (cmp(pos, change.to) <= 0) { return changeEnd(change) }
6210
6211 var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;
6212 if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch; }
6213 return Pos(line, ch)
6214 }
6215
6216 function computeSelAfterChange(doc, change) {
6217 var out = [];
6218 for (var i = 0; i < doc.sel.ranges.length; i++) {
6219 var range = doc.sel.ranges[i];
6220 out.push(new Range(adjustForChange(range.anchor, change),
6221 adjustForChange(range.head, change)));
6222 }
6223 return normalizeSelection(doc.cm, out, doc.sel.primIndex)
6224 }
6225
6226 function offsetPos(pos, old, nw) {
6227 if (pos.line == old.line)
6228 { return Pos(nw.line, pos.ch - old.ch + nw.ch) }
6229 else
6230 { return Pos(nw.line + (pos.line - old.line), pos.ch) }
6231 }
6232
6233 // Used by replaceSelections to allow moving the selection to the
6234 // start or around the replaced test. Hint may be "start" or "around".
6235 function computeReplacedSel(doc, changes, hint) {
6236 var out = [];
6237 var oldPrev = Pos(doc.first, 0), newPrev = oldPrev;
6238 for (var i = 0; i < changes.length; i++) {
6239 var change = changes[i];
6240 var from = offsetPos(change.from, oldPrev, newPrev);
6241 var to = offsetPos(changeEnd(change), oldPrev, newPrev);
6242 oldPrev = change.to;
6243 newPrev = to;
6244 if (hint == "around") {
6245 var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0;
6246 out[i] = new Range(inv ? to : from, inv ? from : to);
6247 } else {
6248 out[i] = new Range(from, from);
6249 }
6250 }
6251 return new Selection(out, doc.sel.primIndex)
6252 }
6253
6254 // Used to get the editor into a consistent state again when options change.
6255
6256 function loadMode(cm) {
6257 cm.doc.mode = getMode(cm.options, cm.doc.modeOption);
6258 resetModeState(cm);
6259 }
6260
6261 function resetModeState(cm) {
6262 cm.doc.iter(function (line) {
6263 if (line.stateAfter) { line.stateAfter = null; }
6264 if (line.styles) { line.styles = null; }
6265 });
6266 cm.doc.modeFrontier = cm.doc.highlightFrontier = cm.doc.first;
6267 startWorker(cm, 100);
6268 cm.state.modeGen++;
6269 if (cm.curOp) { regChange(cm); }
6270 }
6271
6272 // DOCUMENT DATA STRUCTURE
6273
6274 // By default, updates that start and end at the beginning of a line
6275 // are treated specially, in order to make the association of line
6276 // widgets and marker elements with the text behave more intuitive.
6277 function isWholeLineUpdate(doc, change) {
6278 return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" &&
6279 (!doc.cm || doc.cm.options.wholeLineUpdateBefore)
6280 }
6281
6282 // Perform a change on the document data structure.
6283 function updateDoc(doc, change, markedSpans, estimateHeight$$1) {
6284 function spansFor(n) {return markedSpans ? markedSpans[n] : null}
6285 function update(line, text, spans) {
6286 updateLine(line, text, spans, estimateHeight$$1);
6287 signalLater(line, "change", line, change);
6288 }
6289 function linesFor(start, end) {
6290 var result = [];
6291 for (var i = start; i < end; ++i)
6292 { result.push(new Line(text[i], spansFor(i), estimateHeight$$1)); }
6293 return result
6294 }
6295
6296 var from = change.from, to = change.to, text = change.text;
6297 var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
6298 var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;
6299
6300 // Adjust the line structure
6301 if (change.full) {
6302 doc.insert(0, linesFor(0, text.length));
6303 doc.remove(text.length, doc.size - text.length);
6304 } else if (isWholeLineUpdate(doc, change)) {
6305 // This is a whole-line replace. Treated specially to make
6306 // sure line objects move the way they are supposed to.
6307 var added = linesFor(0, text.length - 1);
6308 update(lastLine, lastLine.text, lastSpans);
6309 if (nlines) { doc.remove(from.line, nlines); }
6310 if (added.length) { doc.insert(from.line, added); }
6311 } else if (firstLine == lastLine) {
6312 if (text.length == 1) {
6313 update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
6314 } else {
6315 var added$1 = linesFor(1, text.length - 1);
6316 added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight$$1));
6317 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
6318 doc.insert(from.line + 1, added$1);
6319 }
6320 } else if (text.length == 1) {
6321 update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0));
6322 doc.remove(from.line + 1, nlines);
6323 } else {
6324 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
6325 update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
6326 var added$2 = linesFor(1, text.length - 1);
6327 if (nlines > 1) { doc.remove(from.line + 1, nlines - 1); }
6328 doc.insert(from.line + 1, added$2);
6329 }
6330
6331 signalLater(doc, "change", doc, change);
6332 }
6333
6334 // Call f for all linked documents.
6335 function linkedDocs(doc, f, sharedHistOnly) {
6336 function propagate(doc, skip, sharedHist) {
6337 if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) {
6338 var rel = doc.linked[i];
6339 if (rel.doc == skip) { continue }
6340 var shared = sharedHist && rel.sharedHist;
6341 if (sharedHistOnly && !shared) { continue }
6342 f(rel.doc, shared);
6343 propagate(rel.doc, doc, shared);
6344 } }
6345 }
6346 propagate(doc, null, true);
6347 }
6348
6349 // Attach a document to an editor.
6350 function attachDoc(cm, doc) {
6351 if (doc.cm) { throw new Error("This document is already in use.") }
6352 cm.doc = doc;
6353 doc.cm = cm;
6354 estimateLineHeights(cm);
6355 loadMode(cm);
6356 setDirectionClass(cm);
6357 if (!cm.options.lineWrapping) { findMaxLine(cm); }
6358 cm.options.mode = doc.modeOption;
6359 regChange(cm);
6360 }
6361
6362 function setDirectionClass(cm) {
6363 (cm.doc.direction == "rtl" ? addClass : rmClass)(cm.display.lineDiv, "CodeMirror-rtl");
6364 }
6365
6366 function directionChanged(cm) {
6367 runInOp(cm, function () {
6368 setDirectionClass(cm);
6369 regChange(cm);
6370 });
6371 }
6372
6373 function History(startGen) {
6374 // Arrays of change events and selections. Doing something adds an
6375 // event to done and clears undo. Undoing moves events from done
6376 // to undone, redoing moves them in the other direction.
6377 this.done = []; this.undone = [];
6378 this.undoDepth = Infinity;
6379 // Used to track when changes can be merged into a single undo
6380 // event
6381 this.lastModTime = this.lastSelTime = 0;
6382 this.lastOp = this.lastSelOp = null;
6383 this.lastOrigin = this.lastSelOrigin = null;
6384 // Used by the isClean() method
6385 this.generation = this.maxGeneration = startGen || 1;
6386 }
6387
6388 // Create a history change event from an updateDoc-style change
6389 // object.
6390 function historyChangeFromChange(doc, change) {
6391 var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
6392 attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
6393 linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); }, true);
6394 return histChange
6395 }
6396
6397 // Pop all selection events off the end of a history array. Stop at
6398 // a change event.
6399 function clearSelectionEvents(array) {
6400 while (array.length) {
6401 var last = lst(array);
6402 if (last.ranges) { array.pop(); }
6403 else { break }
6404 }
6405 }
6406
6407 // Find the top change event in the history. Pop off selection
6408 // events that are in the way.
6409 function lastChangeEvent(hist, force) {
6410 if (force) {
6411 clearSelectionEvents(hist.done);
6412 return lst(hist.done)
6413 } else if (hist.done.length && !lst(hist.done).ranges) {
6414 return lst(hist.done)
6415 } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) {
6416 hist.done.pop();
6417 return lst(hist.done)
6418 }
6419 }
6420
6421 // Register a change in the history. Merges changes that are within
6422 // a single operation, or are close together with an origin that
6423 // allows merging (starting with "+") into a single event.
6424 function addChangeToHistory(doc, change, selAfter, opId) {
6425 var hist = doc.history;
6426 hist.undone.length = 0;
6427 var time = +new Date, cur;
6428 var last;
6429
6430 if ((hist.lastOp == opId ||
6431 hist.lastOrigin == change.origin && change.origin &&
6432 ((change.origin.charAt(0) == "+" && hist.lastModTime > time - (doc.cm ? doc.cm.options.historyEventDelay : 500)) ||
6433 change.origin.charAt(0) == "*")) &&
6434 (cur = lastChangeEvent(hist, hist.lastOp == opId))) {
6435 // Merge this change into the last event
6436 last = lst(cur.changes);
6437 if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) {
6438 // Optimized case for simple insertion -- don't want to add
6439 // new changesets for every character typed
6440 last.to = changeEnd(change);
6441 } else {
6442 // Add new sub-event
6443 cur.changes.push(historyChangeFromChange(doc, change));
6444 }
6445 } else {
6446 // Can not be merged, start a new event.
6447 var before = lst(hist.done);
6448 if (!before || !before.ranges)
6449 { pushSelectionToHistory(doc.sel, hist.done); }
6450 cur = {changes: [historyChangeFromChange(doc, change)],
6451 generation: hist.generation};
6452 hist.done.push(cur);
6453 while (hist.done.length > hist.undoDepth) {
6454 hist.done.shift();
6455 if (!hist.done[0].ranges) { hist.done.shift(); }
6456 }
6457 }
6458 hist.done.push(selAfter);
6459 hist.generation = ++hist.maxGeneration;
6460 hist.lastModTime = hist.lastSelTime = time;
6461 hist.lastOp = hist.lastSelOp = opId;
6462 hist.lastOrigin = hist.lastSelOrigin = change.origin;
6463
6464 if (!last) { signal(doc, "historyAdded"); }
6465 }
6466
6467 function selectionEventCanBeMerged(doc, origin, prev, sel) {
6468 var ch = origin.charAt(0);
6469 return ch == "*" ||
6470 ch == "+" &&
6471 prev.ranges.length == sel.ranges.length &&
6472 prev.somethingSelected() == sel.somethingSelected() &&
6473 new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500)
6474 }
6475
6476 // Called whenever the selection changes, sets the new selection as
6477 // the pending selection in the history, and pushes the old pending
6478 // selection into the 'done' array when it was significantly
6479 // different (in number of selected ranges, emptiness, or time).
6480 function addSelectionToHistory(doc, sel, opId, options) {
6481 var hist = doc.history, origin = options && options.origin;
6482
6483 // A new event is started when the previous origin does not match
6484 // the current, or the origins don't allow matching. Origins
6485 // starting with * are always merged, those starting with + are
6486 // merged when similar and close together in time.
6487 if (opId == hist.lastSelOp ||
6488 (origin && hist.lastSelOrigin == origin &&
6489 (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||
6490 selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))
6491 { hist.done[hist.done.length - 1] = sel; }
6492 else
6493 { pushSelectionToHistory(sel, hist.done); }
6494
6495 hist.lastSelTime = +new Date;
6496 hist.lastSelOrigin = origin;
6497 hist.lastSelOp = opId;
6498 if (options && options.clearRedo !== false)
6499 { clearSelectionEvents(hist.undone); }
6500 }
6501
6502 function pushSelectionToHistory(sel, dest) {
6503 var top = lst(dest);
6504 if (!(top && top.ranges && top.equals(sel)))
6505 { dest.push(sel); }
6506 }
6507
6508 // Used to store marked span information in the history.
6509 function attachLocalSpans(doc, change, from, to) {
6510 var existing = change["spans_" + doc.id], n = 0;
6511 doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function (line) {
6512 if (line.markedSpans)
6513 { (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans; }
6514 ++n;
6515 });
6516 }
6517
6518 // When un/re-doing restores text containing marked spans, those
6519 // that have been explicitly cleared should not be restored.
6520 function removeClearedSpans(spans) {
6521 if (!spans) { return null }
6522 var out;
6523 for (var i = 0; i < spans.length; ++i) {
6524 if (spans[i].marker.explicitlyCleared) { if (!out) { out = spans.slice(0, i); } }
6525 else if (out) { out.push(spans[i]); }
6526 }
6527 return !out ? spans : out.length ? out : null
6528 }
6529
6530 // Retrieve and filter the old marked spans stored in a change event.
6531 function getOldSpans(doc, change) {
6532 var found = change["spans_" + doc.id];
6533 if (!found) { return null }
6534 var nw = [];
6535 for (var i = 0; i < change.text.length; ++i)
6536 { nw.push(removeClearedSpans(found[i])); }
6537 return nw
6538 }
6539
6540 // Used for un/re-doing changes from the history. Combines the
6541 // result of computing the existing spans with the set of spans that
6542 // existed in the history (so that deleting around a span and then
6543 // undoing brings back the span).
6544 function mergeOldSpans(doc, change) {
6545 var old = getOldSpans(doc, change);
6546 var stretched = stretchSpansOverChange(doc, change);
6547 if (!old) { return stretched }
6548 if (!stretched) { return old }
6549
6550 for (var i = 0; i < old.length; ++i) {
6551 var oldCur = old[i], stretchCur = stretched[i];
6552 if (oldCur && stretchCur) {
6553 spans: for (var j = 0; j < stretchCur.length; ++j) {
6554 var span = stretchCur[j];
6555 for (var k = 0; k < oldCur.length; ++k)
6556 { if (oldCur[k].marker == span.marker) { continue spans } }
6557 oldCur.push(span);
6558 }
6559 } else if (stretchCur) {
6560 old[i] = stretchCur;
6561 }
6562 }
6563 return old
6564 }
6565
6566 // Used both to provide a JSON-safe object in .getHistory, and, when
6567 // detaching a document, to split the history in two
6568 function copyHistoryArray(events, newGroup, instantiateSel) {
6569 var copy = [];
6570 for (var i = 0; i < events.length; ++i) {
6571 var event = events[i];
6572 if (event.ranges) {
6573 copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event);
6574 continue
6575 }
6576 var changes = event.changes, newChanges = [];
6577 copy.push({changes: newChanges});
6578 for (var j = 0; j < changes.length; ++j) {
6579 var change = changes[j], m = (void 0);
6580 newChanges.push({from: change.from, to: change.to, text: change.text});
6581 if (newGroup) { for (var prop in change) { if (m = prop.match(/^spans_(\d+)$/)) {
6582 if (indexOf(newGroup, Number(m[1])) > -1) {
6583 lst(newChanges)[prop] = change[prop];
6584 delete change[prop];
6585 }
6586 } } }
6587 }
6588 }
6589 return copy
6590 }
6591
6592 // The 'scroll' parameter given to many of these indicated whether
6593 // the new cursor position should be scrolled into view after
6594 // modifying the selection.
6595
6596 // If shift is held or the extend flag is set, extends a range to
6597 // include a given position (and optionally a second position).
6598 // Otherwise, simply returns the range between the given positions.
6599 // Used for cursor motion and such.
6600 function extendRange(range, head, other, extend) {
6601 if (extend) {
6602 var anchor = range.anchor;
6603 if (other) {
6604 var posBefore = cmp(head, anchor) < 0;
6605 if (posBefore != (cmp(other, anchor) < 0)) {
6606 anchor = head;
6607 head = other;
6608 } else if (posBefore != (cmp(head, other) < 0)) {
6609 head = other;
6610 }
6611 }
6612 return new Range(anchor, head)
6613 } else {
6614 return new Range(other || head, head)
6615 }
6616 }
6617
6618 // Extend the primary selection range, discard the rest.
6619 function extendSelection(doc, head, other, options, extend) {
6620 if (extend == null) { extend = doc.cm && (doc.cm.display.shift || doc.extend); }
6621 setSelection(doc, new Selection([extendRange(doc.sel.primary(), head, other, extend)], 0), options);
6622 }
6623
6624 // Extend all selections (pos is an array of selections with length
6625 // equal the number of selections)
6626 function extendSelections(doc, heads, options) {
6627 var out = [];
6628 var extend = doc.cm && (doc.cm.display.shift || doc.extend);
6629 for (var i = 0; i < doc.sel.ranges.length; i++)
6630 { out[i] = extendRange(doc.sel.ranges[i], heads[i], null, extend); }
6631 var newSel = normalizeSelection(doc.cm, out, doc.sel.primIndex);
6632 setSelection(doc, newSel, options);
6633 }
6634
6635 // Updates a single range in the selection.
6636 function replaceOneSelection(doc, i, range, options) {
6637 var ranges = doc.sel.ranges.slice(0);
6638 ranges[i] = range;
6639 setSelection(doc, normalizeSelection(doc.cm, ranges, doc.sel.primIndex), options);
6640 }
6641
6642 // Reset the selection to a single range.
6643 function setSimpleSelection(doc, anchor, head, options) {
6644 setSelection(doc, simpleSelection(anchor, head), options);
6645 }
6646
6647 // Give beforeSelectionChange handlers a change to influence a
6648 // selection update.
6649 function filterSelectionChange(doc, sel, options) {
6650 var obj = {
6651 ranges: sel.ranges,
6652 update: function(ranges) {
6653 var this$1 = this;
6654
6655 this.ranges = [];
6656 for (var i = 0; i < ranges.length; i++)
6657 { this$1.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),
6658 clipPos(doc, ranges[i].head)); }
6659 },
6660 origin: options && options.origin
6661 };
6662 signal(doc, "beforeSelectionChange", doc, obj);
6663 if (doc.cm) { signal(doc.cm, "beforeSelectionChange", doc.cm, obj); }
6664 if (obj.ranges != sel.ranges) { return normalizeSelection(doc.cm, obj.ranges, obj.ranges.length - 1) }
6665 else { return sel }
6666 }
6667
6668 function setSelectionReplaceHistory(doc, sel, options) {
6669 var done = doc.history.done, last = lst(done);
6670 if (last && last.ranges) {
6671 done[done.length - 1] = sel;
6672 setSelectionNoUndo(doc, sel, options);
6673 } else {
6674 setSelection(doc, sel, options);
6675 }
6676 }
6677
6678 // Set a new selection.
6679 function setSelection(doc, sel, options) {
6680 setSelectionNoUndo(doc, sel, options);
6681 addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options);
6682 }
6683
6684 function setSelectionNoUndo(doc, sel, options) {
6685 if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange"))
6686 { sel = filterSelectionChange(doc, sel, options); }
6687
6688 var bias = options && options.bias ||
6689 (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1);
6690 setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true));
6691
6692 if (!(options && options.scroll === false) && doc.cm)
6693 { ensureCursorVisible(doc.cm); }
6694 }
6695
6696 function setSelectionInner(doc, sel) {
6697 if (sel.equals(doc.sel)) { return }
6698
6699 doc.sel = sel;
6700
6701 if (doc.cm) {
6702 doc.cm.curOp.updateInput = 1;
6703 doc.cm.curOp.selectionChanged = true;
6704 signalCursorActivity(doc.cm);
6705 }
6706 signalLater(doc, "cursorActivity", doc);
6707 }
6708
6709 // Verify that the selection does not partially select any atomic
6710 // marked ranges.
6711 function reCheckSelection(doc) {
6712 setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false));
6713 }
6714
6715 // Return a selection that does not partially select any atomic
6716 // ranges.
6717 function skipAtomicInSelection(doc, sel, bias, mayClear) {
6718 var out;
6719 for (var i = 0; i < sel.ranges.length; i++) {
6720 var range = sel.ranges[i];
6721 var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i];
6722 var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear);
6723 var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear);
6724 if (out || newAnchor != range.anchor || newHead != range.head) {
6725 if (!out) { out = sel.ranges.slice(0, i); }
6726 out[i] = new Range(newAnchor, newHead);
6727 }
6728 }
6729 return out ? normalizeSelection(doc.cm, out, sel.primIndex) : sel
6730 }
6731
6732 function skipAtomicInner(doc, pos, oldPos, dir, mayClear) {
6733 var line = getLine(doc, pos.line);
6734 if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
6735 var sp = line.markedSpans[i], m = sp.marker;
6736 if ((sp.from == null || (m.inclusiveLeft ? sp.from <= pos.ch : sp.from < pos.ch)) &&
6737 (sp.to == null || (m.inclusiveRight ? sp.to >= pos.ch : sp.to > pos.ch))) {
6738 if (mayClear) {
6739 signal(m, "beforeCursorEnter");
6740 if (m.explicitlyCleared) {
6741 if (!line.markedSpans) { break }
6742 else {--i; continue}
6743 }
6744 }
6745 if (!m.atomic) { continue }
6746
6747 if (oldPos) {
6748 var near = m.find(dir < 0 ? 1 : -1), diff = (void 0);
6749 if (dir < 0 ? m.inclusiveRight : m.inclusiveLeft)
6750 { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null); }
6751 if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0))
6752 { return skipAtomicInner(doc, near, pos, dir, mayClear) }
6753 }
6754
6755 var far = m.find(dir < 0 ? -1 : 1);
6756 if (dir < 0 ? m.inclusiveLeft : m.inclusiveRight)
6757 { far = movePos(doc, far, dir, far.line == pos.line ? line : null); }
6758 return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null
6759 }
6760 } }
6761 return pos
6762 }
6763
6764 // Ensure a given position is not inside an atomic range.
6765 function skipAtomic(doc, pos, oldPos, bias, mayClear) {
6766 var dir = bias || 1;
6767 var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) ||
6768 (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) ||
6769 skipAtomicInner(doc, pos, oldPos, -dir, mayClear) ||
6770 (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true));
6771 if (!found) {
6772 doc.cantEdit = true;
6773 return Pos(doc.first, 0)
6774 }
6775 return found
6776 }
6777
6778 function movePos(doc, pos, dir, line) {
6779 if (dir < 0 && pos.ch == 0) {
6780 if (pos.line > doc.first) { return clipPos(doc, Pos(pos.line - 1)) }
6781 else { return null }
6782 } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) {
6783 if (pos.line < doc.first + doc.size - 1) { return Pos(pos.line + 1, 0) }
6784 else { return null }
6785 } else {
6786 return new Pos(pos.line, pos.ch + dir)
6787 }
6788 }
6789
6790 function selectAll(cm) {
6791 cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll);
6792 }
6793
6794 // UPDATING
6795
6796 // Allow "beforeChange" event handlers to influence a change
6797 function filterChange(doc, change, update) {
6798 var obj = {
6799 canceled: false,
6800 from: change.from,
6801 to: change.to,
6802 text: change.text,
6803 origin: change.origin,
6804 cancel: function () { return obj.canceled = true; }
6805 };
6806 if (update) { obj.update = function (from, to, text, origin) {
6807 if (from) { obj.from = clipPos(doc, from); }
6808 if (to) { obj.to = clipPos(doc, to); }
6809 if (text) { obj.text = text; }
6810 if (origin !== undefined) { obj.origin = origin; }
6811 }; }
6812 signal(doc, "beforeChange", doc, obj);
6813 if (doc.cm) { signal(doc.cm, "beforeChange", doc.cm, obj); }
6814
6815 if (obj.canceled) {
6816 if (doc.cm) { doc.cm.curOp.updateInput = 2; }
6817 return null
6818 }
6819 return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin}
6820 }
6821
6822 // Apply a change to a document, and add it to the document's
6823 // history, and propagating it to all linked documents.
6824 function makeChange(doc, change, ignoreReadOnly) {
6825 if (doc.cm) {
6826 if (!doc.cm.curOp) { return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) }
6827 if (doc.cm.state.suppressEdits) { return }
6828 }
6829
6830 if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
6831 change = filterChange(doc, change, true);
6832 if (!change) { return }
6833 }
6834
6835 // Possibly split or suppress the update based on the presence
6836 // of read-only spans in its range.
6837 var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);
6838 if (split) {
6839 for (var i = split.length - 1; i >= 0; --i)
6840 { makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text, origin: change.origin}); }
6841 } else {
6842 makeChangeInner(doc, change);
6843 }
6844 }
6845
6846 function makeChangeInner(doc, change) {
6847 if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) { return }
6848 var selAfter = computeSelAfterChange(doc, change);
6849 addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);
6850
6851 makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));
6852 var rebased = [];
6853
6854 linkedDocs(doc, function (doc, sharedHist) {
6855 if (!sharedHist && indexOf(rebased, doc.history) == -1) {
6856 rebaseHist(doc.history, change);
6857 rebased.push(doc.history);
6858 }
6859 makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change));
6860 });
6861 }
6862
6863 // Revert a change stored in a document's history.
6864 function makeChangeFromHistory(doc, type, allowSelectionOnly) {
6865 var suppress = doc.cm && doc.cm.state.suppressEdits;
6866 if (suppress && !allowSelectionOnly) { return }
6867
6868 var hist = doc.history, event, selAfter = doc.sel;
6869 var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done;
6870
6871 // Verify that there is a useable event (so that ctrl-z won't
6872 // needlessly clear selection events)
6873 var i = 0;
6874 for (; i < source.length; i++) {
6875 event = source[i];
6876 if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges)
6877 { break }
6878 }
6879 if (i == source.length) { return }
6880 hist.lastOrigin = hist.lastSelOrigin = null;
6881
6882 for (;;) {
6883 event = source.pop();
6884 if (event.ranges) {
6885 pushSelectionToHistory(event, dest);
6886 if (allowSelectionOnly && !event.equals(doc.sel)) {
6887 setSelection(doc, event, {clearRedo: false});
6888 return
6889 }
6890 selAfter = event;
6891 } else if (suppress) {
6892 source.push(event);
6893 return
6894 } else { break }
6895 }
6896
6897 // Build up a reverse change object to add to the opposite history
6898 // stack (redo when undoing, and vice versa).
6899 var antiChanges = [];
6900 pushSelectionToHistory(selAfter, dest);
6901 dest.push({changes: antiChanges, generation: hist.generation});
6902 hist.generation = event.generation || ++hist.maxGeneration;
6903
6904 var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange");
6905
6906 var loop = function ( i ) {
6907 var change = event.changes[i];
6908 change.origin = type;
6909 if (filter && !filterChange(doc, change, false)) {
6910 source.length = 0;
6911 return {}
6912 }
6913
6914 antiChanges.push(historyChangeFromChange(doc, change));
6915
6916 var after = i ? computeSelAfterChange(doc, change) : lst(source);
6917 makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
6918 if (!i && doc.cm) { doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}); }
6919 var rebased = [];
6920
6921 // Propagate to the linked documents
6922 linkedDocs(doc, function (doc, sharedHist) {
6923 if (!sharedHist && indexOf(rebased, doc.history) == -1) {
6924 rebaseHist(doc.history, change);
6925 rebased.push(doc.history);
6926 }
6927 makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change));
6928 });
6929 };
6930
6931 for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) {
6932 var returned = loop( i$1 );
6933
6934 if ( returned ) return returned.v;
6935 }
6936 }
6937
6938 // Sub-views need their line numbers shifted when text is added
6939 // above or below them in the parent document.
6940 function shiftDoc(doc, distance) {
6941 if (distance == 0) { return }
6942 doc.first += distance;
6943 doc.sel = new Selection(map(doc.sel.ranges, function (range) { return new Range(
6944 Pos(range.anchor.line + distance, range.anchor.ch),
6945 Pos(range.head.line + distance, range.head.ch)
6946 ); }), doc.sel.primIndex);
6947 if (doc.cm) {
6948 regChange(doc.cm, doc.first, doc.first - distance, distance);
6949 for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++)
6950 { regLineChange(doc.cm, l, "gutter"); }
6951 }
6952 }
6953
6954 // More lower-level change function, handling only a single document
6955 // (not linked ones).
6956 function makeChangeSingleDoc(doc, change, selAfter, spans) {
6957 if (doc.cm && !doc.cm.curOp)
6958 { return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) }
6959
6960 if (change.to.line < doc.first) {
6961 shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line));
6962 return
6963 }
6964 if (change.from.line > doc.lastLine()) { return }
6965
6966 // Clip the change to the size of this doc
6967 if (change.from.line < doc.first) {
6968 var shift = change.text.length - 1 - (doc.first - change.from.line);
6969 shiftDoc(doc, shift);
6970 change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
6971 text: [lst(change.text)], origin: change.origin};
6972 }
6973 var last = doc.lastLine();
6974 if (change.to.line > last) {
6975 change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
6976 text: [change.text[0]], origin: change.origin};
6977 }
6978
6979 change.removed = getBetween(doc, change.from, change.to);
6980
6981 if (!selAfter) { selAfter = computeSelAfterChange(doc, change); }
6982 if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans); }
6983 else { updateDoc(doc, change, spans); }
6984 setSelectionNoUndo(doc, selAfter, sel_dontScroll);
6985 }
6986
6987 // Handle the interaction of a change to a document with the editor
6988 // that this document is part of.
6989 function makeChangeSingleDocInEditor(cm, change, spans) {
6990 var doc = cm.doc, display = cm.display, from = change.from, to = change.to;
6991
6992 var recomputeMaxLength = false, checkWidthStart = from.line;
6993 if (!cm.options.lineWrapping) {
6994 checkWidthStart = lineNo(visualLine(getLine(doc, from.line)));
6995 doc.iter(checkWidthStart, to.line + 1, function (line) {
6996 if (line == display.maxLine) {
6997 recomputeMaxLength = true;
6998 return true
6999 }
7000 });
7001 }
7002
7003 if (doc.sel.contains(change.from, change.to) > -1)
7004 { signalCursorActivity(cm); }
7005
7006 updateDoc(doc, change, spans, estimateHeight(cm));
7007
7008 if (!cm.options.lineWrapping) {
7009 doc.iter(checkWidthStart, from.line + change.text.length, function (line) {
7010 var len = lineLength(line);
7011 if (len > display.maxLineLength) {
7012 display.maxLine = line;
7013 display.maxLineLength = len;
7014 display.maxLineChanged = true;
7015 recomputeMaxLength = false;
7016 }
7017 });
7018 if (recomputeMaxLength) { cm.curOp.updateMaxLine = true; }
7019 }
7020
7021 retreatFrontier(doc, from.line);
7022 startWorker(cm, 400);
7023
7024 var lendiff = change.text.length - (to.line - from.line) - 1;
7025 // Remember that these lines changed, for updating the display
7026 if (change.full)
7027 { regChange(cm); }
7028 else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change))
7029 { regLineChange(cm, from.line, "text"); }
7030 else
7031 { regChange(cm, from.line, to.line + 1, lendiff); }
7032
7033 var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change");
7034 if (changeHandler || changesHandler) {
7035 var obj = {
7036 from: from, to: to,
7037 text: change.text,
7038 removed: change.removed,
7039 origin: change.origin
7040 };
7041 if (changeHandler) { signalLater(cm, "change", cm, obj); }
7042 if (changesHandler) { (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj); }
7043 }
7044 cm.display.selForContextMenu = null;
7045 }
7046
7047 function replaceRange(doc, code, from, to, origin) {
7048 var assign;
7049
7050 if (!to) { to = from; }
7051 if (cmp(to, from) < 0) { (assign = [to, from], from = assign[0], to = assign[1]); }
7052 if (typeof code == "string") { code = doc.splitLines(code); }
7053 makeChange(doc, {from: from, to: to, text: code, origin: origin});
7054 }
7055
7056 // Rebasing/resetting history to deal with externally-sourced changes
7057
7058 function rebaseHistSelSingle(pos, from, to, diff) {
7059 if (to < pos.line) {
7060 pos.line += diff;
7061 } else if (from < pos.line) {
7062 pos.line = from;
7063 pos.ch = 0;
7064 }
7065 }
7066
7067 // Tries to rebase an array of history events given a change in the
7068 // document. If the change touches the same lines as the event, the
7069 // event, and everything 'behind' it, is discarded. If the change is
7070 // before the event, the event's positions are updated. Uses a
7071 // copy-on-write scheme for the positions, to avoid having to
7072 // reallocate them all on every rebase, but also avoid problems with
7073 // shared position objects being unsafely updated.
7074 function rebaseHistArray(array, from, to, diff) {
7075 for (var i = 0; i < array.length; ++i) {
7076 var sub = array[i], ok = true;
7077 if (sub.ranges) {
7078 if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; }
7079 for (var j = 0; j < sub.ranges.length; j++) {
7080 rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff);
7081 rebaseHistSelSingle(sub.ranges[j].head, from, to, diff);
7082 }
7083 continue
7084 }
7085 for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) {
7086 var cur = sub.changes[j$1];
7087 if (to < cur.from.line) {
7088 cur.from = Pos(cur.from.line + diff, cur.from.ch);
7089 cur.to = Pos(cur.to.line + diff, cur.to.ch);
7090 } else if (from <= cur.to.line) {
7091 ok = false;
7092 break
7093 }
7094 }
7095 if (!ok) {
7096 array.splice(0, i + 1);
7097 i = 0;
7098 }
7099 }
7100 }
7101
7102 function rebaseHist(hist, change) {
7103 var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1;
7104 rebaseHistArray(hist.done, from, to, diff);
7105 rebaseHistArray(hist.undone, from, to, diff);
7106 }
7107
7108 // Utility for applying a change to a line by handle or number,
7109 // returning the number and optionally registering the line as
7110 // changed.
7111 function changeLine(doc, handle, changeType, op) {
7112 var no = handle, line = handle;
7113 if (typeof handle == "number") { line = getLine(doc, clipLine(doc, handle)); }
7114 else { no = lineNo(handle); }
7115 if (no == null) { return null }
7116 if (op(line, no) && doc.cm) { regLineChange(doc.cm, no, changeType); }
7117 return line
7118 }
7119
7120 // The document is represented as a BTree consisting of leaves, with
7121 // chunk of lines in them, and branches, with up to ten leaves or
7122 // other branch nodes below them. The top node is always a branch
7123 // node, and is the document object itself (meaning it has
7124 // additional methods and properties).
7125 //
7126 // All nodes have parent links. The tree is used both to go from
7127 // line numbers to line objects, and to go from objects to numbers.
7128 // It also indexes by height, and is used to convert between height
7129 // and line object, and to find the total height of the document.
7130 //
7131 // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html
7132
7133 function LeafChunk(lines) {
7134 var this$1 = this;
7135
7136 this.lines = lines;
7137 this.parent = null;
7138 var height = 0;
7139 for (var i = 0; i < lines.length; ++i) {
7140 lines[i].parent = this$1;
7141 height += lines[i].height;
7142 }
7143 this.height = height;
7144 }
7145
7146 LeafChunk.prototype = {
7147 chunkSize: function() { return this.lines.length },
7148
7149 // Remove the n lines at offset 'at'.
7150 removeInner: function(at, n) {
7151 var this$1 = this;
7152
7153 for (var i = at, e = at + n; i < e; ++i) {
7154 var line = this$1.lines[i];
7155 this$1.height -= line.height;
7156 cleanUpLine(line);
7157 signalLater(line, "delete");
7158 }
7159 this.lines.splice(at, n);
7160 },
7161
7162 // Helper used to collapse a small branch into a single leaf.
7163 collapse: function(lines) {
7164 lines.push.apply(lines, this.lines);
7165 },
7166
7167 // Insert the given array of lines at offset 'at', count them as
7168 // having the given height.
7169 insertInner: function(at, lines, height) {
7170 var this$1 = this;
7171
7172 this.height += height;
7173 this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
7174 for (var i = 0; i < lines.length; ++i) { lines[i].parent = this$1; }
7175 },
7176
7177 // Used to iterate over a part of the tree.
7178 iterN: function(at, n, op) {
7179 var this$1 = this;
7180
7181 for (var e = at + n; at < e; ++at)
7182 { if (op(this$1.lines[at])) { return true } }
7183 }
7184 };
7185
7186 function BranchChunk(children) {
7187 var this$1 = this;
7188
7189 this.children = children;
7190 var size = 0, height = 0;
7191 for (var i = 0; i < children.length; ++i) {
7192 var ch = children[i];
7193 size += ch.chunkSize(); height += ch.height;
7194 ch.parent = this$1;
7195 }
7196 this.size = size;
7197 this.height = height;
7198 this.parent = null;
7199 }
7200
7201 BranchChunk.prototype = {
7202 chunkSize: function() { return this.size },
7203
7204 removeInner: function(at, n) {
7205 var this$1 = this;
7206
7207 this.size -= n;
7208 for (var i = 0; i < this.children.length; ++i) {
7209 var child = this$1.children[i], sz = child.chunkSize();
7210 if (at < sz) {
7211 var rm = Math.min(n, sz - at), oldHeight = child.height;
7212 child.removeInner(at, rm);
7213 this$1.height -= oldHeight - child.height;
7214 if (sz == rm) { this$1.children.splice(i--, 1); child.parent = null; }
7215 if ((n -= rm) == 0) { break }
7216 at = 0;
7217 } else { at -= sz; }
7218 }
7219 // If the result is smaller than 25 lines, ensure that it is a
7220 // single leaf node.
7221 if (this.size - n < 25 &&
7222 (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) {
7223 var lines = [];
7224 this.collapse(lines);
7225 this.children = [new LeafChunk(lines)];
7226 this.children[0].parent = this;
7227 }
7228 },
7229
7230 collapse: function(lines) {
7231 var this$1 = this;
7232
7233 for (var i = 0; i < this.children.length; ++i) { this$1.children[i].collapse(lines); }
7234 },
7235
7236 insertInner: function(at, lines, height) {
7237 var this$1 = this;
7238
7239 this.size += lines.length;
7240 this.height += height;
7241 for (var i = 0; i < this.children.length; ++i) {
7242 var child = this$1.children[i], sz = child.chunkSize();
7243 if (at <= sz) {
7244 child.insertInner(at, lines, height);
7245 if (child.lines && child.lines.length > 50) {
7246 // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced.
7247 // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest.
7248 var remaining = child.lines.length % 25 + 25;
7249 for (var pos = remaining; pos < child.lines.length;) {
7250 var leaf = new LeafChunk(child.lines.slice(pos, pos += 25));
7251 child.height -= leaf.height;
7252 this$1.children.splice(++i, 0, leaf);
7253 leaf.parent = this$1;
7254 }
7255 child.lines = child.lines.slice(0, remaining);
7256 this$1.maybeSpill();
7257 }
7258 break
7259 }
7260 at -= sz;
7261 }
7262 },
7263
7264 // When a node has grown, check whether it should be split.
7265 maybeSpill: function() {
7266 if (this.children.length <= 10) { return }
7267 var me = this;
7268 do {
7269 var spilled = me.children.splice(me.children.length - 5, 5);
7270 var sibling = new BranchChunk(spilled);
7271 if (!me.parent) { // Become the parent node
7272 var copy = new BranchChunk(me.children);
7273 copy.parent = me;
7274 me.children = [copy, sibling];
7275 me = copy;
7276 } else {
7277 me.size -= sibling.size;
7278 me.height -= sibling.height;
7279 var myIndex = indexOf(me.parent.children, me);
7280 me.parent.children.splice(myIndex + 1, 0, sibling);
7281 }
7282 sibling.parent = me.parent;
7283 } while (me.children.length > 10)
7284 me.parent.maybeSpill();
7285 },
7286
7287 iterN: function(at, n, op) {
7288 var this$1 = this;
7289
7290 for (var i = 0; i < this.children.length; ++i) {
7291 var child = this$1.children[i], sz = child.chunkSize();
7292 if (at < sz) {
7293 var used = Math.min(n, sz - at);
7294 if (child.iterN(at, used, op)) { return true }
7295 if ((n -= used) == 0) { break }
7296 at = 0;
7297 } else { at -= sz; }
7298 }
7299 }
7300 };
7301
7302 // Line widgets are block elements displayed above or below a line.
7303
7304 var LineWidget = function(doc, node, options) {
7305 var this$1 = this;
7306
7307 if (options) { for (var opt in options) { if (options.hasOwnProperty(opt))
7308 { this$1[opt] = options[opt]; } } }
7309 this.doc = doc;
7310 this.node = node;
7311 };
7312
7313 LineWidget.prototype.clear = function () {
7314 var this$1 = this;
7315
7316 var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line);
7317 if (no == null || !ws) { return }
7318 for (var i = 0; i < ws.length; ++i) { if (ws[i] == this$1) { ws.splice(i--, 1); } }
7319 if (!ws.length) { line.widgets = null; }
7320 var height = widgetHeight(this);
7321 updateLineHeight(line, Math.max(0, line.height - height));
7322 if (cm) {
7323 runInOp(cm, function () {
7324 adjustScrollWhenAboveVisible(cm, line, -height);
7325 regLineChange(cm, no, "widget");
7326 });
7327 signalLater(cm, "lineWidgetCleared", cm, this, no);
7328 }
7329 };
7330
7331 LineWidget.prototype.changed = function () {
7332 var this$1 = this;
7333
7334 var oldH = this.height, cm = this.doc.cm, line = this.line;
7335 this.height = null;
7336 var diff = widgetHeight(this) - oldH;
7337 if (!diff) { return }
7338 if (!lineIsHidden(this.doc, line)) { updateLineHeight(line, line.height + diff); }
7339 if (cm) {
7340 runInOp(cm, function () {
7341 cm.curOp.forceUpdate = true;
7342 adjustScrollWhenAboveVisible(cm, line, diff);
7343 signalLater(cm, "lineWidgetChanged", cm, this$1, lineNo(line));
7344 });
7345 }
7346 };
7347 eventMixin(LineWidget);
7348
7349 function adjustScrollWhenAboveVisible(cm, line, diff) {
7350 if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))
7351 { addToScrollTop(cm, diff); }
7352 }
7353
7354 function addLineWidget(doc, handle, node, options) {
7355 var widget = new LineWidget(doc, node, options);
7356 var cm = doc.cm;
7357 if (cm && widget.noHScroll) { cm.display.alignWidgets = true; }
7358 changeLine(doc, handle, "widget", function (line) {
7359 var widgets = line.widgets || (line.widgets = []);
7360 if (widget.insertAt == null) { widgets.push(widget); }
7361 else { widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget); }
7362 widget.line = line;
7363 if (cm && !lineIsHidden(doc, line)) {
7364 var aboveVisible = heightAtLine(line) < doc.scrollTop;
7365 updateLineHeight(line, line.height + widgetHeight(widget));
7366 if (aboveVisible) { addToScrollTop(cm, widget.height); }
7367 cm.curOp.forceUpdate = true;
7368 }
7369 return true
7370 });
7371 if (cm) { signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle)); }
7372 return widget
7373 }
7374
7375 // TEXTMARKERS
7376
7377 // Created with markText and setBookmark methods. A TextMarker is a
7378 // handle that can be used to clear or find a marked position in the
7379 // document. Line objects hold arrays (markedSpans) containing
7380 // {from, to, marker} object pointing to such marker objects, and
7381 // indicating that such a marker is present on that line. Multiple
7382 // lines may point to the same marker when it spans across lines.
7383 // The spans will have null for their from/to properties when the
7384 // marker continues beyond the start/end of the line. Markers have
7385 // links back to the lines they currently touch.
7386
7387 // Collapsed markers have unique ids, in order to be able to order
7388 // them, which is needed for uniquely determining an outer marker
7389 // when they overlap (they may nest, but not partially overlap).
7390 var nextMarkerId = 0;
7391
7392 var TextMarker = function(doc, type) {
7393 this.lines = [];
7394 this.type = type;
7395 this.doc = doc;
7396 this.id = ++nextMarkerId;
7397 };
7398
7399 // Clear the marker.
7400 TextMarker.prototype.clear = function () {
7401 var this$1 = this;
7402
7403 if (this.explicitlyCleared) { return }
7404 var cm = this.doc.cm, withOp = cm && !cm.curOp;
7405 if (withOp) { startOperation(cm); }
7406 if (hasHandler(this, "clear")) {
7407 var found = this.find();
7408 if (found) { signalLater(this, "clear", found.from, found.to); }
7409 }
7410 var min = null, max = null;
7411 for (var i = 0; i < this.lines.length; ++i) {
7412 var line = this$1.lines[i];
7413 var span = getMarkedSpanFor(line.markedSpans, this$1);
7414 if (cm && !this$1.collapsed) { regLineChange(cm, lineNo(line), "text"); }
7415 else if (cm) {
7416 if (span.to != null) { max = lineNo(line); }
7417 if (span.from != null) { min = lineNo(line); }
7418 }
7419 line.markedSpans = removeMarkedSpan(line.markedSpans, span);
7420 if (span.from == null && this$1.collapsed && !lineIsHidden(this$1.doc, line) && cm)
7421 { updateLineHeight(line, textHeight(cm.display)); }
7422 }
7423 if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) {
7424 var visual = visualLine(this$1.lines[i$1]), len = lineLength(visual);
7425 if (len > cm.display.maxLineLength) {
7426 cm.display.maxLine = visual;
7427 cm.display.maxLineLength = len;
7428 cm.display.maxLineChanged = true;
7429 }
7430 } }
7431
7432 if (min != null && cm && this.collapsed) { regChange(cm, min, max + 1); }
7433 this.lines.length = 0;
7434 this.explicitlyCleared = true;
7435 if (this.atomic && this.doc.cantEdit) {
7436 this.doc.cantEdit = false;
7437 if (cm) { reCheckSelection(cm.doc); }
7438 }
7439 if (cm) { signalLater(cm, "markerCleared", cm, this, min, max); }
7440 if (withOp) { endOperation(cm); }
7441 if (this.parent) { this.parent.clear(); }
7442 };
7443
7444 // Find the position of the marker in the document. Returns a {from,
7445 // to} object by default. Side can be passed to get a specific side
7446 // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the
7447 // Pos objects returned contain a line object, rather than a line
7448 // number (used to prevent looking up the same line twice).
7449 TextMarker.prototype.find = function (side, lineObj) {
7450 var this$1 = this;
7451
7452 if (side == null && this.type == "bookmark") { side = 1; }
7453 var from, to;
7454 for (var i = 0; i < this.lines.length; ++i) {
7455 var line = this$1.lines[i];
7456 var span = getMarkedSpanFor(line.markedSpans, this$1);
7457 if (span.from != null) {
7458 from = Pos(lineObj ? line : lineNo(line), span.from);
7459 if (side == -1) { return from }
7460 }
7461 if (span.to != null) {
7462 to = Pos(lineObj ? line : lineNo(line), span.to);
7463 if (side == 1) { return to }
7464 }
7465 }
7466 return from && {from: from, to: to}
7467 };
7468
7469 // Signals that the marker's widget changed, and surrounding layout
7470 // should be recomputed.
7471 TextMarker.prototype.changed = function () {
7472 var this$1 = this;
7473
7474 var pos = this.find(-1, true), widget = this, cm = this.doc.cm;
7475 if (!pos || !cm) { return }
7476 runInOp(cm, function () {
7477 var line = pos.line, lineN = lineNo(pos.line);
7478 var view = findViewForLine(cm, lineN);
7479 if (view) {
7480 clearLineMeasurementCacheFor(view);
7481 cm.curOp.selectionChanged = cm.curOp.forceUpdate = true;
7482 }
7483 cm.curOp.updateMaxLine = true;
7484 if (!lineIsHidden(widget.doc, line) && widget.height != null) {
7485 var oldHeight = widget.height;
7486 widget.height = null;
7487 var dHeight = widgetHeight(widget) - oldHeight;
7488 if (dHeight)
7489 { updateLineHeight(line, line.height + dHeight); }
7490 }
7491 signalLater(cm, "markerChanged", cm, this$1);
7492 });
7493 };
7494
7495 TextMarker.prototype.attachLine = function (line) {
7496 if (!this.lines.length && this.doc.cm) {
7497 var op = this.doc.cm.curOp;
7498 if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
7499 { (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this); }
7500 }
7501 this.lines.push(line);
7502 };
7503
7504 TextMarker.prototype.detachLine = function (line) {
7505 this.lines.splice(indexOf(this.lines, line), 1);
7506 if (!this.lines.length && this.doc.cm) {
7507 var op = this.doc.cm.curOp
7508 ;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this);
7509 }
7510 };
7511 eventMixin(TextMarker);
7512
7513 // Create a marker, wire it up to the right lines, and
7514 function markText(doc, from, to, options, type) {
7515 // Shared markers (across linked documents) are handled separately
7516 // (markTextShared will call out to this again, once per
7517 // document).
7518 if (options && options.shared) { return markTextShared(doc, from, to, options, type) }
7519 // Ensure we are in an operation.
7520 if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, markText)(doc, from, to, options, type) }
7521
7522 var marker = new TextMarker(doc, type), diff = cmp(from, to);
7523 if (options) { copyObj(options, marker, false); }
7524 // Don't connect empty markers unless clearWhenEmpty is false
7525 if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false)
7526 { return marker }
7527 if (marker.replacedWith) {
7528 // Showing up as a widget implies collapsed (widget replaces text)
7529 marker.collapsed = true;
7530 marker.widgetNode = eltP("span", [marker.replacedWith], "CodeMirror-widget");
7531 if (!options.handleMouseEvents) { marker.widgetNode.setAttribute("cm-ignore-events", "true"); }
7532 if (options.insertLeft) { marker.widgetNode.insertLeft = true; }
7533 }
7534 if (marker.collapsed) {
7535 if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||
7536 from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker))
7537 { throw new Error("Inserting collapsed marker partially overlapping an existing one") }
7538 seeCollapsedSpans();
7539 }
7540
7541 if (marker.addToHistory)
7542 { addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN); }
7543
7544 var curLine = from.line, cm = doc.cm, updateMaxLine;
7545 doc.iter(curLine, to.line + 1, function (line) {
7546 if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine)
7547 { updateMaxLine = true; }
7548 if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0); }
7549 addMarkedSpan(line, new MarkedSpan(marker,
7550 curLine == from.line ? from.ch : null,
7551 curLine == to.line ? to.ch : null));
7552 ++curLine;
7553 });
7554 // lineIsHidden depends on the presence of the spans, so needs a second pass
7555 if (marker.collapsed) { doc.iter(from.line, to.line + 1, function (line) {
7556 if (lineIsHidden(doc, line)) { updateLineHeight(line, 0); }
7557 }); }
7558
7559 if (marker.clearOnEnter) { on(marker, "beforeCursorEnter", function () { return marker.clear(); }); }
7560
7561 if (marker.readOnly) {
7562 seeReadOnlySpans();
7563 if (doc.history.done.length || doc.history.undone.length)
7564 { doc.clearHistory(); }
7565 }
7566 if (marker.collapsed) {
7567 marker.id = ++nextMarkerId;
7568 marker.atomic = true;
7569 }
7570 if (cm) {
7571 // Sync editor state
7572 if (updateMaxLine) { cm.curOp.updateMaxLine = true; }
7573 if (marker.collapsed)
7574 { regChange(cm, from.line, to.line + 1); }
7575 else if (marker.className || marker.startStyle || marker.endStyle || marker.css ||
7576 marker.attributes || marker.title)
7577 { for (var i = from.line; i <= to.line; i++) { regLineChange(cm, i, "text"); } }
7578 if (marker.atomic) { reCheckSelection(cm.doc); }
7579 signalLater(cm, "markerAdded", cm, marker);
7580 }
7581 return marker
7582 }
7583
7584 // SHARED TEXTMARKERS
7585
7586 // A shared marker spans multiple linked documents. It is
7587 // implemented as a meta-marker-object controlling multiple normal
7588 // markers.
7589 var SharedTextMarker = function(markers, primary) {
7590 var this$1 = this;
7591
7592 this.markers = markers;
7593 this.primary = primary;
7594 for (var i = 0; i < markers.length; ++i)
7595 { markers[i].parent = this$1; }
7596 };
7597
7598 SharedTextMarker.prototype.clear = function () {
7599 var this$1 = this;
7600
7601 if (this.explicitlyCleared) { return }
7602 this.explicitlyCleared = true;
7603 for (var i = 0; i < this.markers.length; ++i)
7604 { this$1.markers[i].clear(); }
7605 signalLater(this, "clear");
7606 };
7607
7608 SharedTextMarker.prototype.find = function (side, lineObj) {
7609 return this.primary.find(side, lineObj)
7610 };
7611 eventMixin(SharedTextMarker);
7612
7613 function markTextShared(doc, from, to, options, type) {
7614 options = copyObj(options);
7615 options.shared = false;
7616 var markers = [markText(doc, from, to, options, type)], primary = markers[0];
7617 var widget = options.widgetNode;
7618 linkedDocs(doc, function (doc) {
7619 if (widget) { options.widgetNode = widget.cloneNode(true); }
7620 markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));
7621 for (var i = 0; i < doc.linked.length; ++i)
7622 { if (doc.linked[i].isParent) { return } }
7623 primary = lst(markers);
7624 });
7625 return new SharedTextMarker(markers, primary)
7626 }
7627
7628 function findSharedMarkers(doc) {
7629 return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function (m) { return m.parent; })
7630 }
7631
7632 function copySharedMarkers(doc, markers) {
7633 for (var i = 0; i < markers.length; i++) {
7634 var marker = markers[i], pos = marker.find();
7635 var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to);
7636 if (cmp(mFrom, mTo)) {
7637 var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type);
7638 marker.markers.push(subMark);
7639 subMark.parent = marker;
7640 }
7641 }
7642 }
7643
7644 function detachSharedMarkers(markers) {
7645 var loop = function ( i ) {
7646 var marker = markers[i], linked = [marker.primary.doc];
7647 linkedDocs(marker.primary.doc, function (d) { return linked.push(d); });
7648 for (var j = 0; j < marker.markers.length; j++) {
7649 var subMarker = marker.markers[j];
7650 if (indexOf(linked, subMarker.doc) == -1) {
7651 subMarker.parent = null;
7652 marker.markers.splice(j--, 1);
7653 }
7654 }
7655 };
7656
7657 for (var i = 0; i < markers.length; i++) loop( i );
7658 }
7659
7660 var nextDocId = 0;
7661 var Doc = function(text, mode, firstLine, lineSep, direction) {
7662 if (!(this instanceof Doc)) { return new Doc(text, mode, firstLine, lineSep, direction) }
7663 if (firstLine == null) { firstLine = 0; }
7664
7665 BranchChunk.call(this, [new LeafChunk([new Line("", null)])]);
7666 this.first = firstLine;
7667 this.scrollTop = this.scrollLeft = 0;
7668 this.cantEdit = false;
7669 this.cleanGeneration = 1;
7670 this.modeFrontier = this.highlightFrontier = firstLine;
7671 var start = Pos(firstLine, 0);
7672 this.sel = simpleSelection(start);
7673 this.history = new History(null);
7674 this.id = ++nextDocId;
7675 this.modeOption = mode;
7676 this.lineSep = lineSep;
7677 this.direction = (direction == "rtl") ? "rtl" : "ltr";
7678 this.extend = false;
7679
7680 if (typeof text == "string") { text = this.splitLines(text); }
7681 updateDoc(this, {from: start, to: start, text: text});
7682 setSelection(this, simpleSelection(start), sel_dontScroll);
7683 };
7684
7685 Doc.prototype = createObj(BranchChunk.prototype, {
7686 constructor: Doc,
7687 // Iterate over the document. Supports two forms -- with only one
7688 // argument, it calls that for each line in the document. With
7689 // three, it iterates over the range given by the first two (with
7690 // the second being non-inclusive).
7691 iter: function(from, to, op) {
7692 if (op) { this.iterN(from - this.first, to - from, op); }
7693 else { this.iterN(this.first, this.first + this.size, from); }
7694 },
7695
7696 // Non-public interface for adding and removing lines.
7697 insert: function(at, lines) {
7698 var height = 0;
7699 for (var i = 0; i < lines.length; ++i) { height += lines[i].height; }
7700 this.insertInner(at - this.first, lines, height);
7701 },
7702 remove: function(at, n) { this.removeInner(at - this.first, n); },
7703
7704 // From here, the methods are part of the public interface. Most
7705 // are also available from CodeMirror (editor) instances.
7706
7707 getValue: function(lineSep) {
7708 var lines = getLines(this, this.first, this.first + this.size);
7709 if (lineSep === false) { return lines }
7710 return lines.join(lineSep || this.lineSeparator())
7711 },
7712 setValue: docMethodOp(function(code) {
7713 var top = Pos(this.first, 0), last = this.first + this.size - 1;
7714 makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
7715 text: this.splitLines(code), origin: "setValue", full: true}, true);
7716 if (this.cm) { scrollToCoords(this.cm, 0, 0); }
7717 setSelection(this, simpleSelection(top), sel_dontScroll);
7718 }),
7719 replaceRange: function(code, from, to, origin) {
7720 from = clipPos(this, from);
7721 to = to ? clipPos(this, to) : from;
7722 replaceRange(this, code, from, to, origin);
7723 },
7724 getRange: function(from, to, lineSep) {
7725 var lines = getBetween(this, clipPos(this, from), clipPos(this, to));
7726 if (lineSep === false) { return lines }
7727 return lines.join(lineSep || this.lineSeparator())
7728 },
7729
7730 getLine: function(line) {var l = this.getLineHandle(line); return l && l.text},
7731
7732 getLineHandle: function(line) {if (isLine(this, line)) { return getLine(this, line) }},
7733 getLineNumber: function(line) {return lineNo(line)},
7734
7735 getLineHandleVisualStart: function(line) {
7736 if (typeof line == "number") { line = getLine(this, line); }
7737 return visualLine(line)
7738 },
7739
7740 lineCount: function() {return this.size},
7741 firstLine: function() {return this.first},
7742 lastLine: function() {return this.first + this.size - 1},
7743
7744 clipPos: function(pos) {return clipPos(this, pos)},
7745
7746 getCursor: function(start) {
7747 var range$$1 = this.sel.primary(), pos;
7748 if (start == null || start == "head") { pos = range$$1.head; }
7749 else if (start == "anchor") { pos = range$$1.anchor; }
7750 else if (start == "end" || start == "to" || start === false) { pos = range$$1.to(); }
7751 else { pos = range$$1.from(); }
7752 return pos
7753 },
7754 listSelections: function() { return this.sel.ranges },
7755 somethingSelected: function() {return this.sel.somethingSelected()},
7756
7757 setCursor: docMethodOp(function(line, ch, options) {
7758 setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options);
7759 }),
7760 setSelection: docMethodOp(function(anchor, head, options) {
7761 setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options);
7762 }),
7763 extendSelection: docMethodOp(function(head, other, options) {
7764 extendSelection(this, clipPos(this, head), other && clipPos(this, other), options);
7765 }),
7766 extendSelections: docMethodOp(function(heads, options) {
7767 extendSelections(this, clipPosArray(this, heads), options);
7768 }),
7769 extendSelectionsBy: docMethodOp(function(f, options) {
7770 var heads = map(this.sel.ranges, f);
7771 extendSelections(this, clipPosArray(this, heads), options);
7772 }),
7773 setSelections: docMethodOp(function(ranges, primary, options) {
7774 var this$1 = this;
7775
7776 if (!ranges.length) { return }
7777 var out = [];
7778 for (var i = 0; i < ranges.length; i++)
7779 { out[i] = new Range(clipPos(this$1, ranges[i].anchor),
7780 clipPos(this$1, ranges[i].head)); }
7781 if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex); }
7782 setSelection(this, normalizeSelection(this.cm, out, primary), options);
7783 }),
7784 addSelection: docMethodOp(function(anchor, head, options) {
7785 var ranges = this.sel.ranges.slice(0);
7786 ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)));
7787 setSelection(this, normalizeSelection(this.cm, ranges, ranges.length - 1), options);
7788 }),
7789
7790 getSelection: function(lineSep) {
7791 var this$1 = this;
7792
7793 var ranges = this.sel.ranges, lines;
7794 for (var i = 0; i < ranges.length; i++) {
7795 var sel = getBetween(this$1, ranges[i].from(), ranges[i].to());
7796 lines = lines ? lines.concat(sel) : sel;
7797 }
7798 if (lineSep === false) { return lines }
7799 else { return lines.join(lineSep || this.lineSeparator()) }
7800 },
7801 getSelections: function(lineSep) {
7802 var this$1 = this;
7803
7804 var parts = [], ranges = this.sel.ranges;
7805 for (var i = 0; i < ranges.length; i++) {
7806 var sel = getBetween(this$1, ranges[i].from(), ranges[i].to());
7807 if (lineSep !== false) { sel = sel.join(lineSep || this$1.lineSeparator()); }
7808 parts[i] = sel;
7809 }
7810 return parts
7811 },
7812 replaceSelection: function(code, collapse, origin) {
7813 var dup = [];
7814 for (var i = 0; i < this.sel.ranges.length; i++)
7815 { dup[i] = code; }
7816 this.replaceSelections(dup, collapse, origin || "+input");
7817 },
7818 replaceSelections: docMethodOp(function(code, collapse, origin) {
7819 var this$1 = this;
7820
7821 var changes = [], sel = this.sel;
7822 for (var i = 0; i < sel.ranges.length; i++) {
7823 var range$$1 = sel.ranges[i];
7824 changes[i] = {from: range$$1.from(), to: range$$1.to(), text: this$1.splitLines(code[i]), origin: origin};
7825 }
7826 var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse);
7827 for (var i$1 = changes.length - 1; i$1 >= 0; i$1--)
7828 { makeChange(this$1, changes[i$1]); }
7829 if (newSel) { setSelectionReplaceHistory(this, newSel); }
7830 else if (this.cm) { ensureCursorVisible(this.cm); }
7831 }),
7832 undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}),
7833 redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}),
7834 undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}),
7835 redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true);}),
7836
7837 setExtending: function(val) {this.extend = val;},
7838 getExtending: function() {return this.extend},
7839
7840 historySize: function() {
7841 var hist = this.history, done = 0, undone = 0;
7842 for (var i = 0; i < hist.done.length; i++) { if (!hist.done[i].ranges) { ++done; } }
7843 for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].ranges) { ++undone; } }
7844 return {undo: done, redo: undone}
7845 },
7846 clearHistory: function() {this.history = new History(this.history.maxGeneration);},
7847
7848 markClean: function() {
7849 this.cleanGeneration = this.changeGeneration(true);
7850 },
7851 changeGeneration: function(forceSplit) {
7852 if (forceSplit)
7853 { this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null; }
7854 return this.history.generation
7855 },
7856 isClean: function (gen) {
7857 return this.history.generation == (gen || this.cleanGeneration)
7858 },
7859
7860 getHistory: function() {
7861 return {done: copyHistoryArray(this.history.done),
7862 undone: copyHistoryArray(this.history.undone)}
7863 },
7864 setHistory: function(histData) {
7865 var hist = this.history = new History(this.history.maxGeneration);
7866 hist.done = copyHistoryArray(histData.done.slice(0), null, true);
7867 hist.undone = copyHistoryArray(histData.undone.slice(0), null, true);
7868 },
7869
7870 setGutterMarker: docMethodOp(function(line, gutterID, value) {
7871 return changeLine(this, line, "gutter", function (line) {
7872 var markers = line.gutterMarkers || (line.gutterMarkers = {});
7873 markers[gutterID] = value;
7874 if (!value && isEmpty(markers)) { line.gutterMarkers = null; }
7875 return true
7876 })
7877 }),
7878
7879 clearGutter: docMethodOp(function(gutterID) {
7880 var this$1 = this;
7881
7882 this.iter(function (line) {
7883 if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
7884 changeLine(this$1, line, "gutter", function () {
7885 line.gutterMarkers[gutterID] = null;
7886 if (isEmpty(line.gutterMarkers)) { line.gutterMarkers = null; }
7887 return true
7888 });
7889 }
7890 });
7891 }),
7892
7893 lineInfo: function(line) {
7894 var n;
7895 if (typeof line == "number") {
7896 if (!isLine(this, line)) { return null }
7897 n = line;
7898 line = getLine(this, line);
7899 if (!line) { return null }
7900 } else {
7901 n = lineNo(line);
7902 if (n == null) { return null }
7903 }
7904 return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
7905 textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
7906 widgets: line.widgets}
7907 },
7908
7909 addLineClass: docMethodOp(function(handle, where, cls) {
7910 return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
7911 var prop = where == "text" ? "textClass"
7912 : where == "background" ? "bgClass"
7913 : where == "gutter" ? "gutterClass" : "wrapClass";
7914 if (!line[prop]) { line[prop] = cls; }
7915 else if (classTest(cls).test(line[prop])) { return false }
7916 else { line[prop] += " " + cls; }
7917 return true
7918 })
7919 }),
7920 removeLineClass: docMethodOp(function(handle, where, cls) {
7921 return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
7922 var prop = where == "text" ? "textClass"
7923 : where == "background" ? "bgClass"
7924 : where == "gutter" ? "gutterClass" : "wrapClass";
7925 var cur = line[prop];
7926 if (!cur) { return false }
7927 else if (cls == null) { line[prop] = null; }
7928 else {
7929 var found = cur.match(classTest(cls));
7930 if (!found) { return false }
7931 var end = found.index + found[0].length;
7932 line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null;
7933 }
7934 return true
7935 })
7936 }),
7937
7938 addLineWidget: docMethodOp(function(handle, node, options) {
7939 return addLineWidget(this, handle, node, options)
7940 }),
7941 removeLineWidget: function(widget) { widget.clear(); },
7942
7943 markText: function(from, to, options) {
7944 return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range")
7945 },
7946 setBookmark: function(pos, options) {
7947 var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
7948 insertLeft: options && options.insertLeft,
7949 clearWhenEmpty: false, shared: options && options.shared,
7950 handleMouseEvents: options && options.handleMouseEvents};
7951 pos = clipPos(this, pos);
7952 return markText(this, pos, pos, realOpts, "bookmark")
7953 },
7954 findMarksAt: function(pos) {
7955 pos = clipPos(this, pos);
7956 var markers = [], spans = getLine(this, pos.line).markedSpans;
7957 if (spans) { for (var i = 0; i < spans.length; ++i) {
7958 var span = spans[i];
7959 if ((span.from == null || span.from <= pos.ch) &&
7960 (span.to == null || span.to >= pos.ch))
7961 { markers.push(span.marker.parent || span.marker); }
7962 } }
7963 return markers
7964 },
7965 findMarks: function(from, to, filter) {
7966 from = clipPos(this, from); to = clipPos(this, to);
7967 var found = [], lineNo$$1 = from.line;
7968 this.iter(from.line, to.line + 1, function (line) {
7969 var spans = line.markedSpans;
7970 if (spans) { for (var i = 0; i < spans.length; i++) {
7971 var span = spans[i];
7972 if (!(span.to != null && lineNo$$1 == from.line && from.ch >= span.to ||
7973 span.from == null && lineNo$$1 != from.line ||
7974 span.from != null && lineNo$$1 == to.line && span.from >= to.ch) &&
7975 (!filter || filter(span.marker)))
7976 { found.push(span.marker.parent || span.marker); }
7977 } }
7978 ++lineNo$$1;
7979 });
7980 return found
7981 },
7982 getAllMarks: function() {
7983 var markers = [];
7984 this.iter(function (line) {
7985 var sps = line.markedSpans;
7986 if (sps) { for (var i = 0; i < sps.length; ++i)
7987 { if (sps[i].from != null) { markers.push(sps[i].marker); } } }
7988 });
7989 return markers
7990 },
7991
7992 posFromIndex: function(off) {
7993 var ch, lineNo$$1 = this.first, sepSize = this.lineSeparator().length;
7994 this.iter(function (line) {
7995 var sz = line.text.length + sepSize;
7996 if (sz > off) { ch = off; return true }
7997 off -= sz;
7998 ++lineNo$$1;
7999 });
8000 return clipPos(this, Pos(lineNo$$1, ch))
8001 },
8002 indexFromPos: function (coords) {
8003 coords = clipPos(this, coords);
8004 var index = coords.ch;
8005 if (coords.line < this.first || coords.ch < 0) { return 0 }
8006 var sepSize = this.lineSeparator().length;
8007 this.iter(this.first, coords.line, function (line) { // iter aborts when callback returns a truthy value
8008 index += line.text.length + sepSize;
8009 });
8010 return index
8011 },
8012
8013 copy: function(copyHistory) {
8014 var doc = new Doc(getLines(this, this.first, this.first + this.size),
8015 this.modeOption, this.first, this.lineSep, this.direction);
8016 doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;
8017 doc.sel = this.sel;
8018 doc.extend = false;
8019 if (copyHistory) {
8020 doc.history.undoDepth = this.history.undoDepth;
8021 doc.setHistory(this.getHistory());
8022 }
8023 return doc
8024 },
8025
8026 linkedDoc: function(options) {
8027 if (!options) { options = {}; }
8028 var from = this.first, to = this.first + this.size;
8029 if (options.from != null && options.from > from) { from = options.from; }
8030 if (options.to != null && options.to < to) { to = options.to; }
8031 var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep, this.direction);
8032 if (options.sharedHist) { copy.history = this.history
8033 ; }(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});
8034 copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];
8035 copySharedMarkers(copy, findSharedMarkers(this));
8036 return copy
8037 },
8038 unlinkDoc: function(other) {
8039 var this$1 = this;
8040
8041 if (other instanceof CodeMirror) { other = other.doc; }
8042 if (this.linked) { for (var i = 0; i < this.linked.length; ++i) {
8043 var link = this$1.linked[i];
8044 if (link.doc != other) { continue }
8045 this$1.linked.splice(i, 1);
8046 other.unlinkDoc(this$1);
8047 detachSharedMarkers(findSharedMarkers(this$1));
8048 break
8049 } }
8050 // If the histories were shared, split them again
8051 if (other.history == this.history) {
8052 var splitIds = [other.id];
8053 linkedDocs(other, function (doc) { return splitIds.push(doc.id); }, true);
8054 other.history = new History(null);
8055 other.history.done = copyHistoryArray(this.history.done, splitIds);
8056 other.history.undone = copyHistoryArray(this.history.undone, splitIds);
8057 }
8058 },
8059 iterLinkedDocs: function(f) {linkedDocs(this, f);},
8060
8061 getMode: function() {return this.mode},
8062 getEditor: function() {return this.cm},
8063
8064 splitLines: function(str) {
8065 if (this.lineSep) { return str.split(this.lineSep) }
8066 return splitLinesAuto(str)
8067 },
8068 lineSeparator: function() { return this.lineSep || "\n" },
8069
8070 setDirection: docMethodOp(function (dir) {
8071 if (dir != "rtl") { dir = "ltr"; }
8072 if (dir == this.direction) { return }
8073 this.direction = dir;
8074 this.iter(function (line) { return line.order = null; });
8075 if (this.cm) { directionChanged(this.cm); }
8076 })
8077 });
8078
8079 // Public alias.
8080 Doc.prototype.eachLine = Doc.prototype.iter;
8081
8082 // Kludge to work around strange IE behavior where it'll sometimes
8083 // re-fire a series of drag-related events right after the drop (#1551)
8084 var lastDrop = 0;
8085
8086 function onDrop(e) {
8087 var cm = this;
8088 clearDragCursor(cm);
8089 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))
8090 { return }
8091 e_preventDefault(e);
8092 if (ie) { lastDrop = +new Date; }
8093 var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
8094 if (!pos || cm.isReadOnly()) { return }
8095 // Might be a file drop, in which case we simply extract the text
8096 // and insert it.
8097 if (files && files.length && window.FileReader && window.File) {
8098 var n = files.length, text = Array(n), read = 0;
8099 var loadFile = function (file, i) {
8100 if (cm.options.allowDropFileTypes &&
8101 indexOf(cm.options.allowDropFileTypes, file.type) == -1)
8102 { return }
8103
8104 var reader = new FileReader;
8105 reader.onload = operation(cm, function () {
8106 var content = reader.result;
8107 if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) { content = ""; }
8108 text[i] = content;
8109 if (++read == n) {
8110 pos = clipPos(cm.doc, pos);
8111 var change = {from: pos, to: pos,
8112 text: cm.doc.splitLines(text.join(cm.doc.lineSeparator())),
8113 origin: "paste"};
8114 makeChange(cm.doc, change);
8115 setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change)));
8116 }
8117 });
8118 reader.readAsText(file);
8119 };
8120 for (var i = 0; i < n; ++i) { loadFile(files[i], i); }
8121 } else { // Normal drop
8122 // Don't do a replace if the drop happened inside of the selected text.
8123 if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {
8124 cm.state.draggingText(e);
8125 // Ensure the editor is re-focused
8126 setTimeout(function () { return cm.display.input.focus(); }, 20);
8127 return
8128 }
8129 try {
8130 var text$1 = e.dataTransfer.getData("Text");
8131 if (text$1) {
8132 var selected;
8133 if (cm.state.draggingText && !cm.state.draggingText.copy)
8134 { selected = cm.listSelections(); }
8135 setSelectionNoUndo(cm.doc, simpleSelection(pos, pos));
8136 if (selected) { for (var i$1 = 0; i$1 < selected.length; ++i$1)
8137 { replaceRange(cm.doc, "", selected[i$1].anchor, selected[i$1].head, "drag"); } }
8138 cm.replaceSelection(text$1, "around", "paste");
8139 cm.display.input.focus();
8140 }
8141 }
8142 catch(e){}
8143 }
8144 }
8145
8146 function onDragStart(cm, e) {
8147 if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return }
8148 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return }
8149
8150 e.dataTransfer.setData("Text", cm.getSelection());
8151 e.dataTransfer.effectAllowed = "copyMove";
8152
8153 // Use dummy image instead of default browsers image.
8154 // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
8155 if (e.dataTransfer.setDragImage && !safari) {
8156 var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
8157 img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
8158 if (presto) {
8159 img.width = img.height = 1;
8160 cm.display.wrapper.appendChild(img);
8161 // Force a relayout, or Opera won't use our image for some obscure reason
8162 img._top = img.offsetTop;
8163 }
8164 e.dataTransfer.setDragImage(img, 0, 0);
8165 if (presto) { img.parentNode.removeChild(img); }
8166 }
8167 }
8168
8169 function onDragOver(cm, e) {
8170 var pos = posFromMouse(cm, e);
8171 if (!pos) { return }
8172 var frag = document.createDocumentFragment();
8173 drawSelectionCursor(cm, pos, frag);
8174 if (!cm.display.dragCursor) {
8175 cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors");
8176 cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv);
8177 }
8178 removeChildrenAndAdd(cm.display.dragCursor, frag);
8179 }
8180
8181 function clearDragCursor(cm) {
8182 if (cm.display.dragCursor) {
8183 cm.display.lineSpace.removeChild(cm.display.dragCursor);
8184 cm.display.dragCursor = null;
8185 }
8186 }
8187
8188 // These must be handled carefully, because naively registering a
8189 // handler for each editor will cause the editors to never be
8190 // garbage collected.
8191
8192 function forEachCodeMirror(f) {
8193 if (!document.getElementsByClassName) { return }
8194 var byClass = document.getElementsByClassName("CodeMirror"), editors = [];
8195 for (var i = 0; i < byClass.length; i++) {
8196 var cm = byClass[i].CodeMirror;
8197 if (cm) { editors.push(cm); }
8198 }
8199 if (editors.length) { editors[0].operation(function () {
8200 for (var i = 0; i < editors.length; i++) { f(editors[i]); }
8201 }); }
8202 }
8203
8204 var globalsRegistered = false;
8205 function ensureGlobalHandlers() {
8206 if (globalsRegistered) { return }
8207 registerGlobalHandlers();
8208 globalsRegistered = true;
8209 }
8210 function registerGlobalHandlers() {
8211 // When the window resizes, we need to refresh active editors.
8212 var resizeTimer;
8213 on(window, "resize", function () {
8214 if (resizeTimer == null) { resizeTimer = setTimeout(function () {
8215 resizeTimer = null;
8216 forEachCodeMirror(onResize);
8217 }, 100); }
8218 });
8219 // When the window loses focus, we want to show the editor as blurred
8220 on(window, "blur", function () { return forEachCodeMirror(onBlur); });
8221 }
8222 // Called when the window resizes
8223 function onResize(cm) {
8224 var d = cm.display;
8225 // Might be a text scaling operation, clear size caches.
8226 d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
8227 d.scrollbarsClipped = false;
8228 cm.setSize();
8229 }
8230
8231 var keyNames = {
8232 3: "Pause", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
8233 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
8234 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
8235 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod",
8236 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 145: "ScrollLock",
8237 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
8238 221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete",
8239 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert"
8240 };
8241
8242 // Number keys
8243 for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i); }
8244 // Alphabetic keys
8245 for (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1); }
8246 // Function keys
8247 for (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235] = "F" + i$2; }
8248
8249 var keyMap = {};
8250
8251 keyMap.basic = {
8252 "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
8253 "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
8254 "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore",
8255 "Tab": "defaultTab", "Shift-Tab": "indentAuto",
8256 "Enter": "newlineAndIndent", "Insert": "toggleOverwrite",
8257 "Esc": "singleSelection"
8258 };
8259 // Note that the save and find-related commands aren't defined by
8260 // default. User code or addons can define them. Unknown commands
8261 // are simply ignored.
8262 keyMap.pcDefault = {
8263 "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
8264 "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown",
8265 "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
8266 "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
8267 "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
8268 "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
8269 "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection",
8270 "fallthrough": "basic"
8271 };
8272 // Very basic readline/emacs-style bindings, which are standard on Mac.
8273 keyMap.emacsy = {
8274 "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
8275 "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
8276 "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
8277 "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars",
8278 "Ctrl-O": "openLine"
8279 };
8280 keyMap.macDefault = {
8281 "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
8282 "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
8283 "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore",
8284 "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
8285 "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
8286 "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight",
8287 "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd",
8288 "fallthrough": ["basic", "emacsy"]
8289 };
8290 keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
8291
8292 // KEYMAP DISPATCH
8293
8294 function normalizeKeyName(name) {
8295 var parts = name.split(/-(?!$)/);
8296 name = parts[parts.length - 1];
8297 var alt, ctrl, shift, cmd;
8298 for (var i = 0; i < parts.length - 1; i++) {
8299 var mod = parts[i];
8300 if (/^(cmd|meta|m)$/i.test(mod)) { cmd = true; }
8301 else if (/^a(lt)?$/i.test(mod)) { alt = true; }
8302 else if (/^(c|ctrl|control)$/i.test(mod)) { ctrl = true; }
8303 else if (/^s(hift)?$/i.test(mod)) { shift = true; }
8304 else { throw new Error("Unrecognized modifier name: " + mod) }
8305 }
8306 if (alt) { name = "Alt-" + name; }
8307 if (ctrl) { name = "Ctrl-" + name; }
8308 if (cmd) { name = "Cmd-" + name; }
8309 if (shift) { name = "Shift-" + name; }
8310 return name
8311 }
8312
8313 // This is a kludge to keep keymaps mostly working as raw objects
8314 // (backwards compatibility) while at the same time support features
8315 // like normalization and multi-stroke key bindings. It compiles a
8316 // new normalized keymap, and then updates the old object to reflect
8317 // this.
8318 function normalizeKeyMap(keymap) {
8319 var copy = {};
8320 for (var keyname in keymap) { if (keymap.hasOwnProperty(keyname)) {
8321 var value = keymap[keyname];
8322 if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) { continue }
8323 if (value == "...") { delete keymap[keyname]; continue }
8324
8325 var keys = map(keyname.split(" "), normalizeKeyName);
8326 for (var i = 0; i < keys.length; i++) {
8327 var val = (void 0), name = (void 0);
8328 if (i == keys.length - 1) {
8329 name = keys.join(" ");
8330 val = value;
8331 } else {
8332 name = keys.slice(0, i + 1).join(" ");
8333 val = "...";
8334 }
8335 var prev = copy[name];
8336 if (!prev) { copy[name] = val; }
8337 else if (prev != val) { throw new Error("Inconsistent bindings for " + name) }
8338 }
8339 delete keymap[keyname];
8340 } }
8341 for (var prop in copy) { keymap[prop] = copy[prop]; }
8342 return keymap
8343 }
8344
8345 function lookupKey(key, map$$1, handle, context) {
8346 map$$1 = getKeyMap(map$$1);
8347 var found = map$$1.call ? map$$1.call(key, context) : map$$1[key];
8348 if (found === false) { return "nothing" }
8349 if (found === "...") { return "multi" }
8350 if (found != null && handle(found)) { return "handled" }
8351
8352 if (map$$1.fallthrough) {
8353 if (Object.prototype.toString.call(map$$1.fallthrough) != "[object Array]")
8354 { return lookupKey(key, map$$1.fallthrough, handle, context) }
8355 for (var i = 0; i < map$$1.fallthrough.length; i++) {
8356 var result = lookupKey(key, map$$1.fallthrough[i], handle, context);
8357 if (result) { return result }
8358 }
8359 }
8360 }
8361
8362 // Modifier key presses don't count as 'real' key presses for the
8363 // purpose of keymap fallthrough.
8364 function isModifierKey(value) {
8365 var name = typeof value == "string" ? value : keyNames[value.keyCode];
8366 return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod"
8367 }
8368
8369 function addModifierNames(name, event, noShift) {
8370 var base = name;
8371 if (event.altKey && base != "Alt") { name = "Alt-" + name; }
8372 if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") { name = "Ctrl-" + name; }
8373 if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") { name = "Cmd-" + name; }
8374 if (!noShift && event.shiftKey && base != "Shift") { name = "Shift-" + name; }
8375 return name
8376 }
8377
8378 // Look up the name of a key as indicated by an event object.
8379 function keyName(event, noShift) {
8380 if (presto && event.keyCode == 34 && event["char"]) { return false }
8381 var name = keyNames[event.keyCode];
8382 if (name == null || event.altGraphKey) { return false }
8383 // Ctrl-ScrollLock has keyCode 3, same as Ctrl-Pause,
8384 // so we'll use event.code when available (Chrome 48+, FF 38+, Safari 10.1+)
8385 if (event.keyCode == 3 && event.code) { name = event.code; }
8386 return addModifierNames(name, event, noShift)
8387 }
8388
8389 function getKeyMap(val) {
8390 return typeof val == "string" ? keyMap[val] : val
8391 }
8392
8393 // Helper for deleting text near the selection(s), used to implement
8394 // backspace, delete, and similar functionality.
8395 function deleteNearSelection(cm, compute) {
8396 var ranges = cm.doc.sel.ranges, kill = [];
8397 // Build up a set of ranges to kill first, merging overlapping
8398 // ranges.
8399 for (var i = 0; i < ranges.length; i++) {
8400 var toKill = compute(ranges[i]);
8401 while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {
8402 var replaced = kill.pop();
8403 if (cmp(replaced.from, toKill.from) < 0) {
8404 toKill.from = replaced.from;
8405 break
8406 }
8407 }
8408 kill.push(toKill);
8409 }
8410 // Next, remove those actual ranges.
8411 runInOp(cm, function () {
8412 for (var i = kill.length - 1; i >= 0; i--)
8413 { replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete"); }
8414 ensureCursorVisible(cm);
8415 });
8416 }
8417
8418 function moveCharLogically(line, ch, dir) {
8419 var target = skipExtendingChars(line.text, ch + dir, dir);
8420 return target < 0 || target > line.text.length ? null : target
8421 }
8422
8423 function moveLogically(line, start, dir) {
8424 var ch = moveCharLogically(line, start.ch, dir);
8425 return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before")
8426 }
8427
8428 function endOfLine(visually, cm, lineObj, lineNo, dir) {
8429 if (visually) {
8430 var order = getOrder(lineObj, cm.doc.direction);
8431 if (order) {
8432 var part = dir < 0 ? lst(order) : order[0];
8433 var moveInStorageOrder = (dir < 0) == (part.level == 1);
8434 var sticky = moveInStorageOrder ? "after" : "before";
8435 var ch;
8436 // With a wrapped rtl chunk (possibly spanning multiple bidi parts),
8437 // it could be that the last bidi part is not on the last visual line,
8438 // since visual lines contain content order-consecutive chunks.
8439 // Thus, in rtl, we are looking for the first (content-order) character
8440 // in the rtl chunk that is on the last line (that is, the same line
8441 // as the last (content-order) character).
8442 if (part.level > 0 || cm.doc.direction == "rtl") {
8443 var prep = prepareMeasureForLine(cm, lineObj);
8444 ch = dir < 0 ? lineObj.text.length - 1 : 0;
8445 var targetTop = measureCharPrepared(cm, prep, ch).top;
8446 ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch);
8447 if (sticky == "before") { ch = moveCharLogically(lineObj, ch, 1); }
8448 } else { ch = dir < 0 ? part.to : part.from; }
8449 return new Pos(lineNo, ch, sticky)
8450 }
8451 }
8452 return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after")
8453 }
8454
8455 function moveVisually(cm, line, start, dir) {
8456 var bidi = getOrder(line, cm.doc.direction);
8457 if (!bidi) { return moveLogically(line, start, dir) }
8458 if (start.ch >= line.text.length) {
8459 start.ch = line.text.length;
8460 start.sticky = "before";
8461 } else if (start.ch <= 0) {
8462 start.ch = 0;
8463 start.sticky = "after";
8464 }
8465 var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos];
8466 if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) {
8467 // Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines,
8468 // nothing interesting happens.
8469 return moveLogically(line, start, dir)
8470 }
8471
8472 var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); };
8473 var prep;
8474 var getWrappedLineExtent = function (ch) {
8475 if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} }
8476 prep = prep || prepareMeasureForLine(cm, line);
8477 return wrappedLineExtentChar(cm, line, prep, ch)
8478 };
8479 var wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch);
8480
8481 if (cm.doc.direction == "rtl" || part.level == 1) {
8482 var moveInStorageOrder = (part.level == 1) == (dir < 0);
8483 var ch = mv(start, moveInStorageOrder ? 1 : -1);
8484 if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) {
8485 // Case 2: We move within an rtl part or in an rtl editor on the same visual line
8486 var sticky = moveInStorageOrder ? "before" : "after";
8487 return new Pos(start.line, ch, sticky)
8488 }
8489 }
8490
8491 // Case 3: Could not move within this bidi part in this visual line, so leave
8492 // the current bidi part
8493
8494 var searchInVisualLine = function (partPos, dir, wrappedLineExtent) {
8495 var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder
8496 ? new Pos(start.line, mv(ch, 1), "before")
8497 : new Pos(start.line, ch, "after"); };
8498
8499 for (; partPos >= 0 && partPos < bidi.length; partPos += dir) {
8500 var part = bidi[partPos];
8501 var moveInStorageOrder = (dir > 0) == (part.level != 1);
8502 var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1);
8503 if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) }
8504 ch = moveInStorageOrder ? part.from : mv(part.to, -1);
8505 if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) }
8506 }
8507 };
8508
8509 // Case 3a: Look for other bidi parts on the same visual line
8510 var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent);
8511 if (res) { return res }
8512
8513 // Case 3b: Look for other bidi parts on the next visual line
8514 var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1);
8515 if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) {
8516 res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh));
8517 if (res) { return res }
8518 }
8519
8520 // Case 4: Nowhere to move
8521 return null
8522 }
8523
8524 // Commands are parameter-less actions that can be performed on an
8525 // editor, mostly used for keybindings.
8526 var commands = {
8527 selectAll: selectAll,
8528 singleSelection: function (cm) { return cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll); },
8529 killLine: function (cm) { return deleteNearSelection(cm, function (range) {
8530 if (range.empty()) {
8531 var len = getLine(cm.doc, range.head.line).text.length;
8532 if (range.head.ch == len && range.head.line < cm.lastLine())
8533 { return {from: range.head, to: Pos(range.head.line + 1, 0)} }
8534 else
8535 { return {from: range.head, to: Pos(range.head.line, len)} }
8536 } else {
8537 return {from: range.from(), to: range.to()}
8538 }
8539 }); },
8540 deleteLine: function (cm) { return deleteNearSelection(cm, function (range) { return ({
8541 from: Pos(range.from().line, 0),
8542 to: clipPos(cm.doc, Pos(range.to().line + 1, 0))
8543 }); }); },
8544 delLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { return ({
8545 from: Pos(range.from().line, 0), to: range.from()
8546 }); }); },
8547 delWrappedLineLeft: function (cm) { return deleteNearSelection(cm, function (range) {
8548 var top = cm.charCoords(range.head, "div").top + 5;
8549 var leftPos = cm.coordsChar({left: 0, top: top}, "div");
8550 return {from: leftPos, to: range.from()}
8551 }); },
8552 delWrappedLineRight: function (cm) { return deleteNearSelection(cm, function (range) {
8553 var top = cm.charCoords(range.head, "div").top + 5;
8554 var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div");
8555 return {from: range.from(), to: rightPos }
8556 }); },
8557 undo: function (cm) { return cm.undo(); },
8558 redo: function (cm) { return cm.redo(); },
8559 undoSelection: function (cm) { return cm.undoSelection(); },
8560 redoSelection: function (cm) { return cm.redoSelection(); },
8561 goDocStart: function (cm) { return cm.extendSelection(Pos(cm.firstLine(), 0)); },
8562 goDocEnd: function (cm) { return cm.extendSelection(Pos(cm.lastLine())); },
8563 goLineStart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStart(cm, range.head.line); },
8564 {origin: "+move", bias: 1}
8565 ); },
8566 goLineStartSmart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStartSmart(cm, range.head); },
8567 {origin: "+move", bias: 1}
8568 ); },
8569 goLineEnd: function (cm) { return cm.extendSelectionsBy(function (range) { return lineEnd(cm, range.head.line); },
8570 {origin: "+move", bias: -1}
8571 ); },
8572 goLineRight: function (cm) { return cm.extendSelectionsBy(function (range) {
8573 var top = cm.cursorCoords(range.head, "div").top + 5;
8574 return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div")
8575 }, sel_move); },
8576 goLineLeft: function (cm) { return cm.extendSelectionsBy(function (range) {
8577 var top = cm.cursorCoords(range.head, "div").top + 5;
8578 return cm.coordsChar({left: 0, top: top}, "div")
8579 }, sel_move); },
8580 goLineLeftSmart: function (cm) { return cm.extendSelectionsBy(function (range) {
8581 var top = cm.cursorCoords(range.head, "div").top + 5;
8582 var pos = cm.coordsChar({left: 0, top: top}, "div");
8583 if (pos.ch < cm.getLine(pos.line).search(/\S/)) { return lineStartSmart(cm, range.head) }
8584 return pos
8585 }, sel_move); },
8586 goLineUp: function (cm) { return cm.moveV(-1, "line"); },
8587 goLineDown: function (cm) { return cm.moveV(1, "line"); },
8588 goPageUp: function (cm) { return cm.moveV(-1, "page"); },
8589 goPageDown: function (cm) { return cm.moveV(1, "page"); },
8590 goCharLeft: function (cm) { return cm.moveH(-1, "char"); },
8591 goCharRight: function (cm) { return cm.moveH(1, "char"); },
8592 goColumnLeft: function (cm) { return cm.moveH(-1, "column"); },
8593 goColumnRight: function (cm) { return cm.moveH(1, "column"); },
8594 goWordLeft: function (cm) { return cm.moveH(-1, "word"); },
8595 goGroupRight: function (cm) { return cm.moveH(1, "group"); },
8596 goGroupLeft: function (cm) { return cm.moveH(-1, "group"); },
8597 goWordRight: function (cm) { return cm.moveH(1, "word"); },
8598 delCharBefore: function (cm) { return cm.deleteH(-1, "char"); },
8599 delCharAfter: function (cm) { return cm.deleteH(1, "char"); },
8600 delWordBefore: function (cm) { return cm.deleteH(-1, "word"); },
8601 delWordAfter: function (cm) { return cm.deleteH(1, "word"); },
8602 delGroupBefore: function (cm) { return cm.deleteH(-1, "group"); },
8603 delGroupAfter: function (cm) { return cm.deleteH(1, "group"); },
8604 indentAuto: function (cm) { return cm.indentSelection("smart"); },
8605 indentMore: function (cm) { return cm.indentSelection("add"); },
8606 indentLess: function (cm) { return cm.indentSelection("subtract"); },
8607 insertTab: function (cm) { return cm.replaceSelection("\t"); },
8608 insertSoftTab: function (cm) {
8609 var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize;
8610 for (var i = 0; i < ranges.length; i++) {
8611 var pos = ranges[i].from();
8612 var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize);
8613 spaces.push(spaceStr(tabSize - col % tabSize));
8614 }
8615 cm.replaceSelections(spaces);
8616 },
8617 defaultTab: function (cm) {
8618 if (cm.somethingSelected()) { cm.indentSelection("add"); }
8619 else { cm.execCommand("insertTab"); }
8620 },
8621 // Swap the two chars left and right of each selection's head.
8622 // Move cursor behind the two swapped characters afterwards.
8623 //
8624 // Doesn't consider line feeds a character.
8625 // Doesn't scan more than one line above to find a character.
8626 // Doesn't do anything on an empty line.
8627 // Doesn't do anything with non-empty selections.
8628 transposeChars: function (cm) { return runInOp(cm, function () {
8629 var ranges = cm.listSelections(), newSel = [];
8630 for (var i = 0; i < ranges.length; i++) {
8631 if (!ranges[i].empty()) { continue }
8632 var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text;
8633 if (line) {
8634 if (cur.ch == line.length) { cur = new Pos(cur.line, cur.ch - 1); }
8635 if (cur.ch > 0) {
8636 cur = new Pos(cur.line, cur.ch + 1);
8637 cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),
8638 Pos(cur.line, cur.ch - 2), cur, "+transpose");
8639 } else if (cur.line > cm.doc.first) {
8640 var prev = getLine(cm.doc, cur.line - 1).text;
8641 if (prev) {
8642 cur = new Pos(cur.line, 1);
8643 cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() +
8644 prev.charAt(prev.length - 1),
8645 Pos(cur.line - 1, prev.length - 1), cur, "+transpose");
8646 }
8647 }
8648 }
8649 newSel.push(new Range(cur, cur));
8650 }
8651 cm.setSelections(newSel);
8652 }); },
8653 newlineAndIndent: function (cm) { return runInOp(cm, function () {
8654 var sels = cm.listSelections();
8655 for (var i = sels.length - 1; i >= 0; i--)
8656 { cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input"); }
8657 sels = cm.listSelections();
8658 for (var i$1 = 0; i$1 < sels.length; i$1++)
8659 { cm.indentLine(sels[i$1].from().line, null, true); }
8660 ensureCursorVisible(cm);
8661 }); },
8662 openLine: function (cm) { return cm.replaceSelection("\n", "start"); },
8663 toggleOverwrite: function (cm) { return cm.toggleOverwrite(); }
8664 };
8665
8666
8667 function lineStart(cm, lineN) {
8668 var line = getLine(cm.doc, lineN);
8669 var visual = visualLine(line);
8670 if (visual != line) { lineN = lineNo(visual); }
8671 return endOfLine(true, cm, visual, lineN, 1)
8672 }
8673 function lineEnd(cm, lineN) {
8674 var line = getLine(cm.doc, lineN);
8675 var visual = visualLineEnd(line);
8676 if (visual != line) { lineN = lineNo(visual); }
8677 return endOfLine(true, cm, line, lineN, -1)
8678 }
8679 function lineStartSmart(cm, pos) {
8680 var start = lineStart(cm, pos.line);
8681 var line = getLine(cm.doc, start.line);
8682 var order = getOrder(line, cm.doc.direction);
8683 if (!order || order[0].level == 0) {
8684 var firstNonWS = Math.max(0, line.text.search(/\S/));
8685 var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch;
8686 return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky)
8687 }
8688 return start
8689 }
8690
8691 // Run a handler that was bound to a key.
8692 function doHandleBinding(cm, bound, dropShift) {
8693 if (typeof bound == "string") {
8694 bound = commands[bound];
8695 if (!bound) { return false }
8696 }
8697 // Ensure previous input has been read, so that the handler sees a
8698 // consistent view of the document
8699 cm.display.input.ensurePolled();
8700 var prevShift = cm.display.shift, done = false;
8701 try {
8702 if (cm.isReadOnly()) { cm.state.suppressEdits = true; }
8703 if (dropShift) { cm.display.shift = false; }
8704 done = bound(cm) != Pass;
8705 } finally {
8706 cm.display.shift = prevShift;
8707 cm.state.suppressEdits = false;
8708 }
8709 return done
8710 }
8711
8712 function lookupKeyForEditor(cm, name, handle) {
8713 for (var i = 0; i < cm.state.keyMaps.length; i++) {
8714 var result = lookupKey(name, cm.state.keyMaps[i], handle, cm);
8715 if (result) { return result }
8716 }
8717 return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm))
8718 || lookupKey(name, cm.options.keyMap, handle, cm)
8719 }
8720
8721 // Note that, despite the name, this function is also used to check
8722 // for bound mouse clicks.
8723
8724 var stopSeq = new Delayed;
8725
8726 function dispatchKey(cm, name, e, handle) {
8727 var seq = cm.state.keySeq;
8728 if (seq) {
8729 if (isModifierKey(name)) { return "handled" }
8730 if (/\'$/.test(name))
8731 { cm.state.keySeq = null; }
8732 else
8733 { stopSeq.set(50, function () {
8734 if (cm.state.keySeq == seq) {
8735 cm.state.keySeq = null;
8736 cm.display.input.reset();
8737 }
8738 }); }
8739 if (dispatchKeyInner(cm, seq + " " + name, e, handle)) { return true }
8740 }
8741 return dispatchKeyInner(cm, name, e, handle)
8742 }
8743
8744 function dispatchKeyInner(cm, name, e, handle) {
8745 var result = lookupKeyForEditor(cm, name, handle);
8746
8747 if (result == "multi")
8748 { cm.state.keySeq = name; }
8749 if (result == "handled")
8750 { signalLater(cm, "keyHandled", cm, name, e); }
8751
8752 if (result == "handled" || result == "multi") {
8753 e_preventDefault(e);
8754 restartBlink(cm);
8755 }
8756
8757 return !!result
8758 }
8759
8760 // Handle a key from the keydown event.
8761 function handleKeyBinding(cm, e) {
8762 var name = keyName(e, true);
8763 if (!name) { return false }
8764
8765 if (e.shiftKey && !cm.state.keySeq) {
8766 // First try to resolve full name (including 'Shift-'). Failing
8767 // that, see if there is a cursor-motion command (starting with
8768 // 'go') bound to the keyname without 'Shift-'.
8769 return dispatchKey(cm, "Shift-" + name, e, function (b) { return doHandleBinding(cm, b, true); })
8770 || dispatchKey(cm, name, e, function (b) {
8771 if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
8772 { return doHandleBinding(cm, b) }
8773 })
8774 } else {
8775 return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b); })
8776 }
8777 }
8778
8779 // Handle a key from the keypress event
8780 function handleCharBinding(cm, e, ch) {
8781 return dispatchKey(cm, "'" + ch + "'", e, function (b) { return doHandleBinding(cm, b, true); })
8782 }
8783
8784 var lastStoppedKey = null;
8785 function onKeyDown(e) {
8786 var cm = this;
8787 cm.curOp.focus = activeElt();
8788 if (signalDOMEvent(cm, e)) { return }
8789 // IE does strange things with escape.
8790 if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false; }
8791 var code = e.keyCode;
8792 cm.display.shift = code == 16 || e.shiftKey;
8793 var handled = handleKeyBinding(cm, e);
8794 if (presto) {
8795 lastStoppedKey = handled ? code : null;
8796 // Opera has no cut event... we try to at least catch the key combo
8797 if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
8798 { cm.replaceSelection("", null, "cut"); }
8799 }
8800
8801 // Turn mouse into crosshair when Alt is held on Mac.
8802 if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className))
8803 { showCrossHair(cm); }
8804 }
8805
8806 function showCrossHair(cm) {
8807 var lineDiv = cm.display.lineDiv;
8808 addClass(lineDiv, "CodeMirror-crosshair");
8809
8810 function up(e) {
8811 if (e.keyCode == 18 || !e.altKey) {
8812 rmClass(lineDiv, "CodeMirror-crosshair");
8813 off(document, "keyup", up);
8814 off(document, "mouseover", up);
8815 }
8816 }
8817 on(document, "keyup", up);
8818 on(document, "mouseover", up);
8819 }
8820
8821 function onKeyUp(e) {
8822 if (e.keyCode == 16) { this.doc.sel.shift = false; }
8823 signalDOMEvent(this, e);
8824 }
8825
8826 function onKeyPress(e) {
8827 var cm = this;
8828 if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return }
8829 var keyCode = e.keyCode, charCode = e.charCode;
8830 if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return}
8831 if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) { return }
8832 var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
8833 // Some browsers fire keypress events for backspace
8834 if (ch == "\x08") { return }
8835 if (handleCharBinding(cm, e, ch)) { return }
8836 cm.display.input.onKeyPress(e);
8837 }
8838
8839 var DOUBLECLICK_DELAY = 400;
8840
8841 var PastClick = function(time, pos, button) {
8842 this.time = time;
8843 this.pos = pos;
8844 this.button = button;
8845 };
8846
8847 PastClick.prototype.compare = function (time, pos, button) {
8848 return this.time + DOUBLECLICK_DELAY > time &&
8849 cmp(pos, this.pos) == 0 && button == this.button
8850 };
8851
8852 var lastClick, lastDoubleClick;
8853 function clickRepeat(pos, button) {
8854 var now = +new Date;
8855 if (lastDoubleClick && lastDoubleClick.compare(now, pos, button)) {
8856 lastClick = lastDoubleClick = null;
8857 return "triple"
8858 } else if (lastClick && lastClick.compare(now, pos, button)) {
8859 lastDoubleClick = new PastClick(now, pos, button);
8860 lastClick = null;
8861 return "double"
8862 } else {
8863 lastClick = new PastClick(now, pos, button);
8864 lastDoubleClick = null;
8865 return "single"
8866 }
8867 }
8868
8869 // A mouse down can be a single click, double click, triple click,
8870 // start of selection drag, start of text drag, new cursor
8871 // (ctrl-click), rectangle drag (alt-drag), or xwin
8872 // middle-click-paste. Or it might be a click on something we should
8873 // not interfere with, such as a scrollbar or widget.
8874 function onMouseDown(e) {
8875 var cm = this, display = cm.display;
8876 if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) { return }
8877 display.input.ensurePolled();
8878 display.shift = e.shiftKey;
8879
8880 if (eventInWidget(display, e)) {
8881 if (!webkit) {
8882 // Briefly turn off draggability, to allow widgets to do
8883 // normal dragging things.
8884 display.scroller.draggable = false;
8885 setTimeout(function () { return display.scroller.draggable = true; }, 100);
8886 }
8887 return
8888 }
8889 if (clickInGutter(cm, e)) { return }
8890 var pos = posFromMouse(cm, e), button = e_button(e), repeat = pos ? clickRepeat(pos, button) : "single";
8891 window.focus();
8892
8893 // #3261: make sure, that we're not starting a second selection
8894 if (button == 1 && cm.state.selectingText)
8895 { cm.state.selectingText(e); }
8896
8897 if (pos && handleMappedButton(cm, button, pos, repeat, e)) { return }
8898
8899 if (button == 1) {
8900 if (pos) { leftButtonDown(cm, pos, repeat, e); }
8901 else if (e_target(e) == display.scroller) { e_preventDefault(e); }
8902 } else if (button == 2) {
8903 if (pos) { extendSelection(cm.doc, pos); }
8904 setTimeout(function () { return display.input.focus(); }, 20);
8905 } else if (button == 3) {
8906 if (captureRightClick) { cm.display.input.onContextMenu(e); }
8907 else { delayBlurEvent(cm); }
8908 }
8909 }
8910
8911 function handleMappedButton(cm, button, pos, repeat, event) {
8912 var name = "Click";
8913 if (repeat == "double") { name = "Double" + name; }
8914 else if (repeat == "triple") { name = "Triple" + name; }
8915 name = (button == 1 ? "Left" : button == 2 ? "Middle" : "Right") + name;
8916
8917 return dispatchKey(cm, addModifierNames(name, event), event, function (bound) {
8918 if (typeof bound == "string") { bound = commands[bound]; }
8919 if (!bound) { return false }
8920 var done = false;
8921 try {
8922 if (cm.isReadOnly()) { cm.state.suppressEdits = true; }
8923 done = bound(cm, pos) != Pass;
8924 } finally {
8925 cm.state.suppressEdits = false;
8926 }
8927 return done
8928 })
8929 }
8930
8931 function configureMouse(cm, repeat, event) {
8932 var option = cm.getOption("configureMouse");
8933 var value = option ? option(cm, repeat, event) : {};
8934 if (value.unit == null) {
8935 var rect = chromeOS ? event.shiftKey && event.metaKey : event.altKey;
8936 value.unit = rect ? "rectangle" : repeat == "single" ? "char" : repeat == "double" ? "word" : "line";
8937 }
8938 if (value.extend == null || cm.doc.extend) { value.extend = cm.doc.extend || event.shiftKey; }
8939 if (value.addNew == null) { value.addNew = mac ? event.metaKey : event.ctrlKey; }
8940 if (value.moveOnDrag == null) { value.moveOnDrag = !(mac ? event.altKey : event.ctrlKey); }
8941 return value
8942 }
8943
8944 function leftButtonDown(cm, pos, repeat, event) {
8945 if (ie) { setTimeout(bind(ensureFocus, cm), 0); }
8946 else { cm.curOp.focus = activeElt(); }
8947
8948 var behavior = configureMouse(cm, repeat, event);
8949
8950 var sel = cm.doc.sel, contained;
8951 if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() &&
8952 repeat == "single" && (contained = sel.contains(pos)) > -1 &&
8953 (cmp((contained = sel.ranges[contained]).from(), pos) < 0 || pos.xRel > 0) &&
8954 (cmp(contained.to(), pos) > 0 || pos.xRel < 0))
8955 { leftButtonStartDrag(cm, event, pos, behavior); }
8956 else
8957 { leftButtonSelect(cm, event, pos, behavior); }
8958 }
8959
8960 // Start a text drag. When it ends, see if any dragging actually
8961 // happen, and treat as a click if it didn't.
8962 function leftButtonStartDrag(cm, event, pos, behavior) {
8963 var display = cm.display, moved = false;
8964 var dragEnd = operation(cm, function (e) {
8965 if (webkit) { display.scroller.draggable = false; }
8966 cm.state.draggingText = false;
8967 off(display.wrapper.ownerDocument, "mouseup", dragEnd);
8968 off(display.wrapper.ownerDocument, "mousemove", mouseMove);
8969 off(display.scroller, "dragstart", dragStart);
8970 off(display.scroller, "drop", dragEnd);
8971 if (!moved) {
8972 e_preventDefault(e);
8973 if (!behavior.addNew)
8974 { extendSelection(cm.doc, pos, null, null, behavior.extend); }
8975 // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)
8976 if (webkit || ie && ie_version == 9)
8977 { setTimeout(function () {display.wrapper.ownerDocument.body.focus(); display.input.focus();}, 20); }
8978 else
8979 { display.input.focus(); }
8980 }
8981 });
8982 var mouseMove = function(e2) {
8983 moved = moved || Math.abs(event.clientX - e2.clientX) + Math.abs(event.clientY - e2.clientY) >= 10;
8984 };
8985 var dragStart = function () { return moved = true; };
8986 // Let the drag handler handle this.
8987 if (webkit) { display.scroller.draggable = true; }
8988 cm.state.draggingText = dragEnd;
8989 dragEnd.copy = !behavior.moveOnDrag;
8990 // IE's approach to draggable
8991 if (display.scroller.dragDrop) { display.scroller.dragDrop(); }
8992 on(display.wrapper.ownerDocument, "mouseup", dragEnd);
8993 on(display.wrapper.ownerDocument, "mousemove", mouseMove);
8994 on(display.scroller, "dragstart", dragStart);
8995 on(display.scroller, "drop", dragEnd);
8996
8997 delayBlurEvent(cm);
8998 setTimeout(function () { return display.input.focus(); }, 20);
8999 }
9000
9001 function rangeForUnit(cm, pos, unit) {
9002 if (unit == "char") { return new Range(pos, pos) }
9003 if (unit == "word") { return cm.findWordAt(pos) }
9004 if (unit == "line") { return new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) }
9005 var result = unit(cm, pos);
9006 return new Range(result.from, result.to)
9007 }
9008
9009 // Normal selection, as opposed to text dragging.
9010 function leftButtonSelect(cm, event, start, behavior) {
9011 var display = cm.display, doc = cm.doc;
9012 e_preventDefault(event);
9013
9014 var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges;
9015 if (behavior.addNew && !behavior.extend) {
9016 ourIndex = doc.sel.contains(start);
9017 if (ourIndex > -1)
9018 { ourRange = ranges[ourIndex]; }
9019 else
9020 { ourRange = new Range(start, start); }
9021 } else {
9022 ourRange = doc.sel.primary();
9023 ourIndex = doc.sel.primIndex;
9024 }
9025
9026 if (behavior.unit == "rectangle") {
9027 if (!behavior.addNew) { ourRange = new Range(start, start); }
9028 start = posFromMouse(cm, event, true, true);
9029 ourIndex = -1;
9030 } else {
9031 var range$$1 = rangeForUnit(cm, start, behavior.unit);
9032 if (behavior.extend)
9033 { ourRange = extendRange(ourRange, range$$1.anchor, range$$1.head, behavior.extend); }
9034 else
9035 { ourRange = range$$1; }
9036 }
9037
9038 if (!behavior.addNew) {
9039 ourIndex = 0;
9040 setSelection(doc, new Selection([ourRange], 0), sel_mouse);
9041 startSel = doc.sel;
9042 } else if (ourIndex == -1) {
9043 ourIndex = ranges.length;
9044 setSelection(doc, normalizeSelection(cm, ranges.concat([ourRange]), ourIndex),
9045 {scroll: false, origin: "*mouse"});
9046 } else if (ranges.length > 1 && ranges[ourIndex].empty() && behavior.unit == "char" && !behavior.extend) {
9047 setSelection(doc, normalizeSelection(cm, ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0),
9048 {scroll: false, origin: "*mouse"});
9049 startSel = doc.sel;
9050 } else {
9051 replaceOneSelection(doc, ourIndex, ourRange, sel_mouse);
9052 }
9053
9054 var lastPos = start;
9055 function extendTo(pos) {
9056 if (cmp(lastPos, pos) == 0) { return }
9057 lastPos = pos;
9058
9059 if (behavior.unit == "rectangle") {
9060 var ranges = [], tabSize = cm.options.tabSize;
9061 var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize);
9062 var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize);
9063 var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol);
9064 for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line));
9065 line <= end; line++) {
9066 var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize);
9067 if (left == right)
9068 { ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))); }
9069 else if (text.length > leftPos)
9070 { ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))); }
9071 }
9072 if (!ranges.length) { ranges.push(new Range(start, start)); }
9073 setSelection(doc, normalizeSelection(cm, startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex),
9074 {origin: "*mouse", scroll: false});
9075 cm.scrollIntoView(pos);
9076 } else {
9077 var oldRange = ourRange;
9078 var range$$1 = rangeForUnit(cm, pos, behavior.unit);
9079 var anchor = oldRange.anchor, head;
9080 if (cmp(range$$1.anchor, anchor) > 0) {
9081 head = range$$1.head;
9082 anchor = minPos(oldRange.from(), range$$1.anchor);
9083 } else {
9084 head = range$$1.anchor;
9085 anchor = maxPos(oldRange.to(), range$$1.head);
9086 }
9087 var ranges$1 = startSel.ranges.slice(0);
9088 ranges$1[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc, anchor), head));
9089 setSelection(doc, normalizeSelection(cm, ranges$1, ourIndex), sel_mouse);
9090 }
9091 }
9092
9093 var editorSize = display.wrapper.getBoundingClientRect();
9094 // Used to ensure timeout re-tries don't fire when another extend
9095 // happened in the meantime (clearTimeout isn't reliable -- at
9096 // least on Chrome, the timeouts still happen even when cleared,
9097 // if the clear happens after their scheduled firing time).
9098 var counter = 0;
9099
9100 function extend(e) {
9101 var curCount = ++counter;
9102 var cur = posFromMouse(cm, e, true, behavior.unit == "rectangle");
9103 if (!cur) { return }
9104 if (cmp(cur, lastPos) != 0) {
9105 cm.curOp.focus = activeElt();
9106 extendTo(cur);
9107 var visible = visibleLines(display, doc);
9108 if (cur.line >= visible.to || cur.line < visible.from)
9109 { setTimeout(operation(cm, function () {if (counter == curCount) { extend(e); }}), 150); }
9110 } else {
9111 var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;
9112 if (outside) { setTimeout(operation(cm, function () {
9113 if (counter != curCount) { return }
9114 display.scroller.scrollTop += outside;
9115 extend(e);
9116 }), 50); }
9117 }
9118 }
9119
9120 function done(e) {
9121 cm.state.selectingText = false;
9122 counter = Infinity;
9123 // If e is null or undefined we interpret this as someone trying
9124 // to explicitly cancel the selection rather than the user
9125 // letting go of the mouse button.
9126 if (e) {
9127 e_preventDefault(e);
9128 display.input.focus();
9129 }
9130 off(display.wrapper.ownerDocument, "mousemove", move);
9131 off(display.wrapper.ownerDocument, "mouseup", up);
9132 doc.history.lastSelOrigin = null;
9133 }
9134
9135 var move = operation(cm, function (e) {
9136 if (e.buttons === 0 || !e_button(e)) { done(e); }
9137 else { extend(e); }
9138 });
9139 var up = operation(cm, done);
9140 cm.state.selectingText = up;
9141 on(display.wrapper.ownerDocument, "mousemove", move);
9142 on(display.wrapper.ownerDocument, "mouseup", up);
9143 }
9144
9145 // Used when mouse-selecting to adjust the anchor to the proper side
9146 // of a bidi jump depending on the visual position of the head.
9147 function bidiSimplify(cm, range$$1) {
9148 var anchor = range$$1.anchor;
9149 var head = range$$1.head;
9150 var anchorLine = getLine(cm.doc, anchor.line);
9151 if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) { return range$$1 }
9152 var order = getOrder(anchorLine);
9153 if (!order) { return range$$1 }
9154 var index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index];
9155 if (part.from != anchor.ch && part.to != anchor.ch) { return range$$1 }
9156 var boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1);
9157 if (boundary == 0 || boundary == order.length) { return range$$1 }
9158
9159 // Compute the relative visual position of the head compared to the
9160 // anchor (<0 is to the left, >0 to the right)
9161 var leftSide;
9162 if (head.line != anchor.line) {
9163 leftSide = (head.line - anchor.line) * (cm.doc.direction == "ltr" ? 1 : -1) > 0;
9164 } else {
9165 var headIndex = getBidiPartAt(order, head.ch, head.sticky);
9166 var dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1);
9167 if (headIndex == boundary - 1 || headIndex == boundary)
9168 { leftSide = dir < 0; }
9169 else
9170 { leftSide = dir > 0; }
9171 }
9172
9173 var usePart = order[boundary + (leftSide ? -1 : 0)];
9174 var from = leftSide == (usePart.level == 1);
9175 var ch = from ? usePart.from : usePart.to, sticky = from ? "after" : "before";
9176 return anchor.ch == ch && anchor.sticky == sticky ? range$$1 : new Range(new Pos(anchor.line, ch, sticky), head)
9177 }
9178
9179
9180 // Determines whether an event happened in the gutter, and fires the
9181 // handlers for the corresponding event.
9182 function gutterEvent(cm, e, type, prevent) {
9183 var mX, mY;
9184 if (e.touches) {
9185 mX = e.touches[0].clientX;
9186 mY = e.touches[0].clientY;
9187 } else {
9188 try { mX = e.clientX; mY = e.clientY; }
9189 catch(e) { return false }
9190 }
9191 if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false }
9192 if (prevent) { e_preventDefault(e); }
9193
9194 var display = cm.display;
9195 var lineBox = display.lineDiv.getBoundingClientRect();
9196
9197 if (mY > lineBox.bottom || !hasHandler(cm, type)) { return e_defaultPrevented(e) }
9198 mY -= lineBox.top - display.viewOffset;
9199
9200 for (var i = 0; i < cm.display.gutterSpecs.length; ++i) {
9201 var g = display.gutters.childNodes[i];
9202 if (g && g.getBoundingClientRect().right >= mX) {
9203 var line = lineAtHeight(cm.doc, mY);
9204 var gutter = cm.display.gutterSpecs[i];
9205 signal(cm, type, cm, line, gutter.className, e);
9206 return e_defaultPrevented(e)
9207 }
9208 }
9209 }
9210
9211 function clickInGutter(cm, e) {
9212 return gutterEvent(cm, e, "gutterClick", true)
9213 }
9214
9215 // CONTEXT MENU HANDLING
9216
9217 // To make the context menu work, we need to briefly unhide the
9218 // textarea (making it as unobtrusive as possible) to let the
9219 // right-click take effect on it.
9220 function onContextMenu(cm, e) {
9221 if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) { return }
9222 if (signalDOMEvent(cm, e, "contextmenu")) { return }
9223 if (!captureRightClick) { cm.display.input.onContextMenu(e); }
9224 }
9225
9226 function contextMenuInGutter(cm, e) {
9227 if (!hasHandler(cm, "gutterContextMenu")) { return false }
9228 return gutterEvent(cm, e, "gutterContextMenu", false)
9229 }
9230
9231 function themeChanged(cm) {
9232 cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
9233 cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
9234 clearCaches(cm);
9235 }
9236
9237 var Init = {toString: function(){return "CodeMirror.Init"}};
9238
9239 var defaults = {};
9240 var optionHandlers = {};
9241
9242 function defineOptions(CodeMirror) {
9243 var optionHandlers = CodeMirror.optionHandlers;
9244
9245 function option(name, deflt, handle, notOnInit) {
9246 CodeMirror.defaults[name] = deflt;
9247 if (handle) { optionHandlers[name] =
9248 notOnInit ? function (cm, val, old) {if (old != Init) { handle(cm, val, old); }} : handle; }
9249 }
9250
9251 CodeMirror.defineOption = option;
9252
9253 // Passed to option handlers when there is no old value.
9254 CodeMirror.Init = Init;
9255
9256 // These two are, on init, called from the constructor because they
9257 // have to be initialized before the editor can start at all.
9258 option("value", "", function (cm, val) { return cm.setValue(val); }, true);
9259 option("mode", null, function (cm, val) {
9260 cm.doc.modeOption = val;
9261 loadMode(cm);
9262 }, true);
9263
9264 option("indentUnit", 2, loadMode, true);
9265 option("indentWithTabs", false);
9266 option("smartIndent", true);
9267 option("tabSize", 4, function (cm) {
9268 resetModeState(cm);
9269 clearCaches(cm);
9270 regChange(cm);
9271 }, true);
9272
9273 option("lineSeparator", null, function (cm, val) {
9274 cm.doc.lineSep = val;
9275 if (!val) { return }
9276 var newBreaks = [], lineNo = cm.doc.first;
9277 cm.doc.iter(function (line) {
9278 for (var pos = 0;;) {
9279 var found = line.text.indexOf(val, pos);
9280 if (found == -1) { break }
9281 pos = found + val.length;
9282 newBreaks.push(Pos(lineNo, found));
9283 }
9284 lineNo++;
9285 });
9286 for (var i = newBreaks.length - 1; i >= 0; i--)
9287 { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)); }
9288 });
9289 option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff]/g, function (cm, val, old) {
9290 cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g");
9291 if (old != Init) { cm.refresh(); }
9292 });
9293 option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function (cm) { return cm.refresh(); }, true);
9294 option("electricChars", true);
9295 option("inputStyle", mobile ? "contenteditable" : "textarea", function () {
9296 throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME
9297 }, true);
9298 option("spellcheck", false, function (cm, val) { return cm.getInputField().spellcheck = val; }, true);
9299 option("autocorrect", false, function (cm, val) { return cm.getInputField().autocorrect = val; }, true);
9300 option("autocapitalize", false, function (cm, val) { return cm.getInputField().autocapitalize = val; }, true);
9301 option("rtlMoveVisually", !windows);
9302 option("wholeLineUpdateBefore", true);
9303
9304 option("theme", "default", function (cm) {
9305 themeChanged(cm);
9306 updateGutters(cm);
9307 }, true);
9308 option("keyMap", "default", function (cm, val, old) {
9309 var next = getKeyMap(val);
9310 var prev = old != Init && getKeyMap(old);
9311 if (prev && prev.detach) { prev.detach(cm, next); }
9312 if (next.attach) { next.attach(cm, prev || null); }
9313 });
9314 option("extraKeys", null);
9315 option("configureMouse", null);
9316
9317 option("lineWrapping", false, wrappingChanged, true);
9318 option("gutters", [], function (cm, val) {
9319 cm.display.gutterSpecs = getGutters(val, cm.options.lineNumbers);
9320 updateGutters(cm);
9321 }, true);
9322 option("fixedGutter", true, function (cm, val) {
9323 cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
9324 cm.refresh();
9325 }, true);
9326 option("coverGutterNextToScrollbar", false, function (cm) { return updateScrollbars(cm); }, true);
9327 option("scrollbarStyle", "native", function (cm) {
9328 initScrollbars(cm);
9329 updateScrollbars(cm);
9330 cm.display.scrollbars.setScrollTop(cm.doc.scrollTop);
9331 cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft);
9332 }, true);
9333 option("lineNumbers", false, function (cm, val) {
9334 cm.display.gutterSpecs = getGutters(cm.options.gutters, val);
9335 updateGutters(cm);
9336 }, true);
9337 option("firstLineNumber", 1, updateGutters, true);
9338 option("lineNumberFormatter", function (integer) { return integer; }, updateGutters, true);
9339 option("showCursorWhenSelecting", false, updateSelection, true);
9340
9341 option("resetSelectionOnContextMenu", true);
9342 option("lineWiseCopyCut", true);
9343 option("pasteLinesPerSelection", true);
9344 option("selectionsMayTouch", false);
9345
9346 option("readOnly", false, function (cm, val) {
9347 if (val == "nocursor") {
9348 onBlur(cm);
9349 cm.display.input.blur();
9350 }
9351 cm.display.input.readOnlyChanged(val);
9352 });
9353 option("disableInput", false, function (cm, val) {if (!val) { cm.display.input.reset(); }}, true);
9354 option("dragDrop", true, dragDropChanged);
9355 option("allowDropFileTypes", null);
9356
9357 option("cursorBlinkRate", 530);
9358 option("cursorScrollMargin", 0);
9359 option("cursorHeight", 1, updateSelection, true);
9360 option("singleCursorHeightPerLine", true, updateSelection, true);
9361 option("workTime", 100);
9362 option("workDelay", 100);
9363 option("flattenSpans", true, resetModeState, true);
9364 option("addModeClass", false, resetModeState, true);
9365 option("pollInterval", 100);
9366 option("undoDepth", 200, function (cm, val) { return cm.doc.history.undoDepth = val; });
9367 option("historyEventDelay", 1250);
9368 option("viewportMargin", 10, function (cm) { return cm.refresh(); }, true);
9369 option("maxHighlightLength", 10000, resetModeState, true);
9370 option("moveInputWithCursor", true, function (cm, val) {
9371 if (!val) { cm.display.input.resetPosition(); }
9372 });
9373
9374 option("tabindex", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || ""; });
9375 option("autofocus", null);
9376 option("direction", "ltr", function (cm, val) { return cm.doc.setDirection(val); }, true);
9377 option("phrases", null);
9378 }
9379
9380 function dragDropChanged(cm, value, old) {
9381 var wasOn = old && old != Init;
9382 if (!value != !wasOn) {
9383 var funcs = cm.display.dragFunctions;
9384 var toggle = value ? on : off;
9385 toggle(cm.display.scroller, "dragstart", funcs.start);
9386 toggle(cm.display.scroller, "dragenter", funcs.enter);
9387 toggle(cm.display.scroller, "dragover", funcs.over);
9388 toggle(cm.display.scroller, "dragleave", funcs.leave);
9389 toggle(cm.display.scroller, "drop", funcs.drop);
9390 }
9391 }
9392
9393 function wrappingChanged(cm) {
9394 if (cm.options.lineWrapping) {
9395 addClass(cm.display.wrapper, "CodeMirror-wrap");
9396 cm.display.sizer.style.minWidth = "";
9397 cm.display.sizerWidth = null;
9398 } else {
9399 rmClass(cm.display.wrapper, "CodeMirror-wrap");
9400 findMaxLine(cm);
9401 }
9402 estimateLineHeights(cm);
9403 regChange(cm);
9404 clearCaches(cm);
9405 setTimeout(function () { return updateScrollbars(cm); }, 100);
9406 }
9407
9408 // A CodeMirror instance represents an editor. This is the object
9409 // that user code is usually dealing with.
9410
9411 function CodeMirror(place, options) {
9412 var this$1 = this;
9413
9414 if (!(this instanceof CodeMirror)) { return new CodeMirror(place, options) }
9415
9416 this.options = options = options ? copyObj(options) : {};
9417 // Determine effective options based on given values and defaults.
9418 copyObj(defaults, options, false);
9419
9420 var doc = options.value;
9421 if (typeof doc == "string") { doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction); }
9422 else if (options.mode) { doc.modeOption = options.mode; }
9423 this.doc = doc;
9424
9425 var input = new CodeMirror.inputStyles[options.inputStyle](this);
9426 var display = this.display = new Display(place, doc, input, options);
9427 display.wrapper.CodeMirror = this;
9428 themeChanged(this);
9429 if (options.lineWrapping)
9430 { this.display.wrapper.className += " CodeMirror-wrap"; }
9431 initScrollbars(this);
9432
9433 this.state = {
9434 keyMaps: [], // stores maps added by addKeyMap
9435 overlays: [], // highlighting overlays, as added by addOverlay
9436 modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info
9437 overwrite: false,
9438 delayingBlurEvent: false,
9439 focused: false,
9440 suppressEdits: false, // used to disable editing during key handlers when in readOnly mode
9441 pasteIncoming: -1, cutIncoming: -1, // help recognize paste/cut edits in input.poll
9442 selectingText: false,
9443 draggingText: false,
9444 highlight: new Delayed(), // stores highlight worker timeout
9445 keySeq: null, // Unfinished key sequence
9446 specialChars: null
9447 };
9448
9449 if (options.autofocus && !mobile) { display.input.focus(); }
9450
9451 // Override magic textarea content restore that IE sometimes does
9452 // on our hidden textarea on reload
9453 if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20); }
9454
9455 registerEventHandlers(this);
9456 ensureGlobalHandlers();
9457
9458 startOperation(this);
9459 this.curOp.forceUpdate = true;
9460 attachDoc(this, doc);
9461
9462 if ((options.autofocus && !mobile) || this.hasFocus())
9463 { setTimeout(bind(onFocus, this), 20); }
9464 else
9465 { onBlur(this); }
9466
9467 for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt))
9468 { optionHandlers[opt](this$1, options[opt], Init); } }
9469 maybeUpdateLineNumberWidth(this);
9470 if (options.finishInit) { options.finishInit(this); }
9471 for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this$1); }
9472 endOperation(this);
9473 // Suppress optimizelegibility in Webkit, since it breaks text
9474 // measuring on line wrapping boundaries.
9475 if (webkit && options.lineWrapping &&
9476 getComputedStyle(display.lineDiv).textRendering == "optimizelegibility")
9477 { display.lineDiv.style.textRendering = "auto"; }
9478 }
9479
9480 // The default configuration options.
9481 CodeMirror.defaults = defaults;
9482 // Functions to run when options are changed.
9483 CodeMirror.optionHandlers = optionHandlers;
9484
9485 // Attach the necessary event handlers when initializing the editor
9486 function registerEventHandlers(cm) {
9487 var d = cm.display;
9488 on(d.scroller, "mousedown", operation(cm, onMouseDown));
9489 // Older IE's will not fire a second mousedown for a double click
9490 if (ie && ie_version < 11)
9491 { on(d.scroller, "dblclick", operation(cm, function (e) {
9492 if (signalDOMEvent(cm, e)) { return }
9493 var pos = posFromMouse(cm, e);
9494 if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) { return }
9495 e_preventDefault(e);
9496 var word = cm.findWordAt(pos);
9497 extendSelection(cm.doc, word.anchor, word.head);
9498 })); }
9499 else
9500 { on(d.scroller, "dblclick", function (e) { return signalDOMEvent(cm, e) || e_preventDefault(e); }); }
9501 // Some browsers fire contextmenu *after* opening the menu, at
9502 // which point we can't mess with it anymore. Context menu is
9503 // handled in onMouseDown for these browsers.
9504 on(d.scroller, "contextmenu", function (e) { return onContextMenu(cm, e); });
9505
9506 // Used to suppress mouse event handling when a touch happens
9507 var touchFinished, prevTouch = {end: 0};
9508 function finishTouch() {
9509 if (d.activeTouch) {
9510 touchFinished = setTimeout(function () { return d.activeTouch = null; }, 1000);
9511 prevTouch = d.activeTouch;
9512 prevTouch.end = +new Date;
9513 }
9514 }
9515 function isMouseLikeTouchEvent(e) {
9516 if (e.touches.length != 1) { return false }
9517 var touch = e.touches[0];
9518 return touch.radiusX <= 1 && touch.radiusY <= 1
9519 }
9520 function farAway(touch, other) {
9521 if (other.left == null) { return true }
9522 var dx = other.left - touch.left, dy = other.top - touch.top;
9523 return dx * dx + dy * dy > 20 * 20
9524 }
9525 on(d.scroller, "touchstart", function (e) {
9526 if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e) && !clickInGutter(cm, e)) {
9527 d.input.ensurePolled();
9528 clearTimeout(touchFinished);
9529 var now = +new Date;
9530 d.activeTouch = {start: now, moved: false,
9531 prev: now - prevTouch.end <= 300 ? prevTouch : null};
9532 if (e.touches.length == 1) {
9533 d.activeTouch.left = e.touches[0].pageX;
9534 d.activeTouch.top = e.touches[0].pageY;
9535 }
9536 }
9537 });
9538 on(d.scroller, "touchmove", function () {
9539 if (d.activeTouch) { d.activeTouch.moved = true; }
9540 });
9541 on(d.scroller, "touchend", function (e) {
9542 var touch = d.activeTouch;
9543 if (touch && !eventInWidget(d, e) && touch.left != null &&
9544 !touch.moved && new Date - touch.start < 300) {
9545 var pos = cm.coordsChar(d.activeTouch, "page"), range;
9546 if (!touch.prev || farAway(touch, touch.prev)) // Single tap
9547 { range = new Range(pos, pos); }
9548 else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap
9549 { range = cm.findWordAt(pos); }
9550 else // Triple tap
9551 { range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))); }
9552 cm.setSelection(range.anchor, range.head);
9553 cm.focus();
9554 e_preventDefault(e);
9555 }
9556 finishTouch();
9557 });
9558 on(d.scroller, "touchcancel", finishTouch);
9559
9560 // Sync scrolling between fake scrollbars and real scrollable
9561 // area, ensure viewport is updated when scrolling.
9562 on(d.scroller, "scroll", function () {
9563 if (d.scroller.clientHeight) {
9564 updateScrollTop(cm, d.scroller.scrollTop);
9565 setScrollLeft(cm, d.scroller.scrollLeft, true);
9566 signal(cm, "scroll", cm);
9567 }
9568 });
9569
9570 // Listen to wheel events in order to try and update the viewport on time.
9571 on(d.scroller, "mousewheel", function (e) { return onScrollWheel(cm, e); });
9572 on(d.scroller, "DOMMouseScroll", function (e) { return onScrollWheel(cm, e); });
9573
9574 // Prevent wrapper from ever scrolling
9575 on(d.wrapper, "scroll", function () { return d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
9576
9577 d.dragFunctions = {
9578 enter: function (e) {if (!signalDOMEvent(cm, e)) { e_stop(e); }},
9579 over: function (e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e); }},
9580 start: function (e) { return onDragStart(cm, e); },
9581 drop: operation(cm, onDrop),
9582 leave: function (e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm); }}
9583 };
9584
9585 var inp = d.input.getField();
9586 on(inp, "keyup", function (e) { return onKeyUp.call(cm, e); });
9587 on(inp, "keydown", operation(cm, onKeyDown));
9588 on(inp, "keypress", operation(cm, onKeyPress));
9589 on(inp, "focus", function (e) { return onFocus(cm, e); });
9590 on(inp, "blur", function (e) { return onBlur(cm, e); });
9591 }
9592
9593 var initHooks = [];
9594 CodeMirror.defineInitHook = function (f) { return initHooks.push(f); };
9595
9596 // Indent the given line. The how parameter can be "smart",
9597 // "add"/null, "subtract", or "prev". When aggressive is false
9598 // (typically set to true for forced single-line indents), empty
9599 // lines are not indented, and places where the mode returns Pass
9600 // are left alone.
9601 function indentLine(cm, n, how, aggressive) {
9602 var doc = cm.doc, state;
9603 if (how == null) { how = "add"; }
9604 if (how == "smart") {
9605 // Fall back to "prev" when the mode doesn't have an indentation
9606 // method.
9607 if (!doc.mode.indent) { how = "prev"; }
9608 else { state = getContextBefore(cm, n).state; }
9609 }
9610
9611 var tabSize = cm.options.tabSize;
9612 var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
9613 if (line.stateAfter) { line.stateAfter = null; }
9614 var curSpaceString = line.text.match(/^\s*/)[0], indentation;
9615 if (!aggressive && !/\S/.test(line.text)) {
9616 indentation = 0;
9617 how = "not";
9618 } else if (how == "smart") {
9619 indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
9620 if (indentation == Pass || indentation > 150) {
9621 if (!aggressive) { return }
9622 how = "prev";
9623 }
9624 }
9625 if (how == "prev") {
9626 if (n > doc.first) { indentation = countColumn(getLine(doc, n-1).text, null, tabSize); }
9627 else { indentation = 0; }
9628 } else if (how == "add") {
9629 indentation = curSpace + cm.options.indentUnit;
9630 } else if (how == "subtract") {
9631 indentation = curSpace - cm.options.indentUnit;
9632 } else if (typeof how == "number") {
9633 indentation = curSpace + how;
9634 }
9635 indentation = Math.max(0, indentation);
9636
9637 var indentString = "", pos = 0;
9638 if (cm.options.indentWithTabs)
9639 { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";} }
9640 if (pos < indentation) { indentString += spaceStr(indentation - pos); }
9641
9642 if (indentString != curSpaceString) {
9643 replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
9644 line.stateAfter = null;
9645 return true
9646 } else {
9647 // Ensure that, if the cursor was in the whitespace at the start
9648 // of the line, it is moved to the end of that space.
9649 for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) {
9650 var range = doc.sel.ranges[i$1];
9651 if (range.head.line == n && range.head.ch < curSpaceString.length) {
9652 var pos$1 = Pos(n, curSpaceString.length);
9653 replaceOneSelection(doc, i$1, new Range(pos$1, pos$1));
9654 break
9655 }
9656 }
9657 }
9658 }
9659
9660 // This will be set to a {lineWise: bool, text: [string]} object, so
9661 // that, when pasting, we know what kind of selections the copied
9662 // text was made out of.
9663 var lastCopied = null;
9664
9665 function setLastCopied(newLastCopied) {
9666 lastCopied = newLastCopied;
9667 }
9668
9669 function applyTextInput(cm, inserted, deleted, sel, origin) {
9670 var doc = cm.doc;
9671 cm.display.shift = false;
9672 if (!sel) { sel = doc.sel; }
9673
9674 var recent = +new Date - 200;
9675 var paste = origin == "paste" || cm.state.pasteIncoming > recent;
9676 var textLines = splitLinesAuto(inserted), multiPaste = null;
9677 // When pasting N lines into N selections, insert one line per selection
9678 if (paste && sel.ranges.length > 1) {
9679 if (lastCopied && lastCopied.text.join("\n") == inserted) {
9680 if (sel.ranges.length % lastCopied.text.length == 0) {
9681 multiPaste = [];
9682 for (var i = 0; i < lastCopied.text.length; i++)
9683 { multiPaste.push(doc.splitLines(lastCopied.text[i])); }
9684 }
9685 } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) {
9686 multiPaste = map(textLines, function (l) { return [l]; });
9687 }
9688 }
9689
9690 var updateInput = cm.curOp.updateInput;
9691 // Normal behavior is to insert the new text into every selection
9692 for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) {
9693 var range$$1 = sel.ranges[i$1];
9694 var from = range$$1.from(), to = range$$1.to();
9695 if (range$$1.empty()) {
9696 if (deleted && deleted > 0) // Handle deletion
9697 { from = Pos(from.line, from.ch - deleted); }
9698 else if (cm.state.overwrite && !paste) // Handle overwrite
9699 { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); }
9700 else if (paste && lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == inserted)
9701 { from = to = Pos(from.line, 0); }
9702 }
9703 var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines,
9704 origin: origin || (paste ? "paste" : cm.state.cutIncoming > recent ? "cut" : "+input")};
9705 makeChange(cm.doc, changeEvent);
9706 signalLater(cm, "inputRead", cm, changeEvent);
9707 }
9708 if (inserted && !paste)
9709 { triggerElectric(cm, inserted); }
9710
9711 ensureCursorVisible(cm);
9712 if (cm.curOp.updateInput < 2) { cm.curOp.updateInput = updateInput; }
9713 cm.curOp.typing = true;
9714 cm.state.pasteIncoming = cm.state.cutIncoming = -1;
9715 }
9716
9717 function handlePaste(e, cm) {
9718 var pasted = e.clipboardData && e.clipboardData.getData("Text");
9719 if (pasted) {
9720 e.preventDefault();
9721 if (!cm.isReadOnly() && !cm.options.disableInput)
9722 { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, "paste"); }); }
9723 return true
9724 }
9725 }
9726
9727 function triggerElectric(cm, inserted) {
9728 // When an 'electric' character is inserted, immediately trigger a reindent
9729 if (!cm.options.electricChars || !cm.options.smartIndent) { return }
9730 var sel = cm.doc.sel;
9731
9732 for (var i = sel.ranges.length - 1; i >= 0; i--) {
9733 var range$$1 = sel.ranges[i];
9734 if (range$$1.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range$$1.head.line)) { continue }
9735 var mode = cm.getModeAt(range$$1.head);
9736 var indented = false;
9737 if (mode.electricChars) {
9738 for (var j = 0; j < mode.electricChars.length; j++)
9739 { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
9740 indented = indentLine(cm, range$$1.head.line, "smart");
9741 break
9742 } }
9743 } else if (mode.electricInput) {
9744 if (mode.electricInput.test(getLine(cm.doc, range$$1.head.line).text.slice(0, range$$1.head.ch)))
9745 { indented = indentLine(cm, range$$1.head.line, "smart"); }
9746 }
9747 if (indented) { signalLater(cm, "electricInput", cm, range$$1.head.line); }
9748 }
9749 }
9750
9751 function copyableRanges(cm) {
9752 var text = [], ranges = [];
9753 for (var i = 0; i < cm.doc.sel.ranges.length; i++) {
9754 var line = cm.doc.sel.ranges[i].head.line;
9755 var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)};
9756 ranges.push(lineRange);
9757 text.push(cm.getRange(lineRange.anchor, lineRange.head));
9758 }
9759 return {text: text, ranges: ranges}
9760 }
9761
9762 function disableBrowserMagic(field, spellcheck, autocorrect, autocapitalize) {
9763 field.setAttribute("autocorrect", autocorrect ? "" : "off");
9764 field.setAttribute("autocapitalize", autocapitalize ? "" : "off");
9765 field.setAttribute("spellcheck", !!spellcheck);
9766 }
9767
9768 function hiddenTextarea() {
9769 var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none");
9770 var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
9771 // The textarea is kept positioned near the cursor to prevent the
9772 // fact that it'll be scrolled into view on input from scrolling
9773 // our fake cursor out of view. On webkit, when wrap=off, paste is
9774 // very slow. So make the area wide instead.
9775 if (webkit) { te.style.width = "1000px"; }
9776 else { te.setAttribute("wrap", "off"); }
9777 // If border: 0; -- iOS fails to open keyboard (issue #1287)
9778 if (ios) { te.style.border = "1px solid black"; }
9779 disableBrowserMagic(te);
9780 return div
9781 }
9782
9783 // The publicly visible API. Note that methodOp(f) means
9784 // 'wrap f in an operation, performed on its `this` parameter'.
9785
9786 // This is not the complete set of editor methods. Most of the
9787 // methods defined on the Doc type are also injected into
9788 // CodeMirror.prototype, for backwards compatibility and
9789 // convenience.
9790
9791 function addEditorMethods(CodeMirror) {
9792 var optionHandlers = CodeMirror.optionHandlers;
9793
9794 var helpers = CodeMirror.helpers = {};
9795
9796 CodeMirror.prototype = {
9797 constructor: CodeMirror,
9798 focus: function(){window.focus(); this.display.input.focus();},
9799
9800 setOption: function(option, value) {
9801 var options = this.options, old = options[option];
9802 if (options[option] == value && option != "mode") { return }
9803 options[option] = value;
9804 if (optionHandlers.hasOwnProperty(option))
9805 { operation(this, optionHandlers[option])(this, value, old); }
9806 signal(this, "optionChange", this, option);
9807 },
9808
9809 getOption: function(option) {return this.options[option]},
9810 getDoc: function() {return this.doc},
9811
9812 addKeyMap: function(map$$1, bottom) {
9813 this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map$$1));
9814 },
9815 removeKeyMap: function(map$$1) {
9816 var maps = this.state.keyMaps;
9817 for (var i = 0; i < maps.length; ++i)
9818 { if (maps[i] == map$$1 || maps[i].name == map$$1) {
9819 maps.splice(i, 1);
9820 return true
9821 } }
9822 },
9823
9824 addOverlay: methodOp(function(spec, options) {
9825 var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
9826 if (mode.startState) { throw new Error("Overlays may not be stateful.") }
9827 insertSorted(this.state.overlays,
9828 {mode: mode, modeSpec: spec, opaque: options && options.opaque,
9829 priority: (options && options.priority) || 0},
9830 function (overlay) { return overlay.priority; });
9831 this.state.modeGen++;
9832 regChange(this);
9833 }),
9834 removeOverlay: methodOp(function(spec) {
9835 var this$1 = this;
9836
9837 var overlays = this.state.overlays;
9838 for (var i = 0; i < overlays.length; ++i) {
9839 var cur = overlays[i].modeSpec;
9840 if (cur == spec || typeof spec == "string" && cur.name == spec) {
9841 overlays.splice(i, 1);
9842 this$1.state.modeGen++;
9843 regChange(this$1);
9844 return
9845 }
9846 }
9847 }),
9848
9849 indentLine: methodOp(function(n, dir, aggressive) {
9850 if (typeof dir != "string" && typeof dir != "number") {
9851 if (dir == null) { dir = this.options.smartIndent ? "smart" : "prev"; }
9852 else { dir = dir ? "add" : "subtract"; }
9853 }
9854 if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive); }
9855 }),
9856 indentSelection: methodOp(function(how) {
9857 var this$1 = this;
9858
9859 var ranges = this.doc.sel.ranges, end = -1;
9860 for (var i = 0; i < ranges.length; i++) {
9861 var range$$1 = ranges[i];
9862 if (!range$$1.empty()) {
9863 var from = range$$1.from(), to = range$$1.to();
9864 var start = Math.max(end, from.line);
9865 end = Math.min(this$1.lastLine(), to.line - (to.ch ? 0 : 1)) + 1;
9866 for (var j = start; j < end; ++j)
9867 { indentLine(this$1, j, how); }
9868 var newRanges = this$1.doc.sel.ranges;
9869 if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0)
9870 { replaceOneSelection(this$1.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll); }
9871 } else if (range$$1.head.line > end) {
9872 indentLine(this$1, range$$1.head.line, how, true);
9873 end = range$$1.head.line;
9874 if (i == this$1.doc.sel.primIndex) { ensureCursorVisible(this$1); }
9875 }
9876 }
9877 }),
9878
9879 // Fetch the parser token for a given character. Useful for hacks
9880 // that want to inspect the mode state (say, for completion).
9881 getTokenAt: function(pos, precise) {
9882 return takeToken(this, pos, precise)
9883 },
9884
9885 getLineTokens: function(line, precise) {
9886 return takeToken(this, Pos(line), precise, true)
9887 },
9888
9889 getTokenTypeAt: function(pos) {
9890 pos = clipPos(this.doc, pos);
9891 var styles = getLineStyles(this, getLine(this.doc, pos.line));
9892 var before = 0, after = (styles.length - 1) / 2, ch = pos.ch;
9893 var type;
9894 if (ch == 0) { type = styles[2]; }
9895 else { for (;;) {
9896 var mid = (before + after) >> 1;
9897 if ((mid ? styles[mid * 2 - 1] : 0) >= ch) { after = mid; }
9898 else if (styles[mid * 2 + 1] < ch) { before = mid + 1; }
9899 else { type = styles[mid * 2 + 2]; break }
9900 } }
9901 var cut = type ? type.indexOf("overlay ") : -1;
9902 return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1)
9903 },
9904
9905 getModeAt: function(pos) {
9906 var mode = this.doc.mode;
9907 if (!mode.innerMode) { return mode }
9908 return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode
9909 },
9910
9911 getHelper: function(pos, type) {
9912 return this.getHelpers(pos, type)[0]
9913 },
9914
9915 getHelpers: function(pos, type) {
9916 var this$1 = this;
9917
9918 var found = [];
9919 if (!helpers.hasOwnProperty(type)) { return found }
9920 var help = helpers[type], mode = this.getModeAt(pos);
9921 if (typeof mode[type] == "string") {
9922 if (help[mode[type]]) { found.push(help[mode[type]]); }
9923 } else if (mode[type]) {
9924 for (var i = 0; i < mode[type].length; i++) {
9925 var val = help[mode[type][i]];
9926 if (val) { found.push(val); }
9927 }
9928 } else if (mode.helperType && help[mode.helperType]) {
9929 found.push(help[mode.helperType]);
9930 } else if (help[mode.name]) {
9931 found.push(help[mode.name]);
9932 }
9933 for (var i$1 = 0; i$1 < help._global.length; i$1++) {
9934 var cur = help._global[i$1];
9935 if (cur.pred(mode, this$1) && indexOf(found, cur.val) == -1)
9936 { found.push(cur.val); }
9937 }
9938 return found
9939 },
9940
9941 getStateAfter: function(line, precise) {
9942 var doc = this.doc;
9943 line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);
9944 return getContextBefore(this, line + 1, precise).state
9945 },
9946
9947 cursorCoords: function(start, mode) {
9948 var pos, range$$1 = this.doc.sel.primary();
9949 if (start == null) { pos = range$$1.head; }
9950 else if (typeof start == "object") { pos = clipPos(this.doc, start); }
9951 else { pos = start ? range$$1.from() : range$$1.to(); }
9952 return cursorCoords(this, pos, mode || "page")
9953 },
9954
9955 charCoords: function(pos, mode) {
9956 return charCoords(this, clipPos(this.doc, pos), mode || "page")
9957 },
9958
9959 coordsChar: function(coords, mode) {
9960 coords = fromCoordSystem(this, coords, mode || "page");
9961 return coordsChar(this, coords.left, coords.top)
9962 },
9963
9964 lineAtHeight: function(height, mode) {
9965 height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top;
9966 return lineAtHeight(this.doc, height + this.display.viewOffset)
9967 },
9968 heightAtLine: function(line, mode, includeWidgets) {
9969 var end = false, lineObj;
9970 if (typeof line == "number") {
9971 var last = this.doc.first + this.doc.size - 1;
9972 if (line < this.doc.first) { line = this.doc.first; }
9973 else if (line > last) { line = last; end = true; }
9974 lineObj = getLine(this.doc, line);
9975 } else {
9976 lineObj = line;
9977 }
9978 return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page", includeWidgets || end).top +
9979 (end ? this.doc.height - heightAtLine(lineObj) : 0)
9980 },
9981
9982 defaultTextHeight: function() { return textHeight(this.display) },
9983 defaultCharWidth: function() { return charWidth(this.display) },
9984
9985 getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}},
9986
9987 addWidget: function(pos, node, scroll, vert, horiz) {
9988 var display = this.display;
9989 pos = cursorCoords(this, clipPos(this.doc, pos));
9990 var top = pos.bottom, left = pos.left;
9991 node.style.position = "absolute";
9992 node.setAttribute("cm-ignore-events", "true");
9993 this.display.input.setUneditable(node);
9994 display.sizer.appendChild(node);
9995 if (vert == "over") {
9996 top = pos.top;
9997 } else if (vert == "above" || vert == "near") {
9998 var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
9999 hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
10000 // Default to positioning above (if specified and possible); otherwise default to positioning below
10001 if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
10002 { top = pos.top - node.offsetHeight; }
10003 else if (pos.bottom + node.offsetHeight <= vspace)
10004 { top = pos.bottom; }
10005 if (left + node.offsetWidth > hspace)
10006 { left = hspace - node.offsetWidth; }
10007 }
10008 node.style.top = top + "px";
10009 node.style.left = node.style.right = "";
10010 if (horiz == "right") {
10011 left = display.sizer.clientWidth - node.offsetWidth;
10012 node.style.right = "0px";
10013 } else {
10014 if (horiz == "left") { left = 0; }
10015 else if (horiz == "middle") { left = (display.sizer.clientWidth - node.offsetWidth) / 2; }
10016 node.style.left = left + "px";
10017 }
10018 if (scroll)
10019 { scrollIntoView(this, {left: left, top: top, right: left + node.offsetWidth, bottom: top + node.offsetHeight}); }
10020 },
10021
10022 triggerOnKeyDown: methodOp(onKeyDown),
10023 triggerOnKeyPress: methodOp(onKeyPress),
10024 triggerOnKeyUp: onKeyUp,
10025 triggerOnMouseDown: methodOp(onMouseDown),
10026
10027 execCommand: function(cmd) {
10028 if (commands.hasOwnProperty(cmd))
10029 { return commands[cmd].call(null, this) }
10030 },
10031
10032 triggerElectric: methodOp(function(text) { triggerElectric(this, text); }),
10033
10034 findPosH: function(from, amount, unit, visually) {
10035 var this$1 = this;
10036
10037 var dir = 1;
10038 if (amount < 0) { dir = -1; amount = -amount; }
10039 var cur = clipPos(this.doc, from);
10040 for (var i = 0; i < amount; ++i) {
10041 cur = findPosH(this$1.doc, cur, dir, unit, visually);
10042 if (cur.hitSide) { break }
10043 }
10044 return cur
10045 },
10046
10047 moveH: methodOp(function(dir, unit) {
10048 var this$1 = this;
10049
10050 this.extendSelectionsBy(function (range$$1) {
10051 if (this$1.display.shift || this$1.doc.extend || range$$1.empty())
10052 { return findPosH(this$1.doc, range$$1.head, dir, unit, this$1.options.rtlMoveVisually) }
10053 else
10054 { return dir < 0 ? range$$1.from() : range$$1.to() }
10055 }, sel_move);
10056 }),
10057
10058 deleteH: methodOp(function(dir, unit) {
10059 var sel = this.doc.sel, doc = this.doc;
10060 if (sel.somethingSelected())
10061 { doc.replaceSelection("", null, "+delete"); }
10062 else
10063 { deleteNearSelection(this, function (range$$1) {
10064 var other = findPosH(doc, range$$1.head, dir, unit, false);
10065 return dir < 0 ? {from: other, to: range$$1.head} : {from: range$$1.head, to: other}
10066 }); }
10067 }),
10068
10069 findPosV: function(from, amount, unit, goalColumn) {
10070 var this$1 = this;
10071
10072 var dir = 1, x = goalColumn;
10073 if (amount < 0) { dir = -1; amount = -amount; }
10074 var cur = clipPos(this.doc, from);
10075 for (var i = 0; i < amount; ++i) {
10076 var coords = cursorCoords(this$1, cur, "div");
10077 if (x == null) { x = coords.left; }
10078 else { coords.left = x; }
10079 cur = findPosV(this$1, coords, dir, unit);
10080 if (cur.hitSide) { break }
10081 }
10082 return cur
10083 },
10084
10085 moveV: methodOp(function(dir, unit) {
10086 var this$1 = this;
10087
10088 var doc = this.doc, goals = [];
10089 var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected();
10090 doc.extendSelectionsBy(function (range$$1) {
10091 if (collapse)
10092 { return dir < 0 ? range$$1.from() : range$$1.to() }
10093 var headPos = cursorCoords(this$1, range$$1.head, "div");
10094 if (range$$1.goalColumn != null) { headPos.left = range$$1.goalColumn; }
10095 goals.push(headPos.left);
10096 var pos = findPosV(this$1, headPos, dir, unit);
10097 if (unit == "page" && range$$1 == doc.sel.primary())
10098 { addToScrollTop(this$1, charCoords(this$1, pos, "div").top - headPos.top); }
10099 return pos
10100 }, sel_move);
10101 if (goals.length) { for (var i = 0; i < doc.sel.ranges.length; i++)
10102 { doc.sel.ranges[i].goalColumn = goals[i]; } }
10103 }),
10104
10105 // Find the word at the given position (as returned by coordsChar).
10106 findWordAt: function(pos) {
10107 var doc = this.doc, line = getLine(doc, pos.line).text;
10108 var start = pos.ch, end = pos.ch;
10109 if (line) {
10110 var helper = this.getHelper(pos, "wordChars");
10111 if ((pos.sticky == "before" || end == line.length) && start) { --start; } else { ++end; }
10112 var startChar = line.charAt(start);
10113 var check = isWordChar(startChar, helper)
10114 ? function (ch) { return isWordChar(ch, helper); }
10115 : /\s/.test(startChar) ? function (ch) { return /\s/.test(ch); }
10116 : function (ch) { return (!/\s/.test(ch) && !isWordChar(ch)); };
10117 while (start > 0 && check(line.charAt(start - 1))) { --start; }
10118 while (end < line.length && check(line.charAt(end))) { ++end; }
10119 }
10120 return new Range(Pos(pos.line, start), Pos(pos.line, end))
10121 },
10122
10123 toggleOverwrite: function(value) {
10124 if (value != null && value == this.state.overwrite) { return }
10125 if (this.state.overwrite = !this.state.overwrite)
10126 { addClass(this.display.cursorDiv, "CodeMirror-overwrite"); }
10127 else
10128 { rmClass(this.display.cursorDiv, "CodeMirror-overwrite"); }
10129
10130 signal(this, "overwriteToggle", this, this.state.overwrite);
10131 },
10132 hasFocus: function() { return this.display.input.getField() == activeElt() },
10133 isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) },
10134
10135 scrollTo: methodOp(function (x, y) { scrollToCoords(this, x, y); }),
10136 getScrollInfo: function() {
10137 var scroller = this.display.scroller;
10138 return {left: scroller.scrollLeft, top: scroller.scrollTop,
10139 height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight,
10140 width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth,
10141 clientHeight: displayHeight(this), clientWidth: displayWidth(this)}
10142 },
10143
10144 scrollIntoView: methodOp(function(range$$1, margin) {
10145 if (range$$1 == null) {
10146 range$$1 = {from: this.doc.sel.primary().head, to: null};
10147 if (margin == null) { margin = this.options.cursorScrollMargin; }
10148 } else if (typeof range$$1 == "number") {
10149 range$$1 = {from: Pos(range$$1, 0), to: null};
10150 } else if (range$$1.from == null) {
10151 range$$1 = {from: range$$1, to: null};
10152 }
10153 if (!range$$1.to) { range$$1.to = range$$1.from; }
10154 range$$1.margin = margin || 0;
10155
10156 if (range$$1.from.line != null) {
10157 scrollToRange(this, range$$1);
10158 } else {
10159 scrollToCoordsRange(this, range$$1.from, range$$1.to, range$$1.margin);
10160 }
10161 }),
10162
10163 setSize: methodOp(function(width, height) {
10164 var this$1 = this;
10165
10166 var interpret = function (val) { return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; };
10167 if (width != null) { this.display.wrapper.style.width = interpret(width); }
10168 if (height != null) { this.display.wrapper.style.height = interpret(height); }
10169 if (this.options.lineWrapping) { clearLineMeasurementCache(this); }
10170 var lineNo$$1 = this.display.viewFrom;
10171 this.doc.iter(lineNo$$1, this.display.viewTo, function (line) {
10172 if (line.widgets) { for (var i = 0; i < line.widgets.length; i++)
10173 { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo$$1, "widget"); break } } }
10174 ++lineNo$$1;
10175 });
10176 this.curOp.forceUpdate = true;
10177 signal(this, "refresh", this);
10178 }),
10179
10180 operation: function(f){return runInOp(this, f)},
10181 startOperation: function(){return startOperation(this)},
10182 endOperation: function(){return endOperation(this)},
10183
10184 refresh: methodOp(function() {
10185 var oldHeight = this.display.cachedTextHeight;
10186 regChange(this);
10187 this.curOp.forceUpdate = true;
10188 clearCaches(this);
10189 scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop);
10190 updateGutterSpace(this.display);
10191 if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5)
10192 { estimateLineHeights(this); }
10193 signal(this, "refresh", this);
10194 }),
10195
10196 swapDoc: methodOp(function(doc) {
10197 var old = this.doc;
10198 old.cm = null;
10199 // Cancel the current text selection if any (#5821)
10200 if (this.state.selectingText) { this.state.selectingText(); }
10201 attachDoc(this, doc);
10202 clearCaches(this);
10203 this.display.input.reset();
10204 scrollToCoords(this, doc.scrollLeft, doc.scrollTop);
10205 this.curOp.forceScroll = true;
10206 signalLater(this, "swapDoc", this, old);
10207 return old
10208 }),
10209
10210 phrase: function(phraseText) {
10211 var phrases = this.options.phrases;
10212 return phrases && Object.prototype.hasOwnProperty.call(phrases, phraseText) ? phrases[phraseText] : phraseText
10213 },
10214
10215 getInputField: function(){return this.display.input.getField()},
10216 getWrapperElement: function(){return this.display.wrapper},
10217 getScrollerElement: function(){return this.display.scroller},
10218 getGutterElement: function(){return this.display.gutters}
10219 };
10220 eventMixin(CodeMirror);
10221
10222 CodeMirror.registerHelper = function(type, name, value) {
10223 if (!helpers.hasOwnProperty(type)) { helpers[type] = CodeMirror[type] = {_global: []}; }
10224 helpers[type][name] = value;
10225 };
10226 CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {
10227 CodeMirror.registerHelper(type, name, value);
10228 helpers[type]._global.push({pred: predicate, val: value});
10229 };
10230 }
10231
10232 // Used for horizontal relative motion. Dir is -1 or 1 (left or
10233 // right), unit can be "char", "column" (like char, but doesn't
10234 // cross line boundaries), "word" (across next word), or "group" (to
10235 // the start of next group of word or non-word-non-whitespace
10236 // chars). The visually param controls whether, in right-to-left
10237 // text, direction 1 means to move towards the next index in the
10238 // string, or towards the character to the right of the current
10239 // position. The resulting position will have a hitSide=true
10240 // property if it reached the end of the document.
10241 function findPosH(doc, pos, dir, unit, visually) {
10242 var oldPos = pos;
10243 var origDir = dir;
10244 var lineObj = getLine(doc, pos.line);
10245 function findNextLine() {
10246 var l = pos.line + dir;
10247 if (l < doc.first || l >= doc.first + doc.size) { return false }
10248 pos = new Pos(l, pos.ch, pos.sticky);
10249 return lineObj = getLine(doc, l)
10250 }
10251 function moveOnce(boundToLine) {
10252 var next;
10253 if (visually) {
10254 next = moveVisually(doc.cm, lineObj, pos, dir);
10255 } else {
10256 next = moveLogically(lineObj, pos, dir);
10257 }
10258 if (next == null) {
10259 if (!boundToLine && findNextLine())
10260 { pos = endOfLine(visually, doc.cm, lineObj, pos.line, dir); }
10261 else
10262 { return false }
10263 } else {
10264 pos = next;
10265 }
10266 return true
10267 }
10268
10269 if (unit == "char") {
10270 moveOnce();
10271 } else if (unit == "column") {
10272 moveOnce(true);
10273 } else if (unit == "word" || unit == "group") {
10274 var sawType = null, group = unit == "group";
10275 var helper = doc.cm && doc.cm.getHelper(pos, "wordChars");
10276 for (var first = true;; first = false) {
10277 if (dir < 0 && !moveOnce(!first)) { break }
10278 var cur = lineObj.text.charAt(pos.ch) || "\n";
10279 var type = isWordChar(cur, helper) ? "w"
10280 : group && cur == "\n" ? "n"
10281 : !group || /\s/.test(cur) ? null
10282 : "p";
10283 if (group && !first && !type) { type = "s"; }
10284 if (sawType && sawType != type) {
10285 if (dir < 0) {dir = 1; moveOnce(); pos.sticky = "after";}
10286 break
10287 }
10288
10289 if (type) { sawType = type; }
10290 if (dir > 0 && !moveOnce(!first)) { break }
10291 }
10292 }
10293 var result = skipAtomic(doc, pos, oldPos, origDir, true);
10294 if (equalCursorPos(oldPos, result)) { result.hitSide = true; }
10295 return result
10296 }
10297
10298 // For relative vertical movement. Dir may be -1 or 1. Unit can be
10299 // "page" or "line". The resulting position will have a hitSide=true
10300 // property if it reached the end of the document.
10301 function findPosV(cm, pos, dir, unit) {
10302 var doc = cm.doc, x = pos.left, y;
10303 if (unit == "page") {
10304 var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
10305 var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3);
10306 y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount;
10307
10308 } else if (unit == "line") {
10309 y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
10310 }
10311 var target;
10312 for (;;) {
10313 target = coordsChar(cm, x, y);
10314 if (!target.outside) { break }
10315 if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break }
10316 y += dir * 5;
10317 }
10318 return target
10319 }
10320
10321 // CONTENTEDITABLE INPUT STYLE
10322
10323 var ContentEditableInput = function(cm) {
10324 this.cm = cm;
10325 this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null;
10326 this.polling = new Delayed();
10327 this.composing = null;
10328 this.gracePeriod = false;
10329 this.readDOMTimeout = null;
10330 };
10331
10332 ContentEditableInput.prototype.init = function (display) {
10333 var this$1 = this;
10334
10335 var input = this, cm = input.cm;
10336 var div = input.div = display.lineDiv;
10337 disableBrowserMagic(div, cm.options.spellcheck, cm.options.autocorrect, cm.options.autocapitalize);
10338
10339 on(div, "paste", function (e) {
10340 if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
10341 // IE doesn't fire input events, so we schedule a read for the pasted content in this way
10342 if (ie_version <= 11) { setTimeout(operation(cm, function () { return this$1.updateFromDOM(); }), 20); }
10343 });
10344
10345 on(div, "compositionstart", function (e) {
10346 this$1.composing = {data: e.data, done: false};
10347 });
10348 on(div, "compositionupdate", function (e) {
10349 if (!this$1.composing) { this$1.composing = {data: e.data, done: false}; }
10350 });
10351 on(div, "compositionend", function (e) {
10352 if (this$1.composing) {
10353 if (e.data != this$1.composing.data) { this$1.readFromDOMSoon(); }
10354 this$1.composing.done = true;
10355 }
10356 });
10357
10358 on(div, "touchstart", function () { return input.forceCompositionEnd(); });
10359
10360 on(div, "input", function () {
10361 if (!this$1.composing) { this$1.readFromDOMSoon(); }
10362 });
10363
10364 function onCopyCut(e) {
10365 if (signalDOMEvent(cm, e)) { return }
10366 if (cm.somethingSelected()) {
10367 setLastCopied({lineWise: false, text: cm.getSelections()});
10368 if (e.type == "cut") { cm.replaceSelection("", null, "cut"); }
10369 } else if (!cm.options.lineWiseCopyCut) {
10370 return
10371 } else {
10372 var ranges = copyableRanges(cm);
10373 setLastCopied({lineWise: true, text: ranges.text});
10374 if (e.type == "cut") {
10375 cm.operation(function () {
10376 cm.setSelections(ranges.ranges, 0, sel_dontScroll);
10377 cm.replaceSelection("", null, "cut");
10378 });
10379 }
10380 }
10381 if (e.clipboardData) {
10382 e.clipboardData.clearData();
10383 var content = lastCopied.text.join("\n");
10384 // iOS exposes the clipboard API, but seems to discard content inserted into it
10385 e.clipboardData.setData("Text", content);
10386 if (e.clipboardData.getData("Text") == content) {
10387 e.preventDefault();
10388 return
10389 }
10390 }
10391 // Old-fashioned briefly-focus-a-textarea hack
10392 var kludge = hiddenTextarea(), te = kludge.firstChild;
10393 cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild);
10394 te.value = lastCopied.text.join("\n");
10395 var hadFocus = document.activeElement;
10396 selectInput(te);
10397 setTimeout(function () {
10398 cm.display.lineSpace.removeChild(kludge);
10399 hadFocus.focus();
10400 if (hadFocus == div) { input.showPrimarySelection(); }
10401 }, 50);
10402 }
10403 on(div, "copy", onCopyCut);
10404 on(div, "cut", onCopyCut);
10405 };
10406
10407 ContentEditableInput.prototype.prepareSelection = function () {
10408 var result = prepareSelection(this.cm, false);
10409 result.focus = this.cm.state.focused;
10410 return result
10411 };
10412
10413 ContentEditableInput.prototype.showSelection = function (info, takeFocus) {
10414 if (!info || !this.cm.display.view.length) { return }
10415 if (info.focus || takeFocus) { this.showPrimarySelection(); }
10416 this.showMultipleSelections(info);
10417 };
10418
10419 ContentEditableInput.prototype.getSelection = function () {
10420 return this.cm.display.wrapper.ownerDocument.getSelection()
10421 };
10422
10423 ContentEditableInput.prototype.showPrimarySelection = function () {
10424 var sel = this.getSelection(), cm = this.cm, prim = cm.doc.sel.primary();
10425 var from = prim.from(), to = prim.to();
10426
10427 if (cm.display.viewTo == cm.display.viewFrom || from.line >= cm.display.viewTo || to.line < cm.display.viewFrom) {
10428 sel.removeAllRanges();
10429 return
10430 }
10431
10432 var curAnchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
10433 var curFocus = domToPos(cm, sel.focusNode, sel.focusOffset);
10434 if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad &&
10435 cmp(minPos(curAnchor, curFocus), from) == 0 &&
10436 cmp(maxPos(curAnchor, curFocus), to) == 0)
10437 { return }
10438
10439 var view = cm.display.view;
10440 var start = (from.line >= cm.display.viewFrom && posToDOM(cm, from)) ||
10441 {node: view[0].measure.map[2], offset: 0};
10442 var end = to.line < cm.display.viewTo && posToDOM(cm, to);
10443 if (!end) {
10444 var measure = view[view.length - 1].measure;
10445 var map$$1 = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map;
10446 end = {node: map$$1[map$$1.length - 1], offset: map$$1[map$$1.length - 2] - map$$1[map$$1.length - 3]};
10447 }
10448
10449 if (!start || !end) {
10450 sel.removeAllRanges();
10451 return
10452 }
10453
10454 var old = sel.rangeCount && sel.getRangeAt(0), rng;
10455 try { rng = range(start.node, start.offset, end.offset, end.node); }
10456 catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible
10457 if (rng) {
10458 if (!gecko && cm.state.focused) {
10459 sel.collapse(start.node, start.offset);
10460 if (!rng.collapsed) {
10461 sel.removeAllRanges();
10462 sel.addRange(rng);
10463 }
10464 } else {
10465 sel.removeAllRanges();
10466 sel.addRange(rng);
10467 }
10468 if (old && sel.anchorNode == null) { sel.addRange(old); }
10469 else if (gecko) { this.startGracePeriod(); }
10470 }
10471 this.rememberSelection();
10472 };
10473
10474 ContentEditableInput.prototype.startGracePeriod = function () {
10475 var this$1 = this;
10476
10477 clearTimeout(this.gracePeriod);
10478 this.gracePeriod = setTimeout(function () {
10479 this$1.gracePeriod = false;
10480 if (this$1.selectionChanged())
10481 { this$1.cm.operation(function () { return this$1.cm.curOp.selectionChanged = true; }); }
10482 }, 20);
10483 };
10484
10485 ContentEditableInput.prototype.showMultipleSelections = function (info) {
10486 removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors);
10487 removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection);
10488 };
10489
10490 ContentEditableInput.prototype.rememberSelection = function () {
10491 var sel = this.getSelection();
10492 this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset;
10493 this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset;
10494 };
10495
10496 ContentEditableInput.prototype.selectionInEditor = function () {
10497 var sel = this.getSelection();
10498 if (!sel.rangeCount) { return false }
10499 var node = sel.getRangeAt(0).commonAncestorContainer;
10500 return contains(this.div, node)
10501 };
10502
10503 ContentEditableInput.prototype.focus = function () {
10504 if (this.cm.options.readOnly != "nocursor") {
10505 if (!this.selectionInEditor())
10506 { this.showSelection(this.prepareSelection(), true); }
10507 this.div.focus();
10508 }
10509 };
10510 ContentEditableInput.prototype.blur = function () { this.div.blur(); };
10511 ContentEditableInput.prototype.getField = function () { return this.div };
10512
10513 ContentEditableInput.prototype.supportsTouch = function () { return true };
10514
10515 ContentEditableInput.prototype.receivedFocus = function () {
10516 var input = this;
10517 if (this.selectionInEditor())
10518 { this.pollSelection(); }
10519 else
10520 { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }); }
10521
10522 function poll() {
10523 if (input.cm.state.focused) {
10524 input.pollSelection();
10525 input.polling.set(input.cm.options.pollInterval, poll);
10526 }
10527 }
10528 this.polling.set(this.cm.options.pollInterval, poll);
10529 };
10530
10531 ContentEditableInput.prototype.selectionChanged = function () {
10532 var sel = this.getSelection();
10533 return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||
10534 sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset
10535 };
10536
10537 ContentEditableInput.prototype.pollSelection = function () {
10538 if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) { return }
10539 var sel = this.getSelection(), cm = this.cm;
10540 // On Android Chrome (version 56, at least), backspacing into an
10541 // uneditable block element will put the cursor in that element,
10542 // and then, because it's not editable, hide the virtual keyboard.
10543 // Because Android doesn't allow us to actually detect backspace
10544 // presses in a sane way, this code checks for when that happens
10545 // and simulates a backspace press in this case.
10546 if (android && chrome && this.cm.display.gutterSpecs.length && isInGutter(sel.anchorNode)) {
10547 this.cm.triggerOnKeyDown({type: "keydown", keyCode: 8, preventDefault: Math.abs});
10548 this.blur();
10549 this.focus();
10550 return
10551 }
10552 if (this.composing) { return }
10553 this.rememberSelection();
10554 var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
10555 var head = domToPos(cm, sel.focusNode, sel.focusOffset);
10556 if (anchor && head) { runInOp(cm, function () {
10557 setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll);
10558 if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true; }
10559 }); }
10560 };
10561
10562 ContentEditableInput.prototype.pollContent = function () {
10563 if (this.readDOMTimeout != null) {
10564 clearTimeout(this.readDOMTimeout);
10565 this.readDOMTimeout = null;
10566 }
10567
10568 var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary();
10569 var from = sel.from(), to = sel.to();
10570 if (from.ch == 0 && from.line > cm.firstLine())
10571 { from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length); }
10572 if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine())
10573 { to = Pos(to.line + 1, 0); }
10574 if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return false }
10575
10576 var fromIndex, fromLine, fromNode;
10577 if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) {
10578 fromLine = lineNo(display.view[0].line);
10579 fromNode = display.view[0].node;
10580 } else {
10581 fromLine = lineNo(display.view[fromIndex].line);
10582 fromNode = display.view[fromIndex - 1].node.nextSibling;
10583 }
10584 var toIndex = findViewIndex(cm, to.line);
10585 var toLine, toNode;
10586 if (toIndex == display.view.length - 1) {
10587 toLine = display.viewTo - 1;
10588 toNode = display.lineDiv.lastChild;
10589 } else {
10590 toLine = lineNo(display.view[toIndex + 1].line) - 1;
10591 toNode = display.view[toIndex + 1].node.previousSibling;
10592 }
10593
10594 if (!fromNode) { return false }
10595 var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine));
10596 var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length));
10597 while (newText.length > 1 && oldText.length > 1) {
10598 if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; }
10599 else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; }
10600 else { break }
10601 }
10602
10603 var cutFront = 0, cutEnd = 0;
10604 var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length);
10605 while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront))
10606 { ++cutFront; }
10607 var newBot = lst(newText), oldBot = lst(oldText);
10608 var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0),
10609 oldBot.length - (oldText.length == 1 ? cutFront : 0));
10610 while (cutEnd < maxCutEnd &&
10611 newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1))
10612 { ++cutEnd; }
10613 // Try to move start of change to start of selection if ambiguous
10614 if (newText.length == 1 && oldText.length == 1 && fromLine == from.line) {
10615 while (cutFront && cutFront > from.ch &&
10616 newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) {
10617 cutFront--;
10618 cutEnd++;
10619 }
10620 }
10621
10622 newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, "");
10623 newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, "");
10624
10625 var chFrom = Pos(fromLine, cutFront);
10626 var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0);
10627 if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {
10628 replaceRange(cm.doc, newText, chFrom, chTo, "+input");
10629 return true
10630 }
10631 };
10632
10633 ContentEditableInput.prototype.ensurePolled = function () {
10634 this.forceCompositionEnd();
10635 };
10636 ContentEditableInput.prototype.reset = function () {
10637 this.forceCompositionEnd();
10638 };
10639 ContentEditableInput.prototype.forceCompositionEnd = function () {
10640 if (!this.composing) { return }
10641 clearTimeout(this.readDOMTimeout);
10642 this.composing = null;
10643 this.updateFromDOM();
10644 this.div.blur();
10645 this.div.focus();
10646 };
10647 ContentEditableInput.prototype.readFromDOMSoon = function () {
10648 var this$1 = this;
10649
10650 if (this.readDOMTimeout != null) { return }
10651 this.readDOMTimeout = setTimeout(function () {
10652 this$1.readDOMTimeout = null;
10653 if (this$1.composing) {
10654 if (this$1.composing.done) { this$1.composing = null; }
10655 else { return }
10656 }
10657 this$1.updateFromDOM();
10658 }, 80);
10659 };
10660
10661 ContentEditableInput.prototype.updateFromDOM = function () {
10662 var this$1 = this;
10663
10664 if (this.cm.isReadOnly() || !this.pollContent())
10665 { runInOp(this.cm, function () { return regChange(this$1.cm); }); }
10666 };
10667
10668 ContentEditableInput.prototype.setUneditable = function (node) {
10669 node.contentEditable = "false";
10670 };
10671
10672 ContentEditableInput.prototype.onKeyPress = function (e) {
10673 if (e.charCode == 0 || this.composing) { return }
10674 e.preventDefault();
10675 if (!this.cm.isReadOnly())
10676 { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0); }
10677 };
10678
10679 ContentEditableInput.prototype.readOnlyChanged = function (val) {
10680 this.div.contentEditable = String(val != "nocursor");
10681 };
10682
10683 ContentEditableInput.prototype.onContextMenu = function () {};
10684 ContentEditableInput.prototype.resetPosition = function () {};
10685
10686 ContentEditableInput.prototype.needsContentAttribute = true;
10687
10688 function posToDOM(cm, pos) {
10689 var view = findViewForLine(cm, pos.line);
10690 if (!view || view.hidden) { return null }
10691 var line = getLine(cm.doc, pos.line);
10692 var info = mapFromLineView(view, line, pos.line);
10693
10694 var order = getOrder(line, cm.doc.direction), side = "left";
10695 if (order) {
10696 var partPos = getBidiPartAt(order, pos.ch);
10697 side = partPos % 2 ? "right" : "left";
10698 }
10699 var result = nodeAndOffsetInLineMap(info.map, pos.ch, side);
10700 result.offset = result.collapse == "right" ? result.end : result.start;
10701 return result
10702 }
10703
10704 function isInGutter(node) {
10705 for (var scan = node; scan; scan = scan.parentNode)
10706 { if (/CodeMirror-gutter-wrapper/.test(scan.className)) { return true } }
10707 return false
10708 }
10709
10710 function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos }
10711
10712 function domTextBetween(cm, from, to, fromLine, toLine) {
10713 var text = "", closing = false, lineSep = cm.doc.lineSeparator(), extraLinebreak = false;
10714 function recognizeMarker(id) { return function (marker) { return marker.id == id; } }
10715 function close() {
10716 if (closing) {
10717 text += lineSep;
10718 if (extraLinebreak) { text += lineSep; }
10719 closing = extraLinebreak = false;
10720 }
10721 }
10722 function addText(str) {
10723 if (str) {
10724 close();
10725 text += str;
10726 }
10727 }
10728 function walk(node) {
10729 if (node.nodeType == 1) {
10730 var cmText = node.getAttribute("cm-text");
10731 if (cmText) {
10732 addText(cmText);
10733 return
10734 }
10735 var markerID = node.getAttribute("cm-marker"), range$$1;
10736 if (markerID) {
10737 var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID));
10738 if (found.length && (range$$1 = found[0].find(0)))
10739 { addText(getBetween(cm.doc, range$$1.from, range$$1.to).join(lineSep)); }
10740 return
10741 }
10742 if (node.getAttribute("contenteditable") == "false") { return }
10743 var isBlock = /^(pre|div|p|li|table|br)$/i.test(node.nodeName);
10744 if (!/^br$/i.test(node.nodeName) && node.textContent.length == 0) { return }
10745
10746 if (isBlock) { close(); }
10747 for (var i = 0; i < node.childNodes.length; i++)
10748 { walk(node.childNodes[i]); }
10749
10750 if (/^(pre|p)$/i.test(node.nodeName)) { extraLinebreak = true; }
10751 if (isBlock) { closing = true; }
10752 } else if (node.nodeType == 3) {
10753 addText(node.nodeValue.replace(/\u200b/g, "").replace(/\u00a0/g, " "));
10754 }
10755 }
10756 for (;;) {
10757 walk(from);
10758 if (from == to) { break }
10759 from = from.nextSibling;
10760 extraLinebreak = false;
10761 }
10762 return text
10763 }
10764
10765 function domToPos(cm, node, offset) {
10766 var lineNode;
10767 if (node == cm.display.lineDiv) {
10768 lineNode = cm.display.lineDiv.childNodes[offset];
10769 if (!lineNode) { return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) }
10770 node = null; offset = 0;
10771 } else {
10772 for (lineNode = node;; lineNode = lineNode.parentNode) {
10773 if (!lineNode || lineNode == cm.display.lineDiv) { return null }
10774 if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) { break }
10775 }
10776 }
10777 for (var i = 0; i < cm.display.view.length; i++) {
10778 var lineView = cm.display.view[i];
10779 if (lineView.node == lineNode)
10780 { return locateNodeInLineView(lineView, node, offset) }
10781 }
10782 }
10783
10784 function locateNodeInLineView(lineView, node, offset) {
10785 var wrapper = lineView.text.firstChild, bad = false;
10786 if (!node || !contains(wrapper, node)) { return badPos(Pos(lineNo(lineView.line), 0), true) }
10787 if (node == wrapper) {
10788 bad = true;
10789 node = wrapper.childNodes[offset];
10790 offset = 0;
10791 if (!node) {
10792 var line = lineView.rest ? lst(lineView.rest) : lineView.line;
10793 return badPos(Pos(lineNo(line), line.text.length), bad)
10794 }
10795 }
10796
10797 var textNode = node.nodeType == 3 ? node : null, topNode = node;
10798 if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {
10799 textNode = node.firstChild;
10800 if (offset) { offset = textNode.nodeValue.length; }
10801 }
10802 while (topNode.parentNode != wrapper) { topNode = topNode.parentNode; }
10803 var measure = lineView.measure, maps = measure.maps;
10804
10805 function find(textNode, topNode, offset) {
10806 for (var i = -1; i < (maps ? maps.length : 0); i++) {
10807 var map$$1 = i < 0 ? measure.map : maps[i];
10808 for (var j = 0; j < map$$1.length; j += 3) {
10809 var curNode = map$$1[j + 2];
10810 if (curNode == textNode || curNode == topNode) {
10811 var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]);
10812 var ch = map$$1[j] + offset;
10813 if (offset < 0 || curNode != textNode) { ch = map$$1[j + (offset ? 1 : 0)]; }
10814 return Pos(line, ch)
10815 }
10816 }
10817 }
10818 }
10819 var found = find(textNode, topNode, offset);
10820 if (found) { return badPos(found, bad) }
10821
10822 // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems
10823 for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) {
10824 found = find(after, after.firstChild, 0);
10825 if (found)
10826 { return badPos(Pos(found.line, found.ch - dist), bad) }
10827 else
10828 { dist += after.textContent.length; }
10829 }
10830 for (var before = topNode.previousSibling, dist$1 = offset; before; before = before.previousSibling) {
10831 found = find(before, before.firstChild, -1);
10832 if (found)
10833 { return badPos(Pos(found.line, found.ch + dist$1), bad) }
10834 else
10835 { dist$1 += before.textContent.length; }
10836 }
10837 }
10838
10839 // TEXTAREA INPUT STYLE
10840
10841 var TextareaInput = function(cm) {
10842 this.cm = cm;
10843 // See input.poll and input.reset
10844 this.prevInput = "";
10845
10846 // Flag that indicates whether we expect input to appear real soon
10847 // now (after some event like 'keypress' or 'input') and are
10848 // polling intensively.
10849 this.pollingFast = false;
10850 // Self-resetting timeout for the poller
10851 this.polling = new Delayed();
10852 // Used to work around IE issue with selection being forgotten when focus moves away from textarea
10853 this.hasSelection = false;
10854 this.composing = null;
10855 };
10856
10857 TextareaInput.prototype.init = function (display) {
10858 var this$1 = this;
10859
10860 var input = this, cm = this.cm;
10861 this.createField(display);
10862 var te = this.textarea;
10863
10864 display.wrapper.insertBefore(this.wrapper, display.wrapper.firstChild);
10865
10866 // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)
10867 if (ios) { te.style.width = "0px"; }
10868
10869 on(te, "input", function () {
10870 if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = null; }
10871 input.poll();
10872 });
10873
10874 on(te, "paste", function (e) {
10875 if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
10876
10877 cm.state.pasteIncoming = +new Date;
10878 input.fastPoll();
10879 });
10880
10881 function prepareCopyCut(e) {
10882 if (signalDOMEvent(cm, e)) { return }
10883 if (cm.somethingSelected()) {
10884 setLastCopied({lineWise: false, text: cm.getSelections()});
10885 } else if (!cm.options.lineWiseCopyCut) {
10886 return
10887 } else {
10888 var ranges = copyableRanges(cm);
10889 setLastCopied({lineWise: true, text: ranges.text});
10890 if (e.type == "cut") {
10891 cm.setSelections(ranges.ranges, null, sel_dontScroll);
10892 } else {
10893 input.prevInput = "";
10894 te.value = ranges.text.join("\n");
10895 selectInput(te);
10896 }
10897 }
10898 if (e.type == "cut") { cm.state.cutIncoming = +new Date; }
10899 }
10900 on(te, "cut", prepareCopyCut);
10901 on(te, "copy", prepareCopyCut);
10902
10903 on(display.scroller, "paste", function (e) {
10904 if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return }
10905 if (!te.dispatchEvent) {
10906 cm.state.pasteIncoming = +new Date;
10907 input.focus();
10908 return
10909 }
10910
10911 // Pass the `paste` event to the textarea so it's handled by its event listener.
10912 var event = new Event("paste");
10913 event.clipboardData = e.clipboardData;
10914 te.dispatchEvent(event);
10915 });
10916
10917 // Prevent normal selection in the editor (we handle our own)
10918 on(display.lineSpace, "selectstart", function (e) {
10919 if (!eventInWidget(display, e)) { e_preventDefault(e); }
10920 });
10921
10922 on(te, "compositionstart", function () {
10923 var start = cm.getCursor("from");
10924 if (input.composing) { input.composing.range.clear(); }
10925 input.composing = {
10926 start: start,
10927 range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"})
10928 };
10929 });
10930 on(te, "compositionend", function () {
10931 if (input.composing) {
10932 input.poll();
10933 input.composing.range.clear();
10934 input.composing = null;
10935 }
10936 });
10937 };
10938
10939 TextareaInput.prototype.createField = function (_display) {
10940 // Wraps and hides input textarea
10941 this.wrapper = hiddenTextarea();
10942 // The semihidden textarea that is focused when the editor is
10943 // focused, and receives input.
10944 this.textarea = this.wrapper.firstChild;
10945 };
10946
10947 TextareaInput.prototype.prepareSelection = function () {
10948 // Redraw the selection and/or cursor
10949 var cm = this.cm, display = cm.display, doc = cm.doc;
10950 var result = prepareSelection(cm);
10951
10952 // Move the hidden textarea near the cursor to prevent scrolling artifacts
10953 if (cm.options.moveInputWithCursor) {
10954 var headPos = cursorCoords(cm, doc.sel.primary().head, "div");
10955 var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();
10956 result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
10957 headPos.top + lineOff.top - wrapOff.top));
10958 result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
10959 headPos.left + lineOff.left - wrapOff.left));
10960 }
10961
10962 return result
10963 };
10964
10965 TextareaInput.prototype.showSelection = function (drawn) {
10966 var cm = this.cm, display = cm.display;
10967 removeChildrenAndAdd(display.cursorDiv, drawn.cursors);
10968 removeChildrenAndAdd(display.selectionDiv, drawn.selection);
10969 if (drawn.teTop != null) {
10970 this.wrapper.style.top = drawn.teTop + "px";
10971 this.wrapper.style.left = drawn.teLeft + "px";
10972 }
10973 };
10974
10975 // Reset the input to correspond to the selection (or to be empty,
10976 // when not typing and nothing is selected)
10977 TextareaInput.prototype.reset = function (typing) {
10978 if (this.contextMenuPending || this.composing) { return }
10979 var cm = this.cm;
10980 if (cm.somethingSelected()) {
10981 this.prevInput = "";
10982 var content = cm.getSelection();
10983 this.textarea.value = content;
10984 if (cm.state.focused) { selectInput(this.textarea); }
10985 if (ie && ie_version >= 9) { this.hasSelection = content; }
10986 } else if (!typing) {
10987 this.prevInput = this.textarea.value = "";
10988 if (ie && ie_version >= 9) { this.hasSelection = null; }
10989 }
10990 };
10991
10992 TextareaInput.prototype.getField = function () { return this.textarea };
10993
10994 TextareaInput.prototype.supportsTouch = function () { return false };
10995
10996 TextareaInput.prototype.focus = function () {
10997 if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) {
10998 try { this.textarea.focus(); }
10999 catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM
11000 }
11001 };
11002
11003 TextareaInput.prototype.blur = function () { this.textarea.blur(); };
11004
11005 TextareaInput.prototype.resetPosition = function () {
11006 this.wrapper.style.top = this.wrapper.style.left = 0;
11007 };
11008
11009 TextareaInput.prototype.receivedFocus = function () { this.slowPoll(); };
11010
11011 // Poll for input changes, using the normal rate of polling. This
11012 // runs as long as the editor is focused.
11013 TextareaInput.prototype.slowPoll = function () {
11014 var this$1 = this;
11015
11016 if (this.pollingFast) { return }
11017 this.polling.set(this.cm.options.pollInterval, function () {
11018 this$1.poll();
11019 if (this$1.cm.state.focused) { this$1.slowPoll(); }
11020 });
11021 };
11022
11023 // When an event has just come in that is likely to add or change
11024 // something in the input textarea, we poll faster, to ensure that
11025 // the change appears on the screen quickly.
11026 TextareaInput.prototype.fastPoll = function () {
11027 var missed = false, input = this;
11028 input.pollingFast = true;
11029 function p() {
11030 var changed = input.poll();
11031 if (!changed && !missed) {missed = true; input.polling.set(60, p);}
11032 else {input.pollingFast = false; input.slowPoll();}
11033 }
11034 input.polling.set(20, p);
11035 };
11036
11037 // Read input from the textarea, and update the document to match.
11038 // When something is selected, it is present in the textarea, and
11039 // selected (unless it is huge, in which case a placeholder is
11040 // used). When nothing is selected, the cursor sits after previously
11041 // seen text (can be empty), which is stored in prevInput (we must
11042 // not reset the textarea when typing, because that breaks IME).
11043 TextareaInput.prototype.poll = function () {
11044 var this$1 = this;
11045
11046 var cm = this.cm, input = this.textarea, prevInput = this.prevInput;
11047 // Since this is called a *lot*, try to bail out as cheaply as
11048 // possible when it is clear that nothing happened. hasSelection
11049 // will be the case when there is a lot of text in the textarea,
11050 // in which case reading its value would be expensive.
11051 if (this.contextMenuPending || !cm.state.focused ||
11052 (hasSelection(input) && !prevInput && !this.composing) ||
11053 cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq)
11054 { return false }
11055
11056 var text = input.value;
11057 // If nothing changed, bail.
11058 if (text == prevInput && !cm.somethingSelected()) { return false }
11059 // Work around nonsensical selection resetting in IE9/10, and
11060 // inexplicable appearance of private area unicode characters on
11061 // some key combos in Mac (#2689).
11062 if (ie && ie_version >= 9 && this.hasSelection === text ||
11063 mac && /[\uf700-\uf7ff]/.test(text)) {
11064 cm.display.input.reset();
11065 return false
11066 }
11067
11068 if (cm.doc.sel == cm.display.selForContextMenu) {
11069 var first = text.charCodeAt(0);
11070 if (first == 0x200b && !prevInput) { prevInput = "\u200b"; }
11071 if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") }
11072 }
11073 // Find the part of the input that is actually new
11074 var same = 0, l = Math.min(prevInput.length, text.length);
11075 while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same; }
11076
11077 runInOp(cm, function () {
11078 applyTextInput(cm, text.slice(same), prevInput.length - same,
11079 null, this$1.composing ? "*compose" : null);
11080
11081 // Don't leave long text in the textarea, since it makes further polling slow
11082 if (text.length > 1000 || text.indexOf("\n") > -1) { input.value = this$1.prevInput = ""; }
11083 else { this$1.prevInput = text; }
11084
11085 if (this$1.composing) {
11086 this$1.composing.range.clear();
11087 this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor("to"),
11088 {className: "CodeMirror-composing"});
11089 }
11090 });
11091 return true
11092 };
11093
11094 TextareaInput.prototype.ensurePolled = function () {
11095 if (this.pollingFast && this.poll()) { this.pollingFast = false; }
11096 };
11097
11098 TextareaInput.prototype.onKeyPress = function () {
11099 if (ie && ie_version >= 9) { this.hasSelection = null; }
11100 this.fastPoll();
11101 };
11102
11103 TextareaInput.prototype.onContextMenu = function (e) {
11104 var input = this, cm = input.cm, display = cm.display, te = input.textarea;
11105 if (input.contextMenuPending) { input.contextMenuPending(); }
11106 var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
11107 if (!pos || presto) { return } // Opera is difficult.
11108
11109 // Reset the current text selection only if the click is done outside of the selection
11110 // and 'resetSelectionOnContextMenu' option is true.
11111 var reset = cm.options.resetSelectionOnContextMenu;
11112 if (reset && cm.doc.sel.contains(pos) == -1)
11113 { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll); }
11114
11115 var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText;
11116 var wrapperBox = input.wrapper.offsetParent.getBoundingClientRect();
11117 input.wrapper.style.cssText = "position: static";
11118 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);";
11119 var oldScrollY;
11120 if (webkit) { oldScrollY = window.scrollY; } // Work around Chrome issue (#2712)
11121 display.input.focus();
11122 if (webkit) { window.scrollTo(null, oldScrollY); }
11123 display.input.reset();
11124 // Adds "Select all" to context menu in FF
11125 if (!cm.somethingSelected()) { te.value = input.prevInput = " "; }
11126 input.contextMenuPending = rehide;
11127 display.selForContextMenu = cm.doc.sel;
11128 clearTimeout(display.detectingSelectAll);
11129
11130 // Select-all will be greyed out if there's nothing to select, so
11131 // this adds a zero-width space so that we can later check whether
11132 // it got selected.
11133 function prepareSelectAllHack() {
11134 if (te.selectionStart != null) {
11135 var selected = cm.somethingSelected();
11136 var extval = "\u200b" + (selected ? te.value : "");
11137 te.value = "\u21da"; // Used to catch context-menu undo
11138 te.value = extval;
11139 input.prevInput = selected ? "" : "\u200b";
11140 te.selectionStart = 1; te.selectionEnd = extval.length;
11141 // Re-set this, in case some other handler touched the
11142 // selection in the meantime.
11143 display.selForContextMenu = cm.doc.sel;
11144 }
11145 }
11146 function rehide() {
11147 if (input.contextMenuPending != rehide) { return }
11148 input.contextMenuPending = false;
11149 input.wrapper.style.cssText = oldWrapperCSS;
11150 te.style.cssText = oldCSS;
11151 if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos); }
11152
11153 // Try to detect the user choosing select-all
11154 if (te.selectionStart != null) {
11155 if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack(); }
11156 var i = 0, poll = function () {
11157 if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 &&
11158 te.selectionEnd > 0 && input.prevInput == "\u200b") {
11159 operation(cm, selectAll)(cm);
11160 } else if (i++ < 10) {
11161 display.detectingSelectAll = setTimeout(poll, 500);
11162 } else {
11163 display.selForContextMenu = null;
11164 display.input.reset();
11165 }
11166 };
11167 display.detectingSelectAll = setTimeout(poll, 200);
11168 }
11169 }
11170
11171 if (ie && ie_version >= 9) { prepareSelectAllHack(); }
11172 if (captureRightClick) {
11173 e_stop(e);
11174 var mouseup = function () {
11175 off(window, "mouseup", mouseup);
11176 setTimeout(rehide, 20);
11177 };
11178 on(window, "mouseup", mouseup);
11179 } else {
11180 setTimeout(rehide, 50);
11181 }
11182 };
11183
11184 TextareaInput.prototype.readOnlyChanged = function (val) {
11185 if (!val) { this.reset(); }
11186 this.textarea.disabled = val == "nocursor";
11187 };
11188
11189 TextareaInput.prototype.setUneditable = function () {};
11190
11191 TextareaInput.prototype.needsContentAttribute = false;
11192
11193 function fromTextArea(textarea, options) {
11194 options = options ? copyObj(options) : {};
11195 options.value = textarea.value;
11196 if (!options.tabindex && textarea.tabIndex)
11197 { options.tabindex = textarea.tabIndex; }
11198 if (!options.placeholder && textarea.placeholder)
11199 { options.placeholder = textarea.placeholder; }
11200 // Set autofocus to true if this textarea is focused, or if it has
11201 // autofocus and no other element is focused.
11202 if (options.autofocus == null) {
11203 var hasFocus = activeElt();
11204 options.autofocus = hasFocus == textarea ||
11205 textarea.getAttribute("autofocus") != null && hasFocus == document.body;
11206 }
11207
11208 function save() {textarea.value = cm.getValue();}
11209
11210 var realSubmit;
11211 if (textarea.form) {
11212 on(textarea.form, "submit", save);
11213 // Deplorable hack to make the submit method do the right thing.
11214 if (!options.leaveSubmitMethodAlone) {
11215 var form = textarea.form;
11216 realSubmit = form.submit;
11217 try {
11218 var wrappedSubmit = form.submit = function () {
11219 save();
11220 form.submit = realSubmit;
11221 form.submit();
11222 form.submit = wrappedSubmit;
11223 };
11224 } catch(e) {}
11225 }
11226 }
11227
11228 options.finishInit = function (cm) {
11229 cm.save = save;
11230 cm.getTextArea = function () { return textarea; };
11231 cm.toTextArea = function () {
11232 cm.toTextArea = isNaN; // Prevent this from being ran twice
11233 save();
11234 textarea.parentNode.removeChild(cm.getWrapperElement());
11235 textarea.style.display = "";
11236 if (textarea.form) {
11237 off(textarea.form, "submit", save);
11238 if (typeof textarea.form.submit == "function")
11239 { textarea.form.submit = realSubmit; }
11240 }
11241 };
11242 };
11243
11244 textarea.style.display = "none";
11245 var cm = CodeMirror(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); },
11246 options);
11247 return cm
11248 }
11249
11250 function addLegacyProps(CodeMirror) {
11251 CodeMirror.off = off;
11252 CodeMirror.on = on;
11253 CodeMirror.wheelEventPixels = wheelEventPixels;
11254 CodeMirror.Doc = Doc;
11255 CodeMirror.splitLines = splitLinesAuto;
11256 CodeMirror.countColumn = countColumn;
11257 CodeMirror.findColumn = findColumn;
11258 CodeMirror.isWordChar = isWordCharBasic;
11259 CodeMirror.Pass = Pass;
11260 CodeMirror.signal = signal;
11261 CodeMirror.Line = Line;
11262 CodeMirror.changeEnd = changeEnd;
11263 CodeMirror.scrollbarModel = scrollbarModel;
11264 CodeMirror.Pos = Pos;
11265 CodeMirror.cmpPos = cmp;
11266 CodeMirror.modes = modes;
11267 CodeMirror.mimeModes = mimeModes;
11268 CodeMirror.resolveMode = resolveMode;
11269 CodeMirror.getMode = getMode;
11270 CodeMirror.modeExtensions = modeExtensions;
11271 CodeMirror.extendMode = extendMode;
11272 CodeMirror.copyState = copyState;
11273 CodeMirror.startState = startState;
11274 CodeMirror.innerMode = innerMode;
11275 CodeMirror.commands = commands;
11276 CodeMirror.keyMap = keyMap;
11277 CodeMirror.keyName = keyName;
11278 CodeMirror.isModifierKey = isModifierKey;
11279 CodeMirror.lookupKey = lookupKey;
11280 CodeMirror.normalizeKeyMap = normalizeKeyMap;
11281 CodeMirror.StringStream = StringStream;
11282 CodeMirror.SharedTextMarker = SharedTextMarker;
11283 CodeMirror.TextMarker = TextMarker;
11284 CodeMirror.LineWidget = LineWidget;
11285 CodeMirror.e_preventDefault = e_preventDefault;
11286 CodeMirror.e_stopPropagation = e_stopPropagation;
11287 CodeMirror.e_stop = e_stop;
11288 CodeMirror.addClass = addClass;
11289 CodeMirror.contains = contains;
11290 CodeMirror.rmClass = rmClass;
11291 CodeMirror.keyNames = keyNames;
11292 }
11293
11294 // EDITOR CONSTRUCTOR
11295
11296 defineOptions(CodeMirror);
11297
11298 addEditorMethods(CodeMirror);
11299
11300 // Set up methods on CodeMirror's prototype to redirect to the editor's document.
11301 var dontDelegate = "iter insert remove copy getEditor constructor".split(" ");
11302 for (var prop in Doc.prototype) { if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
11303 { CodeMirror.prototype[prop] = (function(method) {
11304 return function() {return method.apply(this.doc, arguments)}
11305 })(Doc.prototype[prop]); } }
11306
11307 eventMixin(Doc);
11308 CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput};
11309
11310 // Extra arguments are stored as the mode's dependencies, which is
11311 // used by (legacy) mechanisms like loadmode.js to automatically
11312 // load a mode. (Preferred mechanism is the require/define calls.)
11313 CodeMirror.defineMode = function(name/*, mode, …*/) {
11314 if (!CodeMirror.defaults.mode && name != "null") { CodeMirror.defaults.mode = name; }
11315 defineMode.apply(this, arguments);
11316 };
11317
11318 CodeMirror.defineMIME = defineMIME;
11319
11320 // Minimal default mode.
11321 CodeMirror.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); });
11322 CodeMirror.defineMIME("text/plain", "null");
11323
11324 // EXTENSIONS
11325
11326 CodeMirror.defineExtension = function (name, func) {
11327 CodeMirror.prototype[name] = func;
11328 };
11329 CodeMirror.defineDocExtension = function (name, func) {
11330 Doc.prototype[name] = func;
11331 };
11332
11333 CodeMirror.fromTextArea = fromTextArea;
11334
11335 addLegacyProps(CodeMirror);
11336
11337 CodeMirror.version = "5.47.0";
11338
11339 return CodeMirror;
11340
11341 })));
11342
11343 },{}],15:[function(require,module,exports){
11344 // CodeMirror, copyright (c) by Marijn Haverbeke and others
11345 // Distributed under an MIT license: https://codemirror.net/LICENSE
11346
11347 (function(mod) {
11348 if (typeof exports == "object" && typeof module == "object") // CommonJS
11349 mod(require("../../lib/codemirror"));
11350 else if (typeof define == "function" && define.amd) // AMD
11351 define(["../../lib/codemirror"], mod);
11352 else // Plain browser env
11353 mod(CodeMirror);
11354 })(function(CodeMirror) {
11355 "use strict";
11356
11357 CodeMirror.defineMode("css", function(config, parserConfig) {
11358 var inline = parserConfig.inline
11359 if (!parserConfig.propertyKeywords) parserConfig = CodeMirror.resolveMode("text/css");
11360
11361 var indentUnit = config.indentUnit,
11362 tokenHooks = parserConfig.tokenHooks,
11363 documentTypes = parserConfig.documentTypes || {},
11364 mediaTypes = parserConfig.mediaTypes || {},
11365 mediaFeatures = parserConfig.mediaFeatures || {},
11366 mediaValueKeywords = parserConfig.mediaValueKeywords || {},
11367 propertyKeywords = parserConfig.propertyKeywords || {},
11368 nonStandardPropertyKeywords = parserConfig.nonStandardPropertyKeywords || {},
11369 fontProperties = parserConfig.fontProperties || {},
11370 counterDescriptors = parserConfig.counterDescriptors || {},
11371 colorKeywords = parserConfig.colorKeywords || {},
11372 valueKeywords = parserConfig.valueKeywords || {},
11373 allowNested = parserConfig.allowNested,
11374 lineComment = parserConfig.lineComment,
11375 supportsAtComponent = parserConfig.supportsAtComponent === true;
11376
11377 var type, override;
11378 function ret(style, tp) { type = tp; return style; }
11379
11380 // Tokenizers
11381
11382 function tokenBase(stream, state) {
11383 var ch = stream.next();
11384 if (tokenHooks[ch]) {
11385 var result = tokenHooks[ch](stream, state);
11386 if (result !== false) return result;
11387 }
11388 if (ch == "@") {
11389 stream.eatWhile(/[\w\\\-]/);
11390 return ret("def", stream.current());
11391 } else if (ch == "=" || (ch == "~" || ch == "|") && stream.eat("=")) {
11392 return ret(null, "compare");
11393 } else if (ch == "\"" || ch == "'") {
11394 state.tokenize = tokenString(ch);
11395 return state.tokenize(stream, state);
11396 } else if (ch == "#") {
11397 stream.eatWhile(/[\w\\\-]/);
11398 return ret("atom", "hash");
11399 } else if (ch == "!") {
11400 stream.match(/^\s*\w*/);
11401 return ret("keyword", "important");
11402 } else if (/\d/.test(ch) || ch == "." && stream.eat(/\d/)) {
11403 stream.eatWhile(/[\w.%]/);
11404 return ret("number", "unit");
11405 } else if (ch === "-") {
11406 if (/[\d.]/.test(stream.peek())) {
11407 stream.eatWhile(/[\w.%]/);
11408 return ret("number", "unit");
11409 } else if (stream.match(/^-[\w\\\-]*/)) {
11410 stream.eatWhile(/[\w\\\-]/);
11411 if (stream.match(/^\s*:/, false))
11412 return ret("variable-2", "variable-definition");
11413 return ret("variable-2", "variable");
11414 } else if (stream.match(/^\w+-/)) {
11415 return ret("meta", "meta");
11416 }
11417 } else if (/[,+>*\/]/.test(ch)) {
11418 return ret(null, "select-op");
11419 } else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {
11420 return ret("qualifier", "qualifier");
11421 } else if (/[:;{}\[\]\(\)]/.test(ch)) {
11422 return ret(null, ch);
11423 } else if (stream.match(/[\w-.]+(?=\()/)) {
11424 if (/^(url(-prefix)?|domain|regexp)$/.test(stream.current().toLowerCase())) {
11425 state.tokenize = tokenParenthesized;
11426 }
11427 return ret("variable callee", "variable");
11428 } else if (/[\w\\\-]/.test(ch)) {
11429 stream.eatWhile(/[\w\\\-]/);
11430 return ret("property", "word");
11431 } else {
11432 return ret(null, null);
11433 }
11434 }
11435
11436 function tokenString(quote) {
11437 return function(stream, state) {
11438 var escaped = false, ch;
11439 while ((ch = stream.next()) != null) {
11440 if (ch == quote && !escaped) {
11441 if (quote == ")") stream.backUp(1);
11442 break;
11443 }
11444 escaped = !escaped && ch == "\\";
11445 }
11446 if (ch == quote || !escaped && quote != ")") state.tokenize = null;
11447 return ret("string", "string");
11448 };
11449 }
11450
11451 function tokenParenthesized(stream, state) {
11452 stream.next(); // Must be '('
11453 if (!stream.match(/\s*[\"\')]/, false))
11454 state.tokenize = tokenString(")");
11455 else
11456 state.tokenize = null;
11457 return ret(null, "(");
11458 }
11459
11460 // Context management
11461
11462 function Context(type, indent, prev) {
11463 this.type = type;
11464 this.indent = indent;
11465 this.prev = prev;
11466 }
11467
11468 function pushContext(state, stream, type, indent) {
11469 state.context = new Context(type, stream.indentation() + (indent === false ? 0 : indentUnit), state.context);
11470 return type;
11471 }
11472
11473 function popContext(state) {
11474 if (state.context.prev)
11475 state.context = state.context.prev;
11476 return state.context.type;
11477 }
11478
11479 function pass(type, stream, state) {
11480 return states[state.context.type](type, stream, state);
11481 }
11482 function popAndPass(type, stream, state, n) {
11483 for (var i = n || 1; i > 0; i--)
11484 state.context = state.context.prev;
11485 return pass(type, stream, state);
11486 }
11487
11488 // Parser
11489
11490 function wordAsValue(stream) {
11491 var word = stream.current().toLowerCase();
11492 if (valueKeywords.hasOwnProperty(word))
11493 override = "atom";
11494 else if (colorKeywords.hasOwnProperty(word))
11495 override = "keyword";
11496 else
11497 override = "variable";
11498 }
11499
11500 var states = {};
11501
11502 states.top = function(type, stream, state) {
11503 if (type == "{") {
11504 return pushContext(state, stream, "block");
11505 } else if (type == "}" && state.context.prev) {
11506 return popContext(state);
11507 } else if (supportsAtComponent && /@component/i.test(type)) {
11508 return pushContext(state, stream, "atComponentBlock");
11509 } else if (/^@(-moz-)?document$/i.test(type)) {
11510 return pushContext(state, stream, "documentTypes");
11511 } else if (/^@(media|supports|(-moz-)?document|import)$/i.test(type)) {
11512 return pushContext(state, stream, "atBlock");
11513 } else if (/^@(font-face|counter-style)/i.test(type)) {
11514 state.stateArg = type;
11515 return "restricted_atBlock_before";
11516 } else if (/^@(-(moz|ms|o|webkit)-)?keyframes$/i.test(type)) {
11517 return "keyframes";
11518 } else if (type && type.charAt(0) == "@") {
11519 return pushContext(state, stream, "at");
11520 } else if (type == "hash") {
11521 override = "builtin";
11522 } else if (type == "word") {
11523 override = "tag";
11524 } else if (type == "variable-definition") {
11525 return "maybeprop";
11526 } else if (type == "interpolation") {
11527 return pushContext(state, stream, "interpolation");
11528 } else if (type == ":") {
11529 return "pseudo";
11530 } else if (allowNested && type == "(") {
11531 return pushContext(state, stream, "parens");
11532 }
11533 return state.context.type;
11534 };
11535
11536 states.block = function(type, stream, state) {
11537 if (type == "word") {
11538 var word = stream.current().toLowerCase();
11539 if (propertyKeywords.hasOwnProperty(word)) {
11540 override = "property";
11541 return "maybeprop";
11542 } else if (nonStandardPropertyKeywords.hasOwnProperty(word)) {
11543 override = "string-2";
11544 return "maybeprop";
11545 } else if (allowNested) {
11546 override = stream.match(/^\s*:(?:\s|$)/, false) ? "property" : "tag";
11547 return "block";
11548 } else {
11549 override += " error";
11550 return "maybeprop";
11551 }
11552 } else if (type == "meta") {
11553 return "block";
11554 } else if (!allowNested && (type == "hash" || type == "qualifier")) {
11555 override = "error";
11556 return "block";
11557 } else {
11558 return states.top(type, stream, state);
11559 }
11560 };
11561
11562 states.maybeprop = function(type, stream, state) {
11563 if (type == ":") return pushContext(state, stream, "prop");
11564 return pass(type, stream, state);
11565 };
11566
11567 states.prop = function(type, stream, state) {
11568 if (type == ";") return popContext(state);
11569 if (type == "{" && allowNested) return pushContext(state, stream, "propBlock");
11570 if (type == "}" || type == "{") return popAndPass(type, stream, state);
11571 if (type == "(") return pushContext(state, stream, "parens");
11572
11573 if (type == "hash" && !/^#([0-9a-fA-f]{3,4}|[0-9a-fA-f]{6}|[0-9a-fA-f]{8})$/.test(stream.current())) {
11574 override += " error";
11575 } else if (type == "word") {
11576 wordAsValue(stream);
11577 } else if (type == "interpolation") {
11578 return pushContext(state, stream, "interpolation");
11579 }
11580 return "prop";
11581 };
11582
11583 states.propBlock = function(type, _stream, state) {
11584 if (type == "}") return popContext(state);
11585 if (type == "word") { override = "property"; return "maybeprop"; }
11586 return state.context.type;
11587 };
11588
11589 states.parens = function(type, stream, state) {
11590 if (type == "{" || type == "}") return popAndPass(type, stream, state);
11591 if (type == ")") return popContext(state);
11592 if (type == "(") return pushContext(state, stream, "parens");
11593 if (type == "interpolation") return pushContext(state, stream, "interpolation");
11594 if (type == "word") wordAsValue(stream);
11595 return "parens";
11596 };
11597
11598 states.pseudo = function(type, stream, state) {
11599 if (type == "meta") return "pseudo";
11600
11601 if (type == "word") {
11602 override = "variable-3";
11603 return state.context.type;
11604 }
11605 return pass(type, stream, state);
11606 };
11607
11608 states.documentTypes = function(type, stream, state) {
11609 if (type == "word" && documentTypes.hasOwnProperty(stream.current())) {
11610 override = "tag";
11611 return state.context.type;
11612 } else {
11613 return states.atBlock(type, stream, state);
11614 }
11615 };
11616
11617 states.atBlock = function(type, stream, state) {
11618 if (type == "(") return pushContext(state, stream, "atBlock_parens");
11619 if (type == "}" || type == ";") return popAndPass(type, stream, state);
11620 if (type == "{") return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top");
11621
11622 if (type == "interpolation") return pushContext(state, stream, "interpolation");
11623
11624 if (type == "word") {
11625 var word = stream.current().toLowerCase();
11626 if (word == "only" || word == "not" || word == "and" || word == "or")
11627 override = "keyword";
11628 else if (mediaTypes.hasOwnProperty(word))
11629 override = "attribute";
11630 else if (mediaFeatures.hasOwnProperty(word))
11631 override = "property";
11632 else if (mediaValueKeywords.hasOwnProperty(word))
11633 override = "keyword";
11634 else if (propertyKeywords.hasOwnProperty(word))
11635 override = "property";
11636 else if (nonStandardPropertyKeywords.hasOwnProperty(word))
11637 override = "string-2";
11638 else if (valueKeywords.hasOwnProperty(word))
11639 override = "atom";
11640 else if (colorKeywords.hasOwnProperty(word))
11641 override = "keyword";
11642 else
11643 override = "error";
11644 }
11645 return state.context.type;
11646 };
11647
11648 states.atComponentBlock = function(type, stream, state) {
11649 if (type == "}")
11650 return popAndPass(type, stream, state);
11651 if (type == "{")
11652 return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top", false);
11653 if (type == "word")
11654 override = "error";
11655 return state.context.type;
11656 };
11657
11658 states.atBlock_parens = function(type, stream, state) {
11659 if (type == ")") return popContext(state);
11660 if (type == "{" || type == "}") return popAndPass(type, stream, state, 2);
11661 return states.atBlock(type, stream, state);
11662 };
11663
11664 states.restricted_atBlock_before = function(type, stream, state) {
11665 if (type == "{")
11666 return pushContext(state, stream, "restricted_atBlock");
11667 if (type == "word" && state.stateArg == "@counter-style") {
11668 override = "variable";
11669 return "restricted_atBlock_before";
11670 }
11671 return pass(type, stream, state);
11672 };
11673
11674 states.restricted_atBlock = function(type, stream, state) {
11675 if (type == "}") {
11676 state.stateArg = null;
11677 return popContext(state);
11678 }
11679 if (type == "word") {
11680 if ((state.stateArg == "@font-face" && !fontProperties.hasOwnProperty(stream.current().toLowerCase())) ||
11681 (state.stateArg == "@counter-style" && !counterDescriptors.hasOwnProperty(stream.current().toLowerCase())))
11682 override = "error";
11683 else
11684 override = "property";
11685 return "maybeprop";
11686 }
11687 return "restricted_atBlock";
11688 };
11689
11690 states.keyframes = function(type, stream, state) {
11691 if (type == "word") { override = "variable"; return "keyframes"; }
11692 if (type == "{") return pushContext(state, stream, "top");
11693 return pass(type, stream, state);
11694 };
11695
11696 states.at = function(type, stream, state) {
11697 if (type == ";") return popContext(state);
11698 if (type == "{" || type == "}") return popAndPass(type, stream, state);
11699 if (type == "word") override = "tag";
11700 else if (type == "hash") override = "builtin";
11701 return "at";
11702 };
11703
11704 states.interpolation = function(type, stream, state) {
11705 if (type == "}") return popContext(state);
11706 if (type == "{" || type == ";") return popAndPass(type, stream, state);
11707 if (type == "word") override = "variable";
11708 else if (type != "variable" && type != "(" && type != ")") override = "error";
11709 return "interpolation";
11710 };
11711
11712 return {
11713 startState: function(base) {
11714 return {tokenize: null,
11715 state: inline ? "block" : "top",
11716 stateArg: null,
11717 context: new Context(inline ? "block" : "top", base || 0, null)};
11718 },
11719
11720 token: function(stream, state) {
11721 if (!state.tokenize && stream.eatSpace()) return null;
11722 var style = (state.tokenize || tokenBase)(stream, state);
11723 if (style && typeof style == "object") {
11724 type = style[1];
11725 style = style[0];
11726 }
11727 override = style;
11728 if (type != "comment")
11729 state.state = states[state.state](type, stream, state);
11730 return override;
11731 },
11732
11733 indent: function(state, textAfter) {
11734 var cx = state.context, ch = textAfter && textAfter.charAt(0);
11735 var indent = cx.indent;
11736 if (cx.type == "prop" && (ch == "}" || ch == ")")) cx = cx.prev;
11737 if (cx.prev) {
11738 if (ch == "}" && (cx.type == "block" || cx.type == "top" ||
11739 cx.type == "interpolation" || cx.type == "restricted_atBlock")) {
11740 // Resume indentation from parent context.
11741 cx = cx.prev;
11742 indent = cx.indent;
11743 } else if (ch == ")" && (cx.type == "parens" || cx.type == "atBlock_parens") ||
11744 ch == "{" && (cx.type == "at" || cx.type == "atBlock")) {
11745 // Dedent relative to current context.
11746 indent = Math.max(0, cx.indent - indentUnit);
11747 }
11748 }
11749 return indent;
11750 },
11751
11752 electricChars: "}",
11753 blockCommentStart: "/*",
11754 blockCommentEnd: "*/",
11755 blockCommentContinue: " * ",
11756 lineComment: lineComment,
11757 fold: "brace"
11758 };
11759 });
11760
11761 function keySet(array) {
11762 var keys = {};
11763 for (var i = 0; i < array.length; ++i) {
11764 keys[array[i].toLowerCase()] = true;
11765 }
11766 return keys;
11767 }
11768
11769 var documentTypes_ = [
11770 "domain", "regexp", "url", "url-prefix"
11771 ], documentTypes = keySet(documentTypes_);
11772
11773 var mediaTypes_ = [
11774 "all", "aural", "braille", "handheld", "print", "projection", "screen",
11775 "tty", "tv", "embossed"
11776 ], mediaTypes = keySet(mediaTypes_);
11777
11778 var mediaFeatures_ = [
11779 "width", "min-width", "max-width", "height", "min-height", "max-height",
11780 "device-width", "min-device-width", "max-device-width", "device-height",
11781 "min-device-height", "max-device-height", "aspect-ratio",
11782 "min-aspect-ratio", "max-aspect-ratio", "device-aspect-ratio",
11783 "min-device-aspect-ratio", "max-device-aspect-ratio", "color", "min-color",
11784 "max-color", "color-index", "min-color-index", "max-color-index",
11785 "monochrome", "min-monochrome", "max-monochrome", "resolution",
11786 "min-resolution", "max-resolution", "scan", "grid", "orientation",
11787 "device-pixel-ratio", "min-device-pixel-ratio", "max-device-pixel-ratio",
11788 "pointer", "any-pointer", "hover", "any-hover"
11789 ], mediaFeatures = keySet(mediaFeatures_);
11790
11791 var mediaValueKeywords_ = [
11792 "landscape", "portrait", "none", "coarse", "fine", "on-demand", "hover",
11793 "interlace", "progressive"
11794 ], mediaValueKeywords = keySet(mediaValueKeywords_);
11795
11796 var propertyKeywords_ = [
11797 "align-content", "align-items", "align-self", "alignment-adjust",
11798 "alignment-baseline", "anchor-point", "animation", "animation-delay",
11799 "animation-direction", "animation-duration", "animation-fill-mode",
11800 "animation-iteration-count", "animation-name", "animation-play-state",
11801 "animation-timing-function", "appearance", "azimuth", "backface-visibility",
11802 "background", "background-attachment", "background-blend-mode", "background-clip",
11803 "background-color", "background-image", "background-origin", "background-position",
11804 "background-repeat", "background-size", "baseline-shift", "binding",
11805 "bleed", "bookmark-label", "bookmark-level", "bookmark-state",
11806 "bookmark-target", "border", "border-bottom", "border-bottom-color",
11807 "border-bottom-left-radius", "border-bottom-right-radius",
11808 "border-bottom-style", "border-bottom-width", "border-collapse",
11809 "border-color", "border-image", "border-image-outset",
11810 "border-image-repeat", "border-image-slice", "border-image-source",
11811 "border-image-width", "border-left", "border-left-color",
11812 "border-left-style", "border-left-width", "border-radius", "border-right",
11813 "border-right-color", "border-right-style", "border-right-width",
11814 "border-spacing", "border-style", "border-top", "border-top-color",
11815 "border-top-left-radius", "border-top-right-radius", "border-top-style",
11816 "border-top-width", "border-width", "bottom", "box-decoration-break",
11817 "box-shadow", "box-sizing", "break-after", "break-before", "break-inside",
11818 "caption-side", "caret-color", "clear", "clip", "color", "color-profile", "column-count",
11819 "column-fill", "column-gap", "column-rule", "column-rule-color",
11820 "column-rule-style", "column-rule-width", "column-span", "column-width",
11821 "columns", "content", "counter-increment", "counter-reset", "crop", "cue",
11822 "cue-after", "cue-before", "cursor", "direction", "display",
11823 "dominant-baseline", "drop-initial-after-adjust",
11824 "drop-initial-after-align", "drop-initial-before-adjust",
11825 "drop-initial-before-align", "drop-initial-size", "drop-initial-value",
11826 "elevation", "empty-cells", "fit", "fit-position", "flex", "flex-basis",
11827 "flex-direction", "flex-flow", "flex-grow", "flex-shrink", "flex-wrap",
11828 "float", "float-offset", "flow-from", "flow-into", "font", "font-feature-settings",
11829 "font-family", "font-kerning", "font-language-override", "font-size", "font-size-adjust",
11830 "font-stretch", "font-style", "font-synthesis", "font-variant",
11831 "font-variant-alternates", "font-variant-caps", "font-variant-east-asian",
11832 "font-variant-ligatures", "font-variant-numeric", "font-variant-position",
11833 "font-weight", "grid", "grid-area", "grid-auto-columns", "grid-auto-flow",
11834 "grid-auto-rows", "grid-column", "grid-column-end", "grid-column-gap",
11835 "grid-column-start", "grid-gap", "grid-row", "grid-row-end", "grid-row-gap",
11836 "grid-row-start", "grid-template", "grid-template-areas", "grid-template-columns",
11837 "grid-template-rows", "hanging-punctuation", "height", "hyphens",
11838 "icon", "image-orientation", "image-rendering", "image-resolution",
11839 "inline-box-align", "justify-content", "justify-items", "justify-self", "left", "letter-spacing",
11840 "line-break", "line-height", "line-stacking", "line-stacking-ruby",
11841 "line-stacking-shift", "line-stacking-strategy", "list-style",
11842 "list-style-image", "list-style-position", "list-style-type", "margin",
11843 "margin-bottom", "margin-left", "margin-right", "margin-top",
11844 "marks", "marquee-direction", "marquee-loop",
11845 "marquee-play-count", "marquee-speed", "marquee-style", "max-height",
11846 "max-width", "min-height", "min-width", "mix-blend-mode", "move-to", "nav-down", "nav-index",
11847 "nav-left", "nav-right", "nav-up", "object-fit", "object-position",
11848 "opacity", "order", "orphans", "outline",
11849 "outline-color", "outline-offset", "outline-style", "outline-width",
11850 "overflow", "overflow-style", "overflow-wrap", "overflow-x", "overflow-y",
11851 "padding", "padding-bottom", "padding-left", "padding-right", "padding-top",
11852 "page", "page-break-after", "page-break-before", "page-break-inside",
11853 "page-policy", "pause", "pause-after", "pause-before", "perspective",
11854 "perspective-origin", "pitch", "pitch-range", "place-content", "place-items", "place-self", "play-during", "position",
11855 "presentation-level", "punctuation-trim", "quotes", "region-break-after",
11856 "region-break-before", "region-break-inside", "region-fragment",
11857 "rendering-intent", "resize", "rest", "rest-after", "rest-before", "richness",
11858 "right", "rotation", "rotation-point", "ruby-align", "ruby-overhang",
11859 "ruby-position", "ruby-span", "shape-image-threshold", "shape-inside", "shape-margin",
11860 "shape-outside", "size", "speak", "speak-as", "speak-header",
11861 "speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set",
11862 "tab-size", "table-layout", "target", "target-name", "target-new",
11863 "target-position", "text-align", "text-align-last", "text-decoration",
11864 "text-decoration-color", "text-decoration-line", "text-decoration-skip",
11865 "text-decoration-style", "text-emphasis", "text-emphasis-color",
11866 "text-emphasis-position", "text-emphasis-style", "text-height",
11867 "text-indent", "text-justify", "text-outline", "text-overflow", "text-shadow",
11868 "text-size-adjust", "text-space-collapse", "text-transform", "text-underline-position",
11869 "text-wrap", "top", "transform", "transform-origin", "transform-style",
11870 "transition", "transition-delay", "transition-duration",
11871 "transition-property", "transition-timing-function", "unicode-bidi",
11872 "user-select", "vertical-align", "visibility", "voice-balance", "voice-duration",
11873 "voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress",
11874 "voice-volume", "volume", "white-space", "widows", "width", "will-change", "word-break",
11875 "word-spacing", "word-wrap", "z-index",
11876 // SVG-specific
11877 "clip-path", "clip-rule", "mask", "enable-background", "filter", "flood-color",
11878 "flood-opacity", "lighting-color", "stop-color", "stop-opacity", "pointer-events",
11879 "color-interpolation", "color-interpolation-filters",
11880 "color-rendering", "fill", "fill-opacity", "fill-rule", "image-rendering",
11881 "marker", "marker-end", "marker-mid", "marker-start", "shape-rendering", "stroke",
11882 "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin",
11883 "stroke-miterlimit", "stroke-opacity", "stroke-width", "text-rendering",
11884 "baseline-shift", "dominant-baseline", "glyph-orientation-horizontal",
11885 "glyph-orientation-vertical", "text-anchor", "writing-mode"
11886 ], propertyKeywords = keySet(propertyKeywords_);
11887
11888 var nonStandardPropertyKeywords_ = [
11889 "scrollbar-arrow-color", "scrollbar-base-color", "scrollbar-dark-shadow-color",
11890 "scrollbar-face-color", "scrollbar-highlight-color", "scrollbar-shadow-color",
11891 "scrollbar-3d-light-color", "scrollbar-track-color", "shape-inside",
11892 "searchfield-cancel-button", "searchfield-decoration", "searchfield-results-button",
11893 "searchfield-results-decoration", "zoom"
11894 ], nonStandardPropertyKeywords = keySet(nonStandardPropertyKeywords_);
11895
11896 var fontProperties_ = [
11897 "font-family", "src", "unicode-range", "font-variant", "font-feature-settings",
11898 "font-stretch", "font-weight", "font-style"
11899 ], fontProperties = keySet(fontProperties_);
11900
11901 var counterDescriptors_ = [
11902 "additive-symbols", "fallback", "negative", "pad", "prefix", "range",
11903 "speak-as", "suffix", "symbols", "system"
11904 ], counterDescriptors = keySet(counterDescriptors_);
11905
11906 var colorKeywords_ = [
11907 "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige",
11908 "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown",
11909 "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue",
11910 "cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod",
11911 "darkgray", "darkgreen", "darkkhaki", "darkmagenta", "darkolivegreen",
11912 "darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen",
11913 "darkslateblue", "darkslategray", "darkturquoise", "darkviolet",
11914 "deeppink", "deepskyblue", "dimgray", "dodgerblue", "firebrick",
11915 "floralwhite", "forestgreen", "fuchsia", "gainsboro", "ghostwhite",
11916 "gold", "goldenrod", "gray", "grey", "green", "greenyellow", "honeydew",
11917 "hotpink", "indianred", "indigo", "ivory", "khaki", "lavender",
11918 "lavenderblush", "lawngreen", "lemonchiffon", "lightblue", "lightcoral",
11919 "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightpink",
11920 "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray",
11921 "lightsteelblue", "lightyellow", "lime", "limegreen", "linen", "magenta",
11922 "maroon", "mediumaquamarine", "mediumblue", "mediumorchid", "mediumpurple",
11923 "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise",
11924 "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin",
11925 "navajowhite", "navy", "oldlace", "olive", "olivedrab", "orange", "orangered",
11926 "orchid", "palegoldenrod", "palegreen", "paleturquoise", "palevioletred",
11927 "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue",
11928 "purple", "rebeccapurple", "red", "rosybrown", "royalblue", "saddlebrown",
11929 "salmon", "sandybrown", "seagreen", "seashell", "sienna", "silver", "skyblue",
11930 "slateblue", "slategray", "snow", "springgreen", "steelblue", "tan",
11931 "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white",
11932 "whitesmoke", "yellow", "yellowgreen"
11933 ], colorKeywords = keySet(colorKeywords_);
11934
11935 var valueKeywords_ = [
11936 "above", "absolute", "activeborder", "additive", "activecaption", "afar",
11937 "after-white-space", "ahead", "alias", "all", "all-scroll", "alphabetic", "alternate",
11938 "always", "amharic", "amharic-abegede", "antialiased", "appworkspace",
11939 "arabic-indic", "armenian", "asterisks", "attr", "auto", "auto-flow", "avoid", "avoid-column", "avoid-page",
11940 "avoid-region", "background", "backwards", "baseline", "below", "bidi-override", "binary",
11941 "bengali", "blink", "block", "block-axis", "bold", "bolder", "border", "border-box",
11942 "both", "bottom", "break", "break-all", "break-word", "bullets", "button", "button-bevel",
11943 "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "calc", "cambodian",
11944 "capitalize", "caps-lock-indicator", "caption", "captiontext", "caret",
11945 "cell", "center", "checkbox", "circle", "cjk-decimal", "cjk-earthly-branch",
11946 "cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote",
11947 "col-resize", "collapse", "color", "color-burn", "color-dodge", "column", "column-reverse",
11948 "compact", "condensed", "contain", "content", "contents",
11949 "content-box", "context-menu", "continuous", "copy", "counter", "counters", "cover", "crop",
11950 "cross", "crosshair", "currentcolor", "cursive", "cyclic", "darken", "dashed", "decimal",
11951 "decimal-leading-zero", "default", "default-button", "dense", "destination-atop",
11952 "destination-in", "destination-out", "destination-over", "devanagari", "difference",
11953 "disc", "discard", "disclosure-closed", "disclosure-open", "document",
11954 "dot-dash", "dot-dot-dash",
11955 "dotted", "double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out",
11956 "element", "ellipse", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede",
11957 "ethiopic-abegede-am-et", "ethiopic-abegede-gez", "ethiopic-abegede-ti-er",
11958 "ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er",
11959 "ethiopic-halehame-aa-et", "ethiopic-halehame-am-et",
11960 "ethiopic-halehame-gez", "ethiopic-halehame-om-et",
11961 "ethiopic-halehame-sid-et", "ethiopic-halehame-so-et",
11962 "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et", "ethiopic-halehame-tig",
11963 "ethiopic-numeric", "ew-resize", "exclusion", "expanded", "extends", "extra-condensed",
11964 "extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "flex", "flex-end", "flex-start", "footnotes",
11965 "forwards", "from", "geometricPrecision", "georgian", "graytext", "grid", "groove",
11966 "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hard-light", "hebrew",
11967 "help", "hidden", "hide", "higher", "highlight", "highlighttext",
11968 "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "hue", "icon", "ignore",
11969 "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite",
11970 "infobackground", "infotext", "inherit", "initial", "inline", "inline-axis",
11971 "inline-block", "inline-flex", "inline-grid", "inline-table", "inset", "inside", "intrinsic", "invert",
11972 "italic", "japanese-formal", "japanese-informal", "justify", "kannada",
11973 "katakana", "katakana-iroha", "keep-all", "khmer",
11974 "korean-hangul-formal", "korean-hanja-formal", "korean-hanja-informal",
11975 "landscape", "lao", "large", "larger", "left", "level", "lighter", "lighten",
11976 "line-through", "linear", "linear-gradient", "lines", "list-item", "listbox", "listitem",
11977 "local", "logical", "loud", "lower", "lower-alpha", "lower-armenian",
11978 "lower-greek", "lower-hexadecimal", "lower-latin", "lower-norwegian",
11979 "lower-roman", "lowercase", "ltr", "luminosity", "malayalam", "match", "matrix", "matrix3d",
11980 "media-controls-background", "media-current-time-display",
11981 "media-fullscreen-button", "media-mute-button", "media-play-button",
11982 "media-return-to-realtime-button", "media-rewind-button",
11983 "media-seek-back-button", "media-seek-forward-button", "media-slider",
11984 "media-sliderthumb", "media-time-remaining-display", "media-volume-slider",
11985 "media-volume-slider-container", "media-volume-sliderthumb", "medium",
11986 "menu", "menulist", "menulist-button", "menulist-text",
11987 "menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic",
11988 "mix", "mongolian", "monospace", "move", "multiple", "multiply", "myanmar", "n-resize",
11989 "narrower", "ne-resize", "nesw-resize", "no-close-quote", "no-drop",
11990 "no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap",
11991 "ns-resize", "numbers", "numeric", "nw-resize", "nwse-resize", "oblique", "octal", "opacity", "open-quote",
11992 "optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset",
11993 "outside", "outside-shape", "overlay", "overline", "padding", "padding-box",
11994 "painted", "page", "paused", "persian", "perspective", "plus-darker", "plus-lighter",
11995 "pointer", "polygon", "portrait", "pre", "pre-line", "pre-wrap", "preserve-3d",
11996 "progress", "push-button", "radial-gradient", "radio", "read-only",
11997 "read-write", "read-write-plaintext-only", "rectangle", "region",
11998 "relative", "repeat", "repeating-linear-gradient",
11999 "repeating-radial-gradient", "repeat-x", "repeat-y", "reset", "reverse",
12000 "rgb", "rgba", "ridge", "right", "rotate", "rotate3d", "rotateX", "rotateY",
12001 "rotateZ", "round", "row", "row-resize", "row-reverse", "rtl", "run-in", "running",
12002 "s-resize", "sans-serif", "saturation", "scale", "scale3d", "scaleX", "scaleY", "scaleZ", "screen",
12003 "scroll", "scrollbar", "scroll-position", "se-resize", "searchfield",
12004 "searchfield-cancel-button", "searchfield-decoration",
12005 "searchfield-results-button", "searchfield-results-decoration", "self-start", "self-end",
12006 "semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama",
12007 "simp-chinese-formal", "simp-chinese-informal", "single",
12008 "skew", "skewX", "skewY", "skip-white-space", "slide", "slider-horizontal",
12009 "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow",
12010 "small", "small-caps", "small-caption", "smaller", "soft-light", "solid", "somali",
12011 "source-atop", "source-in", "source-out", "source-over", "space", "space-around", "space-between", "space-evenly", "spell-out", "square",
12012 "square-button", "start", "static", "status-bar", "stretch", "stroke", "sub",
12013 "subpixel-antialiased", "super", "sw-resize", "symbolic", "symbols", "system-ui", "table",
12014 "table-caption", "table-cell", "table-column", "table-column-group",
12015 "table-footer-group", "table-header-group", "table-row", "table-row-group",
12016 "tamil",
12017 "telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai",
12018 "thick", "thin", "threeddarkshadow", "threedface", "threedhighlight",
12019 "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er",
12020 "tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top",
12021 "trad-chinese-formal", "trad-chinese-informal", "transform",
12022 "translate", "translate3d", "translateX", "translateY", "translateZ",
12023 "transparent", "ultra-condensed", "ultra-expanded", "underline", "unset", "up",
12024 "upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal",
12025 "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url",
12026 "var", "vertical", "vertical-text", "visible", "visibleFill", "visiblePainted",
12027 "visibleStroke", "visual", "w-resize", "wait", "wave", "wider",
12028 "window", "windowframe", "windowtext", "words", "wrap", "wrap-reverse", "x-large", "x-small", "xor",
12029 "xx-large", "xx-small"
12030 ], valueKeywords = keySet(valueKeywords_);
12031
12032 var allWords = documentTypes_.concat(mediaTypes_).concat(mediaFeatures_).concat(mediaValueKeywords_)
12033 .concat(propertyKeywords_).concat(nonStandardPropertyKeywords_).concat(colorKeywords_)
12034 .concat(valueKeywords_);
12035 CodeMirror.registerHelper("hintWords", "css", allWords);
12036
12037 function tokenCComment(stream, state) {
12038 var maybeEnd = false, ch;
12039 while ((ch = stream.next()) != null) {
12040 if (maybeEnd && ch == "/") {
12041 state.tokenize = null;
12042 break;
12043 }
12044 maybeEnd = (ch == "*");
12045 }
12046 return ["comment", "comment"];
12047 }
12048
12049 CodeMirror.defineMIME("text/css", {
12050 documentTypes: documentTypes,
12051 mediaTypes: mediaTypes,
12052 mediaFeatures: mediaFeatures,
12053 mediaValueKeywords: mediaValueKeywords,
12054 propertyKeywords: propertyKeywords,
12055 nonStandardPropertyKeywords: nonStandardPropertyKeywords,
12056 fontProperties: fontProperties,
12057 counterDescriptors: counterDescriptors,
12058 colorKeywords: colorKeywords,
12059 valueKeywords: valueKeywords,
12060 tokenHooks: {
12061 "/": function(stream, state) {
12062 if (!stream.eat("*")) return false;
12063 state.tokenize = tokenCComment;
12064 return tokenCComment(stream, state);
12065 }
12066 },
12067 name: "css"
12068 });
12069
12070 CodeMirror.defineMIME("text/x-scss", {
12071 mediaTypes: mediaTypes,
12072 mediaFeatures: mediaFeatures,
12073 mediaValueKeywords: mediaValueKeywords,
12074 propertyKeywords: propertyKeywords,
12075 nonStandardPropertyKeywords: nonStandardPropertyKeywords,
12076 colorKeywords: colorKeywords,
12077 valueKeywords: valueKeywords,
12078 fontProperties: fontProperties,
12079 allowNested: true,
12080 lineComment: "//",
12081 tokenHooks: {
12082 "/": function(stream, state) {
12083 if (stream.eat("/")) {
12084 stream.skipToEnd();
12085 return ["comment", "comment"];
12086 } else if (stream.eat("*")) {
12087 state.tokenize = tokenCComment;
12088 return tokenCComment(stream, state);
12089 } else {
12090 return ["operator", "operator"];
12091 }
12092 },
12093 ":": function(stream) {
12094 if (stream.match(/\s*\{/, false))
12095 return [null, null]
12096 return false;
12097 },
12098 "$": function(stream) {
12099 stream.match(/^[\w-]+/);
12100 if (stream.match(/^\s*:/, false))
12101 return ["variable-2", "variable-definition"];
12102 return ["variable-2", "variable"];
12103 },
12104 "#": function(stream) {
12105 if (!stream.eat("{")) return false;
12106 return [null, "interpolation"];
12107 }
12108 },
12109 name: "css",
12110 helperType: "scss"
12111 });
12112
12113 CodeMirror.defineMIME("text/x-less", {
12114 mediaTypes: mediaTypes,
12115 mediaFeatures: mediaFeatures,
12116 mediaValueKeywords: mediaValueKeywords,
12117 propertyKeywords: propertyKeywords,
12118 nonStandardPropertyKeywords: nonStandardPropertyKeywords,
12119 colorKeywords: colorKeywords,
12120 valueKeywords: valueKeywords,
12121 fontProperties: fontProperties,
12122 allowNested: true,
12123 lineComment: "//",
12124 tokenHooks: {
12125 "/": function(stream, state) {
12126 if (stream.eat("/")) {
12127 stream.skipToEnd();
12128 return ["comment", "comment"];
12129 } else if (stream.eat("*")) {
12130 state.tokenize = tokenCComment;
12131 return tokenCComment(stream, state);
12132 } else {
12133 return ["operator", "operator"];
12134 }
12135 },
12136 "@": function(stream) {
12137 if (stream.eat("{")) return [null, "interpolation"];
12138 if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/i, false)) return false;
12139 stream.eatWhile(/[\w\\\-]/);
12140 if (stream.match(/^\s*:/, false))
12141 return ["variable-2", "variable-definition"];
12142 return ["variable-2", "variable"];
12143 },
12144 "&": function() {
12145 return ["atom", "atom"];
12146 }
12147 },
12148 name: "css",
12149 helperType: "less"
12150 });
12151
12152 CodeMirror.defineMIME("text/x-gss", {
12153 documentTypes: documentTypes,
12154 mediaTypes: mediaTypes,
12155 mediaFeatures: mediaFeatures,
12156 propertyKeywords: propertyKeywords,
12157 nonStandardPropertyKeywords: nonStandardPropertyKeywords,
12158 fontProperties: fontProperties,
12159 counterDescriptors: counterDescriptors,
12160 colorKeywords: colorKeywords,
12161 valueKeywords: valueKeywords,
12162 supportsAtComponent: true,
12163 tokenHooks: {
12164 "/": function(stream, state) {
12165 if (!stream.eat("*")) return false;
12166 state.tokenize = tokenCComment;
12167 return tokenCComment(stream, state);
12168 }
12169 },
12170 name: "css",
12171 helperType: "gss"
12172 });
12173
12174 });
12175
12176 },{"../../lib/codemirror":14}],16:[function(require,module,exports){
12177 // CodeMirror, copyright (c) by Marijn Haverbeke and others
12178 // Distributed under an MIT license: https://codemirror.net/LICENSE
12179
12180 (function(mod) {
12181 if (typeof exports == "object" && typeof module == "object") // CommonJS
12182 mod(require("../../lib/codemirror"), require("../xml/xml"), require("../javascript/javascript"), require("../css/css"));
12183 else if (typeof define == "function" && define.amd) // AMD
12184 define(["../../lib/codemirror", "../xml/xml", "../javascript/javascript", "../css/css"], mod);
12185 else // Plain browser env
12186 mod(CodeMirror);
12187 })(function(CodeMirror) {
12188 "use strict";
12189
12190 var defaultTags = {
12191 script: [
12192 ["lang", /(javascript|babel)/i, "javascript"],
12193 ["type", /^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^module$|^$/i, "javascript"],
12194 ["type", /./, "text/plain"],
12195 [null, null, "javascript"]
12196 ],
12197 style: [
12198 ["lang", /^css$/i, "css"],
12199 ["type", /^(text\/)?(x-)?(stylesheet|css)$/i, "css"],
12200 ["type", /./, "text/plain"],
12201 [null, null, "css"]
12202 ]
12203 };
12204
12205 function maybeBackup(stream, pat, style) {
12206 var cur = stream.current(), close = cur.search(pat);
12207 if (close > -1) {
12208 stream.backUp(cur.length - close);
12209 } else if (cur.match(/<\/?$/)) {
12210 stream.backUp(cur.length);
12211 if (!stream.match(pat, false)) stream.match(cur);
12212 }
12213 return style;
12214 }
12215
12216 var attrRegexpCache = {};
12217 function getAttrRegexp(attr) {
12218 var regexp = attrRegexpCache[attr];
12219 if (regexp) return regexp;
12220 return attrRegexpCache[attr] = new RegExp("\\s+" + attr + "\\s*=\\s*('|\")?([^'\"]+)('|\")?\\s*");
12221 }
12222
12223 function getAttrValue(text, attr) {
12224 var match = text.match(getAttrRegexp(attr))
12225 return match ? /^\s*(.*?)\s*$/.exec(match[2])[1] : ""
12226 }
12227
12228 function getTagRegexp(tagName, anchored) {
12229 return new RegExp((anchored ? "^" : "") + "<\/\s*" + tagName + "\s*>", "i");
12230 }
12231
12232 function addTags(from, to) {
12233 for (var tag in from) {
12234 var dest = to[tag] || (to[tag] = []);
12235 var source = from[tag];
12236 for (var i = source.length - 1; i >= 0; i--)
12237 dest.unshift(source[i])
12238 }
12239 }
12240
12241 function findMatchingMode(tagInfo, tagText) {
12242 for (var i = 0; i < tagInfo.length; i++) {
12243 var spec = tagInfo[i];
12244 if (!spec[0] || spec[1].test(getAttrValue(tagText, spec[0]))) return spec[2];
12245 }
12246 }
12247
12248 CodeMirror.defineMode("htmlmixed", function (config, parserConfig) {
12249 var htmlMode = CodeMirror.getMode(config, {
12250 name: "xml",
12251 htmlMode: true,
12252 multilineTagIndentFactor: parserConfig.multilineTagIndentFactor,
12253 multilineTagIndentPastTag: parserConfig.multilineTagIndentPastTag
12254 });
12255
12256 var tags = {};
12257 var configTags = parserConfig && parserConfig.tags, configScript = parserConfig && parserConfig.scriptTypes;
12258 addTags(defaultTags, tags);
12259 if (configTags) addTags(configTags, tags);
12260 if (configScript) for (var i = configScript.length - 1; i >= 0; i--)
12261 tags.script.unshift(["type", configScript[i].matches, configScript[i].mode])
12262
12263 function html(stream, state) {
12264 var style = htmlMode.token(stream, state.htmlState), tag = /\btag\b/.test(style), tagName
12265 if (tag && !/[<>\s\/]/.test(stream.current()) &&
12266 (tagName = state.htmlState.tagName && state.htmlState.tagName.toLowerCase()) &&
12267 tags.hasOwnProperty(tagName)) {
12268 state.inTag = tagName + " "
12269 } else if (state.inTag && tag && />$/.test(stream.current())) {
12270 var inTag = /^([\S]+) (.*)/.exec(state.inTag)
12271 state.inTag = null
12272 var modeSpec = stream.current() == ">" && findMatchingMode(tags[inTag[1]], inTag[2])
12273 var mode = CodeMirror.getMode(config, modeSpec)
12274 var endTagA = getTagRegexp(inTag[1], true), endTag = getTagRegexp(inTag[1], false);
12275 state.token = function (stream, state) {
12276 if (stream.match(endTagA, false)) {
12277 state.token = html;
12278 state.localState = state.localMode = null;
12279 return null;
12280 }
12281 return maybeBackup(stream, endTag, state.localMode.token(stream, state.localState));
12282 };
12283 state.localMode = mode;
12284 state.localState = CodeMirror.startState(mode, htmlMode.indent(state.htmlState, "", ""));
12285 } else if (state.inTag) {
12286 state.inTag += stream.current()
12287 if (stream.eol()) state.inTag += " "
12288 }
12289 return style;
12290 };
12291
12292 return {
12293 startState: function () {
12294 var state = CodeMirror.startState(htmlMode);
12295 return {token: html, inTag: null, localMode: null, localState: null, htmlState: state};
12296 },
12297
12298 copyState: function (state) {
12299 var local;
12300 if (state.localState) {
12301 local = CodeMirror.copyState(state.localMode, state.localState);
12302 }
12303 return {token: state.token, inTag: state.inTag,
12304 localMode: state.localMode, localState: local,
12305 htmlState: CodeMirror.copyState(htmlMode, state.htmlState)};
12306 },
12307
12308 token: function (stream, state) {
12309 return state.token(stream, state);
12310 },
12311
12312 indent: function (state, textAfter, line) {
12313 if (!state.localMode || /^\s*<\//.test(textAfter))
12314 return htmlMode.indent(state.htmlState, textAfter, line);
12315 else if (state.localMode.indent)
12316 return state.localMode.indent(state.localState, textAfter, line);
12317 else
12318 return CodeMirror.Pass;
12319 },
12320
12321 innerMode: function (state) {
12322 return {state: state.localState || state.htmlState, mode: state.localMode || htmlMode};
12323 }
12324 };
12325 }, "xml", "javascript", "css");
12326
12327 CodeMirror.defineMIME("text/html", "htmlmixed");
12328 });
12329
12330 },{"../../lib/codemirror":14,"../css/css":15,"../javascript/javascript":17,"../xml/xml":18}],17:[function(require,module,exports){
12331 // CodeMirror, copyright (c) by Marijn Haverbeke and others
12332 // Distributed under an MIT license: https://codemirror.net/LICENSE
12333
12334 (function(mod) {
12335 if (typeof exports == "object" && typeof module == "object") // CommonJS
12336 mod(require("../../lib/codemirror"));
12337 else if (typeof define == "function" && define.amd) // AMD
12338 define(["../../lib/codemirror"], mod);
12339 else // Plain browser env
12340 mod(CodeMirror);
12341 })(function(CodeMirror) {
12342 "use strict";
12343
12344 CodeMirror.defineMode("javascript", function(config, parserConfig) {
12345 var indentUnit = config.indentUnit;
12346 var statementIndent = parserConfig.statementIndent;
12347 var jsonldMode = parserConfig.jsonld;
12348 var jsonMode = parserConfig.json || jsonldMode;
12349 var isTS = parserConfig.typescript;
12350 var wordRE = parserConfig.wordCharacters || /[\w$\xa1-\uffff]/;
12351
12352 // Tokenizer
12353
12354 var keywords = function(){
12355 function kw(type) {return {type: type, style: "keyword"};}
12356 var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"), D = kw("keyword d");
12357 var operator = kw("operator"), atom = {type: "atom", style: "atom"};
12358
12359 return {
12360 "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
12361 "return": D, "break": D, "continue": D, "new": kw("new"), "delete": C, "void": C, "throw": C,
12362 "debugger": kw("debugger"), "var": kw("var"), "const": kw("var"), "let": kw("var"),
12363 "function": kw("function"), "catch": kw("catch"),
12364 "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
12365 "in": operator, "typeof": operator, "instanceof": operator,
12366 "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom,
12367 "this": kw("this"), "class": kw("class"), "super": kw("atom"),
12368 "yield": C, "export": kw("export"), "import": kw("import"), "extends": C,
12369 "await": C
12370 };
12371 }();
12372
12373 var isOperatorChar = /[+\-*&%=<>!?|~^@]/;
12374 var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/;
12375
12376 function readRegexp(stream) {
12377 var escaped = false, next, inSet = false;
12378 while ((next = stream.next()) != null) {
12379 if (!escaped) {
12380 if (next == "/" && !inSet) return;
12381 if (next == "[") inSet = true;
12382 else if (inSet && next == "]") inSet = false;
12383 }
12384 escaped = !escaped && next == "\\";
12385 }
12386 }
12387
12388 // Used as scratch variables to communicate multiple values without
12389 // consing up tons of objects.
12390 var type, content;
12391 function ret(tp, style, cont) {
12392 type = tp; content = cont;
12393 return style;
12394 }
12395 function tokenBase(stream, state) {
12396 var ch = stream.next();
12397 if (ch == '"' || ch == "'") {
12398 state.tokenize = tokenString(ch);
12399 return state.tokenize(stream, state);
12400 } else if (ch == "." && stream.match(/^\d+(?:[eE][+\-]?\d+)?/)) {
12401 return ret("number", "number");
12402 } else if (ch == "." && stream.match("..")) {
12403 return ret("spread", "meta");
12404 } else if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
12405 return ret(ch);
12406 } else if (ch == "=" && stream.eat(">")) {
12407 return ret("=>", "operator");
12408 } else if (ch == "0" && stream.match(/^(?:x[\da-f]+|o[0-7]+|b[01]+)n?/i)) {
12409 return ret("number", "number");
12410 } else if (/\d/.test(ch)) {
12411 stream.match(/^\d*(?:n|(?:\.\d*)?(?:[eE][+\-]?\d+)?)?/);
12412 return ret("number", "number");
12413 } else if (ch == "/") {
12414 if (stream.eat("*")) {
12415 state.tokenize = tokenComment;
12416 return tokenComment(stream, state);
12417 } else if (stream.eat("/")) {
12418 stream.skipToEnd();
12419 return ret("comment", "comment");
12420 } else if (expressionAllowed(stream, state, 1)) {
12421 readRegexp(stream);
12422 stream.match(/^\b(([gimyus])(?![gimyus]*\2))+\b/);
12423 return ret("regexp", "string-2");
12424 } else {
12425 stream.eat("=");
12426 return ret("operator", "operator", stream.current());
12427 }
12428 } else if (ch == "`") {
12429 state.tokenize = tokenQuasi;
12430 return tokenQuasi(stream, state);
12431 } else if (ch == "#") {
12432 stream.skipToEnd();
12433 return ret("error", "error");
12434 } else if (isOperatorChar.test(ch)) {
12435 if (ch != ">" || !state.lexical || state.lexical.type != ">") {
12436 if (stream.eat("=")) {
12437 if (ch == "!" || ch == "=") stream.eat("=")
12438 } else if (/[<>*+\-]/.test(ch)) {
12439 stream.eat(ch)
12440 if (ch == ">") stream.eat(ch)
12441 }
12442 }
12443 return ret("operator", "operator", stream.current());
12444 } else if (wordRE.test(ch)) {
12445 stream.eatWhile(wordRE);
12446 var word = stream.current()
12447 if (state.lastType != ".") {
12448 if (keywords.propertyIsEnumerable(word)) {
12449 var kw = keywords[word]
12450 return ret(kw.type, kw.style, word)
12451 }
12452 if (word == "async" && stream.match(/^(\s|\/\*.*?\*\/)*[\[\(\w]/, false))
12453 return ret("async", "keyword", word)
12454 }
12455 return ret("variable", "variable", word)
12456 }
12457 }
12458
12459 function tokenString(quote) {
12460 return function(stream, state) {
12461 var escaped = false, next;
12462 if (jsonldMode && stream.peek() == "@" && stream.match(isJsonldKeyword)){
12463 state.tokenize = tokenBase;
12464 return ret("jsonld-keyword", "meta");
12465 }
12466 while ((next = stream.next()) != null) {
12467 if (next == quote && !escaped) break;
12468 escaped = !escaped && next == "\\";
12469 }
12470 if (!escaped) state.tokenize = tokenBase;
12471 return ret("string", "string");
12472 };
12473 }
12474
12475 function tokenComment(stream, state) {
12476 var maybeEnd = false, ch;
12477 while (ch = stream.next()) {
12478 if (ch == "/" && maybeEnd) {
12479 state.tokenize = tokenBase;
12480 break;
12481 }
12482 maybeEnd = (ch == "*");
12483 }
12484 return ret("comment", "comment");
12485 }
12486
12487 function tokenQuasi(stream, state) {
12488 var escaped = false, next;
12489 while ((next = stream.next()) != null) {
12490 if (!escaped && (next == "`" || next == "$" && stream.eat("{"))) {
12491 state.tokenize = tokenBase;
12492 break;
12493 }
12494 escaped = !escaped && next == "\\";
12495 }
12496 return ret("quasi", "string-2", stream.current());
12497 }
12498
12499 var brackets = "([{}])";
12500 // This is a crude lookahead trick to try and notice that we're
12501 // parsing the argument patterns for a fat-arrow function before we
12502 // actually hit the arrow token. It only works if the arrow is on
12503 // the same line as the arguments and there's no strange noise
12504 // (comments) in between. Fallback is to only notice when we hit the
12505 // arrow, and not declare the arguments as locals for the arrow
12506 // body.
12507 function findFatArrow(stream, state) {
12508 if (state.fatArrowAt) state.fatArrowAt = null;
12509 var arrow = stream.string.indexOf("=>", stream.start);
12510 if (arrow < 0) return;
12511
12512 if (isTS) { // Try to skip TypeScript return type declarations after the arguments
12513 var m = /:\s*(?:\w+(?:<[^>]*>|\[\])?|\{[^}]*\})\s*$/.exec(stream.string.slice(stream.start, arrow))
12514 if (m) arrow = m.index
12515 }
12516
12517 var depth = 0, sawSomething = false;
12518 for (var pos = arrow - 1; pos >= 0; --pos) {
12519 var ch = stream.string.charAt(pos);
12520 var bracket = brackets.indexOf(ch);
12521 if (bracket >= 0 && bracket < 3) {
12522 if (!depth) { ++pos; break; }
12523 if (--depth == 0) { if (ch == "(") sawSomething = true; break; }
12524 } else if (bracket >= 3 && bracket < 6) {
12525 ++depth;
12526 } else if (wordRE.test(ch)) {
12527 sawSomething = true;
12528 } else if (/["'\/]/.test(ch)) {
12529 return;
12530 } else if (sawSomething && !depth) {
12531 ++pos;
12532 break;
12533 }
12534 }
12535 if (sawSomething && !depth) state.fatArrowAt = pos;
12536 }
12537
12538 // Parser
12539
12540 var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true, "jsonld-keyword": true};
12541
12542 function JSLexical(indented, column, type, align, prev, info) {
12543 this.indented = indented;
12544 this.column = column;
12545 this.type = type;
12546 this.prev = prev;
12547 this.info = info;
12548 if (align != null) this.align = align;
12549 }
12550
12551 function inScope(state, varname) {
12552 for (var v = state.localVars; v; v = v.next)
12553 if (v.name == varname) return true;
12554 for (var cx = state.context; cx; cx = cx.prev) {
12555 for (var v = cx.vars; v; v = v.next)
12556 if (v.name == varname) return true;
12557 }
12558 }
12559
12560 function parseJS(state, style, type, content, stream) {
12561 var cc = state.cc;
12562 // Communicate our context to the combinators.
12563 // (Less wasteful than consing up a hundred closures on every call.)
12564 cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; cx.style = style;
12565
12566 if (!state.lexical.hasOwnProperty("align"))
12567 state.lexical.align = true;
12568
12569 while(true) {
12570 var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;
12571 if (combinator(type, content)) {
12572 while(cc.length && cc[cc.length - 1].lex)
12573 cc.pop()();
12574 if (cx.marked) return cx.marked;
12575 if (type == "variable" && inScope(state, content)) return "variable-2";
12576 return style;
12577 }
12578 }
12579 }
12580
12581 // Combinator utils
12582
12583 var cx = {state: null, column: null, marked: null, cc: null};
12584 function pass() {
12585 for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
12586 }
12587 function cont() {
12588 pass.apply(null, arguments);
12589 return true;
12590 }
12591 function inList(name, list) {
12592 for (var v = list; v; v = v.next) if (v.name == name) return true
12593 return false;
12594 }
12595 function register(varname) {
12596 var state = cx.state;
12597 cx.marked = "def";
12598 if (state.context) {
12599 if (state.lexical.info == "var" && state.context && state.context.block) {
12600 // FIXME function decls are also not block scoped
12601 var newContext = registerVarScoped(varname, state.context)
12602 if (newContext != null) {
12603 state.context = newContext
12604 return
12605 }
12606 } else if (!inList(varname, state.localVars)) {
12607 state.localVars = new Var(varname, state.localVars)
12608 return
12609 }
12610 }
12611 // Fall through means this is global
12612 if (parserConfig.globalVars && !inList(varname, state.globalVars))
12613 state.globalVars = new Var(varname, state.globalVars)
12614 }
12615 function registerVarScoped(varname, context) {
12616 if (!context) {
12617 return null
12618 } else if (context.block) {
12619 var inner = registerVarScoped(varname, context.prev)
12620 if (!inner) return null
12621 if (inner == context.prev) return context
12622 return new Context(inner, context.vars, true)
12623 } else if (inList(varname, context.vars)) {
12624 return context
12625 } else {
12626 return new Context(context.prev, new Var(varname, context.vars), false)
12627 }
12628 }
12629
12630 function isModifier(name) {
12631 return name == "public" || name == "private" || name == "protected" || name == "abstract" || name == "readonly"
12632 }
12633
12634 // Combinators
12635
12636 function Context(prev, vars, block) { this.prev = prev; this.vars = vars; this.block = block }
12637 function Var(name, next) { this.name = name; this.next = next }
12638
12639 var defaultVars = new Var("this", new Var("arguments", null))
12640 function pushcontext() {
12641 cx.state.context = new Context(cx.state.context, cx.state.localVars, false)
12642 cx.state.localVars = defaultVars
12643 }
12644 function pushblockcontext() {
12645 cx.state.context = new Context(cx.state.context, cx.state.localVars, true)
12646 cx.state.localVars = null
12647 }
12648 function popcontext() {
12649 cx.state.localVars = cx.state.context.vars
12650 cx.state.context = cx.state.context.prev
12651 }
12652 popcontext.lex = true
12653 function pushlex(type, info) {
12654 var result = function() {
12655 var state = cx.state, indent = state.indented;
12656 if (state.lexical.type == "stat") indent = state.lexical.indented;
12657 else for (var outer = state.lexical; outer && outer.type == ")" && outer.align; outer = outer.prev)
12658 indent = outer.indented;
12659 state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info);
12660 };
12661 result.lex = true;
12662 return result;
12663 }
12664 function poplex() {
12665 var state = cx.state;
12666 if (state.lexical.prev) {
12667 if (state.lexical.type == ")")
12668 state.indented = state.lexical.indented;
12669 state.lexical = state.lexical.prev;
12670 }
12671 }
12672 poplex.lex = true;
12673
12674 function expect(wanted) {
12675 function exp(type) {
12676 if (type == wanted) return cont();
12677 else if (wanted == ";" || type == "}" || type == ")" || type == "]") return pass();
12678 else return cont(exp);
12679 };
12680 return exp;
12681 }
12682
12683 function statement(type, value) {
12684 if (type == "var") return cont(pushlex("vardef", value), vardef, expect(";"), poplex);
12685 if (type == "keyword a") return cont(pushlex("form"), parenExpr, statement, poplex);
12686 if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
12687 if (type == "keyword d") return cx.stream.match(/^\s*$/, false) ? cont() : cont(pushlex("stat"), maybeexpression, expect(";"), poplex);
12688 if (type == "debugger") return cont(expect(";"));
12689 if (type == "{") return cont(pushlex("}"), pushblockcontext, block, poplex, popcontext);
12690 if (type == ";") return cont();
12691 if (type == "if") {
12692 if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex)
12693 cx.state.cc.pop()();
12694 return cont(pushlex("form"), parenExpr, statement, poplex, maybeelse);
12695 }
12696 if (type == "function") return cont(functiondef);
12697 if (type == "for") return cont(pushlex("form"), forspec, statement, poplex);
12698 if (type == "class" || (isTS && value == "interface")) {
12699 cx.marked = "keyword"
12700 return cont(pushlex("form", type == "class" ? type : value), className, poplex)
12701 }
12702 if (type == "variable") {
12703 if (isTS && value == "declare") {
12704 cx.marked = "keyword"
12705 return cont(statement)
12706 } else if (isTS && (value == "module" || value == "enum" || value == "type") && cx.stream.match(/^\s*\w/, false)) {
12707 cx.marked = "keyword"
12708 if (value == "enum") return cont(enumdef);
12709 else if (value == "type") return cont(typename, expect("operator"), typeexpr, expect(";"));
12710 else return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex)
12711 } else if (isTS && value == "namespace") {
12712 cx.marked = "keyword"
12713 return cont(pushlex("form"), expression, statement, poplex)
12714 } else if (isTS && value == "abstract") {
12715 cx.marked = "keyword"
12716 return cont(statement)
12717 } else {
12718 return cont(pushlex("stat"), maybelabel);
12719 }
12720 }
12721 if (type == "switch") return cont(pushlex("form"), parenExpr, expect("{"), pushlex("}", "switch"), pushblockcontext,
12722 block, poplex, poplex, popcontext);
12723 if (type == "case") return cont(expression, expect(":"));
12724 if (type == "default") return cont(expect(":"));
12725 if (type == "catch") return cont(pushlex("form"), pushcontext, maybeCatchBinding, statement, poplex, popcontext);
12726 if (type == "export") return cont(pushlex("stat"), afterExport, poplex);
12727 if (type == "import") return cont(pushlex("stat"), afterImport, poplex);
12728 if (type == "async") return cont(statement)
12729 if (value == "@") return cont(expression, statement)
12730 return pass(pushlex("stat"), expression, expect(";"), poplex);
12731 }
12732 function maybeCatchBinding(type) {
12733 if (type == "(") return cont(funarg, expect(")"))
12734 }
12735 function expression(type, value) {
12736 return expressionInner(type, value, false);
12737 }
12738 function expressionNoComma(type, value) {
12739 return expressionInner(type, value, true);
12740 }
12741 function parenExpr(type) {
12742 if (type != "(") return pass()
12743 return cont(pushlex(")"), expression, expect(")"), poplex)
12744 }
12745 function expressionInner(type, value, noComma) {
12746 if (cx.state.fatArrowAt == cx.stream.start) {
12747 var body = noComma ? arrowBodyNoComma : arrowBody;
12748 if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, expect("=>"), body, popcontext);
12749 else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext);
12750 }
12751
12752 var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
12753 if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
12754 if (type == "function") return cont(functiondef, maybeop);
12755 if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword"; return cont(pushlex("form"), classExpression, poplex); }
12756 if (type == "keyword c" || type == "async") return cont(noComma ? expressionNoComma : expression);
12757 if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop);
12758 if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression);
12759 if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop);
12760 if (type == "{") return contCommasep(objprop, "}", null, maybeop);
12761 if (type == "quasi") return pass(quasi, maybeop);
12762 if (type == "new") return cont(maybeTarget(noComma));
12763 if (type == "import") return cont(expression);
12764 return cont();
12765 }
12766 function maybeexpression(type) {
12767 if (type.match(/[;\}\)\],]/)) return pass();
12768 return pass(expression);
12769 }
12770
12771 function maybeoperatorComma(type, value) {
12772 if (type == ",") return cont(expression);
12773 return maybeoperatorNoComma(type, value, false);
12774 }
12775 function maybeoperatorNoComma(type, value, noComma) {
12776 var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma;
12777 var expr = noComma == false ? expression : expressionNoComma;
12778 if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext);
12779 if (type == "operator") {
12780 if (/\+\+|--/.test(value) || isTS && value == "!") return cont(me);
12781 if (isTS && value == "<" && cx.stream.match(/^([^>]|<.*?>)*>\s*\(/, false))
12782 return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, me);
12783 if (value == "?") return cont(expression, expect(":"), expr);
12784 return cont(expr);
12785 }
12786 if (type == "quasi") { return pass(quasi, me); }
12787 if (type == ";") return;
12788 if (type == "(") return contCommasep(expressionNoComma, ")", "call", me);
12789 if (type == ".") return cont(property, me);
12790 if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me);
12791 if (isTS && value == "as") { cx.marked = "keyword"; return cont(typeexpr, me) }
12792 if (type == "regexp") {
12793 cx.state.lastType = cx.marked = "operator"
12794 cx.stream.backUp(cx.stream.pos - cx.stream.start - 1)
12795 return cont(expr)
12796 }
12797 }
12798 function quasi(type, value) {
12799 if (type != "quasi") return pass();
12800 if (value.slice(value.length - 2) != "${") return cont(quasi);
12801 return cont(expression, continueQuasi);
12802 }
12803 function continueQuasi(type) {
12804 if (type == "}") {
12805 cx.marked = "string-2";
12806 cx.state.tokenize = tokenQuasi;
12807 return cont(quasi);
12808 }
12809 }
12810 function arrowBody(type) {
12811 findFatArrow(cx.stream, cx.state);
12812 return pass(type == "{" ? statement : expression);
12813 }
12814 function arrowBodyNoComma(type) {
12815 findFatArrow(cx.stream, cx.state);
12816 return pass(type == "{" ? statement : expressionNoComma);
12817 }
12818 function maybeTarget(noComma) {
12819 return function(type) {
12820 if (type == ".") return cont(noComma ? targetNoComma : target);
12821 else if (type == "variable" && isTS) return cont(maybeTypeArgs, noComma ? maybeoperatorNoComma : maybeoperatorComma)
12822 else return pass(noComma ? expressionNoComma : expression);
12823 };
12824 }
12825 function target(_, value) {
12826 if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorComma); }
12827 }
12828 function targetNoComma(_, value) {
12829 if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorNoComma); }
12830 }
12831 function maybelabel(type) {
12832 if (type == ":") return cont(poplex, statement);
12833 return pass(maybeoperatorComma, expect(";"), poplex);
12834 }
12835 function property(type) {
12836 if (type == "variable") {cx.marked = "property"; return cont();}
12837 }
12838 function objprop(type, value) {
12839 if (type == "async") {
12840 cx.marked = "property";
12841 return cont(objprop);
12842 } else if (type == "variable" || cx.style == "keyword") {
12843 cx.marked = "property";
12844 if (value == "get" || value == "set") return cont(getterSetter);
12845 var m // Work around fat-arrow-detection complication for detecting typescript typed arrow params
12846 if (isTS && cx.state.fatArrowAt == cx.stream.start && (m = cx.stream.match(/^\s*:\s*/, false)))
12847 cx.state.fatArrowAt = cx.stream.pos + m[0].length
12848 return cont(afterprop);
12849 } else if (type == "number" || type == "string") {
12850 cx.marked = jsonldMode ? "property" : (cx.style + " property");
12851 return cont(afterprop);
12852 } else if (type == "jsonld-keyword") {
12853 return cont(afterprop);
12854 } else if (isTS && isModifier(value)) {
12855 cx.marked = "keyword"
12856 return cont(objprop)
12857 } else if (type == "[") {
12858 return cont(expression, maybetype, expect("]"), afterprop);
12859 } else if (type == "spread") {
12860 return cont(expressionNoComma, afterprop);
12861 } else if (value == "*") {
12862 cx.marked = "keyword";
12863 return cont(objprop);
12864 } else if (type == ":") {
12865 return pass(afterprop)
12866 }
12867 }
12868 function getterSetter(type) {
12869 if (type != "variable") return pass(afterprop);
12870 cx.marked = "property";
12871 return cont(functiondef);
12872 }
12873 function afterprop(type) {
12874 if (type == ":") return cont(expressionNoComma);
12875 if (type == "(") return pass(functiondef);
12876 }
12877 function commasep(what, end, sep) {
12878 function proceed(type, value) {
12879 if (sep ? sep.indexOf(type) > -1 : type == ",") {
12880 var lex = cx.state.lexical;
12881 if (lex.info == "call") lex.pos = (lex.pos || 0) + 1;
12882 return cont(function(type, value) {
12883 if (type == end || value == end) return pass()
12884 return pass(what)
12885 }, proceed);
12886 }
12887 if (type == end || value == end) return cont();
12888 if (sep && sep.indexOf(";") > -1) return pass(what)
12889 return cont(expect(end));
12890 }
12891 return function(type, value) {
12892 if (type == end || value == end) return cont();
12893 return pass(what, proceed);
12894 };
12895 }
12896 function contCommasep(what, end, info) {
12897 for (var i = 3; i < arguments.length; i++)
12898 cx.cc.push(arguments[i]);
12899 return cont(pushlex(end, info), commasep(what, end), poplex);
12900 }
12901 function block(type) {
12902 if (type == "}") return cont();
12903 return pass(statement, block);
12904 }
12905 function maybetype(type, value) {
12906 if (isTS) {
12907 if (type == ":" || value == "in") return cont(typeexpr);
12908 if (value == "?") return cont(maybetype);
12909 }
12910 }
12911 function mayberettype(type) {
12912 if (isTS && type == ":") {
12913 if (cx.stream.match(/^\s*\w+\s+is\b/, false)) return cont(expression, isKW, typeexpr)
12914 else return cont(typeexpr)
12915 }
12916 }
12917 function isKW(_, value) {
12918 if (value == "is") {
12919 cx.marked = "keyword"
12920 return cont()
12921 }
12922 }
12923 function typeexpr(type, value) {
12924 if (value == "keyof" || value == "typeof" || value == "infer") {
12925 cx.marked = "keyword"
12926 return cont(value == "typeof" ? expressionNoComma : typeexpr)
12927 }
12928 if (type == "variable" || value == "void") {
12929 cx.marked = "type"
12930 return cont(afterType)
12931 }
12932 if (value == "|" || value == "&") return cont(typeexpr)
12933 if (type == "string" || type == "number" || type == "atom") return cont(afterType);
12934 if (type == "[") return cont(pushlex("]"), commasep(typeexpr, "]", ","), poplex, afterType)
12935 if (type == "{") return cont(pushlex("}"), commasep(typeprop, "}", ",;"), poplex, afterType)
12936 if (type == "(") return cont(commasep(typearg, ")"), maybeReturnType, afterType)
12937 if (type == "<") return cont(commasep(typeexpr, ">"), typeexpr)
12938 }
12939 function maybeReturnType(type) {
12940 if (type == "=>") return cont(typeexpr)
12941 }
12942 function typeprop(type, value) {
12943 if (type == "variable" || cx.style == "keyword") {
12944 cx.marked = "property"
12945 return cont(typeprop)
12946 } else if (value == "?" || type == "number" || type == "string") {
12947 return cont(typeprop)
12948 } else if (type == ":") {
12949 return cont(typeexpr)
12950 } else if (type == "[") {
12951 return cont(expect("variable"), maybetype, expect("]"), typeprop)
12952 } else if (type == "(") {
12953 return pass(functiondecl, typeprop)
12954 }
12955 }
12956 function typearg(type, value) {
12957 if (type == "variable" && cx.stream.match(/^\s*[?:]/, false) || value == "?") return cont(typearg)
12958 if (type == ":") return cont(typeexpr)
12959 if (type == "spread") return cont(typearg)
12960 return pass(typeexpr)
12961 }
12962 function afterType(type, value) {
12963 if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
12964 if (value == "|" || type == "." || value == "&") return cont(typeexpr)
12965 if (type == "[") return cont(typeexpr, expect("]"), afterType)
12966 if (value == "extends" || value == "implements") { cx.marked = "keyword"; return cont(typeexpr) }
12967 if (value == "?") return cont(typeexpr, expect(":"), typeexpr)
12968 }
12969 function maybeTypeArgs(_, value) {
12970 if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
12971 }
12972 function typeparam() {
12973 return pass(typeexpr, maybeTypeDefault)
12974 }
12975 function maybeTypeDefault(_, value) {
12976 if (value == "=") return cont(typeexpr)
12977 }
12978 function vardef(_, value) {
12979 if (value == "enum") {cx.marked = "keyword"; return cont(enumdef)}
12980 return pass(pattern, maybetype, maybeAssign, vardefCont);
12981 }
12982 function pattern(type, value) {
12983 if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(pattern) }
12984 if (type == "variable") { register(value); return cont(); }
12985 if (type == "spread") return cont(pattern);
12986 if (type == "[") return contCommasep(eltpattern, "]");
12987 if (type == "{") return contCommasep(proppattern, "}");
12988 }
12989 function proppattern(type, value) {
12990 if (type == "variable" && !cx.stream.match(/^\s*:/, false)) {
12991 register(value);
12992 return cont(maybeAssign);
12993 }
12994 if (type == "variable") cx.marked = "property";
12995 if (type == "spread") return cont(pattern);
12996 if (type == "}") return pass();
12997 if (type == "[") return cont(expression, expect(']'), expect(':'), proppattern);
12998 return cont(expect(":"), pattern, maybeAssign);
12999 }
13000 function eltpattern() {
13001 return pass(pattern, maybeAssign)
13002 }
13003 function maybeAssign(_type, value) {
13004 if (value == "=") return cont(expressionNoComma);
13005 }
13006 function vardefCont(type) {
13007 if (type == ",") return cont(vardef);
13008 }
13009 function maybeelse(type, value) {
13010 if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex);
13011 }
13012 function forspec(type, value) {
13013 if (value == "await") return cont(forspec);
13014 if (type == "(") return cont(pushlex(")"), forspec1, poplex);
13015 }
13016 function forspec1(type) {
13017 if (type == "var") return cont(vardef, forspec2);
13018 if (type == "variable") return cont(forspec2);
13019 return pass(forspec2)
13020 }
13021 function forspec2(type, value) {
13022 if (type == ")") return cont()
13023 if (type == ";") return cont(forspec2)
13024 if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression, forspec2) }
13025 return pass(expression, forspec2)
13026 }
13027 function functiondef(type, value) {
13028 if (value == "*") {cx.marked = "keyword"; return cont(functiondef);}
13029 if (type == "variable") {register(value); return cont(functiondef);}
13030 if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, statement, popcontext);
13031 if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondef)
13032 }
13033 function functiondecl(type, value) {
13034 if (value == "*") {cx.marked = "keyword"; return cont(functiondecl);}
13035 if (type == "variable") {register(value); return cont(functiondecl);}
13036 if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, popcontext);
13037 if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondecl)
13038 }
13039 function typename(type, value) {
13040 if (type == "keyword" || type == "variable") {
13041 cx.marked = "type"
13042 return cont(typename)
13043 } else if (value == "<") {
13044 return cont(pushlex(">"), commasep(typeparam, ">"), poplex)
13045 }
13046 }
13047 function funarg(type, value) {
13048 if (value == "@") cont(expression, funarg)
13049 if (type == "spread") return cont(funarg);
13050 if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(funarg); }
13051 if (isTS && type == "this") return cont(maybetype, maybeAssign)
13052 return pass(pattern, maybetype, maybeAssign);
13053 }
13054 function classExpression(type, value) {
13055 // Class expressions may have an optional name.
13056 if (type == "variable") return className(type, value);
13057 return classNameAfter(type, value);
13058 }
13059 function className(type, value) {
13060 if (type == "variable") {register(value); return cont(classNameAfter);}
13061 }
13062 function classNameAfter(type, value) {
13063 if (value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, classNameAfter)
13064 if (value == "extends" || value == "implements" || (isTS && type == ",")) {
13065 if (value == "implements") cx.marked = "keyword";
13066 return cont(isTS ? typeexpr : expression, classNameAfter);
13067 }
13068 if (type == "{") return cont(pushlex("}"), classBody, poplex);
13069 }
13070 function classBody(type, value) {
13071 if (type == "async" ||
13072 (type == "variable" &&
13073 (value == "static" || value == "get" || value == "set" || (isTS && isModifier(value))) &&
13074 cx.stream.match(/^\s+[\w$\xa1-\uffff]/, false))) {
13075 cx.marked = "keyword";
13076 return cont(classBody);
13077 }
13078 if (type == "variable" || cx.style == "keyword") {
13079 cx.marked = "property";
13080 return cont(isTS ? classfield : functiondef, classBody);
13081 }
13082 if (type == "number" || type == "string") return cont(isTS ? classfield : functiondef, classBody);
13083 if (type == "[")
13084 return cont(expression, maybetype, expect("]"), isTS ? classfield : functiondef, classBody)
13085 if (value == "*") {
13086 cx.marked = "keyword";
13087 return cont(classBody);
13088 }
13089 if (isTS && type == "(") return pass(functiondecl, classBody)
13090 if (type == ";" || type == ",") return cont(classBody);
13091 if (type == "}") return cont();
13092 if (value == "@") return cont(expression, classBody)
13093 }
13094 function classfield(type, value) {
13095 if (value == "?") return cont(classfield)
13096 if (type == ":") return cont(typeexpr, maybeAssign)
13097 if (value == "=") return cont(expressionNoComma)
13098 var context = cx.state.lexical.prev, isInterface = context && context.info == "interface"
13099 return pass(isInterface ? functiondecl : functiondef)
13100 }
13101 function afterExport(type, value) {
13102 if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); }
13103 if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); }
13104 if (type == "{") return cont(commasep(exportField, "}"), maybeFrom, expect(";"));
13105 return pass(statement);
13106 }
13107 function exportField(type, value) {
13108 if (value == "as") { cx.marked = "keyword"; return cont(expect("variable")); }
13109 if (type == "variable") return pass(expressionNoComma, exportField);
13110 }
13111 function afterImport(type) {
13112 if (type == "string") return cont();
13113 if (type == "(") return pass(expression);
13114 return pass(importSpec, maybeMoreImports, maybeFrom);
13115 }
13116 function importSpec(type, value) {
13117 if (type == "{") return contCommasep(importSpec, "}");
13118 if (type == "variable") register(value);
13119 if (value == "*") cx.marked = "keyword";
13120 return cont(maybeAs);
13121 }
13122 function maybeMoreImports(type) {
13123 if (type == ",") return cont(importSpec, maybeMoreImports)
13124 }
13125 function maybeAs(_type, value) {
13126 if (value == "as") { cx.marked = "keyword"; return cont(importSpec); }
13127 }
13128 function maybeFrom(_type, value) {
13129 if (value == "from") { cx.marked = "keyword"; return cont(expression); }
13130 }
13131 function arrayLiteral(type) {
13132 if (type == "]") return cont();
13133 return pass(commasep(expressionNoComma, "]"));
13134 }
13135 function enumdef() {
13136 return pass(pushlex("form"), pattern, expect("{"), pushlex("}"), commasep(enummember, "}"), poplex, poplex)
13137 }
13138 function enummember() {
13139 return pass(pattern, maybeAssign);
13140 }
13141
13142 function isContinuedStatement(state, textAfter) {
13143 return state.lastType == "operator" || state.lastType == "," ||
13144 isOperatorChar.test(textAfter.charAt(0)) ||
13145 /[,.]/.test(textAfter.charAt(0));
13146 }
13147
13148 function expressionAllowed(stream, state, backUp) {
13149 return state.tokenize == tokenBase &&
13150 /^(?:operator|sof|keyword [bcd]|case|new|export|default|spread|[\[{}\(,;:]|=>)$/.test(state.lastType) ||
13151 (state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0))))
13152 }
13153
13154 // Interface
13155
13156 return {
13157 startState: function(basecolumn) {
13158 var state = {
13159 tokenize: tokenBase,
13160 lastType: "sof",
13161 cc: [],
13162 lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
13163 localVars: parserConfig.localVars,
13164 context: parserConfig.localVars && new Context(null, null, false),
13165 indented: basecolumn || 0
13166 };
13167 if (parserConfig.globalVars && typeof parserConfig.globalVars == "object")
13168 state.globalVars = parserConfig.globalVars;
13169 return state;
13170 },
13171
13172 token: function(stream, state) {
13173 if (stream.sol()) {
13174 if (!state.lexical.hasOwnProperty("align"))
13175 state.lexical.align = false;
13176 state.indented = stream.indentation();
13177 findFatArrow(stream, state);
13178 }
13179 if (state.tokenize != tokenComment && stream.eatSpace()) return null;
13180 var style = state.tokenize(stream, state);
13181 if (type == "comment") return style;
13182 state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type;
13183 return parseJS(state, style, type, content, stream);
13184 },
13185
13186 indent: function(state, textAfter) {
13187 if (state.tokenize == tokenComment) return CodeMirror.Pass;
13188 if (state.tokenize != tokenBase) return 0;
13189 var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical, top
13190 // Kludge to prevent 'maybelse' from blocking lexical scope pops
13191 if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) {
13192 var c = state.cc[i];
13193 if (c == poplex) lexical = lexical.prev;
13194 else if (c != maybeelse) break;
13195 }
13196 while ((lexical.type == "stat" || lexical.type == "form") &&
13197 (firstChar == "}" || ((top = state.cc[state.cc.length - 1]) &&
13198 (top == maybeoperatorComma || top == maybeoperatorNoComma) &&
13199 !/^[,\.=+\-*:?[\(]/.test(textAfter))))
13200 lexical = lexical.prev;
13201 if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat")
13202 lexical = lexical.prev;
13203 var type = lexical.type, closing = firstChar == type;
13204
13205 if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info.length + 1 : 0);
13206 else if (type == "form" && firstChar == "{") return lexical.indented;
13207 else if (type == "form") return lexical.indented + indentUnit;
13208 else if (type == "stat")
13209 return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0);
13210 else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false)
13211 return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
13212 else if (lexical.align) return lexical.column + (closing ? 0 : 1);
13213 else return lexical.indented + (closing ? 0 : indentUnit);
13214 },
13215
13216 electricInput: /^\s*(?:case .*?:|default:|\{|\})$/,
13217 blockCommentStart: jsonMode ? null : "/*",
13218 blockCommentEnd: jsonMode ? null : "*/",
13219 blockCommentContinue: jsonMode ? null : " * ",
13220 lineComment: jsonMode ? null : "//",
13221 fold: "brace",
13222 closeBrackets: "()[]{}''\"\"``",
13223
13224 helperType: jsonMode ? "json" : "javascript",
13225 jsonldMode: jsonldMode,
13226 jsonMode: jsonMode,
13227
13228 expressionAllowed: expressionAllowed,
13229
13230 skipExpression: function(state) {
13231 var top = state.cc[state.cc.length - 1]
13232 if (top == expression || top == expressionNoComma) state.cc.pop()
13233 }
13234 };
13235 });
13236
13237 CodeMirror.registerHelper("wordChars", "javascript", /[\w$]/);
13238
13239 CodeMirror.defineMIME("text/javascript", "javascript");
13240 CodeMirror.defineMIME("text/ecmascript", "javascript");
13241 CodeMirror.defineMIME("application/javascript", "javascript");
13242 CodeMirror.defineMIME("application/x-javascript", "javascript");
13243 CodeMirror.defineMIME("application/ecmascript", "javascript");
13244 CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
13245 CodeMirror.defineMIME("application/x-json", {name: "javascript", json: true});
13246 CodeMirror.defineMIME("application/ld+json", {name: "javascript", jsonld: true});
13247 CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true });
13248 CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true });
13249
13250 });
13251
13252 },{"../../lib/codemirror":14}],18:[function(require,module,exports){
13253 // CodeMirror, copyright (c) by Marijn Haverbeke and others
13254 // Distributed under an MIT license: https://codemirror.net/LICENSE
13255
13256 (function(mod) {
13257 if (typeof exports == "object" && typeof module == "object") // CommonJS
13258 mod(require("../../lib/codemirror"));
13259 else if (typeof define == "function" && define.amd) // AMD
13260 define(["../../lib/codemirror"], mod);
13261 else // Plain browser env
13262 mod(CodeMirror);
13263 })(function(CodeMirror) {
13264 "use strict";
13265
13266 var htmlConfig = {
13267 autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true,
13268 'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true,
13269 'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true,
13270 'track': true, 'wbr': true, 'menuitem': true},
13271 implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true,
13272 'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true,
13273 'th': true, 'tr': true},
13274 contextGrabbers: {
13275 'dd': {'dd': true, 'dt': true},
13276 'dt': {'dd': true, 'dt': true},
13277 'li': {'li': true},
13278 'option': {'option': true, 'optgroup': true},
13279 'optgroup': {'optgroup': true},
13280 'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true,
13281 'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true,
13282 'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true,
13283 'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true,
13284 'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true},
13285 'rp': {'rp': true, 'rt': true},
13286 'rt': {'rp': true, 'rt': true},
13287 'tbody': {'tbody': true, 'tfoot': true},
13288 'td': {'td': true, 'th': true},
13289 'tfoot': {'tbody': true},
13290 'th': {'td': true, 'th': true},
13291 'thead': {'tbody': true, 'tfoot': true},
13292 'tr': {'tr': true}
13293 },
13294 doNotIndent: {"pre": true},
13295 allowUnquoted: true,
13296 allowMissing: true,
13297 caseFold: true
13298 }
13299
13300 var xmlConfig = {
13301 autoSelfClosers: {},
13302 implicitlyClosed: {},
13303 contextGrabbers: {},
13304 doNotIndent: {},
13305 allowUnquoted: false,
13306 allowMissing: false,
13307 allowMissingTagName: false,
13308 caseFold: false
13309 }
13310
13311 CodeMirror.defineMode("xml", function(editorConf, config_) {
13312 var indentUnit = editorConf.indentUnit
13313 var config = {}
13314 var defaults = config_.htmlMode ? htmlConfig : xmlConfig
13315 for (var prop in defaults) config[prop] = defaults[prop]
13316 for (var prop in config_) config[prop] = config_[prop]
13317
13318 // Return variables for tokenizers
13319 var type, setStyle;
13320
13321 function inText(stream, state) {
13322 function chain(parser) {
13323 state.tokenize = parser;
13324 return parser(stream, state);
13325 }
13326
13327 var ch = stream.next();
13328 if (ch == "<") {
13329 if (stream.eat("!")) {
13330 if (stream.eat("[")) {
13331 if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));
13332 else return null;
13333 } else if (stream.match("--")) {
13334 return chain(inBlock("comment", "-->"));
13335 } else if (stream.match("DOCTYPE", true, true)) {
13336 stream.eatWhile(/[\w\._\-]/);
13337 return chain(doctype(1));
13338 } else {
13339 return null;
13340 }
13341 } else if (stream.eat("?")) {
13342 stream.eatWhile(/[\w\._\-]/);
13343 state.tokenize = inBlock("meta", "?>");
13344 return "meta";
13345 } else {
13346 type = stream.eat("/") ? "closeTag" : "openTag";
13347 state.tokenize = inTag;
13348 return "tag bracket";
13349 }
13350 } else if (ch == "&") {
13351 var ok;
13352 if (stream.eat("#")) {
13353 if (stream.eat("x")) {
13354 ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";");
13355 } else {
13356 ok = stream.eatWhile(/[\d]/) && stream.eat(";");
13357 }
13358 } else {
13359 ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";");
13360 }
13361 return ok ? "atom" : "error";
13362 } else {
13363 stream.eatWhile(/[^&<]/);
13364 return null;
13365 }
13366 }
13367 inText.isInText = true;
13368
13369 function inTag(stream, state) {
13370 var ch = stream.next();
13371 if (ch == ">" || (ch == "/" && stream.eat(">"))) {
13372 state.tokenize = inText;
13373 type = ch == ">" ? "endTag" : "selfcloseTag";
13374 return "tag bracket";
13375 } else if (ch == "=") {
13376 type = "equals";
13377 return null;
13378 } else if (ch == "<") {
13379 state.tokenize = inText;
13380 state.state = baseState;
13381 state.tagName = state.tagStart = null;
13382 var next = state.tokenize(stream, state);
13383 return next ? next + " tag error" : "tag error";
13384 } else if (/[\'\"]/.test(ch)) {
13385 state.tokenize = inAttribute(ch);
13386 state.stringStartCol = stream.column();
13387 return state.tokenize(stream, state);
13388 } else {
13389 stream.match(/^[^\s\u00a0=<>\"\']*[^\s\u00a0=<>\"\'\/]/);
13390 return "word";
13391 }
13392 }
13393
13394 function inAttribute(quote) {
13395 var closure = function(stream, state) {
13396 while (!stream.eol()) {
13397 if (stream.next() == quote) {
13398 state.tokenize = inTag;
13399 break;
13400 }
13401 }
13402 return "string";
13403 };
13404 closure.isInAttribute = true;
13405 return closure;
13406 }
13407
13408 function inBlock(style, terminator) {
13409 return function(stream, state) {
13410 while (!stream.eol()) {
13411 if (stream.match(terminator)) {
13412 state.tokenize = inText;
13413 break;
13414 }
13415 stream.next();
13416 }
13417 return style;
13418 }
13419 }
13420
13421 function doctype(depth) {
13422 return function(stream, state) {
13423 var ch;
13424 while ((ch = stream.next()) != null) {
13425 if (ch == "<") {
13426 state.tokenize = doctype(depth + 1);
13427 return state.tokenize(stream, state);
13428 } else if (ch == ">") {
13429 if (depth == 1) {
13430 state.tokenize = inText;
13431 break;
13432 } else {
13433 state.tokenize = doctype(depth - 1);
13434 return state.tokenize(stream, state);
13435 }
13436 }
13437 }
13438 return "meta";
13439 };
13440 }
13441
13442 function Context(state, tagName, startOfLine) {
13443 this.prev = state.context;
13444 this.tagName = tagName;
13445 this.indent = state.indented;
13446 this.startOfLine = startOfLine;
13447 if (config.doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.noIndent))
13448 this.noIndent = true;
13449 }
13450 function popContext(state) {
13451 if (state.context) state.context = state.context.prev;
13452 }
13453 function maybePopContext(state, nextTagName) {
13454 var parentTagName;
13455 while (true) {
13456 if (!state.context) {
13457 return;
13458 }
13459 parentTagName = state.context.tagName;
13460 if (!config.contextGrabbers.hasOwnProperty(parentTagName) ||
13461 !config.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
13462 return;
13463 }
13464 popContext(state);
13465 }
13466 }
13467
13468 function baseState(type, stream, state) {
13469 if (type == "openTag") {
13470 state.tagStart = stream.column();
13471 return tagNameState;
13472 } else if (type == "closeTag") {
13473 return closeTagNameState;
13474 } else {
13475 return baseState;
13476 }
13477 }
13478 function tagNameState(type, stream, state) {
13479 if (type == "word") {
13480 state.tagName = stream.current();
13481 setStyle = "tag";
13482 return attrState;
13483 } else if (config.allowMissingTagName && type == "endTag") {
13484 setStyle = "tag bracket";
13485 return attrState(type, stream, state);
13486 } else {
13487 setStyle = "error";
13488 return tagNameState;
13489 }
13490 }
13491 function closeTagNameState(type, stream, state) {
13492 if (type == "word") {
13493 var tagName = stream.current();
13494 if (state.context && state.context.tagName != tagName &&
13495 config.implicitlyClosed.hasOwnProperty(state.context.tagName))
13496 popContext(state);
13497 if ((state.context && state.context.tagName == tagName) || config.matchClosing === false) {
13498 setStyle = "tag";
13499 return closeState;
13500 } else {
13501 setStyle = "tag error";
13502 return closeStateErr;
13503 }
13504 } else if (config.allowMissingTagName && type == "endTag") {
13505 setStyle = "tag bracket";
13506 return closeState(type, stream, state);
13507 } else {
13508 setStyle = "error";
13509 return closeStateErr;
13510 }
13511 }
13512
13513 function closeState(type, _stream, state) {
13514 if (type != "endTag") {
13515 setStyle = "error";
13516 return closeState;
13517 }
13518 popContext(state);
13519 return baseState;
13520 }
13521 function closeStateErr(type, stream, state) {
13522 setStyle = "error";
13523 return closeState(type, stream, state);
13524 }
13525
13526 function attrState(type, _stream, state) {
13527 if (type == "word") {
13528 setStyle = "attribute";
13529 return attrEqState;
13530 } else if (type == "endTag" || type == "selfcloseTag") {
13531 var tagName = state.tagName, tagStart = state.tagStart;
13532 state.tagName = state.tagStart = null;
13533 if (type == "selfcloseTag" ||
13534 config.autoSelfClosers.hasOwnProperty(tagName)) {
13535 maybePopContext(state, tagName);
13536 } else {
13537 maybePopContext(state, tagName);
13538 state.context = new Context(state, tagName, tagStart == state.indented);
13539 }
13540 return baseState;
13541 }
13542 setStyle = "error";
13543 return attrState;
13544 }
13545 function attrEqState(type, stream, state) {
13546 if (type == "equals") return attrValueState;
13547 if (!config.allowMissing) setStyle = "error";
13548 return attrState(type, stream, state);
13549 }
13550 function attrValueState(type, stream, state) {
13551 if (type == "string") return attrContinuedState;
13552 if (type == "word" && config.allowUnquoted) {setStyle = "string"; return attrState;}
13553 setStyle = "error";
13554 return attrState(type, stream, state);
13555 }
13556 function attrContinuedState(type, stream, state) {
13557 if (type == "string") return attrContinuedState;
13558 return attrState(type, stream, state);
13559 }
13560
13561 return {
13562 startState: function(baseIndent) {
13563 var state = {tokenize: inText,
13564 state: baseState,
13565 indented: baseIndent || 0,
13566 tagName: null, tagStart: null,
13567 context: null}
13568 if (baseIndent != null) state.baseIndent = baseIndent
13569 return state
13570 },
13571
13572 token: function(stream, state) {
13573 if (!state.tagName && stream.sol())
13574 state.indented = stream.indentation();
13575
13576 if (stream.eatSpace()) return null;
13577 type = null;
13578 var style = state.tokenize(stream, state);
13579 if ((style || type) && style != "comment") {
13580 setStyle = null;
13581 state.state = state.state(type || style, stream, state);
13582 if (setStyle)
13583 style = setStyle == "error" ? style + " error" : setStyle;
13584 }
13585 return style;
13586 },
13587
13588 indent: function(state, textAfter, fullLine) {
13589 var context = state.context;
13590 // Indent multi-line strings (e.g. css).
13591 if (state.tokenize.isInAttribute) {
13592 if (state.tagStart == state.indented)
13593 return state.stringStartCol + 1;
13594 else
13595 return state.indented + indentUnit;
13596 }
13597 if (context && context.noIndent) return CodeMirror.Pass;
13598 if (state.tokenize != inTag && state.tokenize != inText)
13599 return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
13600 // Indent the starts of attribute names.
13601 if (state.tagName) {
13602 if (config.multilineTagIndentPastTag !== false)
13603 return state.tagStart + state.tagName.length + 2;
13604 else
13605 return state.tagStart + indentUnit * (config.multilineTagIndentFactor || 1);
13606 }
13607 if (config.alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
13608 var tagAfter = textAfter && /^<(\/)?([\w_:\.-]*)/.exec(textAfter);
13609 if (tagAfter && tagAfter[1]) { // Closing tag spotted
13610 while (context) {
13611 if (context.tagName == tagAfter[2]) {
13612 context = context.prev;
13613 break;
13614 } else if (config.implicitlyClosed.hasOwnProperty(context.tagName)) {
13615 context = context.prev;
13616 } else {
13617 break;
13618 }
13619 }
13620 } else if (tagAfter) { // Opening tag spotted
13621 while (context) {
13622 var grabbers = config.contextGrabbers[context.tagName];
13623 if (grabbers && grabbers.hasOwnProperty(tagAfter[2]))
13624 context = context.prev;
13625 else
13626 break;
13627 }
13628 }
13629 while (context && context.prev && !context.startOfLine)
13630 context = context.prev;
13631 if (context) return context.indent + indentUnit;
13632 else return state.baseIndent || 0;
13633 },
13634
13635 electricInput: /<\/[\s\w:]+>$/,
13636 blockCommentStart: "<!--",
13637 blockCommentEnd: "-->",
13638
13639 configuration: config.htmlMode ? "html" : "xml",
13640 helperType: config.htmlMode ? "html" : "xml",
13641
13642 skipAttribute: function(state) {
13643 if (state.state == attrValueState)
13644 state.state = attrState
13645 }
13646 };
13647 });
13648
13649 CodeMirror.defineMIME("text/xml", "xml");
13650 CodeMirror.defineMIME("application/xml", "xml");
13651 if (!CodeMirror.mimeModes.hasOwnProperty("text/html"))
13652 CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
13653
13654 });
13655
13656 },{"../../lib/codemirror":14}],19:[function(require,module,exports){
13657 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;
13658
13659
13660 },{}],20:[function(require,module,exports){
13661 !function() {
13662 'use strict';
13663 function h(nodeName, attributes) {
13664 var lastSimple, child, simple, i, children = EMPTY_CHILDREN;
13665 for (i = arguments.length; i-- > 2; ) stack.push(arguments[i]);
13666 if (attributes && null != attributes.children) {
13667 if (!stack.length) stack.push(attributes.children);
13668 delete attributes.children;
13669 }
13670 while (stack.length) if ((child = stack.pop()) && void 0 !== child.pop) for (i = child.length; i--; ) stack.push(child[i]); else {
13671 if ('boolean' == typeof child) child = null;
13672 if (simple = 'function' != typeof nodeName) if (null == child) child = ''; else if ('number' == typeof child) child = String(child); else if ('string' != typeof child) simple = !1;
13673 if (simple && lastSimple) children[children.length - 1] += child; else if (children === EMPTY_CHILDREN) children = [ child ]; else children.push(child);
13674 lastSimple = simple;
13675 }
13676 var p = new VNode();
13677 p.nodeName = nodeName;
13678 p.children = children;
13679 p.attributes = null == attributes ? void 0 : attributes;
13680 p.key = null == attributes ? void 0 : attributes.key;
13681 if (void 0 !== options.vnode) options.vnode(p);
13682 return p;
13683 }
13684 function extend(obj, props) {
13685 for (var i in props) obj[i] = props[i];
13686 return obj;
13687 }
13688 function applyRef(ref, value) {
13689 if (null != ref) if ('function' == typeof ref) ref(value); else ref.current = value;
13690 }
13691 function cloneElement(vnode, props) {
13692 return h(vnode.nodeName, extend(extend({}, vnode.attributes), props), arguments.length > 2 ? [].slice.call(arguments, 2) : vnode.children);
13693 }
13694 function enqueueRender(component) {
13695 if (!component.__d && (component.__d = !0) && 1 == items.push(component)) (options.debounceRendering || defer)(rerender);
13696 }
13697 function rerender() {
13698 var p;
13699 while (p = items.pop()) if (p.__d) renderComponent(p);
13700 }
13701 function isSameNodeType(node, vnode, hydrating) {
13702 if ('string' == typeof vnode || 'number' == typeof vnode) return void 0 !== node.splitText;
13703 if ('string' == typeof vnode.nodeName) return !node._componentConstructor && isNamedNode(node, vnode.nodeName); else return hydrating || node._componentConstructor === vnode.nodeName;
13704 }
13705 function isNamedNode(node, nodeName) {
13706 return node.__n === nodeName || node.nodeName.toLowerCase() === nodeName.toLowerCase();
13707 }
13708 function getNodeProps(vnode) {
13709 var props = extend({}, vnode.attributes);
13710 props.children = vnode.children;
13711 var defaultProps = vnode.nodeName.defaultProps;
13712 if (void 0 !== defaultProps) for (var i in defaultProps) if (void 0 === props[i]) props[i] = defaultProps[i];
13713 return props;
13714 }
13715 function createNode(nodeName, isSvg) {
13716 var node = isSvg ? document.createElementNS('http://www.w3.org/2000/svg', nodeName) : document.createElement(nodeName);
13717 node.__n = nodeName;
13718 return node;
13719 }
13720 function removeNode(node) {
13721 var parentNode = node.parentNode;
13722 if (parentNode) parentNode.removeChild(node);
13723 }
13724 function setAccessor(node, name, old, value, isSvg) {
13725 if ('className' === name) name = 'class';
13726 if ('key' === name) ; else if ('ref' === name) {
13727 applyRef(old, null);
13728 applyRef(value, node);
13729 } else if ('class' === name && !isSvg) node.className = value || ''; else if ('style' === name) {
13730 if (!value || 'string' == typeof value || 'string' == typeof old) node.style.cssText = value || '';
13731 if (value && 'object' == typeof value) {
13732 if ('string' != typeof old) for (var i in old) if (!(i in value)) node.style[i] = '';
13733 for (var i in value) node.style[i] = 'number' == typeof value[i] && !1 === IS_NON_DIMENSIONAL.test(i) ? value[i] + 'px' : value[i];
13734 }
13735 } else if ('dangerouslySetInnerHTML' === name) {
13736 if (value) node.innerHTML = value.__html || '';
13737 } else if ('o' == name[0] && 'n' == name[1]) {
13738 var useCapture = name !== (name = name.replace(/Capture$/, ''));
13739 name = name.toLowerCase().substring(2);
13740 if (value) {
13741 if (!old) node.addEventListener(name, eventProxy, useCapture);
13742 } else node.removeEventListener(name, eventProxy, useCapture);
13743 (node.__l || (node.__l = {}))[name] = value;
13744 } else if ('list' !== name && 'type' !== name && !isSvg && name in node) {
13745 try {
13746 node[name] = null == value ? '' : value;
13747 } catch (e) {}
13748 if ((null == value || !1 === value) && 'spellcheck' != name) node.removeAttribute(name);
13749 } else {
13750 var ns = isSvg && name !== (name = name.replace(/^xlink:?/, ''));
13751 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);
13752 }
13753 }
13754 function eventProxy(e) {
13755 return this.__l[e.type](options.event && options.event(e) || e);
13756 }
13757 function flushMounts() {
13758 var c;
13759 while (c = mounts.shift()) {
13760 if (options.afterMount) options.afterMount(c);
13761 if (c.componentDidMount) c.componentDidMount();
13762 }
13763 }
13764 function diff(dom, vnode, context, mountAll, parent, componentRoot) {
13765 if (!diffLevel++) {
13766 isSvgMode = null != parent && void 0 !== parent.ownerSVGElement;
13767 hydrating = null != dom && !('__preactattr_' in dom);
13768 }
13769 var ret = idiff(dom, vnode, context, mountAll, componentRoot);
13770 if (parent && ret.parentNode !== parent) parent.appendChild(ret);
13771 if (!--diffLevel) {
13772 hydrating = !1;
13773 if (!componentRoot) flushMounts();
13774 }
13775 return ret;
13776 }
13777 function idiff(dom, vnode, context, mountAll, componentRoot) {
13778 var out = dom, prevSvgMode = isSvgMode;
13779 if (null == vnode || 'boolean' == typeof vnode) vnode = '';
13780 if ('string' == typeof vnode || 'number' == typeof vnode) {
13781 if (dom && void 0 !== dom.splitText && dom.parentNode && (!dom._component || componentRoot)) {
13782 if (dom.nodeValue != vnode) dom.nodeValue = vnode;
13783 } else {
13784 out = document.createTextNode(vnode);
13785 if (dom) {
13786 if (dom.parentNode) dom.parentNode.replaceChild(out, dom);
13787 recollectNodeTree(dom, !0);
13788 }
13789 }
13790 out.__preactattr_ = !0;
13791 return out;
13792 }
13793 var vnodeName = vnode.nodeName;
13794 if ('function' == typeof vnodeName) return buildComponentFromVNode(dom, vnode, context, mountAll);
13795 isSvgMode = 'svg' === vnodeName ? !0 : 'foreignObject' === vnodeName ? !1 : isSvgMode;
13796 vnodeName = String(vnodeName);
13797 if (!dom || !isNamedNode(dom, vnodeName)) {
13798 out = createNode(vnodeName, isSvgMode);
13799 if (dom) {
13800 while (dom.firstChild) out.appendChild(dom.firstChild);
13801 if (dom.parentNode) dom.parentNode.replaceChild(out, dom);
13802 recollectNodeTree(dom, !0);
13803 }
13804 }
13805 var fc = out.firstChild, props = out.__preactattr_, vchildren = vnode.children;
13806 if (null == props) {
13807 props = out.__preactattr_ = {};
13808 for (var a = out.attributes, i = a.length; i--; ) props[a[i].name] = a[i].value;
13809 }
13810 if (!hydrating && vchildren && 1 === vchildren.length && 'string' == typeof vchildren[0] && null != fc && void 0 !== fc.splitText && null == fc.nextSibling) {
13811 if (fc.nodeValue != vchildren[0]) fc.nodeValue = vchildren[0];
13812 } else if (vchildren && vchildren.length || null != fc) innerDiffNode(out, vchildren, context, mountAll, hydrating || null != props.dangerouslySetInnerHTML);
13813 diffAttributes(out, vnode.attributes, props);
13814 isSvgMode = prevSvgMode;
13815 return out;
13816 }
13817 function innerDiffNode(dom, vchildren, context, mountAll, isHydrating) {
13818 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;
13819 if (0 !== len) for (var i = 0; i < len; i++) {
13820 var _child = originalChildren[i], props = _child.__preactattr_, key = vlen && props ? _child._component ? _child._component.__k : props.key : null;
13821 if (null != key) {
13822 keyedLen++;
13823 keyed[key] = _child;
13824 } else if (props || (void 0 !== _child.splitText ? isHydrating ? _child.nodeValue.trim() : !0 : isHydrating)) children[childrenLen++] = _child;
13825 }
13826 if (0 !== vlen) for (var i = 0; i < vlen; i++) {
13827 vchild = vchildren[i];
13828 child = null;
13829 var key = vchild.key;
13830 if (null != key) {
13831 if (keyedLen && void 0 !== keyed[key]) {
13832 child = keyed[key];
13833 keyed[key] = void 0;
13834 keyedLen--;
13835 }
13836 } else if (min < childrenLen) for (j = min; j < childrenLen; j++) if (void 0 !== children[j] && isSameNodeType(c = children[j], vchild, isHydrating)) {
13837 child = c;
13838 children[j] = void 0;
13839 if (j === childrenLen - 1) childrenLen--;
13840 if (j === min) min++;
13841 break;
13842 }
13843 child = idiff(child, vchild, context, mountAll);
13844 f = originalChildren[i];
13845 if (child && child !== dom && child !== f) if (null == f) dom.appendChild(child); else if (child === f.nextSibling) removeNode(f); else dom.insertBefore(child, f);
13846 }
13847 if (keyedLen) for (var i in keyed) if (void 0 !== keyed[i]) recollectNodeTree(keyed[i], !1);
13848 while (min <= childrenLen) if (void 0 !== (child = children[childrenLen--])) recollectNodeTree(child, !1);
13849 }
13850 function recollectNodeTree(node, unmountOnly) {
13851 var component = node._component;
13852 if (component) unmountComponent(component); else {
13853 if (null != node.__preactattr_) applyRef(node.__preactattr_.ref, null);
13854 if (!1 === unmountOnly || null == node.__preactattr_) removeNode(node);
13855 removeChildren(node);
13856 }
13857 }
13858 function removeChildren(node) {
13859 node = node.lastChild;
13860 while (node) {
13861 var next = node.previousSibling;
13862 recollectNodeTree(node, !0);
13863 node = next;
13864 }
13865 }
13866 function diffAttributes(dom, attrs, old) {
13867 var name;
13868 for (name in old) if ((!attrs || null == attrs[name]) && null != old[name]) setAccessor(dom, name, old[name], old[name] = void 0, isSvgMode);
13869 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);
13870 }
13871 function createComponent(Ctor, props, context) {
13872 var inst, i = recyclerComponents.length;
13873 if (Ctor.prototype && Ctor.prototype.render) {
13874 inst = new Ctor(props, context);
13875 Component.call(inst, props, context);
13876 } else {
13877 inst = new Component(props, context);
13878 inst.constructor = Ctor;
13879 inst.render = doRender;
13880 }
13881 while (i--) if (recyclerComponents[i].constructor === Ctor) {
13882 inst.__b = recyclerComponents[i].__b;
13883 recyclerComponents.splice(i, 1);
13884 return inst;
13885 }
13886 return inst;
13887 }
13888 function doRender(props, state, context) {
13889 return this.constructor(props, context);
13890 }
13891 function setComponentProps(component, props, renderMode, context, mountAll) {
13892 if (!component.__x) {
13893 component.__x = !0;
13894 component.__r = props.ref;
13895 component.__k = props.key;
13896 delete props.ref;
13897 delete props.key;
13898 if (void 0 === component.constructor.getDerivedStateFromProps) if (!component.base || mountAll) {
13899 if (component.componentWillMount) component.componentWillMount();
13900 } else if (component.componentWillReceiveProps) component.componentWillReceiveProps(props, context);
13901 if (context && context !== component.context) {
13902 if (!component.__c) component.__c = component.context;
13903 component.context = context;
13904 }
13905 if (!component.__p) component.__p = component.props;
13906 component.props = props;
13907 component.__x = !1;
13908 if (0 !== renderMode) if (1 === renderMode || !1 !== options.syncComponentUpdates || !component.base) renderComponent(component, 1, mountAll); else enqueueRender(component);
13909 applyRef(component.__r, component);
13910 }
13911 }
13912 function renderComponent(component, renderMode, mountAll, isChild) {
13913 if (!component.__x) {
13914 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;
13915 if (component.constructor.getDerivedStateFromProps) {
13916 state = extend(extend({}, state), component.constructor.getDerivedStateFromProps(props, state));
13917 component.state = state;
13918 }
13919 if (isUpdate) {
13920 component.props = previousProps;
13921 component.state = previousState;
13922 component.context = previousContext;
13923 if (2 !== renderMode && component.shouldComponentUpdate && !1 === component.shouldComponentUpdate(props, state, context)) skip = !0; else if (component.componentWillUpdate) component.componentWillUpdate(props, state, context);
13924 component.props = props;
13925 component.state = state;
13926 component.context = context;
13927 }
13928 component.__p = component.__s = component.__c = component.__b = null;
13929 component.__d = !1;
13930 if (!skip) {
13931 rendered = component.render(props, state, context);
13932 if (component.getChildContext) context = extend(extend({}, context), component.getChildContext());
13933 if (isUpdate && component.getSnapshotBeforeUpdate) snapshot = component.getSnapshotBeforeUpdate(previousProps, previousState);
13934 var toUnmount, base, childComponent = rendered && rendered.nodeName;
13935 if ('function' == typeof childComponent) {
13936 var childProps = getNodeProps(rendered);
13937 inst = initialChildComponent;
13938 if (inst && inst.constructor === childComponent && childProps.key == inst.__k) setComponentProps(inst, childProps, 1, context, !1); else {
13939 toUnmount = inst;
13940 component._component = inst = createComponent(childComponent, childProps, context);
13941 inst.__b = inst.__b || nextBase;
13942 inst.__u = component;
13943 setComponentProps(inst, childProps, 0, context, !1);
13944 renderComponent(inst, 1, mountAll, !0);
13945 }
13946 base = inst.base;
13947 } else {
13948 cbase = initialBase;
13949 toUnmount = initialChildComponent;
13950 if (toUnmount) cbase = component._component = null;
13951 if (initialBase || 1 === renderMode) {
13952 if (cbase) cbase._component = null;
13953 base = diff(cbase, rendered, context, mountAll || !isUpdate, initialBase && initialBase.parentNode, !0);
13954 }
13955 }
13956 if (initialBase && base !== initialBase && inst !== initialChildComponent) {
13957 var baseParent = initialBase.parentNode;
13958 if (baseParent && base !== baseParent) {
13959 baseParent.replaceChild(base, initialBase);
13960 if (!toUnmount) {
13961 initialBase._component = null;
13962 recollectNodeTree(initialBase, !1);
13963 }
13964 }
13965 }
13966 if (toUnmount) unmountComponent(toUnmount);
13967 component.base = base;
13968 if (base && !isChild) {
13969 var componentRef = component, t = component;
13970 while (t = t.__u) (componentRef = t).base = base;
13971 base._component = componentRef;
13972 base._componentConstructor = componentRef.constructor;
13973 }
13974 }
13975 if (!isUpdate || mountAll) mounts.push(component); else if (!skip) {
13976 if (component.componentDidUpdate) component.componentDidUpdate(previousProps, previousState, snapshot);
13977 if (options.afterUpdate) options.afterUpdate(component);
13978 }
13979 while (component.__h.length) component.__h.pop().call(component);
13980 if (!diffLevel && !isChild) flushMounts();
13981 }
13982 }
13983 function buildComponentFromVNode(dom, vnode, context, mountAll) {
13984 var c = dom && dom._component, originalComponent = c, oldDom = dom, isDirectOwner = c && dom._componentConstructor === vnode.nodeName, isOwner = isDirectOwner, props = getNodeProps(vnode);
13985 while (c && !isOwner && (c = c.__u)) isOwner = c.constructor === vnode.nodeName;
13986 if (c && isOwner && (!mountAll || c._component)) {
13987 setComponentProps(c, props, 3, context, mountAll);
13988 dom = c.base;
13989 } else {
13990 if (originalComponent && !isDirectOwner) {
13991 unmountComponent(originalComponent);
13992 dom = oldDom = null;
13993 }
13994 c = createComponent(vnode.nodeName, props, context);
13995 if (dom && !c.__b) {
13996 c.__b = dom;
13997 oldDom = null;
13998 }
13999 setComponentProps(c, props, 1, context, mountAll);
14000 dom = c.base;
14001 if (oldDom && dom !== oldDom) {
14002 oldDom._component = null;
14003 recollectNodeTree(oldDom, !1);
14004 }
14005 }
14006 return dom;
14007 }
14008 function unmountComponent(component) {
14009 if (options.beforeUnmount) options.beforeUnmount(component);
14010 var base = component.base;
14011 component.__x = !0;
14012 if (component.componentWillUnmount) component.componentWillUnmount();
14013 component.base = null;
14014 var inner = component._component;
14015 if (inner) unmountComponent(inner); else if (base) {
14016 if (null != base.__preactattr_) applyRef(base.__preactattr_.ref, null);
14017 component.__b = base;
14018 removeNode(base);
14019 recyclerComponents.push(component);
14020 removeChildren(base);
14021 }
14022 applyRef(component.__r, null);
14023 }
14024 function Component(props, context) {
14025 this.__d = !0;
14026 this.context = context;
14027 this.props = props;
14028 this.state = this.state || {};
14029 this.__h = [];
14030 }
14031 function render(vnode, parent, merge) {
14032 return diff(merge, vnode, {}, !1, parent, !1);
14033 }
14034 function createRef() {
14035 return {};
14036 }
14037 var VNode = function() {};
14038 var options = {};
14039 var stack = [];
14040 var EMPTY_CHILDREN = [];
14041 var defer = 'function' == typeof Promise ? Promise.resolve().then.bind(Promise.resolve()) : setTimeout;
14042 var IS_NON_DIMENSIONAL = /acit|ex(?:s|g|n|p|$)|rph|ows|mnc|ntw|ine[ch]|zoo|^ord/i;
14043 var items = [];
14044 var mounts = [];
14045 var diffLevel = 0;
14046 var isSvgMode = !1;
14047 var hydrating = !1;
14048 var recyclerComponents = [];
14049 extend(Component.prototype, {
14050 setState: function(state, callback) {
14051 if (!this.__s) this.__s = this.state;
14052 this.state = extend(extend({}, this.state), 'function' == typeof state ? state(this.state, this.props) : state);
14053 if (callback) this.__h.push(callback);
14054 enqueueRender(this);
14055 },
14056 forceUpdate: function(callback) {
14057 if (callback) this.__h.push(callback);
14058 renderComponent(this, 2);
14059 },
14060 render: function() {}
14061 });
14062 var preact = {
14063 h: h,
14064 createElement: h,
14065 cloneElement: cloneElement,
14066 createRef: createRef,
14067 Component: Component,
14068 render: render,
14069 rerender: rerender,
14070 options: options
14071 };
14072 if ('undefined' != typeof module) module.exports = preact; else self.preact = preact;
14073 }();
14074
14075 },{}],21:[function(require,module,exports){
14076 function tlite(getTooltipOpts) {
14077 document.addEventListener('mouseover', function (e) {
14078 var el = e.target;
14079 var opts = getTooltipOpts(el);
14080
14081 if (!opts) {
14082 el = el.parentElement;
14083 opts = el && getTooltipOpts(el);
14084 }
14085
14086 opts && tlite.show(el, opts, true);
14087 });
14088 }
14089
14090 tlite.show = function (el, opts, isAuto) {
14091 var fallbackAttrib = 'data-tlite';
14092 opts = opts || {};
14093
14094 (el.tooltip || Tooltip(el, opts)).show();
14095
14096 function Tooltip(el, opts) {
14097 var tooltipEl;
14098 var showTimer;
14099 var text;
14100
14101 el.addEventListener('mousedown', autoHide);
14102 el.addEventListener('mouseleave', autoHide);
14103
14104 function show() {
14105 text = el.title || el.getAttribute(fallbackAttrib) || text;
14106 el.title = '';
14107 el.setAttribute(fallbackAttrib, '');
14108 text && !showTimer && (showTimer = setTimeout(fadeIn, isAuto ? 150 : 1))
14109 }
14110
14111 function autoHide() {
14112 tlite.hide(el, true);
14113 }
14114
14115 function hide(isAutoHiding) {
14116 if (isAuto === isAutoHiding) {
14117 showTimer = clearTimeout(showTimer);
14118 var parent = tooltipEl && tooltipEl.parentNode;
14119 parent && parent.removeChild(tooltipEl);
14120 tooltipEl = undefined;
14121 }
14122 }
14123
14124 function fadeIn() {
14125 if (!tooltipEl) {
14126 tooltipEl = createTooltip(el, text, opts);
14127 }
14128 }
14129
14130 return el.tooltip = {
14131 show: show,
14132 hide: hide
14133 };
14134 }
14135
14136 function createTooltip(el, text, opts) {
14137 var tooltipEl = document.createElement('span');
14138 var grav = opts.grav || el.getAttribute('data-tlite') || 'n';
14139
14140 tooltipEl.innerHTML = text;
14141
14142 el.appendChild(tooltipEl);
14143
14144 var vertGrav = grav[0] || '';
14145 var horzGrav = grav[1] || '';
14146
14147 function positionTooltip() {
14148 tooltipEl.className = 'tlite ' + 'tlite-' + vertGrav + horzGrav;
14149
14150 var arrowSize = 10;
14151 var top = el.offsetTop;
14152 var left = el.offsetLeft;
14153
14154 if (tooltipEl.offsetParent === el) {
14155 top = left = 0;
14156 }
14157
14158 var width = el.offsetWidth;
14159 var height = el.offsetHeight;
14160 var tooltipHeight = tooltipEl.offsetHeight;
14161 var tooltipWidth = tooltipEl.offsetWidth;
14162 var centerEl = left + (width / 2);
14163
14164 tooltipEl.style.top = (
14165 vertGrav === 's' ? (top - tooltipHeight - arrowSize) :
14166 vertGrav === 'n' ? (top + height + arrowSize) :
14167 (top + (height / 2) - (tooltipHeight / 2))
14168 ) + 'px';
14169
14170 tooltipEl.style.left = (
14171 horzGrav === 'w' ? left :
14172 horzGrav === 'e' ? left + width - tooltipWidth :
14173 vertGrav === 'w' ? (left + width + arrowSize) :
14174 vertGrav === 'e' ? (left - tooltipWidth - arrowSize) :
14175 (centerEl - tooltipWidth / 2)
14176 ) + 'px';
14177 }
14178
14179 positionTooltip();
14180
14181 var rect = tooltipEl.getBoundingClientRect();
14182
14183 if (vertGrav === 's' && rect.top < 0) {
14184 vertGrav = 'n';
14185 positionTooltip();
14186 } else if (vertGrav === 'n' && rect.bottom > window.innerHeight) {
14187 vertGrav = 's';
14188 positionTooltip();
14189 } else if (vertGrav === 'e' && rect.left < 0) {
14190 vertGrav = 'w';
14191 positionTooltip();
14192 } else if (vertGrav === 'w' && rect.right > window.innerWidth) {
14193 vertGrav = 'e';
14194 positionTooltip();
14195 }
14196
14197 tooltipEl.className += ' tlite-visible';
14198
14199 return tooltipEl;
14200 }
14201 };
14202
14203 tlite.hide = function (el, isAuto) {
14204 el.tooltip && el.tooltip.hide(isAuto);
14205 };
14206
14207 if (typeof module !== 'undefined' && module.exports) {
14208 module.exports = tlite;
14209 }
14210
14211 },{}]},{},[2]);
14212 ; })();